From 3062cd2a72a33bd7e3a31bfab713a29cbc5d3737 Mon Sep 17 00:00:00 2001 From: Torsten Raudssus Date: Sat, 17 Jul 2010 04:43:39 +0200 Subject: [PATCH] First version of package, comments welcome --- Makefile.PL | 41 + ape-server/AUTHORS | 8 + ape-server/COPYING | 340 + ape-server/Makefile | 26 + ape-server/README | 2 + ape-server/bin/ape.conf | 41 + ape-server/build.sh | 36 + ape-server/deps/js/src/Makefile.in | 667 + ape-server/deps/js/src/Makefile.ref | 470 + ape-server/deps/js/src/README.html | 54 + ape-server/deps/js/src/SpiderMonkey.rsp | 3 + ape-server/deps/js/src/Y.js | 19 + ape-server/deps/js/src/aclocal.m4 | 13 + .../deps/js/src/autom4te.cache/output.0t | 0 .../deps/js/src/autom4te.cache/requests | 0 .../deps/js/src/autom4te.cache/traces.0t | 0 ape-server/deps/js/src/bench.sh | 5 + .../js/src/build/autoconf/acoutput-fast.pl | 202 + .../deps/js/src/build/autoconf/acwinpaths.m4 | 65 + .../deps/js/src/build/autoconf/altoptions.m4 | 154 + .../deps/js/src/build/autoconf/config.guess | 1537 ++ .../deps/js/src/build/autoconf/config.sub | 1699 ++ .../deps/js/src/build/autoconf/install-sh | 119 + .../deps/js/src/build/autoconf/make-makefile | 322 + .../deps/js/src/build/autoconf/match-dir.sh | 101 + .../js/src/build/autoconf/moznbytetype.m4 | 136 + .../deps/js/src/build/autoconf/mozprog.m4 | 76 + ape-server/deps/js/src/build/autoconf/nspr.m4 | 101 + ape-server/deps/js/src/build/autoconf/pkg.m4 | 59 + ape-server/deps/js/src/build/cygwin-wrapper | 75 + ape-server/deps/js/src/build/hcc | 111 + ape-server/deps/js/src/build/hcpp | 155 + .../deps/js/src/build/msys-perl-wrapper | 16 + ape-server/deps/js/src/build/unix/mddepend.pl | 171 + ape-server/deps/js/src/build/unix/uniq.pl | 63 + .../deps/js/src/build/win32/pgomerge.py | 40 + ape-server/deps/js/src/builtins.tbl | 91 + ape-server/deps/js/src/config.mk | 193 + ape-server/deps/js/src/config/Makefile | 109 + ape-server/deps/js/src/config/Makefile.in | 109 + .../deps/js/src/config/Moz/Milestone.pm | 232 + ape-server/deps/js/src/config/autoconf.mk.in | 347 + .../deps/js/src/config/check-sync-dirs.py | 103 + .../deps/js/src/config/check-sync-exceptions | 7 + ape-server/deps/js/src/config/config.mk | 837 + ape-server/deps/js/src/config/fastcwd.pl | 66 + ape-server/deps/js/src/config/gcc_hidden.h | 2 + ape-server/deps/js/src/config/insure.mk | 53 + .../js/src/config/make-system-wrappers.pl | 59 + ape-server/deps/js/src/config/milestone.pl | 112 + ape-server/deps/js/src/config/milestone.txt | 13 + .../deps/js/src/config/mkdepend/Makefile | 83 + .../deps/js/src/config/mkdepend/Makefile.in | 83 + .../deps/js/src/config/mkdepend/cppsetup.c | 233 + ape-server/deps/js/src/config/mkdepend/def.h | 184 + .../deps/js/src/config/mkdepend/ifparser.c | 551 + .../deps/js/src/config/mkdepend/ifparser.h | 83 + .../deps/js/src/config/mkdepend/imakemdep.h | 733 + .../deps/js/src/config/mkdepend/include.c | 337 + ape-server/deps/js/src/config/mkdepend/main.c | 860 + .../deps/js/src/config/mkdepend/mkdepend.man | 382 + .../deps/js/src/config/mkdepend/parse.c | 686 + ape-server/deps/js/src/config/mkdepend/pr.c | 124 + ape-server/deps/js/src/config/myconfig.mk | 0 ape-server/deps/js/src/config/myrules.mk | 0 ape-server/deps/js/src/config/nfspwd.pl | 50 + ape-server/deps/js/src/config/nsinstall.c | 481 + .../deps/js/src/config/nsinstall.exe.manifest | 17 + ape-server/deps/js/src/config/nsinstall.py | 155 + ape-server/deps/js/src/config/nsinstall_win.c | 711 + ape-server/deps/js/src/config/pathsub.c | 247 + ape-server/deps/js/src/config/pathsub.h | 74 + ape-server/deps/js/src/config/preprocessor.pl | 671 + ape-server/deps/js/src/config/rules.mk | 2287 +++ .../deps/js/src/config/solaris_ia32.map | 1 + .../js/src/config/static-checking-config.mk | 21 + .../deps/js/src/config/static-checking.js | 108 + .../deps/js/src/config/string-format.js | 61 + ape-server/deps/js/src/config/system-headers | 1020 ++ ape-server/deps/js/src/config/version.mk | 85 + ape-server/deps/js/src/config/version_win.pl | 436 + ape-server/deps/js/src/configure | 14016 +++++++++++++++ ape-server/deps/js/src/configure.in | 5223 ++++++ ape-server/deps/js/src/dtoa.c | 3335 ++++ ape-server/deps/js/src/editline/Makefile | 55 + ape-server/deps/js/src/editline/Makefile.in | 55 + ape-server/deps/js/src/editline/Makefile.ref | 143 + ape-server/deps/js/src/editline/README | 83 + ape-server/deps/js/src/editline/editline.3 | 175 + ape-server/deps/js/src/editline/editline.c | 1371 ++ ape-server/deps/js/src/editline/editline.h | 135 + ape-server/deps/js/src/editline/sysunix.c | 182 + ape-server/deps/js/src/editline/unix.h | 82 + ape-server/deps/js/src/find-child.py | 65 + ape-server/deps/js/src/imacro_asm.js.in | 404 + ape-server/deps/js/src/imacros.c.out | 987 + ape-server/deps/js/src/imacros.jsasm | 728 + ape-server/deps/js/src/javascript-trace.d | 73 + ape-server/deps/js/src/jitstats.tbl | 97 + ape-server/deps/js/src/js-confdefs.h | 97 + ape-server/deps/js/src/js-config | 111 + ape-server/deps/js/src/js-config.h | 85 + ape-server/deps/js/src/js-config.h.in | 84 + ape-server/deps/js/src/js-config.in | 111 + ape-server/deps/js/src/js.mdp | Bin 0 -> 17922 bytes ape-server/deps/js/src/js.msg | 319 + ape-server/deps/js/src/jsapi.cpp | 5929 +++++++ ape-server/deps/js/src/jsapi.h | 2833 +++ ape-server/deps/js/src/jsarena.cpp | 450 + ape-server/deps/js/src/jsarena.h | 289 + ape-server/deps/js/src/jsarray.cpp | 3627 ++++ ape-server/deps/js/src/jsarray.h | 234 + ape-server/deps/js/src/jsatom.cpp | 1269 ++ ape-server/deps/js/src/jsatom.h | 508 + ape-server/deps/js/src/jsatominlines.h | 95 + ape-server/deps/js/src/jsbit.h | 274 + ape-server/deps/js/src/jsbool.cpp | 191 + ape-server/deps/js/src/jsbool.h | 84 + ape-server/deps/js/src/jsbuiltins.cpp | 458 + ape-server/deps/js/src/jsbuiltins.h | 529 + ape-server/deps/js/src/jsclist.h | 139 + ape-server/deps/js/src/jscntxt.cpp | 1950 ++ ape-server/deps/js/src/jscntxt.h | 2025 +++ ape-server/deps/js/src/jscompat.h | 56 + ape-server/deps/js/src/jsconfig.mk | 169 + ape-server/deps/js/src/jscpucfg.cpp | 194 + ape-server/deps/js/src/jscpucfg.h | 91 + ape-server/deps/js/src/jsdate.cpp | 2647 +++ ape-server/deps/js/src/jsdate.h | 133 + ape-server/deps/js/src/jsdbgapi.cpp | 2539 +++ ape-server/deps/js/src/jsdbgapi.h | 509 + ape-server/deps/js/src/jsdhash.cpp | 903 + ape-server/deps/js/src/jsdhash.h | 607 + ape-server/deps/js/src/jsdtoa.cpp | 572 + ape-server/deps/js/src/jsdtoa.h | 131 + ape-server/deps/js/src/jsdtracef.cpp | 281 + ape-server/deps/js/src/jsdtracef.h | 81 + ape-server/deps/js/src/jsemit.cpp | 7382 ++++++++ ape-server/deps/js/src/jsemit.h | 845 + ape-server/deps/js/src/jsexn.cpp | 1321 ++ ape-server/deps/js/src/jsexn.h | 96 + ape-server/deps/js/src/jsfile.cpp | 2767 +++ ape-server/deps/js/src/jsfile.h | 56 + ape-server/deps/js/src/jsfile.msg | 90 + ape-server/deps/js/src/jsfun.cpp | 3092 ++++ ape-server/deps/js/src/jsfun.h | 455 + ape-server/deps/js/src/jsgc.cpp | 3341 ++++ ape-server/deps/js/src/jsgc.h | 451 + ape-server/deps/js/src/jshash.cpp | 477 + ape-server/deps/js/src/jshash.h | 153 + ape-server/deps/js/src/jsinterp.cpp | 3326 ++++ ape-server/deps/js/src/jsinterp.h | 680 + ape-server/deps/js/src/jsinttypes.h | 139 + ape-server/deps/js/src/jsinvoke.cpp | 42 + ape-server/deps/js/src/jsiter.cpp | 1045 ++ ape-server/deps/js/src/jsiter.h | 140 + ape-server/deps/js/src/jskeyword.tbl | 124 + ape-server/deps/js/src/jskwgen.cpp | 460 + ape-server/deps/js/src/jslibmath.h | 86 + ape-server/deps/js/src/jslock.cpp | 1521 ++ ape-server/deps/js/src/jslock.h | 324 + ape-server/deps/js/src/jslocko.asm | 60 + ape-server/deps/js/src/jslog2.cpp | 111 + ape-server/deps/js/src/jslong.h | 167 + ape-server/deps/js/src/jsmath.cpp | 762 + ape-server/deps/js/src/jsmath.h | 73 + ape-server/deps/js/src/jsnum.cpp | 1404 ++ ape-server/deps/js/src/jsnum.h | 429 + ape-server/deps/js/src/jsobj.cpp | 6453 +++++++ ape-server/deps/js/src/jsobj.h | 1018 ++ ape-server/deps/js/src/jsobjinlines.h | 59 + ape-server/deps/js/src/json.cpp | 1254 ++ ape-server/deps/js/src/json.h | 97 + ape-server/deps/js/src/jsopcode.cpp | 5594 ++++++ ape-server/deps/js/src/jsopcode.h | 460 + ape-server/deps/js/src/jsopcode.tbl | 605 + ape-server/deps/js/src/jsoplengen.cpp | 121 + ape-server/deps/js/src/jsops.cpp | 4297 +++++ ape-server/deps/js/src/jsotypes.h | 198 + ape-server/deps/js/src/jsparse.cpp | 9435 ++++++++++ ape-server/deps/js/src/jsparse.h | 936 + ape-server/deps/js/src/jsprf.cpp | 1262 ++ ape-server/deps/js/src/jsprf.h | 150 + ape-server/deps/js/src/jsproto.tbl | 117 + ape-server/deps/js/src/jsprvtd.h | 373 + ape-server/deps/js/src/jspubtd.h | 583 + ape-server/deps/js/src/jsrecursion.cpp | 789 + ape-server/deps/js/src/jsregexp.cpp | 5871 ++++++ ape-server/deps/js/src/jsregexp.h | 204 + ape-server/deps/js/src/jsreops.tbl | 145 + ape-server/deps/js/src/jsscan.cpp | 1945 ++ ape-server/deps/js/src/jsscan.h | 447 + ape-server/deps/js/src/jsscope.cpp | 2193 +++ ape-server/deps/js/src/jsscope.h | 892 + ape-server/deps/js/src/jsscopeinlines.h | 166 + ape-server/deps/js/src/jsscript.cpp | 2038 +++ ape-server/deps/js/src/jsscript.h | 370 + ape-server/deps/js/src/jsscriptinlines.h | 89 + ape-server/deps/js/src/jsshell.msg | 56 + ape-server/deps/js/src/jsstack.js | 373 + ape-server/deps/js/src/jsstaticcheck.h | 69 + ape-server/deps/js/src/jsstdint.h | 121 + ape-server/deps/js/src/jsstr.cpp | 5595 ++++++ ape-server/deps/js/src/jsstr.h | 809 + ape-server/deps/js/src/jsstrinlines.h | 75 + ape-server/deps/js/src/jstask.cpp | 126 + ape-server/deps/js/src/jstask.h | 84 + ape-server/deps/js/src/jstl.h | 242 + ape-server/deps/js/src/jstracer.cpp | 14816 ++++++++++++++++ ape-server/deps/js/src/jstracer.h | 1639 ++ ape-server/deps/js/src/jstypes.h | 469 + ape-server/deps/js/src/jsutil.cpp | 363 + ape-server/deps/js/src/jsutil.h | 304 + ape-server/deps/js/src/jsvector.h | 775 + ape-server/deps/js/src/jsversion.h | 238 + ape-server/deps/js/src/jswince.asm | 82 + ape-server/deps/js/src/jsxdrapi.cpp | 794 + ape-server/deps/js/src/jsxdrapi.h | 221 + ape-server/deps/js/src/jsxml.cpp | 8155 +++++++++ ape-server/deps/js/src/jsxml.h | 296 + ape-server/deps/js/src/lock_sparcv8plus.il | 84 + ape-server/deps/js/src/lock_sparcv9.il | 84 + ape-server/deps/js/src/nanojit-import-filemap | 13 + ape-server/deps/js/src/nanojit-import-rev | 1 + ape-server/deps/js/src/nanojit/Allocator.cpp | 96 + ape-server/deps/js/src/nanojit/Allocator.h | 117 + ape-server/deps/js/src/nanojit/Assembler.cpp | 2023 +++ ape-server/deps/js/src/nanojit/Assembler.h | 416 + ape-server/deps/js/src/nanojit/CodeAlloc.cpp | 522 + ape-server/deps/js/src/nanojit/CodeAlloc.h | 204 + ape-server/deps/js/src/nanojit/Containers.cpp | 95 + ape-server/deps/js/src/nanojit/Containers.h | 463 + ape-server/deps/js/src/nanojit/Fragmento.cpp | 80 + ape-server/deps/js/src/nanojit/Fragmento.h | 135 + ape-server/deps/js/src/nanojit/LIR.cpp | 2401 +++ ape-server/deps/js/src/nanojit/LIR.h | 1503 ++ ape-server/deps/js/src/nanojit/LIRopcode.tbl | 258 + ape-server/deps/js/src/nanojit/Native.h | 209 + ape-server/deps/js/src/nanojit/NativeARM.cpp | 2627 +++ ape-server/deps/js/src/nanojit/NativeARM.h | 974 + ape-server/deps/js/src/nanojit/NativePPC.cpp | 1327 ++ ape-server/deps/js/src/nanojit/NativePPC.h | 582 + .../deps/js/src/nanojit/NativeSparc.cpp | 1036 ++ ape-server/deps/js/src/nanojit/NativeSparc.h | 959 + ape-server/deps/js/src/nanojit/NativeX64.cpp | 1775 ++ ape-server/deps/js/src/nanojit/NativeX64.h | 568 + ape-server/deps/js/src/nanojit/Nativei386.cpp | 1881 ++ ape-server/deps/js/src/nanojit/Nativei386.h | 872 + ape-server/deps/js/src/nanojit/RegAlloc.cpp | 65 + ape-server/deps/js/src/nanojit/RegAlloc.h | 185 + ape-server/deps/js/src/nanojit/VMPI.cpp | 152 + ape-server/deps/js/src/nanojit/VMPI.h | 132 + ape-server/deps/js/src/nanojit/avmplus.cpp | 172 + ape-server/deps/js/src/nanojit/avmplus.h | 458 + ape-server/deps/js/src/nanojit/manifest.mk | 79 + ape-server/deps/js/src/nanojit/nanojit.h | 290 + ape-server/deps/js/src/perfect.js | 39 + ape-server/deps/js/src/plify_jsdhash.sed | 39 + ape-server/deps/js/src/prmjtime.cpp | 911 + ape-server/deps/js/src/prmjtime.h | 103 + ape-server/deps/js/src/ref-config/AIX4.1.mk | 65 + ape-server/deps/js/src/ref-config/AIX4.2.mk | 64 + ape-server/deps/js/src/ref-config/AIX4.3.mk | 65 + ape-server/deps/js/src/ref-config/Darwin.mk | 85 + .../deps/js/src/ref-config/Darwin1.3.mk | 81 + .../deps/js/src/ref-config/Darwin1.4.mk | 41 + .../deps/js/src/ref-config/Darwin5.2.mk | 81 + .../deps/js/src/ref-config/Darwin5.3.mk | 81 + ape-server/deps/js/src/ref-config/Darwin64.mk | 72 + .../deps/js/src/ref-config/HP-UXB.10.10.mk | 77 + .../deps/js/src/ref-config/HP-UXB.10.20.mk | 77 + .../deps/js/src/ref-config/HP-UXB.11.00.mk | 80 + .../deps/js/src/ref-config/HP-UXB.11.31.mk | 65 + ape-server/deps/js/src/ref-config/IRIX.mk | 87 + ape-server/deps/js/src/ref-config/IRIX5.3.mk | 44 + ape-server/deps/js/src/ref-config/IRIX6.1.mk | 44 + ape-server/deps/js/src/ref-config/IRIX6.2.mk | 44 + ape-server/deps/js/src/ref-config/IRIX6.3.mk | 44 + ape-server/deps/js/src/ref-config/IRIX6.5.mk | 44 + .../deps/js/src/ref-config/Linux_All.mk | 105 + .../deps/js/src/ref-config/Mac_OS10.0.mk | 82 + ape-server/deps/js/src/ref-config/OSF1V4.0.mk | 72 + ape-server/deps/js/src/ref-config/OSF1V5.0.mk | 69 + .../deps/js/src/ref-config/SunOS4.1.4.mk | 101 + .../deps/js/src/ref-config/SunOS5.10.mk | 50 + ape-server/deps/js/src/ref-config/SunOS5.3.mk | 91 + ape-server/deps/js/src/ref-config/SunOS5.4.mk | 92 + .../deps/js/src/ref-config/SunOS5.5.1.mk | 44 + ape-server/deps/js/src/ref-config/SunOS5.5.mk | 87 + ape-server/deps/js/src/ref-config/SunOS5.6.mk | 89 + ape-server/deps/js/src/ref-config/SunOS5.7.mk | 44 + ape-server/deps/js/src/ref-config/SunOS5.8.mk | 44 + ape-server/deps/js/src/ref-config/SunOS5.9.mk | 44 + ape-server/deps/js/src/ref-config/WINNT4.0.mk | 118 + ape-server/deps/js/src/ref-config/WINNT5.0.mk | 118 + ape-server/deps/js/src/ref-config/WINNT5.1.mk | 118 + ape-server/deps/js/src/ref-config/WINNT5.2.mk | 118 + ape-server/deps/js/src/ref-config/WINNT6.0.mk | 118 + ape-server/deps/js/src/ref-config/dgux.mk | 64 + ape-server/deps/js/src/resource.h | 15 + ape-server/deps/js/src/rules.mk | 206 + ape-server/deps/js/src/time.sh | 13 + ape-server/deps/js/src/unallmakefiles | 1 + ape-server/deps/js/src/vprof/readme.txt | 130 + ape-server/deps/js/src/vprof/vprof.cpp | 410 + ape-server/deps/js/src/vprof/vprof.h | 271 + ape-server/deps/udns-0.0.9/.cvsignore | 14 + ape-server/deps/udns-0.0.9/COPYING.LGPL | 504 + ape-server/deps/udns-0.0.9/Makefile | 196 + ape-server/deps/udns-0.0.9/Makefile.in | 196 + ape-server/deps/udns-0.0.9/NEWS | 85 + ape-server/deps/udns-0.0.9/NOTES | 222 + ape-server/deps/udns-0.0.9/TODO | 69 + ape-server/deps/udns-0.0.9/config.h | 6 + ape-server/deps/udns-0.0.9/config.status | 3 + ape-server/deps/udns-0.0.9/configure | 167 + ape-server/deps/udns-0.0.9/configure.lib | 268 + ape-server/deps/udns-0.0.9/dnsget | Bin 0 -> 45553 bytes ape-server/deps/udns-0.0.9/dnsget.1 | 182 + ape-server/deps/udns-0.0.9/dnsget.c | 726 + ape-server/deps/udns-0.0.9/ex-rdns | Bin 0 -> 29947 bytes ape-server/deps/udns-0.0.9/ex-rdns.c | 113 + ape-server/deps/udns-0.0.9/getopt.c | 165 + ape-server/deps/udns-0.0.9/inet_XtoX.c | 327 + ape-server/deps/udns-0.0.9/rblcheck | Bin 0 -> 37945 bytes ape-server/deps/udns-0.0.9/rblcheck.1 | 151 + ape-server/deps/udns-0.0.9/rblcheck.c | 377 + ape-server/deps/udns-0.0.9/udns.3 | 1351 ++ ape-server/deps/udns-0.0.9/udns.h | 745 + ape-server/deps/udns-0.0.9/udns_XtoX.c | 50 + ape-server/deps/udns-0.0.9/udns_bl.c | 160 + ape-server/deps/udns-0.0.9/udns_codes.c | 171 + ape-server/deps/udns-0.0.9/udns_dn.c | 382 + ape-server/deps/udns-0.0.9/udns_dntosp.c | 30 + ape-server/deps/udns-0.0.9/udns_init.c | 231 + ape-server/deps/udns-0.0.9/udns_misc.c | 67 + ape-server/deps/udns-0.0.9/udns_parse.c | 169 + ape-server/deps/udns-0.0.9/udns_resolver.c | 1294 ++ ape-server/deps/udns-0.0.9/udns_rr_a.c | 123 + ape-server/deps/udns-0.0.9/udns_rr_mx.c | 91 + ape-server/deps/udns-0.0.9/udns_rr_naptr.c | 128 + ape-server/deps/udns-0.0.9/udns_rr_ptr.c | 109 + ape-server/deps/udns-0.0.9/udns_rr_srv.c | 154 + ape-server/deps/udns-0.0.9/udns_rr_txt.c | 98 + ape-server/doc/RFC | 1 + ape-server/modules/Makefile | 29 + ape-server/modules/conf/inlinepush.conf | 1 + ape-server/modules/conf/javascript.conf | 1 + ape-server/modules/conf/proxy.conf | 2 + ape-server/modules/deps/mysac/COPYING | 143 + ape-server/modules/deps/mysac/Makefile | 76 + ape-server/modules/deps/mysac/README | 21 + ape-server/modules/deps/mysac/VERSION | 1 + ape-server/modules/deps/mysac/mysac.c | 1448 ++ ape-server/modules/deps/mysac/mysac.doxygen | 1252 ++ ape-server/modules/deps/mysac/mysac.h | 920 + .../modules/deps/mysac/mysac_decode_field.c | 209 + .../modules/deps/mysac/mysac_decode_field.h | 41 + .../modules/deps/mysac/mysac_decode_row.c | 310 + .../modules/deps/mysac/mysac_decode_row.h | 54 + .../modules/deps/mysac/mysac_encode_values.c | 285 + .../modules/deps/mysac/mysac_encode_values.h | 23 + ape-server/modules/deps/mysac/mysac_errors.c | 50 + ape-server/modules/deps/mysac/mysac_net.c | 236 + ape-server/modules/deps/mysac/mysac_net.h | 27 + ape-server/modules/deps/mysac/mysac_utils.h | 236 + ape-server/modules/deps/mysac/mysac_ver | 19 + ape-server/modules/global_plugins.h | 22 + ape-server/modules/lib/README | 1 + ape-server/modules/libape-spidermonkey.c | 3193 ++++ ape-server/modules/plugins.h | 96 + ape-server/scripts/commands/inlinepush.js | 18 + ape-server/scripts/commands/proxy.js | 81 + ape-server/scripts/examples/example1.js | 59 + ape-server/scripts/examples/ircserver.js | 236 + ape-server/scripts/examples/move.js | 16 + ape-server/scripts/examples/nickname.js | 20 + ape-server/scripts/examples/server.js | 28 + ape-server/scripts/framework/Http.js | 187 + ape-server/scripts/framework/http_auth.js | 23 + ape-server/scripts/framework/mootools.js | 1096 ++ ape-server/scripts/framework/userslist.js | 15 + ape-server/scripts/main.ape.js | 14 + ape-server/scripts/utils/checkTool.js | 5 + ape-server/scripts/utils/utils.js | 9 + ape-server/src/base64.c | 88 + ape-server/src/base64.h | 29 + ape-server/src/channel.c | 559 + ape-server/src/channel.h | 90 + ape-server/src/cmd.c | 648 + ape-server/src/cmd.h | 146 + ape-server/src/config.c | 172 + ape-server/src/config.h | 51 + ape-server/src/dns.c | 205 + ape-server/src/dns.h | 42 + ape-server/src/entry.c | 343 + ape-server/src/event_epoll.c | 126 + ape-server/src/event_kqueue.c | 146 + ape-server/src/events.c | 86 + ape-server/src/events.h | 87 + ape-server/src/extend.c | 214 + ape-server/src/extend.h | 56 + ape-server/src/handle_http.c | 191 + ape-server/src/handle_http.h | 46 + ape-server/src/hash.c | 191 + ape-server/src/hash.h | 50 + ape-server/src/http.c | 500 + ape-server/src/http.h | 91 + ape-server/src/json.c | 897 + ape-server/src/json.h | 173 + ape-server/src/json_parser.c | 1015 ++ ape-server/src/json_parser.h | 152 + ape-server/src/log.c | 80 + ape-server/src/log.h | 37 + ape-server/src/main.h | 288 + ape-server/src/md5.c | 255 + ape-server/src/md5.h | 30 + ape-server/src/parser.c | 127 + ape-server/src/parser.h | 31 + ape-server/src/pipe.c | 197 + ape-server/src/pipe.h | 72 + ape-server/src/plugins.c | 177 + ape-server/src/plugins.h | 89 + ape-server/src/proxy.c | 395 + ape-server/src/proxy.h | 103 + ape-server/src/raw.c | 439 + ape-server/src/raw.h | 70 + ape-server/src/servers.c | 89 + ape-server/src/servers.h | 29 + ape-server/src/sha1.c | 371 + ape-server/src/sha1.h | 52 + ape-server/src/sock.c | 609 + ape-server/src/sock.h | 95 + ape-server/src/ticks.c | 169 + ape-server/src/ticks.h | 54 + ape-server/src/transports.c | 142 + ape-server/src/transports.h | 53 + ape-server/src/users.c | 724 + ape-server/src/users.h | 252 + ape-server/src/utils.c | 264 + ape-server/src/utils.h | 56 + lib/Alien/APE/Server.pm | 87 + 442 files changed, 252462 insertions(+) create mode 100755 Makefile.PL create mode 100755 ape-server/AUTHORS create mode 100755 ape-server/COPYING create mode 100644 ape-server/Makefile create mode 100755 ape-server/README create mode 100755 ape-server/bin/ape.conf create mode 100755 ape-server/build.sh create mode 100755 ape-server/deps/js/src/Makefile.in create mode 100755 ape-server/deps/js/src/Makefile.ref create mode 100755 ape-server/deps/js/src/README.html create mode 100755 ape-server/deps/js/src/SpiderMonkey.rsp create mode 100755 ape-server/deps/js/src/Y.js create mode 100755 ape-server/deps/js/src/aclocal.m4 create mode 100755 ape-server/deps/js/src/autom4te.cache/output.0t create mode 100755 ape-server/deps/js/src/autom4te.cache/requests create mode 100755 ape-server/deps/js/src/autom4te.cache/traces.0t create mode 100755 ape-server/deps/js/src/bench.sh create mode 100755 ape-server/deps/js/src/build/autoconf/acoutput-fast.pl create mode 100755 ape-server/deps/js/src/build/autoconf/acwinpaths.m4 create mode 100755 ape-server/deps/js/src/build/autoconf/altoptions.m4 create mode 100755 ape-server/deps/js/src/build/autoconf/config.guess create mode 100755 ape-server/deps/js/src/build/autoconf/config.sub create mode 100755 ape-server/deps/js/src/build/autoconf/install-sh create mode 100755 ape-server/deps/js/src/build/autoconf/make-makefile create mode 100755 ape-server/deps/js/src/build/autoconf/match-dir.sh create mode 100755 ape-server/deps/js/src/build/autoconf/moznbytetype.m4 create mode 100755 ape-server/deps/js/src/build/autoconf/mozprog.m4 create mode 100755 ape-server/deps/js/src/build/autoconf/nspr.m4 create mode 100755 ape-server/deps/js/src/build/autoconf/pkg.m4 create mode 100755 ape-server/deps/js/src/build/cygwin-wrapper create mode 100755 ape-server/deps/js/src/build/hcc create mode 100755 ape-server/deps/js/src/build/hcpp create mode 100755 ape-server/deps/js/src/build/msys-perl-wrapper create mode 100755 ape-server/deps/js/src/build/unix/mddepend.pl create mode 100755 ape-server/deps/js/src/build/unix/uniq.pl create mode 100755 ape-server/deps/js/src/build/win32/pgomerge.py create mode 100755 ape-server/deps/js/src/builtins.tbl create mode 100755 ape-server/deps/js/src/config.mk create mode 100755 ape-server/deps/js/src/config/Makefile create mode 100755 ape-server/deps/js/src/config/Makefile.in create mode 100755 ape-server/deps/js/src/config/Moz/Milestone.pm create mode 100755 ape-server/deps/js/src/config/autoconf.mk.in create mode 100755 ape-server/deps/js/src/config/check-sync-dirs.py create mode 100755 ape-server/deps/js/src/config/check-sync-exceptions create mode 100755 ape-server/deps/js/src/config/config.mk create mode 100755 ape-server/deps/js/src/config/fastcwd.pl create mode 100755 ape-server/deps/js/src/config/gcc_hidden.h create mode 100755 ape-server/deps/js/src/config/insure.mk create mode 100755 ape-server/deps/js/src/config/make-system-wrappers.pl create mode 100755 ape-server/deps/js/src/config/milestone.pl create mode 100755 ape-server/deps/js/src/config/milestone.txt create mode 100755 ape-server/deps/js/src/config/mkdepend/Makefile create mode 100755 ape-server/deps/js/src/config/mkdepend/Makefile.in create mode 100755 ape-server/deps/js/src/config/mkdepend/cppsetup.c create mode 100755 ape-server/deps/js/src/config/mkdepend/def.h create mode 100755 ape-server/deps/js/src/config/mkdepend/ifparser.c create mode 100755 ape-server/deps/js/src/config/mkdepend/ifparser.h create mode 100755 ape-server/deps/js/src/config/mkdepend/imakemdep.h create mode 100755 ape-server/deps/js/src/config/mkdepend/include.c create mode 100755 ape-server/deps/js/src/config/mkdepend/main.c create mode 100755 ape-server/deps/js/src/config/mkdepend/mkdepend.man create mode 100755 ape-server/deps/js/src/config/mkdepend/parse.c create mode 100755 ape-server/deps/js/src/config/mkdepend/pr.c create mode 100755 ape-server/deps/js/src/config/myconfig.mk create mode 100755 ape-server/deps/js/src/config/myrules.mk create mode 100755 ape-server/deps/js/src/config/nfspwd.pl create mode 100755 ape-server/deps/js/src/config/nsinstall.c create mode 100755 ape-server/deps/js/src/config/nsinstall.exe.manifest create mode 100755 ape-server/deps/js/src/config/nsinstall.py create mode 100755 ape-server/deps/js/src/config/nsinstall_win.c create mode 100755 ape-server/deps/js/src/config/pathsub.c create mode 100755 ape-server/deps/js/src/config/pathsub.h create mode 100755 ape-server/deps/js/src/config/preprocessor.pl create mode 100755 ape-server/deps/js/src/config/rules.mk create mode 100755 ape-server/deps/js/src/config/solaris_ia32.map create mode 100755 ape-server/deps/js/src/config/static-checking-config.mk create mode 100755 ape-server/deps/js/src/config/static-checking.js create mode 100755 ape-server/deps/js/src/config/string-format.js create mode 100755 ape-server/deps/js/src/config/system-headers create mode 100755 ape-server/deps/js/src/config/version.mk create mode 100755 ape-server/deps/js/src/config/version_win.pl create mode 100755 ape-server/deps/js/src/configure create mode 100755 ape-server/deps/js/src/configure.in create mode 100755 ape-server/deps/js/src/dtoa.c create mode 100755 ape-server/deps/js/src/editline/Makefile create mode 100755 ape-server/deps/js/src/editline/Makefile.in create mode 100755 ape-server/deps/js/src/editline/Makefile.ref create mode 100755 ape-server/deps/js/src/editline/README create mode 100755 ape-server/deps/js/src/editline/editline.3 create mode 100755 ape-server/deps/js/src/editline/editline.c create mode 100755 ape-server/deps/js/src/editline/editline.h create mode 100755 ape-server/deps/js/src/editline/sysunix.c create mode 100755 ape-server/deps/js/src/editline/unix.h create mode 100755 ape-server/deps/js/src/find-child.py create mode 100755 ape-server/deps/js/src/imacro_asm.js.in create mode 100755 ape-server/deps/js/src/imacros.c.out create mode 100755 ape-server/deps/js/src/imacros.jsasm create mode 100755 ape-server/deps/js/src/javascript-trace.d create mode 100755 ape-server/deps/js/src/jitstats.tbl create mode 100644 ape-server/deps/js/src/js-confdefs.h create mode 100755 ape-server/deps/js/src/js-config create mode 100644 ape-server/deps/js/src/js-config.h create mode 100755 ape-server/deps/js/src/js-config.h.in create mode 100755 ape-server/deps/js/src/js-config.in create mode 100755 ape-server/deps/js/src/js.mdp create mode 100755 ape-server/deps/js/src/js.msg create mode 100755 ape-server/deps/js/src/jsapi.cpp create mode 100755 ape-server/deps/js/src/jsapi.h create mode 100755 ape-server/deps/js/src/jsarena.cpp create mode 100755 ape-server/deps/js/src/jsarena.h create mode 100755 ape-server/deps/js/src/jsarray.cpp create mode 100755 ape-server/deps/js/src/jsarray.h create mode 100755 ape-server/deps/js/src/jsatom.cpp create mode 100755 ape-server/deps/js/src/jsatom.h create mode 100755 ape-server/deps/js/src/jsatominlines.h create mode 100755 ape-server/deps/js/src/jsbit.h create mode 100755 ape-server/deps/js/src/jsbool.cpp create mode 100755 ape-server/deps/js/src/jsbool.h create mode 100755 ape-server/deps/js/src/jsbuiltins.cpp create mode 100755 ape-server/deps/js/src/jsbuiltins.h create mode 100755 ape-server/deps/js/src/jsclist.h create mode 100755 ape-server/deps/js/src/jscntxt.cpp create mode 100755 ape-server/deps/js/src/jscntxt.h create mode 100755 ape-server/deps/js/src/jscompat.h create mode 100755 ape-server/deps/js/src/jsconfig.mk create mode 100755 ape-server/deps/js/src/jscpucfg.cpp create mode 100755 ape-server/deps/js/src/jscpucfg.h create mode 100755 ape-server/deps/js/src/jsdate.cpp create mode 100755 ape-server/deps/js/src/jsdate.h create mode 100755 ape-server/deps/js/src/jsdbgapi.cpp create mode 100755 ape-server/deps/js/src/jsdbgapi.h create mode 100755 ape-server/deps/js/src/jsdhash.cpp create mode 100755 ape-server/deps/js/src/jsdhash.h create mode 100755 ape-server/deps/js/src/jsdtoa.cpp create mode 100755 ape-server/deps/js/src/jsdtoa.h create mode 100755 ape-server/deps/js/src/jsdtracef.cpp create mode 100755 ape-server/deps/js/src/jsdtracef.h create mode 100755 ape-server/deps/js/src/jsemit.cpp create mode 100755 ape-server/deps/js/src/jsemit.h create mode 100755 ape-server/deps/js/src/jsexn.cpp create mode 100755 ape-server/deps/js/src/jsexn.h create mode 100755 ape-server/deps/js/src/jsfile.cpp create mode 100755 ape-server/deps/js/src/jsfile.h create mode 100755 ape-server/deps/js/src/jsfile.msg create mode 100755 ape-server/deps/js/src/jsfun.cpp create mode 100755 ape-server/deps/js/src/jsfun.h create mode 100755 ape-server/deps/js/src/jsgc.cpp create mode 100755 ape-server/deps/js/src/jsgc.h create mode 100755 ape-server/deps/js/src/jshash.cpp create mode 100755 ape-server/deps/js/src/jshash.h create mode 100755 ape-server/deps/js/src/jsinterp.cpp create mode 100755 ape-server/deps/js/src/jsinterp.h create mode 100755 ape-server/deps/js/src/jsinttypes.h create mode 100755 ape-server/deps/js/src/jsinvoke.cpp create mode 100755 ape-server/deps/js/src/jsiter.cpp create mode 100755 ape-server/deps/js/src/jsiter.h create mode 100755 ape-server/deps/js/src/jskeyword.tbl create mode 100755 ape-server/deps/js/src/jskwgen.cpp create mode 100755 ape-server/deps/js/src/jslibmath.h create mode 100755 ape-server/deps/js/src/jslock.cpp create mode 100755 ape-server/deps/js/src/jslock.h create mode 100755 ape-server/deps/js/src/jslocko.asm create mode 100755 ape-server/deps/js/src/jslog2.cpp create mode 100755 ape-server/deps/js/src/jslong.h create mode 100755 ape-server/deps/js/src/jsmath.cpp create mode 100755 ape-server/deps/js/src/jsmath.h create mode 100755 ape-server/deps/js/src/jsnum.cpp create mode 100755 ape-server/deps/js/src/jsnum.h create mode 100755 ape-server/deps/js/src/jsobj.cpp create mode 100755 ape-server/deps/js/src/jsobj.h create mode 100755 ape-server/deps/js/src/jsobjinlines.h create mode 100755 ape-server/deps/js/src/json.cpp create mode 100755 ape-server/deps/js/src/json.h create mode 100755 ape-server/deps/js/src/jsopcode.cpp create mode 100755 ape-server/deps/js/src/jsopcode.h create mode 100755 ape-server/deps/js/src/jsopcode.tbl create mode 100755 ape-server/deps/js/src/jsoplengen.cpp create mode 100755 ape-server/deps/js/src/jsops.cpp create mode 100755 ape-server/deps/js/src/jsotypes.h create mode 100755 ape-server/deps/js/src/jsparse.cpp create mode 100755 ape-server/deps/js/src/jsparse.h create mode 100755 ape-server/deps/js/src/jsprf.cpp create mode 100755 ape-server/deps/js/src/jsprf.h create mode 100755 ape-server/deps/js/src/jsproto.tbl create mode 100755 ape-server/deps/js/src/jsprvtd.h create mode 100755 ape-server/deps/js/src/jspubtd.h create mode 100755 ape-server/deps/js/src/jsrecursion.cpp create mode 100755 ape-server/deps/js/src/jsregexp.cpp create mode 100755 ape-server/deps/js/src/jsregexp.h create mode 100755 ape-server/deps/js/src/jsreops.tbl create mode 100755 ape-server/deps/js/src/jsscan.cpp create mode 100755 ape-server/deps/js/src/jsscan.h create mode 100755 ape-server/deps/js/src/jsscope.cpp create mode 100755 ape-server/deps/js/src/jsscope.h create mode 100755 ape-server/deps/js/src/jsscopeinlines.h create mode 100755 ape-server/deps/js/src/jsscript.cpp create mode 100755 ape-server/deps/js/src/jsscript.h create mode 100755 ape-server/deps/js/src/jsscriptinlines.h create mode 100755 ape-server/deps/js/src/jsshell.msg create mode 100755 ape-server/deps/js/src/jsstack.js create mode 100755 ape-server/deps/js/src/jsstaticcheck.h create mode 100755 ape-server/deps/js/src/jsstdint.h create mode 100755 ape-server/deps/js/src/jsstr.cpp create mode 100755 ape-server/deps/js/src/jsstr.h create mode 100755 ape-server/deps/js/src/jsstrinlines.h create mode 100755 ape-server/deps/js/src/jstask.cpp create mode 100755 ape-server/deps/js/src/jstask.h create mode 100755 ape-server/deps/js/src/jstl.h create mode 100755 ape-server/deps/js/src/jstracer.cpp create mode 100755 ape-server/deps/js/src/jstracer.h create mode 100755 ape-server/deps/js/src/jstypes.h create mode 100755 ape-server/deps/js/src/jsutil.cpp create mode 100755 ape-server/deps/js/src/jsutil.h create mode 100755 ape-server/deps/js/src/jsvector.h create mode 100755 ape-server/deps/js/src/jsversion.h create mode 100755 ape-server/deps/js/src/jswince.asm create mode 100755 ape-server/deps/js/src/jsxdrapi.cpp create mode 100755 ape-server/deps/js/src/jsxdrapi.h create mode 100755 ape-server/deps/js/src/jsxml.cpp create mode 100755 ape-server/deps/js/src/jsxml.h create mode 100755 ape-server/deps/js/src/lock_sparcv8plus.il create mode 100755 ape-server/deps/js/src/lock_sparcv9.il create mode 100755 ape-server/deps/js/src/nanojit-import-filemap create mode 100755 ape-server/deps/js/src/nanojit-import-rev create mode 100755 ape-server/deps/js/src/nanojit/Allocator.cpp create mode 100755 ape-server/deps/js/src/nanojit/Allocator.h create mode 100755 ape-server/deps/js/src/nanojit/Assembler.cpp create mode 100755 ape-server/deps/js/src/nanojit/Assembler.h create mode 100755 ape-server/deps/js/src/nanojit/CodeAlloc.cpp create mode 100755 ape-server/deps/js/src/nanojit/CodeAlloc.h create mode 100755 ape-server/deps/js/src/nanojit/Containers.cpp create mode 100755 ape-server/deps/js/src/nanojit/Containers.h create mode 100755 ape-server/deps/js/src/nanojit/Fragmento.cpp create mode 100755 ape-server/deps/js/src/nanojit/Fragmento.h create mode 100755 ape-server/deps/js/src/nanojit/LIR.cpp create mode 100755 ape-server/deps/js/src/nanojit/LIR.h create mode 100755 ape-server/deps/js/src/nanojit/LIRopcode.tbl create mode 100755 ape-server/deps/js/src/nanojit/Native.h create mode 100755 ape-server/deps/js/src/nanojit/NativeARM.cpp create mode 100755 ape-server/deps/js/src/nanojit/NativeARM.h create mode 100755 ape-server/deps/js/src/nanojit/NativePPC.cpp create mode 100755 ape-server/deps/js/src/nanojit/NativePPC.h create mode 100755 ape-server/deps/js/src/nanojit/NativeSparc.cpp create mode 100755 ape-server/deps/js/src/nanojit/NativeSparc.h create mode 100755 ape-server/deps/js/src/nanojit/NativeX64.cpp create mode 100755 ape-server/deps/js/src/nanojit/NativeX64.h create mode 100755 ape-server/deps/js/src/nanojit/Nativei386.cpp create mode 100755 ape-server/deps/js/src/nanojit/Nativei386.h create mode 100755 ape-server/deps/js/src/nanojit/RegAlloc.cpp create mode 100755 ape-server/deps/js/src/nanojit/RegAlloc.h create mode 100755 ape-server/deps/js/src/nanojit/VMPI.cpp create mode 100755 ape-server/deps/js/src/nanojit/VMPI.h create mode 100755 ape-server/deps/js/src/nanojit/avmplus.cpp create mode 100755 ape-server/deps/js/src/nanojit/avmplus.h create mode 100755 ape-server/deps/js/src/nanojit/manifest.mk create mode 100755 ape-server/deps/js/src/nanojit/nanojit.h create mode 100755 ape-server/deps/js/src/perfect.js create mode 100755 ape-server/deps/js/src/plify_jsdhash.sed create mode 100755 ape-server/deps/js/src/prmjtime.cpp create mode 100755 ape-server/deps/js/src/prmjtime.h create mode 100755 ape-server/deps/js/src/ref-config/AIX4.1.mk create mode 100755 ape-server/deps/js/src/ref-config/AIX4.2.mk create mode 100755 ape-server/deps/js/src/ref-config/AIX4.3.mk create mode 100755 ape-server/deps/js/src/ref-config/Darwin.mk create mode 100755 ape-server/deps/js/src/ref-config/Darwin1.3.mk create mode 100755 ape-server/deps/js/src/ref-config/Darwin1.4.mk create mode 100755 ape-server/deps/js/src/ref-config/Darwin5.2.mk create mode 100755 ape-server/deps/js/src/ref-config/Darwin5.3.mk create mode 100755 ape-server/deps/js/src/ref-config/Darwin64.mk create mode 100755 ape-server/deps/js/src/ref-config/HP-UXB.10.10.mk create mode 100755 ape-server/deps/js/src/ref-config/HP-UXB.10.20.mk create mode 100755 ape-server/deps/js/src/ref-config/HP-UXB.11.00.mk create mode 100755 ape-server/deps/js/src/ref-config/HP-UXB.11.31.mk create mode 100755 ape-server/deps/js/src/ref-config/IRIX.mk create mode 100755 ape-server/deps/js/src/ref-config/IRIX5.3.mk create mode 100755 ape-server/deps/js/src/ref-config/IRIX6.1.mk create mode 100755 ape-server/deps/js/src/ref-config/IRIX6.2.mk create mode 100755 ape-server/deps/js/src/ref-config/IRIX6.3.mk create mode 100755 ape-server/deps/js/src/ref-config/IRIX6.5.mk create mode 100755 ape-server/deps/js/src/ref-config/Linux_All.mk create mode 100755 ape-server/deps/js/src/ref-config/Mac_OS10.0.mk create mode 100755 ape-server/deps/js/src/ref-config/OSF1V4.0.mk create mode 100755 ape-server/deps/js/src/ref-config/OSF1V5.0.mk create mode 100755 ape-server/deps/js/src/ref-config/SunOS4.1.4.mk create mode 100755 ape-server/deps/js/src/ref-config/SunOS5.10.mk create mode 100755 ape-server/deps/js/src/ref-config/SunOS5.3.mk create mode 100755 ape-server/deps/js/src/ref-config/SunOS5.4.mk create mode 100755 ape-server/deps/js/src/ref-config/SunOS5.5.1.mk create mode 100755 ape-server/deps/js/src/ref-config/SunOS5.5.mk create mode 100755 ape-server/deps/js/src/ref-config/SunOS5.6.mk create mode 100755 ape-server/deps/js/src/ref-config/SunOS5.7.mk create mode 100755 ape-server/deps/js/src/ref-config/SunOS5.8.mk create mode 100755 ape-server/deps/js/src/ref-config/SunOS5.9.mk create mode 100755 ape-server/deps/js/src/ref-config/WINNT4.0.mk create mode 100755 ape-server/deps/js/src/ref-config/WINNT5.0.mk create mode 100755 ape-server/deps/js/src/ref-config/WINNT5.1.mk create mode 100755 ape-server/deps/js/src/ref-config/WINNT5.2.mk create mode 100755 ape-server/deps/js/src/ref-config/WINNT6.0.mk create mode 100755 ape-server/deps/js/src/ref-config/dgux.mk create mode 100755 ape-server/deps/js/src/resource.h create mode 100755 ape-server/deps/js/src/rules.mk create mode 100755 ape-server/deps/js/src/time.sh create mode 100755 ape-server/deps/js/src/unallmakefiles create mode 100755 ape-server/deps/js/src/vprof/readme.txt create mode 100755 ape-server/deps/js/src/vprof/vprof.cpp create mode 100755 ape-server/deps/js/src/vprof/vprof.h create mode 100755 ape-server/deps/udns-0.0.9/.cvsignore create mode 100755 ape-server/deps/udns-0.0.9/COPYING.LGPL create mode 100644 ape-server/deps/udns-0.0.9/Makefile create mode 100755 ape-server/deps/udns-0.0.9/Makefile.in create mode 100755 ape-server/deps/udns-0.0.9/NEWS create mode 100755 ape-server/deps/udns-0.0.9/NOTES create mode 100755 ape-server/deps/udns-0.0.9/TODO create mode 100755 ape-server/deps/udns-0.0.9/config.h create mode 100644 ape-server/deps/udns-0.0.9/config.status create mode 100755 ape-server/deps/udns-0.0.9/configure create mode 100755 ape-server/deps/udns-0.0.9/configure.lib create mode 100755 ape-server/deps/udns-0.0.9/dnsget create mode 100755 ape-server/deps/udns-0.0.9/dnsget.1 create mode 100755 ape-server/deps/udns-0.0.9/dnsget.c create mode 100755 ape-server/deps/udns-0.0.9/ex-rdns create mode 100755 ape-server/deps/udns-0.0.9/ex-rdns.c create mode 100755 ape-server/deps/udns-0.0.9/getopt.c create mode 100755 ape-server/deps/udns-0.0.9/inet_XtoX.c create mode 100755 ape-server/deps/udns-0.0.9/rblcheck create mode 100755 ape-server/deps/udns-0.0.9/rblcheck.1 create mode 100755 ape-server/deps/udns-0.0.9/rblcheck.c create mode 100755 ape-server/deps/udns-0.0.9/udns.3 create mode 100755 ape-server/deps/udns-0.0.9/udns.h create mode 100755 ape-server/deps/udns-0.0.9/udns_XtoX.c create mode 100755 ape-server/deps/udns-0.0.9/udns_bl.c create mode 100755 ape-server/deps/udns-0.0.9/udns_codes.c create mode 100755 ape-server/deps/udns-0.0.9/udns_dn.c create mode 100755 ape-server/deps/udns-0.0.9/udns_dntosp.c create mode 100755 ape-server/deps/udns-0.0.9/udns_init.c create mode 100755 ape-server/deps/udns-0.0.9/udns_misc.c create mode 100755 ape-server/deps/udns-0.0.9/udns_parse.c create mode 100755 ape-server/deps/udns-0.0.9/udns_resolver.c create mode 100755 ape-server/deps/udns-0.0.9/udns_rr_a.c create mode 100755 ape-server/deps/udns-0.0.9/udns_rr_mx.c create mode 100755 ape-server/deps/udns-0.0.9/udns_rr_naptr.c create mode 100755 ape-server/deps/udns-0.0.9/udns_rr_ptr.c create mode 100755 ape-server/deps/udns-0.0.9/udns_rr_srv.c create mode 100755 ape-server/deps/udns-0.0.9/udns_rr_txt.c create mode 100755 ape-server/doc/RFC create mode 100755 ape-server/modules/Makefile create mode 100755 ape-server/modules/conf/inlinepush.conf create mode 100755 ape-server/modules/conf/javascript.conf create mode 100755 ape-server/modules/conf/proxy.conf create mode 100755 ape-server/modules/deps/mysac/COPYING create mode 100755 ape-server/modules/deps/mysac/Makefile create mode 100755 ape-server/modules/deps/mysac/README create mode 100755 ape-server/modules/deps/mysac/VERSION create mode 100755 ape-server/modules/deps/mysac/mysac.c create mode 100755 ape-server/modules/deps/mysac/mysac.doxygen create mode 100755 ape-server/modules/deps/mysac/mysac.h create mode 100755 ape-server/modules/deps/mysac/mysac_decode_field.c create mode 100755 ape-server/modules/deps/mysac/mysac_decode_field.h create mode 100755 ape-server/modules/deps/mysac/mysac_decode_row.c create mode 100755 ape-server/modules/deps/mysac/mysac_decode_row.h create mode 100755 ape-server/modules/deps/mysac/mysac_encode_values.c create mode 100755 ape-server/modules/deps/mysac/mysac_encode_values.h create mode 100755 ape-server/modules/deps/mysac/mysac_errors.c create mode 100755 ape-server/modules/deps/mysac/mysac_net.c create mode 100755 ape-server/modules/deps/mysac/mysac_net.h create mode 100755 ape-server/modules/deps/mysac/mysac_utils.h create mode 100755 ape-server/modules/deps/mysac/mysac_ver create mode 100755 ape-server/modules/global_plugins.h create mode 100755 ape-server/modules/lib/README create mode 100644 ape-server/modules/libape-spidermonkey.c create mode 100755 ape-server/modules/plugins.h create mode 100755 ape-server/scripts/commands/inlinepush.js create mode 100755 ape-server/scripts/commands/proxy.js create mode 100755 ape-server/scripts/examples/example1.js create mode 100755 ape-server/scripts/examples/ircserver.js create mode 100755 ape-server/scripts/examples/move.js create mode 100755 ape-server/scripts/examples/nickname.js create mode 100755 ape-server/scripts/examples/server.js create mode 100644 ape-server/scripts/framework/Http.js create mode 100755 ape-server/scripts/framework/http_auth.js create mode 100755 ape-server/scripts/framework/mootools.js create mode 100755 ape-server/scripts/framework/userslist.js create mode 100755 ape-server/scripts/main.ape.js create mode 100755 ape-server/scripts/utils/checkTool.js create mode 100755 ape-server/scripts/utils/utils.js create mode 100644 ape-server/src/base64.c create mode 100755 ape-server/src/base64.h create mode 100755 ape-server/src/channel.c create mode 100755 ape-server/src/channel.h create mode 100755 ape-server/src/cmd.c create mode 100755 ape-server/src/cmd.h create mode 100755 ape-server/src/config.c create mode 100755 ape-server/src/config.h create mode 100755 ape-server/src/dns.c create mode 100755 ape-server/src/dns.h create mode 100755 ape-server/src/entry.c create mode 100755 ape-server/src/event_epoll.c create mode 100755 ape-server/src/event_kqueue.c create mode 100755 ape-server/src/events.c create mode 100755 ape-server/src/events.h create mode 100755 ape-server/src/extend.c create mode 100755 ape-server/src/extend.h create mode 100755 ape-server/src/handle_http.c create mode 100755 ape-server/src/handle_http.h create mode 100755 ape-server/src/hash.c create mode 100755 ape-server/src/hash.h create mode 100644 ape-server/src/http.c create mode 100755 ape-server/src/http.h create mode 100755 ape-server/src/json.c create mode 100755 ape-server/src/json.h create mode 100755 ape-server/src/json_parser.c create mode 100755 ape-server/src/json_parser.h create mode 100755 ape-server/src/log.c create mode 100755 ape-server/src/log.h create mode 100755 ape-server/src/main.h create mode 100644 ape-server/src/md5.c create mode 100644 ape-server/src/md5.h create mode 100755 ape-server/src/parser.c create mode 100755 ape-server/src/parser.h create mode 100755 ape-server/src/pipe.c create mode 100755 ape-server/src/pipe.h create mode 100644 ape-server/src/plugins.c create mode 100755 ape-server/src/plugins.h create mode 100755 ape-server/src/proxy.c create mode 100755 ape-server/src/proxy.h create mode 100755 ape-server/src/raw.c create mode 100755 ape-server/src/raw.h create mode 100755 ape-server/src/servers.c create mode 100755 ape-server/src/servers.h create mode 100755 ape-server/src/sha1.c create mode 100755 ape-server/src/sha1.h create mode 100755 ape-server/src/sock.c create mode 100755 ape-server/src/sock.h create mode 100755 ape-server/src/ticks.c create mode 100755 ape-server/src/ticks.h create mode 100755 ape-server/src/transports.c create mode 100755 ape-server/src/transports.h create mode 100644 ape-server/src/users.c create mode 100755 ape-server/src/users.h create mode 100755 ape-server/src/utils.c create mode 100755 ape-server/src/utils.h create mode 100755 lib/Alien/APE/Server.pm diff --git a/Makefile.PL b/Makefile.PL new file mode 100755 index 0000000..5306bed --- /dev/null +++ b/Makefile.PL @@ -0,0 +1,41 @@ +use inc::Module::Install; + +# Create the Makefile +name 'Alien-APE-Server'; +perl_version '5.008'; +license 'perl'; +all_from 'lib/Alien/APE/Server.pm'; +requires 'File::ShareDir'; +test_requires 'Test::More' => '0.42'; + +auto_install; + +my $S = ($^O eq 'MSWin32') ? "\\" : "\/"; +my $root = "\$(INST_LIB)${S}auto${S}share${S}dist${S}\$(DISTNAME)"; + +sub MY::postamble { + return <<"APE_BUILD_SH"; + +all :: ape-server/bin/aped + +ape-server/bin/aped: ape-server/build.sh + ( cd ape-server && ./build.sh ) + +install :: install_aped + +install_aped: ape-server/bin/aped + \$(NOECHO) \$(MKPATH) $root + \$(NOECHO) \$(CHMOD) 0755 $root + \$(NOECHO) \$(CP) -ar ape-server/bin $root + \$(NOECHO) \$(CP) -ar ape-server/modules $root + \$(NOECHO) \$(CP) -ar ape-server/scripts $root + +clean :: delete_aped + +delete_aped: + \$(RM_F) ape-server/bin/aped + +APE_BUILD_SH +} + +WriteAll; diff --git a/ape-server/AUTHORS b/ape-server/AUTHORS new file mode 100755 index 0000000..9fec9ba --- /dev/null +++ b/ape-server/AUTHORS @@ -0,0 +1,8 @@ +http://www.ape-project.org/ + +Weelya + +- Anthony Catel (paraboul) +- Nicolas Trani (efyx) +- Florian Gasquez (Fy-) +- John Chavarria (psi) diff --git a/ape-server/COPYING b/ape-server/COPYING new file mode 100755 index 0000000..5b6e7c6 --- /dev/null +++ b/ape-server/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. diff --git a/ape-server/Makefile b/ape-server/Makefile new file mode 100644 index 0000000..0f24883 --- /dev/null +++ b/ape-server/Makefile @@ -0,0 +1,26 @@ +EXEC=bin/aped + +prefix = /usr/local +bindir = $(prefix)/bin + + +SRC=src/entry.c src/sock.c src/hash.c src/handle_http.c src/cmd.c src/users.c src/channel.c src/config.c src/json.c src/json_parser.c src/plugins.c src/http.c src/extend.c src/utils.c src/ticks.c src/base64.c src/pipe.c src/raw.c src/events.c src/event_kqueue.c src/event_epoll.c src/transports.c src/servers.c src/dns.c src/sha1.c src/log.c src/parser.c src/md5.c + +CFLAGS = -Wall -O2 -minline-all-stringops -rdynamic -I ./deps/udns-0.0.9/ +LFLAGS=-ldl -lm -lpthread +CC=gcc -D_GNU_SOURCE +RM=rm -f + +all: aped + +aped: $(SRC) + $(CC) $(CFLAGS) $(SRC) -o $(EXEC) $(LFLAGS) ./deps/udns-0.0.9/libudns.a -I ./deps/udns-0.0.9/ +install: + install -d $(bindir) + install -m 755 $(EXEC) $(bindir) + +uninstall: + $(RM) $(bindir)/aped + +clean: + $(RM) $(EXEC) diff --git a/ape-server/README b/ape-server/README new file mode 100755 index 0000000..4ed4ef1 --- /dev/null +++ b/ape-server/README @@ -0,0 +1,2 @@ +run build.sh +-> sh build.sh diff --git a/ape-server/bin/ape.conf b/ape-server/bin/ape.conf new file mode 100755 index 0000000..1809efc --- /dev/null +++ b/ape-server/bin/ape.conf @@ -0,0 +1,41 @@ +uid { + # "aped" switch to this user/group if it run as root + user = daemon + group = daemon +} + + +Server { + port = 6969 + daemon = no + ip_listen = 0.0.0.0 + domain = auto + rlimit_nofile = 10000 + pid_file = /var/run/aped.pid +} + +Log { + debug = 1 + use_syslog = 0 + logfile = ./ape.log +} + +JSONP { + eval_func = Ape.transport.read + allowed = 1 +} + +Config { +#relative to ape.conf + modules = ../modules/lib/ + modules_conf = ../modules/conf/ +} + +# Proxy section is used to resolve hostname and allow access to a IP:port (Middleware-TCPSocket feature) + +#Proxy { +# id = freenode +# host = irc.freenode.net +# port = 6667 +# readonly = false +#} diff --git a/ape-server/build.sh b/ape-server/build.sh new file mode 100755 index 0000000..d1d117a --- /dev/null +++ b/ape-server/build.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +OS_TARGET=`uname -s` + +case "$OS_TARGET" in + linux* | Linux*) + HOST_OS=Linux + echo "#define USE_EPOLL_HANDLER" > ./src/configure.h + echo "LINUX_BUILD = 1" > ./modules/plateform.mk;; + Darwin*) + HOST_OS=Darwin + echo "#define USE_KQUEUE_HANDLER" > ./src/configure.h + echo "DARWIN_BUILD = 1" > ./modules/plateform.mk;; + *) + HOST_IS=Linux;; +esac + +if [ -e "/usr/include/mysql/mysql.h" ] +then + echo "HAS_MYSQL = yes" > ./modules/mysql.mk + echo "#define _USE_MYSQL 1" >> ./src/configure.h + cd ./modules/deps/mysac/ + make + cd ../../../ +else + echo "HAS_MYSQL = 0" > ./modules/mysql.mk + echo "#undef _USE_MYSQL" >> ./src/configure.h +fi + +cd ./deps/udns-0.0.9/ +make clean && ./configure && make +cd ../js/src/ +./configure && make +cd ../../../ +make +cd ./modules/ && make diff --git a/ape-server/deps/js/src/Makefile.in b/ape-server/deps/js/src/Makefile.in new file mode 100755 index 0000000..fd9e674 --- /dev/null +++ b/ape-server/deps/js/src/Makefile.in @@ -0,0 +1,667 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = . +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ + +run_for_side_effects := $(shell echo "MAKE: $(MAKE)") +include $(DEPTH)/config/autoconf.mk + +DIRS = config + + +MODULE = js +LIBRARY_NAME = mozjs +STATIC_LIBRARY_NAME = js_static +GRE_MODULE = 1 + +LIBS = $(NSPR_LIBS) + +ifdef GNU_CXX +ifdef INTEL_CXX +# icc gets special optimize flags +ifdef MOZ_PROFILE_GENERATE +MODULE_OPTIMIZE_FLAGS = -O0 +else +MODULE_OPTIMIZE_FLAGS = -O2 -ip +#XXX: do we want different INTERP_OPTIMIZER flags here? +endif +else # not INTEL_CXX +MODULE_OPTIMIZE_FLAGS = -O3 -fstrict-aliasing $(MOZ_OPTIMIZE_SIZE_TWEAK) +# Special optimization flags for jsinterp.c +INTERP_OPTIMIZER = -O3 -fstrict-aliasing +endif +else # not GNU_CXX +ifeq ($(OS_ARCH),SunOS) +MODULE_OPTIMIZE_FLAGS = -xO4 +endif +ifeq ($(OS_ARCH),WINNT) +ifdef WINCE +# -GL is not supported on windows mobile while we are using the arm-wince-link command +MODULE_OPTIMIZE_FLAGS = -O2 +else +MODULE_OPTIMIZE_FLAGS = -O2 -GL +endif +endif +endif + + +# JavaScript must be built shared, even for static builds, as it is used by +# other modules which are always built shared. Failure to do so results in +# the js code getting copied into xpinstall and jsd as well as mozilla-bin, +# and then the static data cells used for locking no longer work. +# +# In fact, we now build both a static and a shared library, as the +# JS shell would like to link to the static library. + +FORCE_SHARED_LIB = 1 +FORCE_STATIC_LIB = 1 + +VPATH = $(srcdir) + +CPPSRCS = \ + jsapi.cpp \ + jsarena.cpp \ + jsarray.cpp \ + jsatom.cpp \ + jsbool.cpp \ + jscntxt.cpp \ + jsdate.cpp \ + jsdbgapi.cpp \ + jsdhash.cpp \ + jsdtoa.cpp \ + jsemit.cpp \ + jsexn.cpp \ + jsfun.cpp \ + jsgc.cpp \ + jshash.cpp \ + jsinterp.cpp \ + jsinvoke.cpp \ + jsiter.cpp \ + jslock.cpp \ + jslog2.cpp \ + jsmath.cpp \ + jsnum.cpp \ + jsobj.cpp \ + json.cpp \ + jsopcode.cpp \ + jsparse.cpp \ + jsprf.cpp \ + jsregexp.cpp \ + jsscan.cpp \ + jsscope.cpp \ + jsscript.cpp \ + jsstr.cpp \ + jstask.cpp \ + jsutil.cpp \ + jsxdrapi.cpp \ + jsxml.cpp \ + prmjtime.cpp \ + $(NULL) + +ifdef HAVE_DTRACE +CPPSRCS += \ + jsdtracef.cpp +endif + +INSTALLED_HEADERS = \ + js-config.h \ + jsautocfg.h \ + $(CURDIR)/jsautokw.h \ + js.msg \ + jsapi.h \ + jsarray.h \ + jsarena.h \ + jsatom.h \ + jsbit.h \ + jsbool.h \ + jsclist.h \ + jscntxt.h \ + jscompat.h \ + jsdate.h \ + jsdbgapi.h \ + jsdhash.h \ + jsdtoa.h \ + jsemit.h \ + jsfun.h \ + jsgc.h \ + jshash.h \ + jsinterp.h \ + jsinttypes.h \ + jsiter.h \ + jslock.h \ + jslong.h \ + jsmath.h \ + jsnum.h \ + jsobj.h \ + jsobjinlines.h \ + json.h \ + jsopcode.tbl \ + jsopcode.h \ + jsotypes.h \ + jsparse.h \ + jsprf.h \ + jsproto.tbl \ + jsprvtd.h \ + jspubtd.h \ + jsregexp.h \ + jsscan.h \ + jsscope.h \ + jsscript.h \ + jsscriptinlines.h \ + jsstaticcheck.h \ + jsstr.h \ + jstask.h \ + jstracer.h \ + jstypes.h \ + jsutil.h \ + jsvector.h \ + jstl.h \ + jsversion.h \ + jsxdrapi.h \ + jsxml.h \ + $(NULL) + +ifdef ENABLE_JIT +VPATH += $(srcdir)/nanojit + +INSTALLED_HEADERS += \ + jsbuiltins.h \ + Assembler.h \ + Allocator.h \ + CodeAlloc.h \ + Containers.h \ + LIR.h \ + avmplus.h \ + Fragmento.h \ + Native.h \ + Native$(NANOJIT_ARCH).h \ + RegAlloc.h \ + nanojit.h \ + VMPI.h \ + $(NULL) + +CPPSRCS += \ + jstracer.cpp \ + Assembler.cpp \ + Allocator.cpp \ + CodeAlloc.cpp \ + Containers.cpp \ + Fragmento.cpp \ + LIR.cpp \ + RegAlloc.cpp \ + avmplus.cpp \ + Native$(NANOJIT_ARCH).cpp \ + jsbuiltins.cpp \ + VMPI.cpp \ + $(NULL) + +ifdef WINCE +# don't need -c +AS_DASH_C_FLAG = +ASFLAGS += -arch 6 +ASFILES += jswince.asm +endif + +endif # ENABLE_JIT + +ifdef HAVE_DTRACE +INSTALLED_HEADERS += \ + jsdtracef.h \ + $(CURDIR)/javascript-trace.h \ + $(NULL) +endif + +ifeq (,$(filter-out WINNT WINCE,$(OS_ARCH))) +INSTALLED_HEADERS += jscpucfg.h +endif + +EXPORTS = $(INSTALLED_HEADERS) + +DASH_R = -r + +ifneq (,$(filter OS2 WINCE WINNT,$(OS_ARCH))) +SDK_LIBRARY = $(IMPORT_LIBRARY) +else +SDK_LIBRARY = $(SHARED_LIBRARY) +endif + +include $(topsrcdir)/config/config.mk + +EXTRA_DSO_LDOPTS += $(NSPR_LIBS) + +ifdef MOZ_MEMORY +ifeq ($(OS_ARCH),SunOS) +EXTRA_DSO_LDOPTS += $(call EXPAND_LIBNAME_PATH,jemalloc,$(DIST)/lib) +endif +endif + +ifndef BUILD_OPT +MOCHAFILE = 1 +endif + +# Define keyword generator before rules.mk, see bug 323979 comment 50 + +HOST_SIMPLE_PROGRAMS += host_jskwgen$(HOST_BIN_SUFFIX) +GARBAGE += jsautokw.h host_jskwgen$(HOST_BIN_SUFFIX) + +HOST_SIMPLE_PROGRAMS += host_jsoplengen$(HOST_BIN_SUFFIX) +GARBAGE += jsautooplen.h host_jsoplengen$(HOST_BIN_SUFFIX) + +USE_HOST_CXX = 1 + +ifdef HAVE_DTRACE +ifneq ($(OS_ARCH),Darwin) +DTRACE_PROBE_OBJ = $(LIBRARY_NAME)-dtrace.$(OBJ_SUFFIX) +endif +MOZILLA_DTRACE_SRC = $(srcdir)/javascript-trace.d +endif + +default:: + +ifneq (,$(CROSS_COMPILE)$(filter-out WINNT OS2,$(OS_ARCH))) +ifneq (,$(filter-out SYMBIAN WINCE,$(OS_ARCH))) +# nsinstall doesn't get built until we enter config/ in the exports phase, +# so we'll have to manually ensure it gets built here if we want to use +# $(EXPORTS) +export:: config/nsinstall$(HOST_BIN_SUFFIX) +$(PUBLIC) $(SDK_PUBLIC): config/nsinstall$(HOST_BIN_SUFFIX) + +config/nsinstall$(HOST_BIN_SUFFIX): $(srcdir)/config/nsinstall.c $(srcdir)/config/pathsub.c + $(MAKE) -C config/ nsinstall$(HOST_BIN_SUFFIX) +endif +endif + +include $(topsrcdir)/config/rules.mk + +ifdef MOZ_SYNC_BUILD_FILES +# Because the SpiderMonkey can be distributed and built independently +# of the Mozilla source tree, it contains its own copies of many of +# the files used by the top-level Mozilla build process, from the +# 'config' and 'build' subtrees. +# +# To make it simpler to keep the copies in sync, we follow the policy +# that the SpiderMonkey copies must always be exact copies of those in +# the containing Mozilla tree. If you've made a change in one, it +# belongs in the other as well. If the change isn't right for both +# places, then that's something to bring up with the other developers. +# +# Some files are reasonable to diverge; for example, +# js/config/autoconf.mk.in doesn't need most of the stuff in +# config/autoconf.mk.in. +check-sync-dirs = $(PYTHON) $(srcdir)/config/check-sync-dirs.py +check:: + $(check-sync-dirs) $(srcdir)/config $(MOZ_SYNC_BUILD_FILES)/config + $(check-sync-dirs) $(srcdir)/build $(MOZ_SYNC_BUILD_FILES)/build + +check-valgrind:: + $(check-sync-dirs) $(srcdir)/config $(MOZ_SYNC_BUILD_FILES)/config + $(check-sync-dirs) $(srcdir)/build $(MOZ_SYNC_BUILD_FILES)/build +endif + +ifdef ENABLE_JIT +check:: + $(wildcard $(RUN_TEST_PROGRAM)) $(PYTHON) -u $(srcdir)/trace-test/trace-test.py \ + --no-slow --no-progress --tinderbox $(DIST)/bin/js$(BIN_SUFFIX) + +check-valgrind:: + $(wildcard $(RUN_TEST_PROGRAM)) $(PYTHON) -u $(srcdir)/trace-test/trace-test.py \ + --valgrind --no-slow --no-progress --tinderbox $(DIST)/bin/js$(BIN_SUFFIX) +endif + +DIST_GARBAGE = config.cache config.log config.status \ + config/myrules.mk config/myconfig.mk \ + unallmakefiles js-config js-config.h js-confdefs.h + +distclean:: + cat unallmakefiles | $(XARGS) rm -f + rm -f $(DIST_GARBAGE) + +# our build system doesn't handle subdir srcs very gracefully today +export:: + mkdir -p nanojit + +DEFINES += -DEXPORT_JS_API + +INCLUDES += -I$(srcdir) + +GARBAGE += jscpucfg.o jsautocfg.h jsautocfg.tmp jscpucfg + +ifneq (,$(CROSS_COMPILE)$(filter-out WINNT,$(OS_ARCH))) +TARGETS += jscpucfg$(HOST_BIN_SUFFIX) +endif + +ifdef JS_THREADSAFE +DEFINES += -DJS_THREADSAFE +endif + +ifdef JS_NO_THIN_LOCKS +DEFINES += -DJS_USE_ONLY_NSPR_LOCKS +endif + +ifdef JS_VERSION +DEFINES += -DJS_VERSION=$(JS_VERSION) +endif + +ifneq ($(findstring -L,$(NSPR_LIBS)),) +NSPR_STATIC_PATH = $(subst -L,,$(findstring -L,$(NSPR_LIBS))) +else +NSPR_STATIC_PATH = $(DIST)/lib +endif + +ifdef MOZ_SHARK +CFLAGS += -F/System/Library/PrivateFrameworks +CXXFLAGS += -F/System/Library/PrivateFrameworks +LDFLAGS += -F/System/Library/PrivateFrameworks -framework CHUD +endif + +ifdef MOZ_VTUNE +CXXFLAGS += -IC:/Program\ Files/Intel/VTune/Analyzer/Include +EXTRA_DSO_LDOPTS += C:/Program\ Files/Intel/VTune/Analyzer/Lib/VtuneApi.lib +LIBS += C:/Program\ Files/Intel/VTune/Analyzer/Lib/VtuneApi.lib +endif + +# BeOS and HP-UX do not require the extra linking of "-lm" +ifeq (,$(filter BeOS HP-UX WINNT WINCE OpenVMS OS2,$(OS_ARCH))) +EXTRA_LIBS += -lm +endif + +# Prevent floating point errors caused by VC++ optimizations +ifeq ($(OS_ARCH)_$(GNU_CC),WINNT_) +ifeq (,$(filter-out 1200 1300 1310,$(_MSC_VER))) +CFLAGS += -Op +else +CFLAGS += -fp:precise +endif +endif # WINNT + +ifeq ($(OS_ARCH),FreeBSD) +EXTRA_LIBS += -pthread +endif +ifeq ($(OS_ARCH),IRIX) +ifdef USE_N32 +DASH_R += -n32 +endif +endif +ifeq ($(OS_ARCH),Linux) +EXTRA_LIBS += -ldl +endif +ifeq ($(OS_ARCH),OSF1) +EXTRA_LIBS += -lc_r +endif +ifeq ($(OS_ARCH),SunOS) +ifeq ($(TARGET_CPU),sparc) + +ifdef GNU_CC +CFLAGS += -mcpu=v9 +CXXFLAGS += -mcpu=v9 +endif # GNU_CC + +endif +ifeq ($(OS_RELEASE),4.1) +EXTRA_LIBS += -ldl -lnsl +else +EXTRA_LIBS += -lposix4 -ldl -lnsl -lsocket +endif +endif + +ifdef MOZ_MEMORY +ifeq ($(OS_ARCH),Darwin) +LDFLAGS += -ljemalloc +endif +endif + +ifdef SOLARIS_SUNPRO_CXX +# Sun Studio SPARC doesn't work well with gcc inline asm, use lock_SunOS_sparc*.il +jslock.o: jslock.cpp Makefile.in lock_sparcv8plus.il lock_sparcv9.il + $(REPORT_BUILD) + @$(MAKE_DEPS_AUTO_CXX) +ifeq (sparcv9,$(findstring sparcv9,$(OS_TEST))) + $(CXX) -o $@ -c $(COMPILE_CFLAGS) $(srcdir)/lock_sparcv9.il $< +else + $(CXX) -o $@ -c $(COMPILE_CFLAGS) $(srcdir)/lock_sparcv8plus.il $< +endif # sparcv9 +endif # SOLARIS_SUNPRO_CXX + +# Allow building jsinterp.c with special optimization flags +ifdef INTERP_OPTIMIZER +jsinterp.$(OBJ_SUFFIX): MODULE_OPTIMIZE_FLAGS=$(INTERP_OPTIMIZER) +endif + +ifeq ($(OS_ARCH),IRIX) +ifndef GNU_CC +_COMPILE_CFLAGS = $(patsubst -O%,-O1,$(COMPILE_CFLAGS)) +jsapi.o jsxdrapi.o jsarena.o jsarray.o jsatom.o jsemit.o jsfun.o jsinterp.o jsregexp.o jsparse.o jsopcode.o jsscript.o: %.o: %.cpp Makefile.in + $(REPORT_BUILD) + @$(MAKE_DEPS_AUTO_CXX) + $(CXX) -o $@ -c $(_COMPILE_CFLAGS) $< +endif +endif + +# An AIX Optimization bug causes PR_dtoa() & JS_dtoa to produce wrong result. +# This suppresses optimization for this single compilation unit. +ifeq ($(OS_ARCH),AIX) +jsatom.o: jsatom.cpp Makefile.in + $(REPORT_BUILD) + @$(MAKE_DEPS_AUTO_CXX) + $(CXX) -o $@ -c $(filter-out $(MOZ_OPTIMIZE_FLAGS), $(COMPILE_CFLAGS)) $< +jsdtoa.o: jsdtoa.cpp Makefile.in + $(REPORT_BUILD) + @$(MAKE_DEPS_AUTO_CXX) + $(CXX) -o $@ -c $(filter-out $(MOZ_OPTIMIZE_FLAGS), $(COMPILE_CFLAGS)) $< +endif + +export:: jsautocfg.h + +ifeq (,$(CROSS_COMPILE)$(GNU_CC)$(filter-out WINNT,$(OS_ARCH))) +jsautocfg.h: + touch $@ +else +ifeq ($(OS_ARCH),WINCE) +jsautocfg.h: + touch $@ +else +jsautocfg.h: jscpucfg$(HOST_BIN_SUFFIX) + @rm -f $@ jsautocfg.tmp + ./jscpucfg > jsautocfg.tmp + mv jsautocfg.tmp $@ +endif +endif + +# jscpucfg is a strange target +# Needs to be built with the host compiler but needs to include +# the mdcpucfg for the target so it needs the appropriate target defines +ifdef HOST_NSPR_MDCPUCFG +HOST_CXX := $(HOST_CXX) -DMDCPUCFG=$(TARGET_NSPR_MDCPUCFG) +HOST_CXXFLAGS := $(patsubst -DXP_%,,$(HOST_CXXFLAGS)) +endif + +ifdef CROSS_COMPILE +# jscpucfg needs to know when it's supposed to produce a config for the target +JSCPUCFG_DEFINES = $(ACDEFINES) +endif + +ifeq ($(OS_ARCH),QNX) +ifneq ($(OS_TARGET),NTO) +# QNX's compiler apparently can't build a binary directly from a source file. +jscpucfg.o: jscpucfg.cpp Makefile.in + $(HOST_CXX) $(HOST_CXXFLAGS) -c $(JSCPUCFG_DEFINES) $(DEFINES) $(NSPR_CFLAGS) -o $@ $< + +jscpucfg: jscpucfg.o + $(HOST_CXX) $(HOST_CXXFLAGS) $(JSCPUCFG_DEFINES) $(DEFINES) -o $@ $< +endif +else +ifeq ($(OS_ARCH),WINCE) +jscpucfg$(HOST_BIN_SUFFIX): + echo no need to build jscpucfg $< +else +jscpucfg$(HOST_BIN_SUFFIX): jscpucfg.cpp Makefile.in + $(HOST_CXX) $(HOST_CXXFLAGS) $(JSCPUCFG_DEFINES) $(DEFINES) $(NSPR_CFLAGS) $(HOST_OUTOPTION)$@ $< +endif +endif + +# Compute the linker flags that programs linking against SpiderMonkey should +# pass to get SpiderMonkey and its dependencies, beyond just the -L and -l +# for the SpiderMonkey library itself. +# - EXTRA_DSO_LDOPTS includes the NSPR -L and -l flags. +# - OS_LIBS includes libraries selected by the configure script. +# - EXTRA_LIBS includes libraries selected by this Makefile. +JS_CONFIG_LIBS=$(EXTRA_DSO_LDOPTS) $(OS_LIBS) $(EXTRA_LIBS) + +# The configure script invokes this rule explicitly at configure time! +# It's important that js-config be ready by the time we're done +# configuring, because we may be running other configure scripts that +# would like to run js-config themselves, before js is built. +# +# This file and rules.mk go through a certain amount of work to decide +# which libraries to build, what to name them, and what flags to pass +# when linking them (and thus what flags its own clients must pass). +# All this information needs to go into the js-config script. To +# avoid trying to re-compute all that in the configure script, we just +# have the configure script generate this Makefile, and then invoke +# this rule. +at=@ +js-config: js-config.in Makefile $(DEPTH)/config/autoconf.mk $(topsrcdir)/config/config.mk $(topsrcdir)/config/rules.mk + rm -f js-config.tmp + sed < $< > js-config.tmp \ + -e 's|$(at)prefix$(at)|$(prefix)|' \ + -e 's|$(at)exec_prefix$(at)|$(exec_prefix)|' \ + -e 's|$(at)includedir$(at)|$(includedir)|' \ + -e 's|$(at)libdir$(at)|$(libdir)|' \ + -e 's|$(at)MOZILLA_VERSION$(at)|$(MOZILLA_VERSION)|' \ + -e 's|$(at)LIBRARY_NAME$(at)|$(LIBRARY_NAME)|' \ + -e 's|$(at)NSPR_CFLAGS$(at)|$(NSPR_CFLAGS)|' \ + -e 's|$(at)JS_CONFIG_LIBS$(at)|$(JS_CONFIG_LIBS)|' \ + -e 's|$(at)MOZ_JS_LIBS$(at)|$(MOZ_JS_LIBS)|' \ + && mv js-config.tmp $@ && chmod +x $@ + +SCRIPTS = js-config +SDK_BINARY = js-config + +install:: $(INSTALLED_HEADERS) + $(SYSINSTALL) $^ $(DESTDIR)$(includedir)/$(MODULE) + +install:: $(SCRIPTS) + $(SYSINSTALL) $^ $(DESTDIR)$(bindir) + +install:: $(LIBRARY) $(SHARED_LIBRARY) $(IMPORT_LIBRARY) +ifneq (,$(LIBRARY)) + $(SYSINSTALL) $(LIBRARY) $(DESTDIR)$(libdir) +endif +ifneq (,$(SHARED_LIBRARY)) + $(SYSINSTALL) $(SHARED_LIBRARY) $(DESTDIR)$(libdir) +endif +ifneq (,$(IMPORT_LIBRARY)) + $(SYSINSTALL) $(IMPORT_LIBRARY) $(DESTDIR)$(libdir) +endif + +# Extra dependancies and rules for auto-generated headers +host_jskwgen.$(OBJ_SUFFIX): jsversion.h jskeyword.tbl + +# Use CURDIR to avoid finding a jsautokw.h in the source tree (from a +# previous build?) via VPATH when we're building in a separate tree. +$(CURDIR)/jsautokw.h: host_jskwgen$(HOST_BIN_SUFFIX) + ./host_jskwgen$(HOST_BIN_SUFFIX) $@ + +host_jsoplengen.$(OBJ_SUFFIX): jsopcode.tbl + +# Use CURDIR to avoid finding a jsautooplen.h in the source tree (from +# a previous build?) via VPATH when we're building in a separate tree. +$(CURDIR)/jsautooplen.h: host_jsoplengen$(HOST_BIN_SUFFIX) + ./host_jsoplengen$(HOST_BIN_SUFFIX) $@ + +# Force auto-header generation before compiling any source that may use them +$(CPPSRCS:%.cpp=%.$(OBJ_SUFFIX)): $(CURDIR)/jsautokw.h $(CURDIR)/jsautooplen.h + +ifdef HAVE_DTRACE +$(CURDIR)/javascript-trace.h: $(srcdir)/javascript-trace.d + dtrace -h -s $(srcdir)/javascript-trace.d -o javascript-trace.h.in + sed 's/if _DTRACE_VERSION/ifdef INCLUDE_MOZILLA_DTRACE/' \ + javascript-trace.h.in > javascript-trace.h + +# We can't automatically generate dependencies on auto-generated headers; +# we have to list them explicitly. +$(addsuffix .$(OBJ_SUFFIX),jsdtracef jsinterp jsobj): $(CURDIR)/javascript-trace.h +endif + +ifdef GNU_CC +ifndef CROSS_COMPILE +# If we don't have run-mozilla.sh *and* we're linking against NSPR, we don't +# know how to run the JS binary. Oh well + +imacro_asm.js: imacro_asm.js.in jsopcode.tbl + $(CC) -c -x c -E -P -I$(srcdir) $< > $@ + +GARBAGE += imacros.c.tmp imacro_asm.js + + +update-imacros: imacros.c.tmp + cp $< $(srcdir)/imacros.c.out + +# Code for importing the nanojit subtree from its repository. + +NANOJIT_CENTRAL_REV=$(shell cat $(srcdir)/nanojit-import-rev) +NANOJIT_CENTRAL_REPO=http://hg.mozilla.org/projects/nanojit-central +NANOJIT_CENTRAL_LOCAL=$(CURDIR)/nanojit-central +CUR_REPO=$(srcdir)/../.. + +update-nanojit: + rm -Rf $(NANOJIT_CENTRAL_LOCAL) import-splicemap import-revmap + hg clone $(NANOJIT_CENTRAL_REPO) $(NANOJIT_CENTRAL_LOCAL) + python $(srcdir)/find-child.py \ + --src=$(NANOJIT_CENTRAL_LOCAL) \ + --dst=$(CUR_REPO) \ + --start=$(NANOJIT_CENTRAL_REV) \ + --filemap=$(srcdir)/nanojit-import-filemap \ + >import-splicemap + hg convert --config convert.hg.saverev=True \ + --config convert.hg.startrev=`cut -d ' ' -f 1 import-splicemap` \ + --filemap=$(srcdir)/nanojit-import-filemap \ + --splicemap=import-splicemap \ + $(NANOJIT_CENTRAL_LOCAL) \ + $(CUR_REPO) \ + import-revmap + (cd $(srcdir) && hg up) + (cd $(NANOJIT_CENTRAL_LOCAL) && hg log -r tip --template "{node}\n") >$(srcdir)/nanojit-import-rev + (cd $(srcdir) && hg commit --message="Update nanojit-import-rev stamp." nanojit-import-rev) + +.PHONY: update-imacros update-nanojit +endif +endif diff --git a/ape-server/deps/js/src/Makefile.ref b/ape-server/deps/js/src/Makefile.ref new file mode 100755 index 0000000..edf7f54 --- /dev/null +++ b/ape-server/deps/js/src/Makefile.ref @@ -0,0 +1,470 @@ +# -*- Mode: makefile -*- +# vim: ft=make +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Michael Ang +# Kevin Buhr +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# JSRef GNUmake makefile. +# +# Note: dependency rules are missing for some files (some +# .h, all .msg, etc.) Re-make clean if in doubt. +# + + +DEPTH = . + +include config.mk + +#NS_USE_NATIVE = 1 + +ifndef NANOJIT_ARCH +$(warning NANOJIT_ARCH not defined in config/$(OS_CONFIG).mk, JIT disabled) +else +ifdef DISABLE_JIT +$(warning disabling JIT per build specification) +else +ENABLE_JIT=1 +endif +endif + +ifdef ENABLE_JIT +DEFINES += -DJS_TRACER +DEFINES += -DFEATURE_NANOJIT +INCLUDES += -Inanojit +endif + +#ifndef BUILD_OPT +#DEFINES += -Ivprof +#endif + +ifdef NARCISSUS +DEFINES += -DNARCISSUS +endif + +# Look in OBJDIR to find jsautocfg.h, jsautokw.h, and js-config.h +INCLUDES += -I. -I$(OBJDIR) + +ifdef JS_THREADSAFE +DEFINES += -DJS_THREADSAFE +INCLUDES += -I$(DIST)/include/nspr +ifdef USE_MSVC +OTHER_LIBS += $(DIST)/lib/libnspr$(NSPR_LIBSUFFIX).lib +else +OTHER_LIBS += -L$(DIST)/lib -lnspr$(NSPR_LIBSUFFIX) +endif +endif + +ifdef JS_NO_THIN_LOCKS +DEFINES += -DJS_USE_ONLY_NSPR_LOCKS +endif + +ifdef JS_HAS_FILE_OBJECT +DEFINES += -DJS_HAS_FILE_OBJECT +endif + +ifdef JS_GC_ZEAL +DEFINES += -DJS_GC_ZEAL +endif + +# +# XCFLAGS may be set in the environment or on the gmake command line +# +#CFLAGS += -DDEBUG -DDEBUG_brendan -DJS_ARENAMETER -DJS_HASHMETER -DJS_DUMP_PROPTREE_STATS -DJS_DUMP_SCOPE_METERS -DJS_SCOPE_DEPTH_METER -DJS_BASIC_STATS +CFLAGS += $(OS_CFLAGS) $(DEFINES) $(INCLUDES) $(XCFLAGS) + +LDFLAGS = $(XLDFLAGS) +LDFLAGS += $(OS_LDFLAGS) + +ifdef MOZ_SHARK +DEFINES += -DMOZ_SHARK +CFLAGS += -F/System/Library/PrivateFrameworks +LDFLAGS += -F/System/Library/PrivateFrameworks -framework CHUD +endif +ifdef MOZ_CALLGRIND +DEFINES += -DMOZ_CALLGRIND +endif +ifdef MOZ_VTUNE +DEFINES += -DMOZ_VTUNE +CXXFLAGS += -IC:/Program\ Files/Intel/VTune/Analyzer/Include +OTHER_LIBS += C:/Program\ Files/Intel/VTune/Analyzer/Lib/VtuneApi.lib +endif + +ifndef NO_LIBM +LDFLAGS += -lm +endif + +# Prevent floating point errors caused by VC++ optimizations +ifeq ($(OS_ARCH),WINNT) +_MSC_VER = $(shell $(CXX) 2>&1 | sed -n 's/.*Compiler Version \([0-9]*\)\.\([0-9]*\).*/\1\2/p') +ifeq (,$(filter-out 1200 1300 1310,$(_MSC_VER))) +CFLAGS += -Op +else +CFLAGS += -fp:precise +endif +endif # WINNT + +# +# Server-related changes : +# +ifdef NES40 +DEFINES += -DNES40 +endif + +# +# Line editing support. +# Define JS_READLINE or JS_EDITLINE to enable line editing in the +# js command-line interpreter. +# +ifdef JS_READLINE +# For those platforms with the readline library installed. +DEFINES += -DEDITLINE +PROG_LIBS += -lreadline -ltermcap +else +ifdef JS_EDITLINE +# Use the editline library, built locally. +PREDIRS += editline +DEFINES += -DEDITLINE +PROG_LIBS += $(OBJDIR)/editline/libedit.a +endif +endif + +# For purify +PURE_CFLAGS = -DXP_UNIX $(OPTIMIZER) $(PURE_OS_CFLAGS) $(DEFINES) \ + $(INCLUDES) $(XCFLAGS) + +# +# JS file lists +# +JS_HFILES = \ + jsarray.h \ + jsatom.h \ + jsbool.h \ + jscntxt.h \ + jsdate.h \ + jsemit.h \ + jsexn.h \ + jsfun.h \ + jsgc.h \ + jsinterp.h \ + jsiter.h \ + jslibmath.h \ + jslock.h \ + jsmath.h \ + jsnum.h \ + jsobj.h \ + json.h \ + jsopcode.h \ + jsparse.h \ + jsarena.h \ + jsclist.h \ + jsdhash.h \ + jsdtoa.h \ + jshash.h \ + jslong.h \ + jstypes.h \ + jsprvtd.h \ + jspubtd.h \ + jsregexp.h \ + jsscan.h \ + jsscope.h \ + jsscript.h \ + jsstr.h \ + jsversion.h \ + jsxdrapi.h \ + jsxml.h \ + $(NULL) + +ifdef ENABLE_JIT +JS_HFILES += \ + jstracer.h \ + nanojit/Assembler.h \ + nanojit/LIR.h \ + nanojit/Native$(NANOJIT_ARCH).h \ + nanojit/avmplus.h \ + nanojit/vm_fops.h \ + nanojit/Fragmento.h \ + nanojit/Native.h \ + nanojit/RegAlloc.h \ + nanojit/nanojit.h \ + $(NULL) +endif + +ifndef BUILD_OPT +#JS_HFILES += \ +# vprof/vprof.h \ +# $(NULL) +endif + +API_HFILES = \ + jsapi.h \ + jsdbgapi.h \ + $(NULL) + +OTHER_HFILES = \ + jsbit.h \ + jscompat.h \ + jscpucfg.h \ + jsotypes.h \ + prmjtime.h \ + resource.h \ + jsopcode.tbl \ + jsproto.tbl \ + js.msg \ + jsshell.msg \ + jskeyword.tbl \ + $(NULL) + +ifndef PREBUILT_CPUCFG +OTHER_HFILES += $(OBJDIR)/jsautocfg.h +endif +OTHER_HFILES += $(OBJDIR)/jsautokw.h $(OBJDIR)/js-config.h + +HFILES = $(JS_HFILES) $(API_HFILES) $(OTHER_HFILES) + +JS_CPPFILES = \ + jsapi.cpp \ + jsarena.cpp \ + jsarray.cpp \ + jsatom.cpp \ + jsbool.cpp \ + jscntxt.cpp \ + jsdate.cpp \ + jsdbgapi.cpp \ + jsdhash.cpp \ + jsdtoa.cpp \ + jsemit.cpp \ + jsexn.cpp \ + jsfun.cpp \ + jsgc.cpp \ + jshash.cpp \ + jsinterp.cpp \ + jsinvoke.cpp \ + jsiter.cpp \ + jslock.cpp \ + jslog2.cpp \ + jslong.cpp \ + jsmath.cpp \ + jsnum.cpp \ + jsobj.cpp \ + json.cpp \ + jsopcode.cpp \ + jsparse.cpp \ + jsprf.cpp \ + jsregexp.cpp \ + jsscan.cpp \ + jsscope.cpp \ + jsscript.cpp \ + jsstr.cpp \ + jsutil.cpp \ + jsxdrapi.cpp \ + jsxml.cpp \ + prmjtime.cpp \ + $(NULL) + +ifdef ENABLE_JIT +JS_CPPFILES += \ + jsbuiltins.cpp \ + jstracer.cpp \ + nanojit/Assembler.cpp \ + nanojit/Fragmento.cpp \ + nanojit/LIR.cpp \ + nanojit/Native$(NANOJIT_ARCH).cpp \ + nanojit/RegAlloc.cpp \ + nanojit/avmplus.cpp \ + $(NULL) + +endif + +ifndef BUILD_OPT +#JS_CPPFILES += \ +# vprof/vprof.cpp \ +# $(NULL) +endif + +ifdef JS_HAS_FILE_OBJECT +JS_CPPFILES += jsfile.cpp +JS_HFILES += jsfile.h +endif + +LIB_CPPFILES = $(JS_CPPFILES) +LIB_ASFILES := $(wildcard *_$(OS_ARCH).s) +PROG_CPPFILES = js.cpp + +ifdef USE_MSVC +LIBRARY = $(OBJDIR)/js32.lib +SHARED_LIBRARY = $(OBJDIR)/js32.dll +PROGRAM = $(OBJDIR)/js.exe +else +LIBRARY = $(OBJDIR)/libjs.a +SHARED_LIBRARY = $(OBJDIR)/libjs.$(SO_SUFFIX) +PROGRAM = $(OBJDIR)/js +endif + +include rules.mk + +MOZ_DEPTH = ../.. +include jsconfig.mk + +nsinstall-target: + cd ../../config; $(MAKE) OBJDIR=$(OBJDIR) OBJDIR_NAME=$(OBJDIR) + +# +# Automatic header generation +# + +AUTO_HEADERS = \ + $(OBJDIR)/jsautokw.h \ + $(OBJDIR)/jsautooplen.h \ + $(NULL) + +$(OBJDIR)/jsautokw.h: jskeyword.tbl + +$(OBJDIR)/jsautooplen.h: jsopcode.tbl + +GARBAGE += $(AUTO_HEADERS) +GARBAGE += $(AUTO_HEADERS:$(OBJDIR)/jsauto%.h=$(OBJDIR)/js%gen$(HOST_BIN_SUFFIX)) + +ifdef USE_MSVC + +GARBAGE += $(AUTO_HEADERS:$(OBJDIR)/jsauto%.h=$(OBJDIR)/js%gen.obj) + +$(AUTO_HEADERS): $(OBJDIR)/jsauto%.h: js%gen.cpp + @$(MAKE_OBJDIR) + $(CXX) -Fo$(OBJDIR)/ -c $(CFLAGS) $(OPTIMIZER) $< + link.exe -out:"$(OBJDIR)/js$*gen$(HOST_BIN_SUFFIX)" $(EXE_LINK_FLAGS) $(OBJDIR)/js$*gen.obj + $(OBJDIR)/js$*gen$(HOST_BIN_SUFFIX) $@ +else + +GARBAGE += $(AUTO_HEADERS:$(OBJDIR)/jsauto%.h=$(OBJDIR)/js%gen.d) +$(AUTO_HEADERS): $(OBJDIR)/jsauto%.h: js%gen.cpp + @$(MAKE_OBJDIR) + $(CXX) -o $(OBJDIR)/js$*gen$(HOST_BIN_SUFFIX) $(CFLAGS) $(OPTIMIZER) $(LDFLAGS) $< + $(OBJDIR)/js$*gen$(HOST_BIN_SUFFIX) $@ + +endif + +# force creation of autoheaders before compiling any source that may use them +$(LIB_OBJS) : $(AUTO_HEADERS) + +# +# An installed header file describing configuration options that affect +# the API. +# + +# Avoid rebuilding unless js-config.h's contents actually change. The +# timestamp on js-config.h.stamp corresponds to the last time we +# checked that js-config.h was up to date. If the stamp changes but +# js-config.h does not, then make concludes that targets depending on +# js-config.h don't need to be rebuilt. The dummy '@true' rule here +# keeps make from concluding that js-config.h never changes. +$(OBJDIR)/js-config.h: $(OBJDIR)/js-config.h.stamp + @true + +js-config-switch=$(if $(value $($1)),-e 's/\#undef $1/\#define $1/') +$(OBJDIR)/js-config.h.stamp: js-config.h.in Makefile.ref + sed < $< > $(@:.stamp=.tmp) \ + $(call js-config-switch,JS_THREADSAFE) \ + $(call js-config-switch,JS_GC_ZEAL) \ + -e :dummy + if ! [ -f $(@:.stamp=) ] || ! cmp $(@:.stamp=.tmp) $(@:.stamp=); then \ + mv $(@:.stamp=.tmp) $(@:.stamp=); \ + fi + touch $@ + +GARBAGE += $(OBJDIR)/js-config.h $(OBJDIR)/js-config.h.stamp + +# Force creation of js-config.h before compiling any source that may use it. +$(LIB_OBJS) : $(OBJDIR)/js-config.h + +# +# JS shell executable +# + +ifdef USE_MSVC +$(PROGRAM): $(PROG_OBJS) $(LIBRARY) + link.exe -out:"$@" $(EXE_LINK_FLAGS) $^ +else +$(PROGRAM): $(PROG_OBJS) $(LIBRARY) + $(CXX) -o $@ $(CFLAGS) $(PROG_OBJS) $(LIBRARY) $(LDFLAGS) $(OTHER_LIBS) \ + $(PROG_LIBS) +endif + +$(PROGRAM).pure: $(PROG_OBJS) $(LIBRARY) + purify $(PUREFLAGS) \ + $(CXX) -o $@ $(PURE_OS_CFLAGS) $(PROG_OBJS) $(LIBRARY) $(LDFLAGS) \ + $(OTHER_LIBS) $(PROG_LIBS) + +ifndef PREBUILT_CPUCFG +$(filter-out jscpucfg.h $(OBJDIR)/jsautocfg.h, $(HFILES)) $(CPPFILES): $(OBJDIR)/jsautocfg.h + +$(OBJDIR)/jsautocfg.h: $(OBJDIR)/jscpucfg + rm -f $@ + $(OBJDIR)/jscpucfg > $@ + +$(OBJDIR)/jscpucfg: $(OBJDIR)/jscpucfg.o + $(CXX) $(OS_LDFLAGS) -o $@ $(OBJDIR)/jscpucfg.o + +GARBAGE += $(OBJDIR)/jsautocfg.h $(OBJDIR)/jscpucfg \ + $(OBJDIR)/jscpucfg.o $(OBJDIR)/jscpucfg.d +endif + +# Automatic make dependencies files +DEPENDENCIES = $(CPPFILES:%.cpp=$(OBJDIR)/%.d) + +# +# Hardwire dependencies for some files +# +ifdef USE_MSVC +OBJ=obj +else +OBJ=o +endif + +$(OBJDIR)/jsinvoke.$(OBJ): jsinterp.h jsinterp.cpp +$(OBJDIR)/jsinvoke.obj : jsinterp.h jsinterp.cpp + +-include $(DEPENDENCIES) + +TARNAME = jsref.tar +TARFILES = files `cat files` + +SUFFIXES: .i +%.i: %.cpp + $(CXX) -C -E $(CFLAGS) $< > $*.i diff --git a/ape-server/deps/js/src/README.html b/ape-server/deps/js/src/README.html new file mode 100755 index 0000000..6eb1567 --- /dev/null +++ b/ape-server/deps/js/src/README.html @@ -0,0 +1,54 @@ + + + + + + + SpiderMonkey README + + + +

SpiderMonkey README

+ +

See the +SpiderMonkey +pages on the Mozilla Developer Center. + + + diff --git a/ape-server/deps/js/src/SpiderMonkey.rsp b/ape-server/deps/js/src/SpiderMonkey.rsp new file mode 100755 index 0000000..eb93040 --- /dev/null +++ b/ape-server/deps/js/src/SpiderMonkey.rsp @@ -0,0 +1,3 @@ +mozilla/js/src/* +mozilla/js/src/config/* +mozilla/js/src/macbuild/* diff --git a/ape-server/deps/js/src/Y.js b/ape-server/deps/js/src/Y.js new file mode 100755 index 0000000..e92a65a --- /dev/null +++ b/ape-server/deps/js/src/Y.js @@ -0,0 +1,19 @@ +// The Y combinator, applied to the factorial function + +function factorial(proc) { + return function (n) { + return (n <= 1) ? 1 : n * proc(n-1); + } +} + +function Y(outer) { + function inner(proc) { + function apply(arg) { + return proc(proc)(arg); + } + return outer(apply); + } + return inner(inner); +} + +print("5! is " + Y(factorial)(5)); diff --git a/ape-server/deps/js/src/aclocal.m4 b/ape-server/deps/js/src/aclocal.m4 new file mode 100755 index 0000000..3745619 --- /dev/null +++ b/ape-server/deps/js/src/aclocal.m4 @@ -0,0 +1,13 @@ +dnl +dnl Local autoconf macros used with mozilla +dnl The contents of this file are under the Public Domain. +dnl + +builtin(include, build/autoconf/pkg.m4)dnl +builtin(include, build/autoconf/nspr.m4)dnl +builtin(include, build/autoconf/altoptions.m4)dnl +builtin(include, build/autoconf/moznbytetype.m4)dnl +builtin(include, build/autoconf/mozprog.m4)dnl +builtin(include, build/autoconf/acwinpaths.m4)dnl + +MOZ_PROG_CHECKMSYS() diff --git a/ape-server/deps/js/src/autom4te.cache/output.0t b/ape-server/deps/js/src/autom4te.cache/output.0t new file mode 100755 index 0000000..e69de29 diff --git a/ape-server/deps/js/src/autom4te.cache/requests b/ape-server/deps/js/src/autom4te.cache/requests new file mode 100755 index 0000000..e69de29 diff --git a/ape-server/deps/js/src/autom4te.cache/traces.0t b/ape-server/deps/js/src/autom4te.cache/traces.0t new file mode 100755 index 0000000..e69de29 diff --git a/ape-server/deps/js/src/bench.sh b/ape-server/deps/js/src/bench.sh new file mode 100755 index 0000000..c5b1ec5 --- /dev/null +++ b/ape-server/deps/js/src/bench.sh @@ -0,0 +1,5 @@ +#!/bin/bash +X="var d = Date.now();"; +for i in t/*.js; do X="$X load(\"$i\");"; done +X="$X print(Date.now() - d);" +echo $X | $1 -j diff --git a/ape-server/deps/js/src/build/autoconf/acoutput-fast.pl b/ape-server/deps/js/src/build/autoconf/acoutput-fast.pl new file mode 100755 index 0000000..973dd86 --- /dev/null +++ b/ape-server/deps/js/src/build/autoconf/acoutput-fast.pl @@ -0,0 +1,202 @@ +#! /usr/bin/env perl +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1999 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# acoutput-fast.pl - Quickly create makefiles that are in a common format. +# +# Most of the makefiles in mozilla only refer to two configure variables: +# @srcdir@ +# @top_srcdir@ +# However, configure does not know any better and it runs sed on each file +# with over 150 replacement rules (slow as molasses). +# +# This script takes a list of makefiles as input. For example, +# +# echo $MAKEFILES | acoutput-fast.pl +# +# The script creates each Makefile that only references @srcdir@ and +# @top_srcdir@. For other files, it lists them in a shell command that is +# printed to stdout: +# +# CONFIG_FILES="unhandled_files..."; export CONFIG_FILES +# +# This command can be used to have config.status create the unhandled +# files. For example, +# +# eval "echo $MAKEFILES | acoutput-fast.pl" +# AC_OUTPUT($MAKEFILES) +# +# Send comments, improvements, bugs to Steve Lamm (slamm@netscape.com). + +#use File::Basename; +sub dirname { + my $dir = $_[0]; + return '.' if not $dir =~ m%/%; + $dir =~ s%/[^/][^/]*$%%; + return $dir; +} + +# Create one directory. Assumes it doesn't already exist. +# Will create parent(s) if needed. +sub create_directory { + my $dir = $_[0]; + my $parent = dirname($dir); + create_directory($parent) if not -d $parent; + mkdir "$dir",0777; +} + +# Create all the directories at once. +# This can be much faster than calling mkdir() for each one. +sub create_directories { + my @makefiles = @_; + my @dirs = (); + my $ac_file; + foreach $ac_file (@makefiles) { + push @dirs, dirname($ac_file); + } + # Call mkdir with the directories sorted by subdir count (how many /'s) + if (@dirs) { + foreach $dir (@dirs) { + if (not -d $dir) { + print STDERR "Creating directory $dir\n"; + create_directory($dir); + } + } + } +} + +while($arg = shift) { + if ($arg =~ /^--srcdir=/) { + $ac_given_srcdir = (split /=/, $arg)[1]; + } + if ($arg =~ /^--cygwin-srcdir/) { + $ac_cygwin_srcdir = (split /=/, $arg)[1]; + } +} + +if (!$ac_given_srcdir) { + $ac_given_srcdir = $0; + $ac_given_srcdir =~ s|/?build/autoconf/.*$||; + $ac_given_srcdir = '.' if $ac_given_srcdir eq ''; +} + +if (!$ac_cygwin_srcdir) { + $ac_cygwin_srcdir = $ac_given_srcdir; +} + +# Read list of makefiles from the stdin or, +# from files listed on the command-line. +# +@makefiles=(); +push @makefiles, split while (); + +# Create all the directories at once. +# This can be much faster than calling mkdir() for each one. +create_directories(@makefiles); + +# Output the makefiles. +# +@unhandled=(); +foreach $ac_file (@makefiles) { + if (not $ac_file =~ /Makefile$/ or $ac_file =~ /:/) { + push @unhandled, $ac_file; + next; + } + $ac_file_in = "$ac_given_srcdir/$ac_file.in"; + $ac_dir = dirname($ac_file); + if ($ac_dir eq '.') { + $ac_dir_suffix = ''; + $ac_dots = ''; + } else { + $ac_dir_suffix = "/$ac_dir"; + $ac_dir_suffix =~ s%^/\./%/%; + $ac_dots = $ac_dir_suffix; + $ac_dots =~ s%/[^/]*%../%g; + } + if ($ac_given_srcdir eq '.') { + $srcdir = '.'; + if ($ac_dots eq '') { + $top_srcdir = '.' + } else { + $top_srcdir = $ac_dots; + $top_srcdir =~ s%/$%%; + } + } elsif ($ac_cygwin_srcdir =~ m%^/% or $ac_cygwin_srcdir =~ m%^.:/%) { + $srcdir = "$ac_cygwin_srcdir$ac_dir_suffix"; + $top_srcdir = "$ac_cygwin_srcdir"; + } else { + $srcdir = "$ac_dots$ac_cygwin_srcdir$ac_dir_suffix"; + $top_srcdir = "$ac_dots$ac_cygwin_srcdir"; + } + + if (-e $ac_file) { + next if -M _ < -M $ac_file_in; + print STDERR "updating $ac_file\n"; + } else { + print STDERR "creating $ac_file\n"; + } + + open (INFILE, "<$ac_file_in") + or ( warn "can't read $ac_file_in: No such file or directory\n" and next); + open (OUTFILE, ">$ac_file") + or ( warn "Unable to create $ac_file\n" and next); + + while () { + #if (/\@[_a-zA-Z]*\@.*\@[_a-zA-Z]*\@/) { + # warn "Two defines on a line:$ac_file:$.:$_"; + # push @unhandled, $ac_file; + # last; + #} + + s/\@srcdir\@/$srcdir/g; + s/\@top_srcdir\@/$top_srcdir/g; + + if (/\@[_a-zA-Z]*\@/) { + warn "Unknown variable:$ac_file:$.:$_"; + push @unhandled, $ac_file; + last; + } + print OUTFILE; + } + close INFILE; + close OUTFILE; +} + +# Print the shell command to be evaluated by configure. +# +print "CONFIG_FILES=\"".join(' ', @unhandled)."\"; export CONFIG_FILES\n"; + diff --git a/ape-server/deps/js/src/build/autoconf/acwinpaths.m4 b/ape-server/deps/js/src/build/autoconf/acwinpaths.m4 new file mode 100755 index 0000000..5f6810e --- /dev/null +++ b/ape-server/deps/js/src/build/autoconf/acwinpaths.m4 @@ -0,0 +1,65 @@ +dnl ***** BEGIN LICENSE BLOCK ***** +dnl Version: MPL 1.1/GPL 2.0/LGPL 2.1 +dnl +dnl The contents of this file are subject to the Mozilla Public License Version +dnl 1.1 (the "License"); you may not use this file except in compliance with +dnl the License. You may obtain a copy of the License at +dnl http://www.mozilla.org/MPL/ +dnl +dnl Software distributed under the License is distributed on an "AS IS" basis, +dnl WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +dnl for the specific language governing rights and limitations under the +dnl License. +dnl +dnl The Original Code is mozilla.org code. +dnl +dnl The Initial Developer of the Original Code is the +dnl Mozilla Foundation +dnl +dnl Portions created by the Initial Developer are Copyright (C) 2009 +dnl the Initial Developer. All Rights Reserved. +dnl +dnl Contributor(s): +dnl Benjamin Smedberg +dnl +dnl Alternatively, the contents of this file may be used under the terms of +dnl either of the GNU General Public License Version 2 or later (the "GPL"), +dnl or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +dnl in which case the provisions of the GPL or the LGPL are applicable instead +dnl of those above. If you wish to allow use of your version of this file only +dnl under the terms of either the GPL or the LGPL, and not to allow others to +dnl use your version of this file under the terms of the MPL, indicate your +dnl decision by deleting the provisions above and replace them with the notice +dnl and other provisions required by the GPL or the LGPL. If you do not delete +dnl the provisions above, a recipient may use your version of this file under +dnl the terms of any one of the MPL, the GPL or the LGPL. +dnl +dnl ***** END LICENSE BLOCK ***** + +define(GENERATE_SUB_ABS, [ +define([AC_OUTPUT_FILES_SUB1], [ +patsubst($@, [/\*)], [/* | ?:/*)]) +]) +]) +GENERATE_SUB_ABS(defn([AC_OUTPUT_FILES])) + +define(GENERATE_SUB_NOSPLIT, [ +define([AC_OUTPUT_FILES], [ +patsubst($@, [-e "s%:% \$ac_given_srcdir/%g"], []) +]) +]) +GENERATE_SUB_NOSPLIT(defn([AC_OUTPUT_FILES_SUB1])) + +define(GENERATE_HEADER_NOSPLIT, [ +define([AC_OUTPUT_HEADER], [ +patsubst($@, [-e "s%:% \$ac_given_srcdir/%g"], []) +]) +]) +GENERATE_HEADER_NOSPLIT(defn([AC_OUTPUT_HEADER])) + +define(GENERATE_SUBDIRS_ABS, [ +define([AC_OUTPUT_SUBDIRS], [ +patsubst($@, [/\*)], [/* | ?:/*)]) +]) +]) +GENERATE_SUBDIRS_ABS(defn([AC_OUTPUT_SUBDIRS])) diff --git a/ape-server/deps/js/src/build/autoconf/altoptions.m4 b/ape-server/deps/js/src/build/autoconf/altoptions.m4 new file mode 100755 index 0000000..f9db0a5 --- /dev/null +++ b/ape-server/deps/js/src/build/autoconf/altoptions.m4 @@ -0,0 +1,154 @@ +dnl ***** BEGIN LICENSE BLOCK ***** +dnl Version: MPL 1.1/GPL 2.0/LGPL 2.1 +dnl +dnl The contents of this file are subject to the Mozilla Public License Version +dnl 1.1 (the "License"); you may not use this file except in compliance with +dnl the License. You may obtain a copy of the License at +dnl http://www.mozilla.org/MPL/ +dnl +dnl Software distributed under the License is distributed on an "AS IS" basis, +dnl WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +dnl for the specific language governing rights and limitations under the +dnl License. +dnl +dnl The Original Code is mozilla.org code. +dnl +dnl The Initial Developer of the Original Code is +dnl Netscape Communications Corporation. +dnl Portions created by the Initial Developer are Copyright (C) 1999 +dnl the Initial Developer. All Rights Reserved. +dnl +dnl Contributor(s): +dnl +dnl Alternatively, the contents of this file may be used under the terms of +dnl either of the GNU General Public License Version 2 or later (the "GPL"), +dnl or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +dnl in which case the provisions of the GPL or the LGPL are applicable instead +dnl of those above. If you wish to allow use of your version of this file only +dnl under the terms of either the GPL or the LGPL, and not to allow others to +dnl use your version of this file under the terms of the MPL, indicate your +dnl decision by deleting the provisions above and replace them with the notice +dnl and other provisions required by the GPL or the LGPL. If you do not delete +dnl the provisions above, a recipient may use your version of this file under +dnl the terms of any one of the MPL, the GPL or the LGPL. +dnl +dnl ***** END LICENSE BLOCK ***** + +dnl altoptions.m4 - An alternative way of specifying command-line options. +dnl These macros are needed to support a menu-based configurator. +dnl This file also includes the macro, AM_READ_MYCONFIG, for reading +dnl the 'myconfig.m4' file. + +dnl Send comments, improvements, bugs to Steve Lamm (slamm@netscape.com). + + +dnl MOZ_ARG_ENABLE_BOOL( NAME, HELP, IF-YES [, IF-NO [, ELSE]]) +dnl MOZ_ARG_DISABLE_BOOL( NAME, HELP, IF-NO [, IF-YES [, ELSE]]) +dnl MOZ_ARG_ENABLE_STRING( NAME, HELP, IF-SET [, ELSE]) +dnl MOZ_ARG_ENABLE_BOOL_OR_STRING( NAME, HELP, IF-YES, IF-NO, IF-SET[, ELSE]]]) +dnl MOZ_ARG_WITH_BOOL( NAME, HELP, IF-YES [, IF-NO [, ELSE]) +dnl MOZ_ARG_WITHOUT_BOOL( NAME, HELP, IF-NO [, IF-YES [, ELSE]) +dnl MOZ_ARG_WITH_STRING( NAME, HELP, IF-SET [, ELSE]) +dnl MOZ_ARG_HEADER(Comment) +dnl MOZ_CHECK_PTHREADS( NAME, IF-YES [, ELSE ]) +dnl MOZ_READ_MYCONFIG() - Read in 'myconfig.sh' file + + +dnl MOZ_TWO_STRING_TEST(NAME, VAL, STR1, IF-STR1, STR2, IF-STR2 [, ELSE]) +AC_DEFUN([MOZ_TWO_STRING_TEST], +[if test "[$2]" = "[$3]"; then + ifelse([$4], , :, [$4]) + elif test "[$2]" = "[$5]"; then + ifelse([$6], , :, [$6]) + else + ifelse([$7], , + [AC_MSG_ERROR([Option, [$1], does not take an argument ([$2]).])], + [$7]) + fi]) + +dnl MOZ_ARG_ENABLE_BOOL(NAME, HELP, IF-YES [, IF-NO [, ELSE]]) +AC_DEFUN([MOZ_ARG_ENABLE_BOOL], +[AC_ARG_ENABLE([$1], [$2], + [MOZ_TWO_STRING_TEST([$1], [$enableval], yes, [$3], no, [$4])], + [$5])]) + +dnl MOZ_ARG_DISABLE_BOOL(NAME, HELP, IF-NO [, IF-YES [, ELSE]]) +AC_DEFUN([MOZ_ARG_DISABLE_BOOL], +[AC_ARG_ENABLE([$1], [$2], + [MOZ_TWO_STRING_TEST([$1], [$enableval], no, [$3], yes, [$4])], + [$5])]) + +dnl MOZ_ARG_ENABLE_STRING(NAME, HELP, IF-SET [, ELSE]) +AC_DEFUN([MOZ_ARG_ENABLE_STRING], +[AC_ARG_ENABLE([$1], [$2], [$3], [$4])]) + +dnl MOZ_ARG_ENABLE_BOOL_OR_STRING(NAME, HELP, IF-YES, IF-NO, IF-SET[, ELSE]]]) +AC_DEFUN([MOZ_ARG_ENABLE_BOOL_OR_STRING], +[ifelse([$5], , + [errprint([Option, $1, needs an "IF-SET" argument. +]) + m4exit(1)], + [AC_ARG_ENABLE([$1], [$2], + [MOZ_TWO_STRING_TEST([$1], [$enableval], yes, [$3], no, [$4], [$5])], + [$6])])]) + +dnl MOZ_ARG_WITH_BOOL(NAME, HELP, IF-YES [, IF-NO [, ELSE]) +AC_DEFUN([MOZ_ARG_WITH_BOOL], +[AC_ARG_WITH([$1], [$2], + [MOZ_TWO_STRING_TEST([$1], [$withval], yes, [$3], no, [$4])], + [$5])]) + +dnl MOZ_ARG_WITHOUT_BOOL(NAME, HELP, IF-NO [, IF-YES [, ELSE]) +AC_DEFUN([MOZ_ARG_WITHOUT_BOOL], +[AC_ARG_WITH([$1], [$2], + [MOZ_TWO_STRING_TEST([$1], [$withval], no, [$3], yes, [$4])], + [$5])]) + +dnl MOZ_ARG_WITH_STRING(NAME, HELP, IF-SET [, ELSE]) +AC_DEFUN([MOZ_ARG_WITH_STRING], +[AC_ARG_WITH([$1], [$2], [$3], [$4])]) + +dnl MOZ_ARG_HEADER(Comment) +dnl This is used by webconfig to group options +define(MOZ_ARG_HEADER, [# $1]) + +dnl +dnl Apparently, some systems cannot properly check for the pthread +dnl library unless is included so we need to test +dnl using it +dnl +dnl MOZ_CHECK_PTHREADS(lib, success, failure) +AC_DEFUN([MOZ_CHECK_PTHREADS], +[ +AC_MSG_CHECKING([for pthread_create in -l$1]) +echo " + #include + #include + void *foo(void *v) { int a = 1; } + int main() { + pthread_t t; + if (!pthread_create(&t, 0, &foo, 0)) { + pthread_join(t, 0); + } + exit(0); + }" > dummy.c ; + echo "${CC-cc} -o dummy${ac_exeext} dummy.c $CFLAGS $CPPFLAGS -l[$1] $LDFLAGS $LIBS" 1>&5; + ${CC-cc} -o dummy${ac_exeext} dummy.c $CFLAGS $CPPFLAGS -l[$1] $LDFLAGS $LIBS 2>&5; + _res=$? ; + rm -f dummy.c dummy${ac_exeext} ; + if test "$_res" = "0"; then + AC_MSG_RESULT([yes]) + [$2] + else + AC_MSG_RESULT([no]) + [$3] + fi +]) + +dnl MOZ_READ_MYCONFIG() - Read in 'myconfig.sh' file +AC_DEFUN([MOZ_READ_MOZCONFIG], +[AC_REQUIRE([AC_INIT_BINSH])dnl +# Read in '.mozconfig' script to set the initial options. +# See the mozconfig2configure script for more details. +_AUTOCONF_TOOLS_DIR=`dirname [$]0`/[$1]/build/autoconf +. $_AUTOCONF_TOOLS_DIR/mozconfig2configure]) diff --git a/ape-server/deps/js/src/build/autoconf/config.guess b/ape-server/deps/js/src/build/autoconf/config.guess new file mode 100755 index 0000000..721b21a --- /dev/null +++ b/ape-server/deps/js/src/build/autoconf/config.guess @@ -0,0 +1,1537 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 +# Free Software Foundation, Inc. + +timestamp='2009-08-19' + +# This file 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. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + + +# Originally written by Per Bothner. Please send patches (context +# diff format) to and include a ChangeLog +# entry. +# +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. +# +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, +2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +trap 'exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +set_cc_for_build=' +trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; +trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; +: ${TMPDIR=/tmp} ; + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; +dummy=$tmp/dummy ; +tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int x;" > $dummy.c ; + for c in cc gcc c89 c99 ; do + if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac ; set_cc_for_build= ;' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || echo unknown)` + case "${UNAME_MACHINE_ARCH}" in + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + sh5el) machine=sh5le-unknown ;; + *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE_ARCH}" in + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ELF__ + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "${UNAME_VERSION}" in + Debian*) + release='-gnu' + ;; + *) + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit ;; + *:OpenBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} + exit ;; + *:ekkoBSD:*:*) + echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} + exit ;; + *:SolidBSD:*:*) + echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} + exit ;; + macppc:MirBSD:*:*) + echo powerpc-unknown-mirbsd${UNAME_RELEASE} + exit ;; + *:MirBSD:*:*) + echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} + exit ;; + alpha:OSF1:*:*) + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case "$ALPHA_CPU_TYPE" in + "EV4 (21064)") + UNAME_MACHINE="alpha" ;; + "EV4.5 (21064)") + UNAME_MACHINE="alpha" ;; + "LCA4 (21066/21068)") + UNAME_MACHINE="alpha" ;; + "EV5 (21164)") + UNAME_MACHINE="alphaev5" ;; + "EV5.6 (21164A)") + UNAME_MACHINE="alphaev56" ;; + "EV5.6 (21164PC)") + UNAME_MACHINE="alphapca56" ;; + "EV5.7 (21164PC)") + UNAME_MACHINE="alphapca57" ;; + "EV6 (21264)") + UNAME_MACHINE="alphaev6" ;; + "EV6.7 (21264A)") + UNAME_MACHINE="alphaev67" ;; + "EV6.8CB (21264C)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8AL (21264B)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8CX (21264D)") + UNAME_MACHINE="alphaev68" ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE="alphaev69" ;; + "EV7 (21364)") + UNAME_MACHINE="alphaev7" ;; + "EV7.9 (21364A)") + UNAME_MACHINE="alphaev79" ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + exit ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-morphos + exit ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit ;; + *:z/VM:*:*) + echo s390-ibm-zvmoe + exit ;; + *:OS400:*:*) + echo powerpc-ibm-os400 + exit ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit ;; + arm:riscos:*:*|arm:RISCOS:*:*) + echo arm-unknown-riscos + exit ;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit ;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit ;; + DRS?6000:unix:4.0:6*) + echo sparc-icl-nx6 + exit ;; + DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7; exit ;; + esac ;; + s390x:SunOS:*:*) + echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) + eval $set_cc_for_build + SUN_ARCH="i386" + # If there is a compiler, see if it is configured for 64-bit objects. + # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. + # This test works for both compilers. + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + SUN_ARCH="x86_64" + fi + fi + echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit ;; + m68k:machten:*:*) + echo m68k-apple-machten${UNAME_RELEASE} + exit ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && + dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`$dummy $dummyarg` && + { echo "$SYSTEM_NAME"; exit; } + echo mips-mips-riscos${UNAME_RELEASE} + exit ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` + then + echo "$SYSTEM_NAME" + else + echo rs6000-ibm-aix3.2.5 + fi + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit ;; + *:AIX:*:[456]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if [ ${HP_ARCH} = "hppa2.0w" ] + then + eval $set_cc_for_build + + # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating + # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler + # generating 64-bit code. GNU and HP use different nomenclature: + # + # $ CC_FOR_BUILD=cc ./config.guess + # => hppa2.0w-hp-hpux11.23 + # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess + # => hppa64-hp-hpux11.23 + + if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | + grep -q __LP64__ + then + HP_ARCH="hppa2.0w" + else + HP_ARCH="hppa64" + fi + fi + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit ;; + 3050*:HI-UX:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + echo unknown-hitachi-hiuxwe2 + exit ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + *:UNICOS/mp:*:*) + echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` + echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:FreeBSD:*:*) + case ${UNAME_MACHINE} in + pc98) + echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + amd64) + echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + *) + echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + esac + exit ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit ;; + *:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit ;; + i*:windows32*:*) + # uname -m includes "-pc" on this system. + echo ${UNAME_MACHINE}-mingw32 + exit ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit ;; + *:Interix*:[3456]*) + case ${UNAME_MACHINE} in + x86) + echo i586-pc-interix${UNAME_RELEASE} + exit ;; + EM64T | authenticamd | genuineintel) + echo x86_64-unknown-interix${UNAME_RELEASE} + exit ;; + IA64) + echo ia64-unknown-interix${UNAME_RELEASE} + exit ;; + esac ;; + [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) + echo i${UNAME_MACHINE}-pc-mks + exit ;; + 8664:Windows_NT:*) + echo x86_64-pc-mks + exit ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i586-pc-interix + exit ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit ;; + amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) + echo x86_64-unknown-cygwin + exit ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + *:GNU:*:*) + # the GNU system + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu + exit ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit ;; + arm*:Linux:*:*) + eval $set_cc_for_build + if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_EABI__ + then + echo ${UNAME_MACHINE}-unknown-linux-gnu + else + echo ${UNAME_MACHINE}-unknown-linux-gnueabi + fi + exit ;; + avr32*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + cris:Linux:*:*) + echo cris-axis-linux-gnu + exit ;; + crisv32:Linux:*:*) + echo crisv32-axis-linux-gnu + exit ;; + frv:Linux:*:*) + echo frv-unknown-linux-gnu + exit ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + m32r*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + mips:Linux:*:* | mips64:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef ${UNAME_MACHINE} + #undef ${UNAME_MACHINE}el + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=${UNAME_MACHINE}el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=${UNAME_MACHINE} + #else + CPU= + #endif + #endif +EOF + eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n ' + /^CPU/{ + s: ::g + p + }'`" + test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } + ;; + or32:Linux:*:*) + echo or32-unknown-linux-gnu + exit ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-gnu + exit ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-gnu + exit ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep -q ld.so.1 + if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi + echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + exit ;; + padre:Linux:*:*) + echo sparc-unknown-linux-gnu + exit ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-gnu ;; + PA8*) echo hppa2.0-unknown-linux-gnu ;; + *) echo hppa-unknown-linux-gnu ;; + esac + exit ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-gnu + exit ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux + exit ;; + sh64*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + vax:Linux:*:*) + echo ${UNAME_MACHINE}-dec-linux-gnu + exit ;; + x86_64:Linux:*:*) + echo x86_64-unknown-linux-gnu + exit ;; + xtensa*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + i*86:Linux:*:*) + # The BFD linker knows what the default object file format is, so + # first see if it will tell us. cd to the root directory to prevent + # problems with other programs or directories called `ld' in the path. + # Set LC_ALL=C to ensure ld outputs messages in English. + ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \ + | sed -ne '/supported targets:/!d + s/[ ][ ]*/ /g + s/.*supported targets: *// + s/ .*// + p'` + case "$ld_supported_targets" in + elf32-i386) + TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu" + ;; + esac + # Determine whether the default compiler is a.out or elf + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + #ifdef __ELF__ + # ifdef __GLIBC__ + # if __GLIBC__ >= 2 + LIBC=gnu + # else + LIBC=gnulibc1 + # endif + # else + LIBC=gnulibc1 + # endif + #else + #if defined(__INTEL_COMPILER) || defined(__PGI) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) + LIBC=gnu + #else + LIBC=gnuaout + #endif + #endif + #ifdef __dietlibc__ + LIBC=dietlibc + #endif +EOF + eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n ' + /^LIBC/{ + s: ::g + p + }'`" + test x"${LIBC}" != x && { + echo "${UNAME_MACHINE}-pc-linux-${LIBC}" + exit + } + test x"${TENTATIVE}" != x && { echo "${TENTATIVE}"; exit; } + ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit ;; + i*86:syllable:*:*) + echo ${UNAME_MACHINE}-pc-syllable + exit ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit ;; + i*86:*:5:[678]*) + # UnixWare 7.x, OpenUNIX and OpenServer 6. + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i586. + # Note: whatever this is, it MUST be the same as what config.sub + # prints for the "djgpp" host, or else GDB configury will decide that + # this is a cross-build. + echo i586-pc-msdosdjgpp + exit ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit ;; + M68*:*:R3V[5678]*:*) + test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4; exit; } ;; + NCR*:*:4.2:* | MPRAS*:*:4.2:*) + OS_REL='.3' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit ;; + i*86:VOS:*:*) + # From Paul.Green@stratus.com. + echo ${UNAME_MACHINE}-stratus-vos + exit ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit ;; + BePC:Haiku:*:*) # Haiku running on Intel PC compatible. + echo i586-pc-haiku + exit ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux${UNAME_RELEASE} + exit ;; + SX-7:SUPER-UX:*:*) + echo sx7-nec-superux${UNAME_RELEASE} + exit ;; + SX-8:SUPER-UX:*:*) + echo sx8-nec-superux${UNAME_RELEASE} + exit ;; + SX-8R:SUPER-UX:*:*) + echo sx8r-nec-superux${UNAME_RELEASE} + exit ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown + case $UNAME_PROCESSOR in + i386) eval $set_cc_for_build + if $CC_FOR_BUILD -E -dM -x c /dev/null | grep __LP64__>/dev/null 2>&1 ; then + UNAME_PROCESSOR=x86_64 + fi ;; + unknown) UNAME_PROCESSOR=powerpc ;; + esac + echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + exit ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = "x86"; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} + exit ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit ;; + NSE-?:NONSTOP_KERNEL:*:*) + echo nse-tandem-nsk${UNAME_RELEASE} + exit ;; + NSR-?:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit ;; + SEI:*:*:SEIUX) + echo mips-sei-seiux${UNAME_RELEASE} + exit ;; + *:DragonFly:*:*) + echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit ;; + *:*VMS:*:*) + UNAME_MACHINE=`(uname -p) 2>/dev/null` + case "${UNAME_MACHINE}" in + A*) echo alpha-dec-vms ; exit ;; + I*) echo ia64-dec-vms ; exit ;; + V*) echo vax-dec-vms ; exit ;; + esac ;; + *:XENIX:*:SysV) + echo i386-pc-xenix + exit ;; + i*86:skyos:*:*) + echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' + exit ;; + i*86:rdos:*:*) + echo ${UNAME_MACHINE}-pc-rdos + exit ;; + i*86:AROS:*:*) + echo ${UNAME_MACHINE}-pc-aros + exit ;; +esac + +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +eval $set_cc_for_build +cat >$dummy.c < +# include +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix\n"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +# if !defined (ultrix) +# include +# if defined (BSD) +# if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +# else +# if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# endif +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# else + printf ("vax-dec-ultrix\n"); exit (0); +# endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + c34*) + echo c34-convex-bsd + exit ;; + c38*) + echo c38-convex-bsd + exit ;; + c4*) + echo c4-convex-bsd + exit ;; + esac +fi + +cat >&2 < in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/ape-server/deps/js/src/build/autoconf/config.sub b/ape-server/deps/js/src/build/autoconf/config.sub new file mode 100755 index 0000000..8ca084b --- /dev/null +++ b/ape-server/deps/js/src/build/autoconf/config.sub @@ -0,0 +1,1699 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 +# Free Software Foundation, Inc. + +timestamp='2009-08-19' + +# This file is (in principle) common to ALL GNU software. +# The presence of a machine in this file suggests that SOME GNU software +# can handle that machine. It does not imply ALL GNU software can. +# +# This file 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. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + + +# Please send patches to . Submit a context +# diff and a properly formatted GNU ChangeLog entry. +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, +2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit ;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \ + uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \ + kopensolaris*-gnu* | \ + storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis | -knuth | -cray | -microblaze) + os= + basic_machine=$1 + ;; + -bluegene*) + os=-cnk + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco6) + os=-sco5v6 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | am33_2.0 \ + | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \ + | bfin \ + | c4x | clipper \ + | d10v | d30v | dlx | dsp16xx \ + | fido | fr30 | frv \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | i370 | i860 | i960 | ia64 \ + | ip2k | iq2000 \ + | lm32 \ + | m32c | m32r | m32rle | m68000 | m68k | m88k \ + | maxq | mb | microblaze | mcore | mep | metag \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64octeon | mips64octeonel \ + | mips64orion | mips64orionel \ + | mips64r5900 | mips64r5900el \ + | mips64vr | mips64vrel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mips64vr5900 | mips64vr5900el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | moxie \ + | mt \ + | msp430 \ + | nios | nios2 \ + | ns16k | ns32k \ + | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ + | pyramid \ + | score \ + | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ + | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ + | spu | strongarm \ + | tahoe | thumb | tic4x | tic80 | tron \ + | v850 | v850e \ + | we32k \ + | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \ + | z8k | z80) + basic_machine=$basic_machine-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12) + # Motorola 68HC11/12. + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + ms1) + basic_machine=mt-unknown + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* | avr32-* \ + | bfin-* | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \ + | clipper-* | craynv-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | elxsi-* \ + | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | ip2k-* | iq2000-* \ + | lm32-* \ + | m32c-* | m32r-* | m32rle-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ + | mips64octeon-* | mips64octeonel-* \ + | mips64orion-* | mips64orionel-* \ + | mips64r5900-* | mips64r5900el-* \ + | mips64vr-* | mips64vrel-* \ + | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* \ + | mips64vr5000-* | mips64vr5000el-* \ + | mips64vr5900-* | mips64vr5900el-* \ + | mipsisa32-* | mipsisa32el-* \ + | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipstx39-* | mipstx39el-* \ + | mmix-* \ + | mt-* \ + | msp430-* \ + | nios-* | nios2-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ + | pyramid-* \ + | romp-* | rs6000-* \ + | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ + | sparclite-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \ + | tahoe-* | thumb-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* | tile-* \ + | tron-* \ + | v850-* | v850e-* | vax-* \ + | we32k-* \ + | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \ + | xstormy16-* | xtensa*-* \ + | ymp-* \ + | z8k-* | z80-*) + ;; + # Recognize the basic CPU types without company name, with glob match. + xtensa*) + basic_machine=$basic_machine-unknown + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + abacus) + basic_machine=abacus-unknown + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amd64) + basic_machine=x86_64-pc + ;; + amd64-*) + basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aros) + basic_machine=i386-pc + os=-aros + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + blackfin) + basic_machine=bfin-unknown + os=-linux + ;; + blackfin-*) + basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + bluegene*) + basic_machine=powerpc-ibm + os=-cnk + ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; + cegcc) + basic_machine=arm-unknown + os=-cegcc + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | j90) + basic_machine=j90-cray + os=-unicos + ;; + craynv) + basic_machine=craynv-cray + os=-unicosmp + ;; + cr16) + basic_machine=cr16-unknown + os=-elf + ;; + crds | unos) + basic_machine=m68k-crds + ;; + crisv32 | crisv32-* | etraxfs*) + basic_machine=crisv32-axis + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + crx) + basic_machine=crx-unknown + os=-elf + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + decsystem10* | dec10*) + basic_machine=pdp10-dec + os=-tops10 + ;; + decsystem20* | dec20*) + basic_machine=pdp10-dec + os=-tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dicos) + basic_machine=i686-pc + os=-dicos + ;; + djgpp) + basic_machine=i586-pc + os=-msdosdjgpp + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; +# I'm not sure what "Sysv32" means. Should this be sysv3.2? + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m68knommu) + basic_machine=m68k-unknown + os=-linux + ;; + m68knommu-*) + basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + microblaze) + basic_machine=microblaze-xilinx + ;; + mingw32) + basic_machine=i386-pc + os=-mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + os=-mingw32ce + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + ms1-*) + basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + openrisc | openrisc-*) + basic_machine=or32-unknown + ;; + os400) + basic_machine=powerpc-ibm + os=-os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + parisc) + basic_machine=hppa-unknown + os=-linux + ;; + parisc-*) + basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pc98) + basic_machine=i386-pc + ;; + pc98-*) + basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon | athlon_*) + basic_machine=i686-pc + ;; + pentiumii | pentium2 | pentiumiii | pentium3) + basic_machine=i686-pc + ;; + pentium4) + basic_machine=i786-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium4-*) + basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc) basic_machine=powerpc-unknown + ;; + ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rdos) + basic_machine=i386-pc + os=-rdos + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + s390 | s390-*) + basic_machine=s390-ibm + ;; + s390x | s390x-*) + basic_machine=s390x-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sb1) + basic_machine=mipsisa64sb1-unknown + ;; + sb1el) + basic_machine=mipsisa64sb1el-unknown + ;; + sde) + basic_machine=mipsisa32-sde + os=-elf + ;; + sei) + basic_machine=mips-sei + os=-seiux + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sh5el) + basic_machine=sh5le-unknown + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparclite-wrs | simso-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=-unicos + ;; + t90) + basic_machine=t90-cray + os=-unicos + ;; + tic54x | c54x*) + basic_machine=tic54x-unknown + os=-coff + ;; + tic55x | c55x*) + basic_machine=tic55x-unknown + os=-coff + ;; + tic6x | c6x*) + basic_machine=tic6x-unknown + os=-coff + ;; + tile*) + basic_machine=tile-unknown + os=-linux-gnu + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + toad1) + basic_machine=pdp10-xkl + os=-tops20 + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + tpf) + basic_machine=s390x-ibm + os=-tpf + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + xbox) + basic_machine=i686-pc + os=-mingw32 + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + ymp) + basic_machine=ymp-cray + os=-unicos + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + z80-*-coff) + basic_machine=z80-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + romp) + basic_machine=romp-ibm + ;; + mmix) + basic_machine=mmix-knuth + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) + basic_machine=sh-unknown + ;; + sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ + | -kopensolaris* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* | -aros* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ + | -openbsd* | -solidbsd* \ + | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ + | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* | -cegcc* \ + | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \ + | -uxpv* | -beos* | -mpeix* | -udk* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ + | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ + | -skyos* | -haiku* | -rdos* | -toppers* | -drops*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto-qnx*) + ;; + -nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux-dietlibc) + os=-linux-dietlibc + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -os400*) + os=-os400 + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -atheos*) + os=-atheos + ;; + -syllable*) + os=-syllable + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -nova*) + os=-rtmk-nova + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -tpf*) + os=-tpf + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -aros*) + os=-aros + ;; + -kaos*) + os=-kaos + ;; + -zvmoe) + os=-zvmoe + ;; + -dicos*) + os=-dicos + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + score-*) + os=-elf + ;; + spu-*) + os=-elf + ;; + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + c4x-* | tic4x-*) + os=-coff + ;; + # This must come before the *-dec entry. + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + # This also exists in the configure program, but was not the + # default. + # os=-sunos4 + ;; + m68*-cisco) + os=-aout + ;; + mep-*) + os=-elf + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + or32-*) + os=-coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-haiku) + os=-haiku + ;; + *-ibm) + os=-aix + ;; + *-knuth) + os=-mmixware + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -cnk*|-aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -os400*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -tpf*) + vendor=ibm + ;; + -vxsim* | -vxworks* | -windiss*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/ape-server/deps/js/src/build/autoconf/install-sh b/ape-server/deps/js/src/build/autoconf/install-sh new file mode 100755 index 0000000..0ff4b6a --- /dev/null +++ b/ape-server/deps/js/src/build/autoconf/install-sh @@ -0,0 +1,119 @@ +#!/bin/sh + +# +# install - install a program, script, or datafile +# This comes from X11R5; it is not part of GNU. +# +# $XConsortium: install.sh,v 1.2 89/12/18 14:47:22 jim Exp $ +# +# This script is compatible with the BSD install script, but was written +# from scratch. +# + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" + +instcmd="$mvprog" +chmodcmd="" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +fi + +if [ x"$dst" = x ] +then + echo "install: no destination specified" + exit 1 +fi + + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + +if [ -d $dst ] +then + dst="$dst"/`basename $src` +fi + +# Make a temp file name in the proper directory. + +dstdir=`dirname $dst` +dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + +$doit $instcmd $src $dsttmp + +# and set any options; do chmod last to preserve setuid bits + +if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; fi +if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; fi +if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; fi +if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; fi + +# Now rename the file to the real destination. + +$doit $rmcmd $dst +$doit $mvcmd $dsttmp $dst + + +exit 0 diff --git a/ape-server/deps/js/src/build/autoconf/make-makefile b/ape-server/deps/js/src/build/autoconf/make-makefile new file mode 100755 index 0000000..57fcdb2 --- /dev/null +++ b/ape-server/deps/js/src/build/autoconf/make-makefile @@ -0,0 +1,322 @@ +#! /usr/bin/env perl +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1999 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# make-makefiles - Quickly create Makefiles for subdirectories. +# Also, creates any needed subdirectories. +# +# usage: make-makefiles [ -t -p -d ] [ | /Makefile ] ... + +# Send comments, improvements, bugs to Steve Lamm (slamm@netscape.com). + +#$debug = 1; + +if ($^O eq 'msys') { + $pwdcmd = 'pwd -W'; +} +else { + $pwdcmd = 'pwd'; +} + +# Determine various tree path variables +# +($topsrcdir, $ptopsrcdir, $depth, @makefiles) = parse_arguments(@ARGV); + +$object_fullpath = `$pwdcmd`; +chdir $depth; +$object_root = `$pwdcmd`; +chomp $object_fullpath; +chomp $object_root; + +# $source_subdir is the path from the object root to where +# 'make-makefile' was called. For example, if make-makefile was +# called from "mozilla/gfx/src", then $source_subdir would be +# "gfx/src/". +$source_subdir = "$object_fullpath/"; +my $quoted_object_root = quotemeta($object_root); +$source_subdir =~ s|^$quoted_object_root/||; + +# Prefix makefiles with $source_subdir so that paths +# will be relative to the top of the object tree. +# +for $makefile (@makefiles) { + $makefile = "$source_subdir$makefile"; +} + +create_directories(@makefiles); + +# Find the path to the source directory based on how 'make-makefile' +# was invoked. The path is either relative to the object directory +# or an absolute path. +$given_srcdir = find_srcdir($topsrcdir, $depth); +$pgiven_srcdir = find_srcdir($ptopsrcdir, $depth); + +if ($debug) { + warn "object_fullpath = $object_fullpath\n"; + warn "object_root = $object_root\n"; + warn "source_subdir = $source_subdir\n"; + warn "makefiles = @makefiles\n"; + warn "given_srcdir = $given_srcdir\n"; +} + +@unhandled = update_makefiles($given_srcdir, $pgiven_srcdir, @makefiles); + +run_config_status(@unhandled); + +# end of Main +############################################################ + +sub dirname { + return $_[0] =~ /(.*)\/.*/ ? "$1" : '.'; +} + +# find_depth: Pull the value of DEPTH out of a Makefile (or Makefile.in) +sub find_depth { + my $depth = ''; + open(MAKEFILE, "<$_[0]") || die "Unable to open $_[0]: $!\n"; + while () { + next unless /^DEPTH\s*=\s*(\..*)/; + $depth = $1; + last; + } + close MAKEFILE; + return $depth; +} + +sub parse_arguments { + my @args = @_; + my $depth = ''; + my $topsrcdir = ''; + my $ptopsrcdir; + my @makefiles = (); + + while (1) { + if ($args[0] eq '-d') { + $depth = $args[1]; + shift @args; + shift @args; + } elsif ($args[0] eq '-t') { + $topsrcdir = $args[1]; + shift @args; + shift @args; + } elsif ($args[0] eq '-p') { + $ptopsrcdir = $args[1]; + shift @args; + shift @args; + } else { + last; + } + } + + if ($topsrcdir eq '') { + $topsrcdir = $0; # Figure out topsrcdir based on program name. + $topsrcdir =~ s|/?build/autoconf/.*$||; + } + if ($ptopsrcdir eq '') { + $ptopsrcdir = $topsrcdir; + } + if ($depth eq '') { + # Use $(DEPTH) in the Makefile or Makefile.in to determine the depth + if (-e "Makefile.in") { + $depth = find_depth("Makefile.in"); + } elsif (-e "Makefile") { + $depth = find_depth("Makefile"); + } elsif (-e "../Makefile") { + $depth = "../".find_depth("../Makefile"); + $depth =~ s/\/\.$//; + } else { + warn "Unable to determine depth (e.g. ../..) to root of objdir tree.\n"; + die "No Makefile(.in) present. Try running with '-d '\n"; + } + } + + # Build the list of makefiles to generate + # + @makefiles = (); + my $makefile; + foreach $makefile (@args) { + $makefile =~ s/\.in$//; + $makefile =~ s/\/$//; + $makefile =~ /Makefile$/ or $makefile .= "/Makefile"; + push @makefiles, "$makefile"; + } + @makefiles = "Makefile" unless @args; + + return ($topsrcdir, $ptopsrcdir, $depth, @makefiles); +} + + +# Create all the directories at once. +# This can be much faster than calling mkdir() for each one. +sub create_directories { + my @makefiles = @_; + my @dirs = (); + my $ac_file; + foreach $ac_file (@makefiles) { + push @dirs, dirname($ac_file); + } + # Call mkdir with the directories sorted by subdir count (how many /'s) + system "mkdir -p ". join(' ', map("\"$_\"", @dirs)) if @dirs; +} + +# Find the top of the source directory +# (Assuming that the executable is in $top_srcdir/build/autoconf) +sub find_srcdir { + my ($ac_given_srcdir, $depth) = @_; + + if ($debug) { + print "ac_given_srcdir = $ac_given_srcdir\n"; + print "depth = $depth\n"; + } + if ($ac_given_srcdir =~ /^\./ and $depth ne '.') { + my $quoted_depth = quotemeta($depth); + $ac_given_srcdir =~ s|^$quoted_depth/?||; + } + if ($debug) { + print "ac_given_srcdir = $ac_given_srcdir\n"; + } + $ac_given_srcdir = '.' if $ac_given_srcdir eq ''; + return $ac_given_srcdir; +} + +# Output the makefiles. +# +sub update_makefiles { + my ($ac_given_srcdir, $pac_given_srcdir, @makefiles) = @_; + my @unhandled=(); + + my $ac_file; + foreach $ac_file (@makefiles) { + my $ac_file_in = "$ac_given_srcdir/${ac_file}.in"; + my $ac_dir = dirname($ac_file); + my $ac_dots = ''; + my $ac_dir_suffix = ''; + my $srcdir = '.'; + my $top_srcdir = '.'; + + # Determine $srcdir and $top_srcdir + # + if ($ac_dir ne '.') { + $ac_dir_suffix = "/$ac_dir"; + $ac_dir_suffix =~ s%^/\./%/%; + $ac_dots = $ac_dir_suffix; + # Remove .. components from the provided dir suffix, and + # also the forward path components they were reversing. + my $backtracks = $ac_dots =~ s%\.\.(/|$)%%g; + while ($backtracks--) { + $ac_dots =~ s%/[^/]*%%; + } + $ac_dots =~ s%/[^/]*%../%g; + } + if ($ac_given_srcdir eq '.') { + if ($ac_dots ne '') { + $top_srcdir = $ac_dots; + $top_srcdir =~ s%/$%%; + } + } elsif ($pac_given_srcdir =~ m%^/% or $pac_given_srcdir =~ m%^.:/%) { + $srcdir = "$pac_given_srcdir$ac_dir_suffix"; + $top_srcdir = "$pac_given_srcdir"; + } else { + if ($debug) { + print "ac_dots = $ac_dots\n"; + print "ac_dir_suffix = $ac_dir_suffix\n"; + } + $srcdir = "$ac_dots$ac_given_srcdir$ac_dir_suffix"; + $top_srcdir = "$ac_dots$ac_given_srcdir"; + } + + if ($debug) { + print "ac_dir = $ac_dir\n"; + print "ac_file = $ac_file\n"; + print "ac_file_in = $ac_file_in\n"; + print "srcdir = $srcdir\n"; + print "top_srcdir = $top_srcdir\n"; + print "cwd = " . `$pwdcmd` . "\n"; + } + + # Copy the file and make substitutions. + # @srcdir@ -> value of $srcdir + # @top_srcdir@ -> value of $top_srcdir + # + if (-e $ac_file) { + next if -M _ < -M $ac_file_in; # Next if Makefile is up-to-date. + warn "updating $ac_file\n"; + } else { + warn "creating $ac_file\n"; + } + + open INFILE, "<$ac_file_in" or do { + warn "$0: Cannot read $ac_file_in: No such file or directory\n"; + next; + }; + open OUTFILE, ">$ac_file" or do { + warn "$0: Unable to create $ac_file\n"; + next; + }; + + while () { + #if (/\@[_a-zA-Z]*\@.*\@[_a-zA-Z]*\@/) { + # #warn "Two defines on a line:$ac_file:$.:$_"; + # push @unhandled, $ac_file; + # last; + #} + + s/\@srcdir\@/$srcdir/g; + s/\@top_srcdir\@/$top_srcdir/g; + + if (/\@[_a-zA-Z]*\@/) { + #warn "Unknown variable:$ac_file:$.:$_"; + push @unhandled, $ac_file; + last; + } + print OUTFILE; + } + close INFILE; + close OUTFILE; + } + return @unhandled; +} + +sub run_config_status { + my @unhandled = @_; + + # Run config.status with any unhandled files. + # + if (@unhandled) { + $ENV{CONFIG_FILES}= join ' ', @unhandled; + system "./config.status"; + } +} diff --git a/ape-server/deps/js/src/build/autoconf/match-dir.sh b/ape-server/deps/js/src/build/autoconf/match-dir.sh new file mode 100755 index 0000000..c579a9d --- /dev/null +++ b/ape-server/deps/js/src/build/autoconf/match-dir.sh @@ -0,0 +1,101 @@ +#!/bin/sh +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# This script will match a dir with a set of dirs. +# +# Usage: match-dir.sh match [dir1 dir2 ... dirn] +# +# Send comments, improvements, bugs to ramiro@netscape.com +# + +if [ -f Makefile ]; then + MAKEFILE="Makefile" +else + if [ -f Makefile.in ]; then + MAKEFILE="Makefile.in" + else + echo + echo "There ain't no 'Makefile' or 'Makefile.in' over here: $pwd, dude." + echo + exit 1 + fi +fi + +# Use DEPTH in the Makefile.in to determine the depth +depth=`grep -w DEPTH ${MAKEFILE} | grep "\.\." | awk -F"=" '{ print $2; }'` +cwd=`pwd` + +# Determine the depth count +n=`echo $depth | tr '/' ' ' | wc -w` + +cd $depth +objdir=`pwd` + +path=`echo $cwd | sed "s|^${objdir}/||"` + +match=$path + +for i in $* +do +# echo "Looking for $match in $i" + + echo $i | grep -q -x $match + + if [ $? -eq 0 ] + then + echo "1" + + exit 0 + fi + +# echo "Looking for $i in $match" + + echo $match | grep -q $i + + if [ $? -eq 0 ] + then + echo "1" + + exit 0 + fi +done + +echo "0" + +exit 0 diff --git a/ape-server/deps/js/src/build/autoconf/moznbytetype.m4 b/ape-server/deps/js/src/build/autoconf/moznbytetype.m4 new file mode 100755 index 0000000..07ef98b --- /dev/null +++ b/ape-server/deps/js/src/build/autoconf/moznbytetype.m4 @@ -0,0 +1,136 @@ +dnl ***** BEGIN LICENSE BLOCK ***** +dnl Version: MPL 1.1/GPL 2.0/LGPL 2.1 +dnl +dnl The contents of this file are subject to the Mozilla Public License Version +dnl 1.1 (the "License"); you may not use this file except in compliance with +dnl the License. You may obtain a copy of the License at +dnl http://www.mozilla.org/MPL/ +dnl +dnl Software distributed under the License is distributed on an "AS IS" basis, +dnl WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +dnl for the specific language governing rights and limitations under the +dnl License. +dnl +dnl The Original Code is mozilla.org code. +dnl +dnl The Initial Developer of the Original Code is +dnl The Mozilla Foundation +dnl Portions created by the Initial Developer are Copyright (C) 2008 +dnl the Initial Developer. All Rights Reserved. +dnl +dnl Contributor(s): +dnl Jim Blandy +dnl +dnl Alternatively, the contents of this file may be used under the terms of +dnl either of the GNU General Public License Version 2 or later (the "GPL"), +dnl or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +dnl in which case the provisions of the GPL or the LGPL are applicable instead +dnl of those above. If you wish to allow use of your version of this file only +dnl under the terms of either the GPL or the LGPL, and not to allow others to +dnl use your version of this file under the terms of the MPL, indicate your +dnl decision by deleting the provisions above and replace them with the notice +dnl and other provisions required by the GPL or the LGPL. If you do not delete +dnl the provisions above, a recipient may use your version of this file under +dnl the terms of any one of the MPL, the GPL or the LGPL. +dnl +dnl ***** END LICENSE BLOCK ***** + +dnl MOZ_N_BYTE_TYPE(VARIABLE, SIZE, POSSIBLE-TYPES) +dnl +dnl Check to see which of POSSIBLE-TYPES has a size of SIZE. If we +dnl find one, define VARIABLE to be the size-BYTE type. If no type +dnl matches, exit the configure script with an error message. Types +dnl whose written form contains spaces should appear in POSSIBLE-TYPES +dnl enclosed by shell quotes. +dnl +dnl The cache variable moz_cv_n_byte_type_VARIABLE gets set to the +dnl type, if found. +dnl +dnl for example: +dnl MOZ_N_BYTE_TYPE([JS_INT32_T], [4], [int long 'long long' short]) +dnl +AC_DEFUN(MOZ_N_BYTE_TYPE, +[ +dnl The simplest approach would simply be to run a program that says +dnl printf ("%d\n", sizeof ($type)); +dnl But that won't work when cross-compiling; this will. +AC_CACHE_CHECK([for a $2-byte type], moz_cv_n_byte_type_$1, [ + moz_cv_n_byte_type_$1= + for type in $3; do + AC_TRY_COMPILE([], + [ + int a[sizeof ($type) == $2 ? 1 : -1]; + return; + ], + [moz_cv_n_byte_type_$1=$type; break], []) + done + if test ! "$moz_cv_n_byte_type_$1"; then + AC_MSG_ERROR([Couldn't find a $2-byte type]) + fi +]) +AC_DEFINE_UNQUOTED($1, [$moz_cv_n_byte_type_$1], + [a $2-byte type on the target machine]) +]) + +dnl MOZ_SIZE_OF_TYPE(VARIABLE, TYPE, POSSIBLE-SIZES) +dnl +dnl Check to see which of POSSIBLE-SIZES is the sizeof(TYPE). If we find one, +dnl define VARIABLE SIZE. If no size matches, exit the configure script with +dnl an error message. +dnl +dnl The cache variable moz_cv_size_of_VARIABLE gets set to the size, if +dnl found. +dnl +dnl for example: +dnl MOZ_SIZE_OF_TYPE([JS_BYTES_PER_WORD], [void*], [4 8]) +AC_DEFUN(MOZ_SIZE_OF_TYPE, +[ +AC_CACHE_CHECK([for the size of $2], moz_cv_size_of_$1, [ + moz_cv_size_of_$1= + for size in $3; do + AC_TRY_COMPILE([], + [ + int a[sizeof ($2) == $size ? 1 : -1]; + return; + ], + [moz_cv_size_of_$1=$size; break], []) + done + if test ! "$moz_cv_size_of_$1"; then + AC_MSG_ERROR([No size found for $2]) + fi +]) +AC_DEFINE_UNQUOTED($1, [$moz_cv_size_of_$1]) +]) + +dnl MOZ_ALIGN_OF_TYPE(VARIABLE, TYPE, POSSIBLE-ALIGNS) +dnl +dnl Check to see which of POSSIBLE-ALIGNS is the necessary alignment of TYPE. +dnl If we find one, define VARIABLE ALIGNMENT. If no alignment matches, exit +dnl the configure script with an error message. +dnl +dnl The cache variable moz_cv_align_of_VARIABLE gets set to the size, if +dnl found. +dnl +dnl for example: +dnl MOZ_ALIGN_OF_TYPE(JS_ALIGN_OF_POINTER, void*, 2 4 8 16) +AC_DEFUN(MOZ_ALIGN_OF_TYPE, +[ +AC_CACHE_CHECK([for the alignment of $2], moz_cv_align_of_$1, [ + moz_cv_align_of_$1= + for align in $3; do + AC_TRY_COMPILE([ + #include + struct aligner { char c; $2 a; }; + ], + [ + int a[offsetof(struct aligner, a) == $align ? 1 : -1]; + return; + ], + [moz_cv_align_of_$1=$align; break], []) + done + if test ! "$moz_cv_align_of_$1"; then + AC_MSG_ERROR([No alignment found for $2]) + fi +]) +AC_DEFINE_UNQUOTED($1, [$moz_cv_align_of_$1]) +]) diff --git a/ape-server/deps/js/src/build/autoconf/mozprog.m4 b/ape-server/deps/js/src/build/autoconf/mozprog.m4 new file mode 100755 index 0000000..f22b4c6 --- /dev/null +++ b/ape-server/deps/js/src/build/autoconf/mozprog.m4 @@ -0,0 +1,76 @@ +dnl ***** BEGIN LICENSE BLOCK ***** +dnl Version: MPL 1.1/GPL 2.0/LGPL 2.1 +dnl +dnl The contents of this file are subject to the Mozilla Public License Version +dnl 1.1 (the "License"); you may not use this file except in compliance with +dnl the License. You may obtain a copy of the License at +dnl http://www.mozilla.org/MPL/ +dnl +dnl Software distributed under the License is distributed on an "AS IS" basis, +dnl WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +dnl for the specific language governing rights and limitations under the +dnl License. +dnl +dnl The Original Code is mozilla.org code. +dnl +dnl The Initial Developer of the Original Code is the +dnl Mozilla Foundation +dnl +dnl Portions created by the Initial Developer are Copyright (C) 2009 +dnl the Initial Developer. All Rights Reserved. +dnl +dnl Contributor(s): +dnl Benjamin Smedberg +dnl +dnl Alternatively, the contents of this file may be used under the terms of +dnl either of the GNU General Public License Version 2 or later (the "GPL"), +dnl or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +dnl in which case the provisions of the GPL or the LGPL are applicable instead +dnl of those above. If you wish to allow use of your version of this file only +dnl under the terms of either the GPL or the LGPL, and not to allow others to +dnl use your version of this file under the terms of the MPL, indicate your +dnl decision by deleting the provisions above and replace them with the notice +dnl and other provisions required by the GPL or the LGPL. If you do not delete +dnl the provisions above, a recipient may use your version of this file under +dnl the terms of any one of the MPL, the GPL or the LGPL. +dnl +dnl ***** END LICENSE BLOCK ***** + +AC_DEFUN(MOZ_PROG_CHECKMSYS, +[AC_REQUIRE([AC_INIT_BINSH])dnl +if test `uname -s | grep -c MINGW 2>/dev/null` != "0"; then + msyshost=1 +fi +]) + +AC_DEFUN(MOZ_PATH_PROG, +[ AC_PATH_PROG($1,$2,$3,$4) + if test "$msyshost"; then + case "[$]$1" in + /*) + tmp_DIRNAME=`dirname "[$]$1"` + tmp_BASENAME=`basename "[$]$1"` + tmp_PWD=`cd "$tmp_DIRNAME" && pwd -W` + $1="$tmp_PWD/$tmp_BASENAME" + if test -e "[$]$1.exe"; then + $1="[$]$1.exe" + fi + esac + fi +]) + +AC_DEFUN(MOZ_PATH_PROGS, +[ AC_PATH_PROGS($1,$2,$3,$4) + if test "$msyshost"; then + case "[$]$1" in + /*) + tmp_DIRNAME=`dirname "[$]$1"` + tmp_BASENAME=`basename "[$]$1"` + tmp_PWD=`cd "$tmp_DIRNAME" && pwd -W` + $1="$tmp_PWD/$tmp_BASENAME" + if test -e "[$]$1.exe"; then + $1="[$]$1.exe" + fi + esac + fi +]) diff --git a/ape-server/deps/js/src/build/autoconf/nspr.m4 b/ape-server/deps/js/src/build/autoconf/nspr.m4 new file mode 100755 index 0000000..fa3564c --- /dev/null +++ b/ape-server/deps/js/src/build/autoconf/nspr.m4 @@ -0,0 +1,101 @@ +# -*- tab-width: 4; -*- +# Configure paths for NSPR +# Public domain - Chris Seawood 2001-04-05 +# Based upon gtk.m4 (also PD) by Owen Taylor + +dnl AM_PATH_NSPR([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) +dnl Test for NSPR, and define NSPR_CFLAGS and NSPR_LIBS +dnl +dnl If the nspr-config script is available, use it to find the +dnl appropriate CFLAGS and LIBS, and to check for the required +dnl version, and run ACTION-IF-FOUND. +dnl +dnl Otherwise, if NO_NSPR_CONFIG_SYSTEM_VERSION is set, we use it, +dnl NO_NSPR_CONFIG_SYSTEM_CFLAGS, and NO_NSPR_CONFIG_SYSTEM_LIBS to +dnl provide default values, and run ACTION-IF-FOUND. (Some systems +dnl ship NSPR without nspr-config, but can glean the appropriate flags +dnl and version.) +dnl +dnl Otherwise, run ACTION-IF-NOT-FOUND. +AC_DEFUN([AM_PATH_NSPR], +[dnl + +AC_ARG_WITH(nspr-prefix, + [ --with-nspr-prefix=PFX Prefix where NSPR is installed], + nspr_config_prefix="$withval", + nspr_config_prefix="") + +AC_ARG_WITH(nspr-exec-prefix, + [ --with-nspr-exec-prefix=PFX + Exec prefix where NSPR is installed], + nspr_config_exec_prefix="$withval", + nspr_config_exec_prefix="") + + if test -n "$nspr_config_exec_prefix"; then + nspr_config_args="$nspr_config_args --exec-prefix=$nspr_config_exec_prefix" + if test -z "$NSPR_CONFIG"; then + NSPR_CONFIG=$nspr_config_exec_prefix/bin/nspr-config + fi + fi + if test -n "$nspr_config_prefix"; then + nspr_config_args="$nspr_config_args --prefix=$nspr_config_prefix" + if test -z "$NSPR_CONFIG"; then + NSPR_CONFIG=$nspr_config_prefix/bin/nspr-config + fi + fi + + unset ac_cv_path_NSPR_CONFIG + AC_PATH_PROG(NSPR_CONFIG, nspr-config, no) + min_nspr_version=ifelse([$1], ,4.0.0,$1) + AC_MSG_CHECKING(for NSPR - version >= $min_nspr_version) + + no_nspr="" + if test "$NSPR_CONFIG" != "no"; then + NSPR_CFLAGS=`$NSPR_CONFIG $nspr_config_args --cflags` + NSPR_LIBS=`$NSPR_CONFIG $nspr_config_args --libs` + NSPR_VERSION_STRING=`$NSPR_CONFIG $nspr_config_args --version` + elif test -n "${NO_NSPR_CONFIG_SYSTEM_VERSION}"; then + NSPR_CFLAGS="${NO_NSPR_CONFIG_SYSTEM_CFLAGS}" + NSPR_LIBS="${NO_NSPR_CONFIG_SYSTEM_LDFLAGS}" + NSPR_VERSION_STRING="$NO_NSPR_CONFIG_SYSTEM_VERSION" + else + no_nspr="yes" + fi + + if test -z "$no_nspr"; then + nspr_config_major_version=`echo $NSPR_VERSION_STRING | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` + nspr_config_minor_version=`echo $NSPR_VERSION_STRING | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` + nspr_config_micro_version=`echo $NSPR_VERSION_STRING | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` + min_nspr_major_version=`echo $min_nspr_version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` + min_nspr_minor_version=`echo $min_nspr_version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` + min_nspr_micro_version=`echo $min_nspr_version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` + if test "$nspr_config_major_version" -ne "$min_nspr_major_version"; then + no_nspr="yes" + elif test "$nspr_config_major_version" -eq "$min_nspr_major_version" && + test "$nspr_config_minor_version" -lt "$min_nspr_minor_version"; then + no_nspr="yes" + elif test "$nspr_config_major_version" -eq "$min_nspr_major_version" && + test "$nspr_config_minor_version" -eq "$min_nspr_minor_version" && + test "$nspr_config_micro_version" -lt "$min_nspr_micro_version"; then + no_nspr="yes" + fi + fi + + if test -z "$no_nspr"; then + AC_MSG_RESULT(yes) + ifelse([$2], , :, [$2]) + else + AC_MSG_RESULT(no) + fi + + + AC_SUBST(NSPR_CFLAGS) + AC_SUBST(NSPR_LIBS) + +]) diff --git a/ape-server/deps/js/src/build/autoconf/pkg.m4 b/ape-server/deps/js/src/build/autoconf/pkg.m4 new file mode 100755 index 0000000..d84d210 --- /dev/null +++ b/ape-server/deps/js/src/build/autoconf/pkg.m4 @@ -0,0 +1,59 @@ +# PKG_CHECK_MODULES(GSTUFF, gtk+-2.0 >= 1.3 glib = 1.3.4, action-if, action-not) +# defines GSTUFF_LIBS, GSTUFF_CFLAGS, see pkg-config man page +# also defines GSTUFF_PKG_ERRORS on error +AC_DEFUN([PKG_CHECK_MODULES], +[succeeded=no + + if test -z "$PKG_CONFIG"; then + AC_PATH_PROG(PKG_CONFIG, pkg-config, no) + fi + + if test "$PKG_CONFIG" = "no" ; then + echo "*** The pkg-config script could not be found. Make sure it is" + echo "*** in your path, or set the PKG_CONFIG environment variable" + echo "*** to the full path to pkg-config." + echo "*** Or see http://www.freedesktop.org/software/pkgconfig to get pkg-config." + else + PKG_CONFIG_MIN_VERSION=0.9.0 + if $PKG_CONFIG --atleast-pkgconfig-version $PKG_CONFIG_MIN_VERSION; then + AC_MSG_CHECKING(for $2) + + if $PKG_CONFIG --exists "$2" ; then + AC_MSG_RESULT(yes) + succeeded=yes + + AC_MSG_CHECKING($1_CFLAGS) + $1_CFLAGS=`$PKG_CONFIG --cflags "$2"` + AC_MSG_RESULT($$1_CFLAGS) + + AC_MSG_CHECKING($1_LIBS) + ## Remove evil flags like -Wl,--export-dynamic + $1_LIBS="`$PKG_CONFIG --libs \"$2\" |sed s/-Wl,--export-dynamic//g`" + AC_MSG_RESULT($$1_LIBS) + else + $1_CFLAGS="" + $1_LIBS="" + ## If we have a custom action on failure, don't print errors, but + ## do set a variable so people can do so. + $1_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"` + ifelse([$4], ,echo $$1_PKG_ERRORS,) + fi + + AC_SUBST($1_CFLAGS) + AC_SUBST($1_LIBS) + else + echo "*** Your version of pkg-config is too old. You need version $PKG_CONFIG_MIN_VERSION or newer." + echo "*** See http://www.freedesktop.org/software/pkgconfig" + fi + fi + + if test $succeeded = yes; then + ifelse([$3], , :, [$3]) + else + if test "$COMPILE_ENVIRONMENT"; then + ifelse([$4], , AC_MSG_ERROR([Library requirements ($2) not met; consider adjusting the PKG_CONFIG_PATH environment variable if your libraries are in a nonstandard prefix so pkg-config can find them.]), [$4]) + fi + fi +]) + + diff --git a/ape-server/deps/js/src/build/cygwin-wrapper b/ape-server/deps/js/src/build/cygwin-wrapper new file mode 100755 index 0000000..679f0a1 --- /dev/null +++ b/ape-server/deps/js/src/build/cygwin-wrapper @@ -0,0 +1,75 @@ +#!/bin/sh +# +# Stupid wrapper to avoid win32 dospath/cygdrive issues +# Try not to spawn programs from within this file. If the stuff in here looks royally +# confusing, see bug: http://bugzilla.mozilla.org/show_bug.cgi?id=206643 +# and look at the older versions of this file that are easier to read, but +# do basically the same thing +# + +prog=$1 +shift +if test -z "$prog"; then + exit 0 +fi + +# If $CYGDRIVE_MOUNT was not set in configure, give $mountpoint the results of mount -p +mountpoint=$CYGDRIVE_MOUNT +if test -z "$mountpoint"; then + mountpoint=`mount -p` + if test -z "$mountpoint"; then + print "Cannot determine cygwin mount points. Exiting" + exit 1 + fi +fi + +# Delete everything but "/cygdrive" (or other mountpoint) from mount=`mount -p` +mountpoint=${mountpoint#*/} +mountpoint=/${mountpoint%%[!A-Za-z0-9_]*} +mountpoint=${mountpoint%/} + +args="" +up="" +if test "${prog}" = "-up"; then + up=1 + prog=${1} + shift +fi + +process=1 + +# Convert the mountpoint in parameters to Win32 filenames +# For instance: /cygdrive/c/foo -> c:/foo +for i in "${@}" +do + if test "${i}" = "-wrap"; then + process=1 + else + if test "${i}" = "-nowrap"; then + process= + else + if test -n "${process}"; then + if test -n "${up}"; then + pathname=${i#-I[a-zA-Z]:/} + if ! test "${pathname}" = "${i}"; then + no_i=${i#-I} + driveletter=${no_i%%:*} + i=-I${mountpoint}/${driveletter}/${pathname} + fi + else + eval 'leader=${i%%'${mountpoint}'/[a-zA-Z]/*}' + if ! test "${leader}" = "${i}"; then + eval 'pathname=${i#'${leader}${mountpoint}'/[a-zA-Z]/}' + eval 'no_mountpoint=${i#'${leader}${mountpoint}'/}' + driveletter=${no_mountpoint%%/*} + i=${leader}${driveletter}:/${pathname} + fi + fi + fi + + args="${args} ${i}" + fi + fi +done + +exec $prog $args diff --git a/ape-server/deps/js/src/build/hcc b/ape-server/deps/js/src/build/hcc new file mode 100755 index 0000000..adadbe4 --- /dev/null +++ b/ape-server/deps/js/src/build/hcc @@ -0,0 +1,111 @@ +#!/bin/sh +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Fix brain-damaged compilers that don't understand -o and -c together +# +CC=`echo $1 | sed -e "s|'||g" -e 's|"||g'` +shift +DASH_C=0 +DASH_O=0 +DUMMY="XxxXxxX" +GET_OBJECT=0 +OBJ="${DUMMY}" +OBJECT="${DUMMY}" + +for i in $* +do + [ "${CHECK_O}" = yes ] && { + case $i in + ./*/*.o) OBJECT="$i" + OPTS="${OPTS} -o" + DASH_O=1 + ;; + ./*.o) OBJECT="`basename $i`" + i="" + DASH_O=1 + ;; + *.o) if [ $i = `basename $i` ] + then + OBJECT="$i" + i="" + else + OPTS="${OPTS} -o" + fi + DASH_O=1 + ;; + *) OPTS="${OPTS} -o $i" + DASH_O=1 + i="" + ;; + esac + CHECK_O=no + } + case $i in + -c) DASH_C=1 + OPTS="${OPTS} -c" + ;; + -o) CHECK_O=yes + ;; + *.c) C_SRC=$i + OPTS="${OPTS} $i" +# cc always creates the .o from the .c name + OBJ=`basename $C_SRC .c`.o + ;; + *.s) S_SRC=$i + OPTS="${OPTS} $i" +# or the .o from the .s name + OBJ=`basename $S_SRC .s`.o + ;; + *.o) OBJECT=$i + OPTS="${OPTS} $i" + ;; + *) OPTS="${OPTS} $i" + ;; + esac +done + +${CC} ${OPTS} || exit $? + +# if there was no -c and -o we're done +[ $DASH_C = 1 -a $DASH_O = 1 ] || exit 0 + +# if $OBJ and $OBJECT are the same we're done +[ $OBJ = $OBJECT ] && exit 0 + +[ -f $OBJ ] && mv -f $OBJ $OBJECT diff --git a/ape-server/deps/js/src/build/hcpp b/ape-server/deps/js/src/build/hcpp new file mode 100755 index 0000000..ea55fa4 --- /dev/null +++ b/ape-server/deps/js/src/build/hcpp @@ -0,0 +1,155 @@ +#!/bin/sh +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Wrapper for brain-damaged compilers that don't understand -o and -c together. +# +CXX=`echo $1 | sed -e "s|'||g" -e 's|"||g'` +shift +DUMMY="XxxXxxX" +DASH_C=0 +DASH_O=0 +GET_OBJECT=0 +C_SRC="${DUMMY}" +CC_SRC="${DUMMY}" +CPP_SRC="${DUMMY}" +S_SRC="${DUMMY}" +OBJECT="${DUMMY}" +NEW_i="${DUMMY}" +PLATFORM=`uname -s` + +for i in $* +do + [ ${GET_OBJECT} -eq 1 ] && { + case $i in + ./*/*.o) OBJECT="$i" + OPTS="${OPTS} -o" + DASH_O=1 + ;; + ./*.o) OBJECT="`basename $i`" + i="" + DASH_O=1 + ;; + *.o) if [ $i = `basename $i` ] + then + i="" + else + OPTS="${OPTS} -o" + DASH_O=1 + fi + ;; + *) OPTS="${OPTS} -o $i" + DASH_O=1 + i="" + ;; + esac + GET_OBJECT=0 + } + case $i in + -c) + DASH_C=1 + OPTS="${OPTS} -c" + ;; + -o) + GET_OBJECT=1 + ;; + *.c) + C_SRC="$i" + OPTS="${OPTS} $i" +# cc always creates the .o from the .c name + OBJ=`basename ${C_SRC} .c`.o + ;; + +.*) + OPTS="${OPTS} $i" + ;; + *.cpp) + CPP_SRC="$i" + if [ "${PLATFORM}" = "SCO_SV" ]; then + OPTS="${OPTS} +.cpp $i" + elif [ "${PLATFORM}" = "IRIX" ]; then + NEW_i=`basename ${CPP_SRC} .cpp`.C + rm -f ${NEW_i} + cp $i ${NEW_i} + OPTS="${OPTS} ${NEW_i}" + else + OPTS="${OPTS} $i" + fi +# cc always creates the .o from the .cpp name + OBJ=`basename ${CPP_SRC} .cpp`.o + ;; + *.cc) + CC_SRC="$i" + OPTS="${OPTS} $i" +# cc always creates the .o from the .cc name + OBJ=`basename ${CC_SRC} .cc`.o + ;; + *.s) + S_SRC="$i" + OPTS="${OPTS} $i" +# cc always creates the .o from the .s name + OBJ=`basename ${S_SRC} .s`.o + ;; + *.o) OBJECT=$i + OPTS="${OPTS} $i" + ;; + *) OPTS="${OPTS} $i" + ;; + esac +done + +${CXX} ${OPTS} || exit $? +rm -f ${NEW_i} + +# Really only needed for NSPR now. +if [ "${PLATFORM}" = "IRIX" -a "$OBJ" != "$OBJECT" ]; then + OBJ=$OBJECT +fi + +# LAME!!! +if [ -f -O ]; then + mv -f -- -O ${OBJECT} +fi + +# if there was no -c and -o we're done +[ ${DASH_C} -eq 1 -a ${DASH_O} -eq 1 ] || exit 0 + +# if $OBJ and $OBJECT are the same we're done +[ $OBJ = $OBJECT ] && exit 0 + +[ -f $OBJ ] && mv -f $OBJ $OBJECT + diff --git a/ape-server/deps/js/src/build/msys-perl-wrapper b/ape-server/deps/js/src/build/msys-perl-wrapper new file mode 100755 index 0000000..0f81e1a --- /dev/null +++ b/ape-server/deps/js/src/build/msys-perl-wrapper @@ -0,0 +1,16 @@ +#!/bin/sh + +args="" + +for i in "${@}" +do + case "$i" in + -I?:/*) + i="$(echo "${i}" | sed -e 's|^-I\(.\):/|-I/\1/|')" + ;; + esac + + args="${args} '${i}'" +done + +eval "exec perl $args" diff --git a/ape-server/deps/js/src/build/unix/mddepend.pl b/ape-server/deps/js/src/build/unix/mddepend.pl new file mode 100755 index 0000000..6889141 --- /dev/null +++ b/ape-server/deps/js/src/build/unix/mddepend.pl @@ -0,0 +1,171 @@ +#!/usr/bin/env perl + +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is this file as it was released upon March 8, 1999. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1999 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# mddepend.pl - Reads in dependencies generated my -MD flag. Prints list +# of objects that need to be rebuilt. These can then be added to the +# PHONY target. Using this script copes with the problem of header +# files that have been removed from the build. +# +# Usage: +# mddepend.pl +# +# Send comments, improvements, bugs to Steve Lamm (slamm@netscape.com). + +use strict; + +use constant DEBUG => 0; + +my $outfile = shift @ARGV; +my $silent = $ENV{MAKEFLAGS} =~ /^\w*s|\s-s/; + +my $line = ''; +my %alldeps; +# Parse dependency files +while (<>) { + s/\r?\n$//; # Handle both unix and DOS line endings + $line .= $_; + if ($line =~ /\\$/) { + chop $line; + next; + } + + $line =~ s|\\|/|g; + + my ($obj,$rest) = split /\s*:\s+/, $line, 2; + $line = ''; + next if !$obj || !$rest; + + my @deps = split /\s+/, $rest; + push @{$alldeps{$obj}}, @deps; + if (DEBUG >= 2) { + foreach my $dep (@deps) { print STDERR "add $obj $dep\n"; } + } +} + +# Test dependencies +my %modtimes; # cache +my @objs; # force rebuild on these +OBJ_LOOP: foreach my $obj (keys %alldeps) { + my $mtime = (stat $obj)[9] or next; + + my %not_in_cache; + my $deps = $alldeps{$obj}; + foreach my $dep_file (@{$deps}) { + my $dep_mtime = $modtimes{$dep_file}; + if (not defined $dep_mtime) { + print STDERR "Skipping $dep_file for $obj, will stat() later\n" if DEBUG >= 2; + $not_in_cache{$dep_file} = 1; + next; + } + + print STDERR "Found $dep_file in cache\n" if DEBUG >= 2; + + if ($dep_mtime > $mtime) { + print STDERR "$dep_file($dep_mtime) newer than $obj($mtime)\n" if DEBUG; + } + elsif ($dep_mtime == -1) { + print STDERR "Couldn't stat $dep_file for $obj\n" if DEBUG; + } + else { + print STDERR "$dep_file($dep_mtime) older than $obj($mtime)\n" if DEBUG >= 2; + next; + } + + push @objs, $obj; # dependency is missing or newer + next OBJ_LOOP; # skip checking the rest of the dependencies + } + + foreach my $dep_file (keys %not_in_cache) { + print STDERR "STAT $dep_file for $obj\n" if DEBUG >= 2; + my $dep_mtime = $modtimes{$dep_file} = (stat $dep_file)[9] || -1; + + if ($dep_mtime > $mtime) { + print STDERR "$dep_file($dep_mtime) newer than $obj($mtime)\n" if DEBUG; + } + elsif ($dep_mtime == -1) { + print STDERR "Couldn't stat $dep_file for $obj\n" if DEBUG; + } + else { + print STDERR "$dep_file($dep_mtime) older than $obj($mtime)\n" if DEBUG >= 2; + next; + } + + push @objs, $obj; # dependency is missing or newer + next OBJ_LOOP; # skip checking the rest of the dependencies + } + + # If we get here it means nothing needs to be done for $obj +} + +# Output objects to rebuild (if needed). +if ($outfile eq '-') { + if (@objs) { + print "@objs: FORCE\n"; + } +} elsif (@objs) { + my $old_output; + my $new_output = "@objs: FORCE\n"; + + # Read in the current dependencies file. + open(OLD, "<$outfile") + and $old_output = ; + close(OLD); + + # Only write out the dependencies if they are different. + if ($new_output ne $old_output) { + open(OUT, ">$outfile") and print OUT "$new_output"; + print "Updating dependencies file, $outfile\n" unless $silent; + if (DEBUG) { + print "new: $new_output\n"; + print "was: $old_output\n" if $old_output ne ''; + } + } +} elsif (-s $outfile) { + # Remove the old dependencies because all objects are up to date. + print "Removing old dependencies file, $outfile\n" unless $silent; + + if (DEBUG) { + my $old_output; + open(OLD, "<$outfile") + and $old_output = ; + close(OLD); + print "was: $old_output\n"; + } + + unlink $outfile; +} diff --git a/ape-server/deps/js/src/build/unix/uniq.pl b/ape-server/deps/js/src/build/unix/uniq.pl new file mode 100755 index 0000000..1b40bda --- /dev/null +++ b/ape-server/deps/js/src/build/unix/uniq.pl @@ -0,0 +1,63 @@ +#!/usr/bin/env perl + +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is this file as it was released upon December 26, 2000. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2000 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Christopher Seawood +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +use Getopt::Std; + +getopts('rs'); +$regexp = 1 if (defined($opt_r)); +$sort = 1 if (defined($opt_s)); + +undef @out; +if ($sort) { + @in = sort @ARGV; +} else { + @in = @ARGV; +} +foreach $d (@in) { + if ($regexp) { + $found = 0; + foreach $dir (@out) { + $found++, last if ($d =~ m/^$dir\// || $d eq $dir); + } + push @out, $d if (!$found); + } else { + push @out, $d if (!grep(/^$d$/, @out)); + } +} +print "@out\n" diff --git a/ape-server/deps/js/src/build/win32/pgomerge.py b/ape-server/deps/js/src/build/win32/pgomerge.py new file mode 100755 index 0000000..383457b --- /dev/null +++ b/ape-server/deps/js/src/build/win32/pgomerge.py @@ -0,0 +1,40 @@ +#!/usr/bin/python +# Usage: pgomerge.py +# Gathers .pgc files from dist/bin and merges them into +# $PWD/$basename.pgd using pgomgr, then deletes them. +# No errors if any of these files don't exist. + +import sys, os, os.path, subprocess +if not sys.platform == "win32": + raise Exception("This script was only meant for Windows.") + +def MergePGOFiles(basename, pgddir, pgcdir): + """Merge pgc files produced from an instrumented binary + into the pgd file for the second pass of profile-guided optimization + with MSVC. |basename| is the name of the DLL or EXE without the + extension. |pgddir| is the path that contains .pgd + (should be the objdir it was built in). |pgcdir| is the path + containing basename!N.pgc files, which is probably dist/bin. + Calls pgomgr to merge each pgc file into the pgd, then deletes + the pgc files.""" + if not os.path.isdir(pgddir) or not os.path.isdir(pgcdir): + return + pgdfile = os.path.abspath(os.path.join(pgddir, basename + ".pgd")) + if not os.path.isfile(pgdfile): + return + for file in os.listdir(pgcdir): + if file.startswith(basename+"!") and file.endswith(".pgc"): + try: + pgcfile = os.path.normpath(os.path.join(pgcdir, file)) + subprocess.call(['pgomgr', '-merge', + pgcfile, + pgdfile]) + os.remove(pgcfile) + except OSError: + pass + +if __name__ == '__main__': + if len(sys.argv) != 3: + print >>sys.stderr, "Usage: pgomerge.py " + sys.exit(1) + MergePGOFiles(sys.argv[1], os.getcwd(), sys.argv[2]) diff --git a/ape-server/deps/js/src/builtins.tbl b/ape-server/deps/js/src/builtins.tbl new file mode 100755 index 0000000..0c1c07c --- /dev/null +++ b/ape-server/deps/js/src/builtins.tbl @@ -0,0 +1,91 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=0 ft=C: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released + * June 22, 2008. + * + * The Initial Developer of the Original Code is + * Andreas Gal + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/** + * This file declares builtin functions that can be called from JITted code. + * Each line starts with "BUILTIN" and an integer, the number of arguments the + * builtin takes. Builtins with no arguments are not supported. + * + * The macro arguments are: + * + * - 'extern' to indicate extern linkage for these functions and the associated + * CallInfo. + * + * - The return type. This identifier must name one of the _JS_TYPEINFO_* + * macros defined in jsbuiltins.h. + * + * - The builtin name. Prefixed with "js_" this gives the native function name. + * + * - The parameter types. + * + * - The cse flag. 1 if the builtin call can be optimized away by common + * subexpression elimination; otherwise 0. This should be 1 only if the + * function is idempotent and the return value is determined solely by the + * arguments. + * + * - The fold flag. Reserved. The same as cse for now. + */ + +/* + * NB: bool FASTCALL is not compatible with Nanojit's calling convention usage. + * Do not use bool FASTCALL, use JSBool only! + */ + +BUILTIN2(extern, JSVAL, js_BoxDouble, CONTEXT, DOUBLE, 1, 1) +BUILTIN2(extern, JSVAL, js_BoxInt32, CONTEXT, INT32, 1, 1) +BUILTIN1(extern, DOUBLE, js_UnboxDouble, JSVAL, 1, 1) +BUILTIN1(extern, INT32, js_UnboxInt32, JSVAL, 1, 1) +BUILTIN2(extern, DOUBLE, js_dmod, DOUBLE, DOUBLE, 1, 1) +BUILTIN2(extern, INT32, js_imod, INT32, INT32, 1, 1) +BUILTIN1(extern, INT32, js_DoubleToInt32, DOUBLE, 1, 1) +BUILTIN1(extern, UINT32, js_DoubleToUint32, DOUBLE, 1, 1) + +BUILTIN2(extern, DOUBLE, js_StringToNumber, CONTEXT, STRING, 1, 1) +BUILTIN2(extern, INT32, js_StringToInt32, CONTEXT, STRING, 1, 1) +BUILTIN2(FRIEND, BOOL, js_CloseIterator, CONTEXT, JSVAL, 0, 0) +BUILTIN2(extern, SIDEEXIT, js_CallTree, INTERPSTATE, FRAGMENT, 0, 0) +BUILTIN3(extern, BOOL, js_AddProperty, CONTEXT, OBJECT, SCOPEPROP, 0, 0) +BUILTIN3(extern, BOOL, js_HasNamedProperty, CONTEXT, OBJECT, STRING, 0, 0) +BUILTIN3(extern, BOOL, js_HasNamedPropertyInt32, CONTEXT, OBJECT, INT32, 0, 0) +BUILTIN3(extern, JSVAL, js_CallGetter, CONTEXT, OBJECT, SCOPEPROP, 0, 0) +BUILTIN2(extern, STRING, js_TypeOfObject, CONTEXT, OBJECT, 1, 1) +BUILTIN2(extern, STRING, js_TypeOfBoolean, CONTEXT, INT32, 1, 1) +BUILTIN2(extern, DOUBLE, js_BooleanOrUndefinedToNumber, CONTEXT, INT32, 1, 1) +BUILTIN2(extern, STRING, js_BooleanOrUndefinedToString, CONTEXT, INT32, 1, 1) +BUILTIN2(extern, OBJECT, js_Arguments, CONTEXT, OBJECT 0, 0) +BUILTIN4(extern, OBJECT, js_NewNullClosure, CONTEXT, OBJECT, OBJECT, OBJECT, 0, 0) diff --git a/ape-server/deps/js/src/config.mk b/ape-server/deps/js/src/config.mk new file mode 100755 index 0000000..0e7696a --- /dev/null +++ b/ape-server/deps/js/src/config.mk @@ -0,0 +1,193 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998-1999 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +ifdef JS_DIST +DIST = $(JS_DIST) +else +DIST = $(DEPTH)/../../dist +endif + +# Set os+release dependent make variables +OS_ARCH := $(subst /,_,$(shell uname -s | sed /\ /s//_/)) + +# Attempt to differentiate between SunOS 5.4 and x86 5.4 +OS_CPUARCH := $(shell uname -m) +ifeq ($(OS_CPUARCH),i86pc) +OS_RELEASE := $(shell uname -r)_$(OS_CPUARCH) +else +ifeq ($(OS_ARCH),AIX) +OS_RELEASE := $(shell uname -v).$(shell uname -r) +else +OS_RELEASE := $(shell uname -r) +endif +endif +ifeq ($(OS_ARCH),IRIX64) +OS_ARCH := IRIX +endif + +# Handle output from win32 unames other than Netscape's version +ifeq (,$(filter-out Windows_95 Windows_98 CYGWIN_95-4.0 CYGWIN_98-4.10, $(OS_ARCH))) + OS_ARCH := WIN95 +endif +ifeq ($(OS_ARCH),WIN95) + OS_ARCH := WINNT + OS_RELEASE := 4.0 +endif +ifeq ($(OS_ARCH), Windows_NT) + OS_ARCH := WINNT + OS_MINOR_RELEASE := $(shell uname -v) + ifeq ($(OS_MINOR_RELEASE),00) + OS_MINOR_RELEASE = 0 + endif + OS_RELEASE := $(OS_RELEASE).$(OS_MINOR_RELEASE) +endif +ifeq (CYGWIN_NT,$(findstring CYGWIN_NT,$(OS_ARCH))) + OS_RELEASE := $(patsubst CYGWIN_NT-%,%,$(OS_ARCH)) + OS_ARCH := WINNT +endif +ifeq ($(OS_ARCH), CYGWIN32_NT) + OS_ARCH := WINNT +endif +ifeq (MINGW32_NT,$(findstring MINGW32_NT,$(OS_ARCH))) + OS_RELEASE := $(patsubst MINGW32_NT-%,%,$(OS_ARCH)) + OS_ARCH := WINNT +endif + +# Virtually all Linux versions are identical. +# Any distinctions are handled in linux.h +ifeq ($(OS_ARCH),Linux) +OS_CONFIG := Linux_All +else +ifeq ($(OS_ARCH),dgux) +OS_CONFIG := dgux +else +ifeq ($(OS_ARCH),Darwin) +OS_CONFIG := Darwin +else +ifeq ($(OS_ARCH),Darwin64) +OS_CONFIG := Darwin64 +else +OS_CONFIG := $(OS_ARCH)$(OS_OBJTYPE)$(OS_RELEASE) +endif +endif +endif +endif + +ASFLAGS = +DEFINES = + +ifeq ($(OS_ARCH), WINNT) +INSTALL = nsinstall +CP = cp +else +INSTALL = $(DIST)/bin/nsinstall +CP = cp +endif + +ifdef BUILD_OPT +ifdef USE_MSVC +OPTIMIZER = -O2 -GL +INTERP_OPTIMIZER = -O2 -GL +BUILTINS_OPTIMIZER = -O2 -GL +LDFLAGS += -LTCG +else +OPTIMIZER = -Os -fstrict-aliasing -fno-exceptions -fno-rtti -Wstrict-aliasing=2 +BUILTINS_OPTIMIZER = -O9 -fstrict-aliasing -fno-exceptions -fno-rtti +INTERP_OPTIMIZER = -O3 -fstrict-aliasing -fno-exceptions -fno-rtti +endif +DEFINES += -UDEBUG -DNDEBUG -UDEBUG_$(USER) +OBJDIR_TAG = _OPT +else +ifdef USE_MSVC +OPTIMIZER = -Zi +INTERP_OPTIMIZER = -Zi +BUILTINS_OPTIMIZER = $(INTERP_OPTIMIZER) +else +OPTIMIZER = -g3 -fstrict-aliasing -fno-exceptions -fno-rtti -Wstrict-aliasing=2 +INTERP_OPTIMIZER = -g3 -fstrict-aliasing -fno-exceptions -fno-rtti +BUILTINS_OPTIMIZER = $(INTERP_OPTIMIZER) +endif +DEFINES += -DDEBUG -DDEBUG_$(USER) +OBJDIR_TAG = _DBG +endif + +SO_SUFFIX = so + +NS_USE_NATIVE = 1 + +include $(DEPTH)/ref-config/$(OS_CONFIG).mk + +ifndef OBJ_SUFFIX +ifdef USE_MSVC +OBJ_SUFFIX = obj +else +OBJ_SUFFIX = o +endif +endif + +ifndef HOST_BIN_SUFFIX +ifeq ($(OS_ARCH),WINNT) +HOST_BIN_SUFFIX = .exe +else +HOST_BIN_SUFFIX = +endif +endif + +# Name of the binary code directories +ifdef OBJROOT +# prepend $(DEPTH) to the root unless it is an absolute path +OBJDIR = $(if $(filter /%,$(OBJROOT)),$(OBJROOT),$(DEPTH)/$(OBJROOT)) +else +ifeq ($(DEPTH),.) +OBJDIR = $(OS_CONFIG)$(OBJDIR_TAG).$(if $(BUILD_IDG),OBJD,OBJ) +else +OBJDIR = $(DEPTH)/$(OS_CONFIG)$(OBJDIR_TAG).$(if $(BUILD_IDG),OBJD,OBJ) +endif +endif + +VPATH = $(OBJDIR) + +LCJAR = js15lc30.jar + +# Library name +LIBDIR := lib +ifeq ($(CPU_ARCH), x86_64) +LIBDIR := lib64 +endif + diff --git a/ape-server/deps/js/src/config/Makefile b/ape-server/deps/js/src/config/Makefile new file mode 100755 index 0000000..0674aa4 --- /dev/null +++ b/ape-server/deps/js/src/config/Makefile @@ -0,0 +1,109 @@ +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Robert Ginda +# John Taylor +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = .. +topsrcdir = .. +srcdir = . +VPATH = . + +include $(DEPTH)/config/autoconf.mk + +# For sanity's sake, we compile nsinstall without the wrapped system +# headers, so that we can use it to set up the wrapped system headers. +VISIBILITY_FLAGS = + +HOST_PROGRAM = nsinstall$(HOST_BIN_SUFFIX) + +ifeq (WINNT,$(HOST_OS_ARCH)) +HOST_CSRCS = nsinstall_win.c +else +HOST_CSRCS = nsinstall.c pathsub.c +endif + +PLSRCS = nfspwd.pl + +TARGETS = $(HOST_PROGRAM) $(PLSRCS:.pl=) $(SIMPLE_PROGRAMS) + +# IMPORTANT: Disable NSBUILDROOT for this directory only, otherwise we have +# a recursive rule for finding nsinstall and the Perl scripts. +ifdef NSBUILDROOT +override NSBUILDROOT := +endif + +ifdef GNU_CC +MODULE_OPTIMIZE_FLAGS = -O3 +endif + +ifndef COMPILER_DEPEND +ifndef MOZ_NATIVE_MAKEDEPEND +DIRS += mkdepend +endif +endif + +include $(topsrcdir)/config/config.mk + +# Do not install util programs +NO_INSTALL=1 + +include $(topsrcdir)/config/rules.mk + +HOST_CFLAGS += -DUNICODE -D_UNICODE + +export:: $(TARGETS) +ifdef HOST_PROGRAM + $(INSTALL) $(HOST_PROGRAM) $(DIST)/bin +endif + +ifdef WRAP_SYSTEM_INCLUDES +export:: + if test ! -d system_wrappers_js; then mkdir system_wrappers_js; fi + $(PERL) $(srcdir)/preprocessor.pl $(DEFINES) $(ACDEFINES) \ + -DBUILD_STATIC_LIBS=$(BUILD_STATIC_LIBS) \ + $(srcdir)/system-headers | $(PERL) $(srcdir)/make-system-wrappers.pl system_wrappers_js + $(INSTALL) system_wrappers_js $(DIST) + +GARBAGE_DIRS += system_wrappers_js +endif + +FORCE: + +ifdef MKDEPEND_DIR +clean clobber realclean clobber_all:: + cd $(MKDEPEND_DIR); $(MAKE) $@ +endif diff --git a/ape-server/deps/js/src/config/Makefile.in b/ape-server/deps/js/src/config/Makefile.in new file mode 100755 index 0000000..a822746 --- /dev/null +++ b/ape-server/deps/js/src/config/Makefile.in @@ -0,0 +1,109 @@ +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Robert Ginda +# John Taylor +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = .. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +# For sanity's sake, we compile nsinstall without the wrapped system +# headers, so that we can use it to set up the wrapped system headers. +VISIBILITY_FLAGS = + +HOST_PROGRAM = nsinstall$(HOST_BIN_SUFFIX) + +ifeq (WINNT,$(HOST_OS_ARCH)) +HOST_CSRCS = nsinstall_win.c +else +HOST_CSRCS = nsinstall.c pathsub.c +endif + +PLSRCS = nfspwd.pl + +TARGETS = $(HOST_PROGRAM) $(PLSRCS:.pl=) $(SIMPLE_PROGRAMS) + +# IMPORTANT: Disable NSBUILDROOT for this directory only, otherwise we have +# a recursive rule for finding nsinstall and the Perl scripts. +ifdef NSBUILDROOT +override NSBUILDROOT := +endif + +ifdef GNU_CC +MODULE_OPTIMIZE_FLAGS = -O3 +endif + +ifndef COMPILER_DEPEND +ifndef MOZ_NATIVE_MAKEDEPEND +DIRS += mkdepend +endif +endif + +include $(topsrcdir)/config/config.mk + +# Do not install util programs +NO_INSTALL=1 + +include $(topsrcdir)/config/rules.mk + +HOST_CFLAGS += -DUNICODE -D_UNICODE + +export:: $(TARGETS) +ifdef HOST_PROGRAM + $(INSTALL) $(HOST_PROGRAM) $(DIST)/bin +endif + +ifdef WRAP_SYSTEM_INCLUDES +export:: + if test ! -d system_wrappers_js; then mkdir system_wrappers_js; fi + $(PERL) $(srcdir)/preprocessor.pl $(DEFINES) $(ACDEFINES) \ + -DBUILD_STATIC_LIBS=$(BUILD_STATIC_LIBS) \ + $(srcdir)/system-headers | $(PERL) $(srcdir)/make-system-wrappers.pl system_wrappers_js + $(INSTALL) system_wrappers_js $(DIST) + +GARBAGE_DIRS += system_wrappers_js +endif + +FORCE: + +ifdef MKDEPEND_DIR +clean clobber realclean clobber_all:: + cd $(MKDEPEND_DIR); $(MAKE) $@ +endif diff --git a/ape-server/deps/js/src/config/Moz/Milestone.pm b/ape-server/deps/js/src/config/Moz/Milestone.pm new file mode 100755 index 0000000..8dd1449 --- /dev/null +++ b/ape-server/deps/js/src/config/Moz/Milestone.pm @@ -0,0 +1,232 @@ +#!/usr/bin/perl -w +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is the Win32 Version System. +# +# The Initial Developer of the Original Code is Netscape Communications Corporation +# Portions created by the Initial Developer are Copyright (C) 2002 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +package Moz::Milestone; +use strict; + +use vars qw($officialMilestone + $milestone); + +local $Moz::Milestone::milestone; +local $Moz::Milestone::officialMilestone; + +# +# Usage: getOfficialMilestone($milestoneFile) +# Returns full milestone (x.x.x.x[ab12pre+]) +# +sub getOfficialMilestone($) { + my $mfile = $_[0]; + open(FILE,"$mfile") || + die ("Can't open $mfile for reading!"); + + my $num = ; + while($num =~ /^\s*#/ || $num !~ /^\d/) { + $num = ; + } + + close(FILE); + if ($num !~ /^\d/) { return; } + chomp($num); + # Remove extra ^M caused by using dos-mode line-endings + chop $num if (substr($num, -1, 1) eq "\r"); + $Moz::Milestone::officialMilestone = $num; + $Moz::Milestone::milestone = &getMilestoneNum; + return $num; +} + +# +# Usage: getMilestoneNum($num) +# Returns: milestone without a + if it exists. +# +sub getMilestoneNum { + if (defined($Moz::Milestone::milestone)) { + return $Moz::Milestone::milestone; + } + + if (defined($Moz::Milestone::officialMilestone)) { + $Moz::Milestone::milestone = $Moz::Milestone::officialMilestone; + } else { + $Moz::Milestone::milestone = $_[0]; + } + + if ($Moz::Milestone::milestone =~ /\+$/) { # for x.x.x+, strip off the + + $Moz::Milestone::milestone =~ s/\+$//; + } + + return $Moz::Milestone::milestone; +} + +# +# Usage: getMilestoneQualifier($num) +# Returns: + if it exists. +# +sub getMilestoneQualifier { + my $milestoneQualifier; + if (defined($Moz::Milestone::officialMilestone)) { + $milestoneQualifier = $Moz::Milestone::officialMilestone; + } else { + $milestoneQualifier = $_[0]; + } + + if ($milestoneQualifier =~ /\+$/) { + return "+"; + } +} + +sub getMilestoneMajor { + my $milestoneMajor; + if (defined($Moz::Milestone::milestone)) { + $milestoneMajor = $Moz::Milestone::milestone; + } else { + $milestoneMajor = $_[0]; + } + my @parts = split(/\./,$milestoneMajor); + return $parts[0]; +} + +sub getMilestoneMinor { + my $milestoneMinor; + if (defined($Moz::Milestone::milestone)) { + $milestoneMinor = $Moz::Milestone::milestone; + } else { + $milestoneMinor = $_[0]; + } + my @parts = split(/\./,$milestoneMinor); + + if ($#parts < 1 ) { return 0; } + return $parts[1]; +} + +sub getMilestoneMini { + my $milestoneMini; + if (defined($Moz::Milestone::milestone)) { + $milestoneMini = $Moz::Milestone::milestone; + } else { + $milestoneMini = $_[0]; + } + my @parts = split(/\./,$milestoneMini); + + if ($#parts < 2 ) { return 0; } + return $parts[2]; +} + +sub getMilestoneMicro { + my $milestoneMicro; + if (defined($Moz::Milestone::milestone)) { + $milestoneMicro = $Moz::Milestone::milestone; + } else { + $milestoneMicro = $_[0]; + } + my @parts = split(/\./,$milestoneMicro); + + if ($#parts < 3 ) { return 0; } + return $parts[3]; +} + +sub getMilestoneAB { + my $milestoneAB; + if (defined($Moz::Milestone::milestone)) { + $milestoneAB = $Moz::Milestone::milestone; + } else { + $milestoneAB = $_[0]; + } + + if ($milestoneAB =~ /a/) { return "alpha"; } + if ($milestoneAB =~ /b/) { return "beta"; } + return "final"; +} + +# +# build_file($template_file,$output_file) +# +sub build_file($$) { + my @FILE; + my @MILESTONE_PARTS; + my $MINI_VERSION = 0; + my $MICRO_VERSION = 0; + my $OFFICIAL = 0; + my $QUALIFIER = ""; + + if (!defined($Moz::Milestone::milestone)) { die("$0: no milestone file set!\n"); } + @MILESTONE_PARTS = split(/\./, &getMilestoneNum); + if ($#MILESTONE_PARTS >= 2) { + $MINI_VERSION = 1; + } else { + $MILESTONE_PARTS[2] = 0; + } + if ($#MILESTONE_PARTS >= 3) { + $MICRO_VERSION = 1; + } else { + $MILESTONE_PARTS[3] = 0; + } + if (! &getMilestoneQualifier) { + $OFFICIAL = 1; + } else { + $QUALIFIER = "+"; + } + + if (-e $_[0]) { + open(FILE, "$_[0]") || die("$0: Can't open $_[0] for reading!\n"); + @FILE = ; + close(FILE); + + open(FILE, ">$_[1]") || die("$0: Can't open $_[1] for writing!\n"); + + # + # There will be more of these based on what we need for files. + # + foreach(@FILE) { + s/__MOZ_MAJOR_VERSION__/$MILESTONE_PARTS[0]/g; + s/__MOZ_MINOR_VERSION__/$MILESTONE_PARTS[1]/g; + s/__MOZ_MINI_VERSION__/$MILESTONE_PARTS[2]/g; + s/__MOZ_MICRO_VERSION__/$MILESTONE_PARTS[3]/g; + if ($MINI_VERSION) { + s/__MOZ_OPTIONAL_MINI_VERSION__/.$MILESTONE_PARTS[2]/g; + } + if ($MICRO_VERSION) { + s/__MOZ_OPTIONAL_MICRO_VERSION__/.$MILESTONE_PARTS[3]/g; + } + + print FILE $_; + } + close(FILE); + } else { + die("$0: $_[0] doesn't exist for autoversioning!\n"); + } + +} + +1; diff --git a/ape-server/deps/js/src/config/autoconf.mk.in b/ape-server/deps/js/src/config/autoconf.mk.in new file mode 100755 index 0000000..144f750 --- /dev/null +++ b/ape-server/deps/js/src/config/autoconf.mk.in @@ -0,0 +1,347 @@ +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is this file as it was released upon August 6, 1998. +# +# The Initial Developer of the Original Code is +# Christopher Seawood. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Benjamin Smedberg +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# A netscape style .mk file for autoconf builds + +INCLUDED_AUTOCONF_MK = 1 +USE_AUTOCONF = 1 +MOZILLA_CLIENT = 1 +target = @target@ +ac_configure_args = @ac_configure_args@ +BUILD_MODULES = @BUILD_MODULES@ +MOZILLA_VERSION = @MOZILLA_VERSION@ + +MOZ_BUILD_APP = @MOZ_BUILD_APP@ +MOZ_APP_NAME = @MOZ_APP_NAME@ +MOZ_APP_DISPLAYNAME = @MOZ_APP_DISPLAYNAME@ +MOZ_APP_VERSION = @MOZ_APP_VERSION@ + +MOZ_PKG_SPECIAL = @MOZ_PKG_SPECIAL@ + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = @bindir@ +includedir = @includedir@ +libdir = @libdir@ +datadir = @datadir@ +mandir = @mandir@ + +installdir = $(libdir)/$(MOZ_APP_NAME)-$(MOZ_APP_VERSION) +sdkdir = $(libdir)/$(MOZ_APP_NAME)-devel-$(MOZ_APP_VERSION) + +TOP_DIST = @TOP_DIST@ +ifneq (,$(filter /%,$(TOP_DIST))) +DIST = $(TOP_DIST) +else +DIST = $(DEPTH)/$(TOP_DIST) +endif + +MOZ_JS_LIBS = @MOZ_JS_LIBS@ + +MOZ_SYNC_BUILD_FILES = @MOZ_SYNC_BUILD_FILES@ + +MOZ_DEBUG = @MOZ_DEBUG@ +MOZ_DEBUG_MODULES = @MOZ_DEBUG_MODULES@ +MOZ_PROFILE_MODULES = @MOZ_PROFILE_MODULES@ +MOZ_DEBUG_ENABLE_DEFS = @MOZ_DEBUG_ENABLE_DEFS@ +MOZ_DEBUG_DISABLE_DEFS = @MOZ_DEBUG_DISABLE_DEFS@ +MOZ_DEBUG_FLAGS = @MOZ_DEBUG_FLAGS@ +MOZ_DEBUG_LDFLAGS=@MOZ_DEBUG_LDFLAGS@ +MOZ_DBGRINFO_MODULES = @MOZ_DBGRINFO_MODULES@ +MOZ_EXTENSIONS = @MOZ_EXTENSIONS@ +MOZ_IMG_DECODERS= @MOZ_IMG_DECODERS@ +MOZ_IMG_ENCODERS= @MOZ_IMG_ENCODERS@ +MOZ_JSDEBUGGER = @MOZ_JSDEBUGGER@ +MOZ_LEAKY = @MOZ_LEAKY@ +MOZ_MEMORY = @MOZ_MEMORY@ +MOZ_JPROF = @MOZ_JPROF@ +MOZ_SHARK = @MOZ_SHARK@ +MOZ_CALLGRIND = @MOZ_CALLGRIND@ +MOZ_VTUNE = @MOZ_VTUNE@ +DEHYDRA_PATH = @DEHYDRA_PATH@ + +NS_TRACE_MALLOC = @NS_TRACE_MALLOC@ +INCREMENTAL_LINKER = @INCREMENTAL_LINKER@ +MACOSX_DEPLOYMENT_TARGET = @MACOSX_DEPLOYMENT_TARGET@ +BUILD_STATIC_LIBS = @BUILD_STATIC_LIBS@ +ENABLE_TESTS = @ENABLE_TESTS@ +JS_ULTRASPARC_OPTS = @JS_ULTRASPARC_OPTS@ + +TAR=@TAR@ + +RM = rm -f + +# The MOZ_UI_LOCALE var is used to build a particular locale. Do *not* +# use the var to change any binary files. Do *not* use this var unless you +# write rules for the "clean-locale" and "locale" targets. +MOZ_UI_LOCALE = @MOZ_UI_LOCALE@ + +MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS = @MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS@ +MOZ_COMPONENT_NSPR_LIBS=@MOZ_COMPONENT_NSPR_LIBS@ + +MOZ_FIX_LINK_PATHS=@MOZ_FIX_LINK_PATHS@ + +XPCOM_FROZEN_LDOPTS=@XPCOM_FROZEN_LDOPTS@ +XPCOM_LIBS=@XPCOM_LIBS@ +MOZ_TIMELINE=@MOZ_TIMELINE@ + +ENABLE_STRIP = @ENABLE_STRIP@ +PKG_SKIP_STRIP = @PKG_SKIP_STRIP@ + +MOZ_POST_DSO_LIB_COMMAND = @MOZ_POST_DSO_LIB_COMMAND@ +MOZ_POST_PROGRAM_COMMAND = @MOZ_POST_PROGRAM_COMMAND@ + +MOZ_BUILD_ROOT = @MOZ_BUILD_ROOT@ + +MOZ_INSURE = @MOZ_INSURE@ +MOZ_INSURIFYING = @MOZ_INSURIFYING@ +MOZ_INSURE_DIRS = @MOZ_INSURE_DIRS@ +MOZ_INSURE_EXCLUDE_DIRS = @MOZ_INSURE_EXCLUDE_DIRS@ + +MOZ_NATIVE_NSPR = @MOZ_NATIVE_NSPR@ + +CROSS_COMPILE = @CROSS_COMPILE@ + +OS_CPPFLAGS = @CPPFLAGS@ +OS_CFLAGS = $(OS_CPPFLAGS) @CFLAGS@ +OS_CXXFLAGS = $(OS_CPPFLAGS) @CXXFLAGS@ +OS_LDFLAGS = @LDFLAGS@ + +OS_COMPILE_CFLAGS = $(OS_CPPFLAGS) @COMPILE_CFLAGS@ +OS_COMPILE_CXXFLAGS = $(OS_CPPFLAGS) @COMPILE_CXXFLAGS@ + +OS_INCLUDES = $(NSPR_CFLAGS) +OS_LIBS = @LIBS@ +ACDEFINES = @MOZ_DEFINES@ + +WARNINGS_AS_ERRORS = @WARNINGS_AS_ERRORS@ + +MOZ_OPTIMIZE = @MOZ_OPTIMIZE@ +MOZ_OPTIMIZE_FLAGS = @MOZ_OPTIMIZE_FLAGS@ +MOZ_OPTIMIZE_LDFLAGS = @MOZ_OPTIMIZE_LDFLAGS@ +MOZ_OPTIMIZE_SIZE_TWEAK = @MOZ_OPTIMIZE_SIZE_TWEAK@ + +MOZ_RTTI_FLAGS_ON = @_MOZ_RTTI_FLAGS_ON@ +MOZ_EXCEPTIONS_FLAGS_ON = @_MOZ_EXCEPTIONS_FLAGS_ON@ + +MOZ_PROFILE_GUIDED_OPTIMIZE_DISABLE = @MOZ_PROFILE_GUIDED_OPTIMIZE_DISABLE@ +PROFILE_GEN_CFLAGS = @PROFILE_GEN_CFLAGS@ +PROFILE_GEN_LDFLAGS = @PROFILE_GEN_LDFLAGS@ +PROFILE_USE_CFLAGS = @PROFILE_USE_CFLAGS@ +PROFILE_USE_LDFLAGS = @PROFILE_USE_LDFLAGS@ + +WIN_TOP_SRC = @WIN_TOP_SRC@ +CYGWIN_WRAPPER = @CYGWIN_WRAPPER@ +AS_PERL = @AS_PERL@ +CYGDRIVE_MOUNT = @CYGDRIVE_MOUNT@ +AR = @AR@ +AR_FLAGS = @AR_FLAGS@ +AR_EXTRACT = @AR_EXTRACT@ +AR_LIST = @AR_LIST@ +AR_DELETE = @AR_DELETE@ +AS = @AS@ +ASFLAGS = @ASFLAGS@ +AS_DASH_C_FLAG = @AS_DASH_C_FLAG@ +LD = @LD@ +RC = @RC@ +RCFLAGS = @RCFLAGS@ +WINDRES = @WINDRES@ +IMPLIB = @IMPLIB@ +FILTER = @FILTER@ +BIN_FLAGS = @BIN_FLAGS@ +_MSC_VER = @_MSC_VER@ + +DLL_PREFIX = @DLL_PREFIX@ +LIB_PREFIX = @LIB_PREFIX@ +OBJ_SUFFIX = @OBJ_SUFFIX@ +LIB_SUFFIX = @LIB_SUFFIX@ +DLL_SUFFIX = @DLL_SUFFIX@ +BIN_SUFFIX = @BIN_SUFFIX@ +ASM_SUFFIX = @ASM_SUFFIX@ +IMPORT_LIB_SUFFIX = @IMPORT_LIB_SUFFIX@ +USE_N32 = @USE_N32@ +HAVE_64BIT_OS = @HAVE_64BIT_OS@ + +# Temp hack. It is not my intention to leave this crap in here for ever. +# Im talking to fur right now to solve the problem without introducing +# NS_USE_NATIVE to the build system -ramiro. +NS_USE_NATIVE = @NS_USE_NATIVE@ + +CC = @CC@ +CXX = @CXX@ + +CC_VERSION = @CC_VERSION@ +CXX_VERSION = @CXX_VERSION@ + +GNU_AS = @GNU_AS@ +GNU_LD = @GNU_LD@ +GNU_CC = @GNU_CC@ +GNU_CXX = @GNU_CXX@ +HAVE_GCC3_ABI = @HAVE_GCC3_ABI@ +INTEL_CC = @INTEL_CC@ +INTEL_CXX = @INTEL_CXX@ + +HOST_CC = @HOST_CC@ +HOST_CXX = @HOST_CXX@ +HOST_CFLAGS = @HOST_CFLAGS@ +HOST_CXXFLAGS = @HOST_CXXFLAGS@ +HOST_OPTIMIZE_FLAGS = @HOST_OPTIMIZE_FLAGS@ +HOST_NSPR_MDCPUCFG = @HOST_NSPR_MDCPUCFG@ +HOST_AR = @HOST_AR@ +HOST_AR_FLAGS = @HOST_AR_FLAGS@ +HOST_LD = @HOST_LD@ +HOST_RANLIB = @HOST_RANLIB@ +HOST_BIN_SUFFIX = @HOST_BIN_SUFFIX@ + +HOST_OS_ARCH = @HOST_OS_ARCH@ +host_cpu = @host_cpu@ +host_vendor = @host_vendor@ +host_os = @host_os@ + +TARGET_NSPR_MDCPUCFG = @TARGET_NSPR_MDCPUCFG@ +TARGET_CPU = @TARGET_CPU@ +TARGET_VENDOR = @TARGET_VENDOR@ +TARGET_OS = @TARGET_OS@ +TARGET_MD_ARCH = @TARGET_MD_ARCH@ +TARGET_XPCOM_ABI = @TARGET_XPCOM_ABI@ + +AUTOCONF = @AUTOCONF@ +PERL = @PERL@ +PYTHON = @PYTHON@ +RANLIB = @RANLIB@ +WHOAMI = @WHOAMI@ +UNZIP = @UNZIP@ +ZIP = @ZIP@ +XARGS = @XARGS@ +STRIP = @STRIP@ +DOXYGEN = @DOXYGEN@ +PBBUILD_BIN = @PBBUILD@ +SDP = @SDP@ +NSINSTALL_BIN = @NSINSTALL_BIN@ + +NSPR_CONFIG = @NSPR_CONFIG@ +NSPR_CFLAGS = @NSPR_CFLAGS@ +NSPR_LIBS = @NSPR_LIBS@ + +USE_DEPENDENT_LIBS = @USE_DEPENDENT_LIBS@ + +JS_NATIVE_EDITLINE = @JS_NATIVE_EDITLINE@ +JS_DISABLE_SHELL = @JS_DISABLE_SHELL@ +EDITLINE_LIBS = @EDITLINE_LIBS@ + +# MKSHLIB_FORCE_ALL is used to force the linker to include all object +# files present in an archive. MKSHLIB_UNFORCE_ALL reverts the linker +# to normal behavior. Makefile's that create shared libraries out of +# archives use these flags to force in all of the .o files in the +# archives into the shared library. +WRAP_MALLOC_LIB = @WRAP_MALLOC_LIB@ +WRAP_MALLOC_CFLAGS = @WRAP_MALLOC_CFLAGS@ +DSO_CFLAGS = @DSO_CFLAGS@ +DSO_PIC_CFLAGS = @DSO_PIC_CFLAGS@ +MKSHLIB = @MKSHLIB@ +MKCSHLIB = @MKCSHLIB@ +MKSHLIB_FORCE_ALL = @MKSHLIB_FORCE_ALL@ +MKSHLIB_UNFORCE_ALL = @MKSHLIB_UNFORCE_ALL@ +DSO_LDOPTS = @DSO_LDOPTS@ +DLL_SUFFIX = @DLL_SUFFIX@ + +NO_LD_ARCHIVE_FLAGS = @NO_LD_ARCHIVE_FLAGS@ + +MOZ_TOOLKIT_REGISTRY_CFLAGS = \ + $(TK_CFLAGS) + +MOZ_NATIVE_MAKEDEPEND = @SYSTEM_MAKEDEPEND@ + +MOZ_AUTO_DEPS = @MOZ_AUTO_DEPS@ +COMPILER_DEPEND = @COMPILER_DEPEND@ +MDDEPDIR := @MDDEPDIR@ + +MOZ_DEMANGLE_SYMBOLS = @MOZ_DEMANGLE_SYMBOLS@ + +# XXX - these need to be cleaned up and have real checks added -cls +CM_BLDTYPE=dbg +AWT_11=1 +MOZ_BITS=32 +OS_TARGET=@OS_TARGET@ +OS_ARCH=@OS_ARCH@ +OS_RELEASE=@OS_RELEASE@ +OS_TEST=@OS_TEST@ + +# For Solaris build +SOLARIS_SUNPRO_CC = @SOLARIS_SUNPRO_CC@ +SOLARIS_SUNPRO_CXX = @SOLARIS_SUNPRO_CXX@ + +# For AIX build +AIX_OBJMODEL = @AIX_OBJMODEL@ + +# For OS/2 build +MOZ_OS2_TOOLS = @MOZ_OS2_TOOLS@ +MOZ_OS2_USE_DECLSPEC = @MOZ_OS2_USE_DECLSPEC@ +MOZ_OS2_HIGH_MEMORY = @MOZ_OS2_HIGH_MEMORY@ + +MOZILLA_OFFICIAL = @MOZILLA_OFFICIAL@ + +# Win32 options +MOZ_BROWSE_INFO = @MOZ_BROWSE_INFO@ +MOZ_TOOLS_DIR = @MOZ_TOOLS_DIR@ +MOZ_DEBUG_SYMBOLS = @MOZ_DEBUG_SYMBOLS@ +MOZ_QUANTIFY = @MOZ_QUANTIFY@ +MSMANIFEST_TOOL = @MSMANIFEST_TOOL@ +WIN32_REDIST_DIR = @WIN32_REDIST_DIR@ +MOZ_MEMORY_LDFLAGS = @MOZ_MEMORY_LDFLAGS@ + +# Codesighs tools option, enables win32 mapfiles. +MOZ_MAPINFO = @MOZ_MAPINFO@ + +WINCE = @WINCE@ +WINCE_WINDOWS_MOBILE = @WINCE_WINDOWS_MOBILE@ + +MACOS_SDK_DIR = @MACOS_SDK_DIR@ +NEXT_ROOT = @NEXT_ROOT@ +GCC_VERSION = @GCC_VERSION@ +XCODEBUILD_VERSION= @XCODEBUILD_VERSION@ +HAS_XCODE_2_1 = @HAS_XCODE_2_1@ +UNIVERSAL_BINARY= @UNIVERSAL_BINARY@ +HAVE_DTRACE= @HAVE_DTRACE@ + +VISIBILITY_FLAGS = @VISIBILITY_FLAGS@ +WRAP_SYSTEM_INCLUDES = @WRAP_SYSTEM_INCLUDES@ + +ENABLE_JIT = @ENABLE_JIT@ +NANOJIT_ARCH = @NANOJIT_ARCH@ +HAVE_ARM_SIMD= @HAVE_ARM_SIMD@ diff --git a/ape-server/deps/js/src/config/check-sync-dirs.py b/ape-server/deps/js/src/config/check-sync-dirs.py new file mode 100755 index 0000000..8049c38 --- /dev/null +++ b/ape-server/deps/js/src/config/check-sync-dirs.py @@ -0,0 +1,103 @@ +# check-sync-dirs.py --- check that one directory is an exact subset of another +# +# Usage: python check-sync-dirs.py COPY ORIGINAL +# +# Check that the files present in the directory tree COPY are exact +# copies of their counterparts in the directory tree ORIGINAL. COPY +# need not have all the files in ORIGINAL, but COPY may not have files +# absent from ORIGINAL. +# +# Each directory in COPY may have a file named +# 'check-sync-exceptions', which lists files in COPY that need not be +# the same as the corresponding file in ORIGINAL, or exist at all in +# ORIGINAL. (The 'check-sync-exceptions' file itself is always +# treated as exceptional.) Blank lines and '#' comments in the file +# are ignored. + +import sys +import os +from os.path import join +import filecmp +import textwrap +import fnmatch + +if len(sys.argv) != 3: + print >> sys.stderr, 'TEST-UNEXPECTED-FAIL | check-sync-dirs.py | Usage: %s COPY ORIGINAL' % sys.argv[0] + sys.exit(1) + +copy = os.path.abspath(sys.argv[1]) +original = os.path.abspath(sys.argv[2]) + +# Ignore detritus left lying around by editing tools. +ignored_patterns = ['*~', '.#*', '#*#', '*.orig', '*.rej'] + +# Return the contents of FILENAME, a 'check-sync-exceptions' file, as +# a dictionary whose keys are exactly the list of filenames, along +# with the basename of FILENAME itself. If FILENAME does not exist, +# return the empty dictionary. +def read_exceptions(filename): + if (os.path.exists(filename)): + f = file(filename) + exceptions = {} + for line in f: + line = line.strip() + if line != '' and line[0] != '#': + exceptions[line] = None + exceptions[os.path.basename (filename)] = None + f.close() + return exceptions + else: + return {} + +# Return true if FILENAME matches any pattern in the list of filename +# patterns PATTERNS. +def fnmatch_any(filename, patterns): + for pattern in patterns: + if fnmatch.fnmatch(filename, pattern): + return True + return False + +# Check the contents of the directory tree COPY against ORIGINAL. For each +# file that differs, apply REPORT to COPY, ORIGINAL, and the file's +# relative path. COPY and ORIGINAL should be absolute. Ignore files +# that match patterns given in the list IGNORE. +def check(copy, original, ignore): + os.chdir(copy) + for (dirpath, dirnames, filenames) in os.walk('.'): + exceptions = read_exceptions(join(dirpath, 'check-sync-exceptions')) + for filename in filenames: + if (filename in exceptions) or fnmatch_any(filename, ignore): + continue + relative_name = join(dirpath, filename) + original_name = join(original, relative_name) + if (os.path.exists(original_name) + and filecmp.cmp(relative_name, original_name)): + continue + report(copy, original, relative_name) + +differences_found = False + +# Print an error message for DIFFERING, which was found to differ +# between COPY and ORIGINAL. Set the global variable differences_found. +def report(copy, original, differing): + global differences_found + if not differences_found: + print >> sys.stderr, 'TEST-UNEXPECTED-FAIL | check-sync-dirs.py | build file copies are not in sync\n' \ + 'TEST-INFO | check-sync-dirs.py | file(s) found in: %s\n' \ + 'TEST-INFO | check-sync-dirs.py | differ from their originals in: %s' \ + % (copy, original) + print >> sys.stderr, 'TEST-INFO | check-sync-dirs.py | differing file: %s' % differing + differences_found = True + +check(copy, original, ignored_patterns) + +if differences_found: + msg = '''In general, the files in '%s' should always be exact copies of +originals in '%s'. A change made to one should also be made to the +other. See 'check-sync-dirs.py' for more details.''' \ + % (copy, original) + print >> sys.stderr, textwrap.fill(msg, 75) + sys.exit(1) + +print >> sys.stderr, 'TEST-PASS | check-sync-dirs.py | %s <= %s' % (copy, original) +sys.exit(0) diff --git a/ape-server/deps/js/src/config/check-sync-exceptions b/ape-server/deps/js/src/config/check-sync-exceptions new file mode 100755 index 0000000..37ebc7e --- /dev/null +++ b/ape-server/deps/js/src/config/check-sync-exceptions @@ -0,0 +1,7 @@ +Makefile.in +autoconf.mk.in +check-sync-dirs.py +static-checking-config.mk + +# This is a copy of nspr's config/make-system-wrappers.pl. +make-system-wrappers.pl diff --git a/ape-server/deps/js/src/config/config.mk b/ape-server/deps/js/src/config/config.mk new file mode 100755 index 0000000..f2a1a16 --- /dev/null +++ b/ape-server/deps/js/src/config/config.mk @@ -0,0 +1,837 @@ +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Benjamin Smedberg +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# config.mk +# +# Determines the platform and builds the macros needed to load the +# appropriate platform-specific .mk file, then defines all (most?) +# of the generic macros. +# + +# Define an include-at-most-once flag +INCLUDED_CONFIG_MK = 1 + +EXIT_ON_ERROR = set -e; # Shell loops continue past errors without this. + +ifndef topsrcdir +topsrcdir = $(DEPTH) +endif + +ifndef INCLUDED_AUTOCONF_MK +include $(DEPTH)/config/autoconf.mk +endif +ifndef INCLUDED_INSURE_MK +ifdef MOZ_INSURIFYING +include $(topsrcdir)/config/insure.mk +endif +endif + +COMMA = , + +# Sanity check some variables +CHECK_VARS := \ + XPI_NAME \ + LIBRARY_NAME \ + MODULE \ + DEPTH \ + SHORT_LIBNAME \ + XPI_PKGNAME \ + INSTALL_EXTENSION_ID \ + SHARED_LIBRARY_NAME \ + STATIC_LIBRARY_NAME \ + $(NULL) + +# checks for internal spaces or trailing spaces in the variable +# named by $x +check-variable = $(if $(filter-out 0 1,$(words $($(x))z)),$(error Spaces are not allowed in $(x))) + +$(foreach x,$(CHECK_VARS),$(check-variable)) + +core_abspath = $(if $(findstring :,$(1)),$(1),$(if $(filter /%,$(1)),$(1),$(CURDIR)/$(1))) + +# FINAL_TARGET specifies the location into which we copy end-user-shipped +# build products (typelibs, components, chrome). +# +# It will usually be the well-loved $(DIST)/bin, today, but can also be an +# XPI-contents staging directory for ambitious and right-thinking extensions. +FINAL_TARGET = $(if $(XPI_NAME),$(DIST)/xpi-stage/$(XPI_NAME),$(DIST)/bin) + +# MAKE_JARS_TARGET is a staging area for make-jars.pl. When packaging in +# the jar format, make-jars leaves behind a directory structure that's not +# needed in $(FINAL_TARGET). For both, flat, and symlink, the directory +# structure contains the chrome, so leave it in $(FINAL_TARGET). +ifeq (jar,$(MOZ_CHROME_FILE_FORMAT)) +MAKE_JARS_TARGET = $(if $(XPI_NAME),$(FINAL_TARGET).stage,$(DIST)/chrome-stage) +else +MAKE_JARS_TARGET = $(FINAL_TARGET) +endif + +# The VERSION_NUMBER is suffixed onto the end of the DLLs we ship. +VERSION_NUMBER = 50 + +ifeq ($(HOST_OS_ARCH),WINNT) +win_srcdir := $(subst $(topsrcdir),$(WIN_TOP_SRC),$(srcdir)) +BUILD_TOOLS = $(WIN_TOP_SRC)/build/unix +else +win_srcdir := $(srcdir) +BUILD_TOOLS = $(topsrcdir)/build/unix +endif + +CONFIG_TOOLS = $(MOZ_BUILD_ROOT)/config +AUTOCONF_TOOLS = $(topsrcdir)/build/autoconf + +ifeq ($(OS_ARCH),QNX) +ifeq ($(OS_TARGET),NTO) +LD := qcc -Vgcc_ntox86 -nostdlib +else +LD := $(CC) +endif +endif +ifeq ($(OS_ARCH),BeOS) +BEOS_ADDON_WORKAROUND = 1 +endif + +# +# Strip off the excessively long version numbers on these platforms, +# but save the version to allow multiple versions of the same base +# platform to be built in the same tree. +# +ifneq (,$(filter FreeBSD HP-UX IRIX Linux NetBSD OpenBSD OSF1 SunOS,$(OS_ARCH))) +OS_RELEASE := $(basename $(OS_RELEASE)) + +# Allow the user to ignore the OS_VERSION, which is usually irrelevant. +ifdef WANT_MOZILLA_CONFIG_OS_VERSION +OS_VERS := $(suffix $(OS_RELEASE)) +OS_VERSION := $(shell echo $(OS_VERS) | sed 's/-.*//') +endif + +endif + +OS_CONFIG := $(OS_ARCH)$(OS_RELEASE) + +FINAL_LINK_LIBS = $(DEPTH)/config/final-link-libs +FINAL_LINK_COMPS = $(DEPTH)/config/final-link-comps +FINAL_LINK_COMP_NAMES = $(DEPTH)/config/final-link-comp-names + +MOZ_UNICHARUTIL_LIBS = $(LIBXUL_DIST)/lib/$(LIB_PREFIX)unicharutil_s.$(LIB_SUFFIX) +MOZ_WIDGET_SUPPORT_LIBS = $(DIST)/lib/$(LIB_PREFIX)widgetsupport_s.$(LIB_SUFFIX) + +ifdef MOZ_MEMORY +ifneq (,$(filter-out WINNT WINCE,$(OS_ARCH))) +JEMALLOC_LIBS = $(MKSHLIB_FORCE_ALL) $(call EXPAND_MOZLIBNAME,jemalloc) $(MKSHLIB_UNFORCE_ALL) +endif +endif + +# determine debug-related options +_DEBUG_CFLAGS := +_DEBUG_LDFLAGS := + +ifndef MOZ_DEBUG + # global debugging is disabled + # check if it was explicitly enabled for this module + ifneq (, $(findstring $(MODULE), $(MOZ_DEBUG_MODULES))) + MOZ_DEBUG:=1 + endif +else + # global debugging is enabled + # check if it was explicitly disabled for this module + ifneq (, $(findstring ^$(MODULE), $(MOZ_DEBUG_MODULES))) + MOZ_DEBUG:= + endif +endif + +ifdef MOZ_DEBUG + _DEBUG_CFLAGS += $(MOZ_DEBUG_ENABLE_DEFS) + XULPPFLAGS += $(MOZ_DEBUG_ENABLE_DEFS) +else + _DEBUG_CFLAGS += $(MOZ_DEBUG_DISABLE_DEFS) + XULPPFLAGS += $(MOZ_DEBUG_DISABLE_DEFS) +endif + +# determine if -g should be passed to the compiler, based on +# the current module, and the value of MOZ_DBGRINFO_MODULES + +ifdef MOZ_DEBUG + MOZ_DBGRINFO_MODULES += ALL_MODULES + pattern := ALL_MODULES ^ALL_MODULES +else + MOZ_DBGRINFO_MODULES += ^ALL_MODULES + pattern := ALL_MODULES ^ALL_MODULES +endif + +ifdef MODULE + # our current Makefile specifies a module name - add it to our pattern + pattern += $(MODULE) ^$(MODULE) +endif + +# start by finding the first relevant module name +# (remember that the order of the module names in MOZ_DBGRINFO_MODULES +# is reversed from the order the user specified to configure - +# this allows the user to put general names at the beginning +# of the list, and to override them with explicit module names later +# in the list) + +first_match:=$(firstword $(filter $(pattern), $(MOZ_DBGRINFO_MODULES))) + +ifeq ($(first_match), $(MODULE)) + # the user specified explicitly that + # this module should be compiled with -g + _DEBUG_CFLAGS += $(MOZ_DEBUG_FLAGS) + _DEBUG_LDFLAGS += $(MOZ_DEBUG_LDFLAGS) +else + ifneq ($(first_match), ^$(MODULE)) + ifeq ($(first_match), ALL_MODULES) + # the user didn't mention this module explicitly, + # but wanted all modules to be compiled with -g + _DEBUG_CFLAGS += $(MOZ_DEBUG_FLAGS) + _DEBUG_LDFLAGS += $(MOZ_DEBUG_LDFLAGS) + endif + endif +endif + + +# append debug flags +# (these might have been above when processing MOZ_DBGRINFO_MODULES) +OS_CFLAGS += $(_DEBUG_CFLAGS) +OS_CXXFLAGS += $(_DEBUG_CFLAGS) +OS_LDFLAGS += $(_DEBUG_LDFLAGS) + +# XXX: What does this? Bug 482434 filed for better explanation. +ifeq ($(OS_ARCH)_$(GNU_CC),WINNT_) +ifdef MOZ_DEBUG +ifneq (,$(MOZ_BROWSE_INFO)$(MOZ_BSCFILE)) +OS_CFLAGS += -FR +OS_CXXFLAGS += -FR +endif +else # ! MOZ_DEBUG + +# We don't build a static CRT when building a custom CRT, +# it appears to be broken. So don't link to jemalloc if +# the Makefile wants static CRT linking. +ifeq ($(MOZ_MEMORY)_$(USE_STATIC_LIBS),1_) +# Disable default CRT libs and add the right lib path for the linker +OS_LDFLAGS += $(MOZ_MEMORY_LDFLAGS) +endif + +# MOZ_DEBUG_SYMBOLS generates debug symbols in separate PDB files. +# Used for generating an optimized build with debugging symbols. +# Used in the Windows nightlies to generate symbols for crash reporting. +ifdef MOZ_DEBUG_SYMBOLS +OS_CXXFLAGS += -Zi -UDEBUG -DNDEBUG +OS_CFLAGS += -Zi -UDEBUG -DNDEBUG +OS_LDFLAGS += -DEBUG -OPT:REF -OPT:nowin98 +endif + +ifdef MOZ_QUANTIFY +# -FIXED:NO is needed for Quantify to work, but it increases the size +# of executables, so only use it if building for Quantify. +WIN32_EXE_LDFLAGS += -FIXED:NO + +# We need -OPT:NOICF to prevent identical methods from being merged together. +# Otherwise, Quantify doesn't know which method was actually called when it's +# showing you the profile. +OS_LDFLAGS += -OPT:NOICF +endif + +# +# Handle trace-malloc in optimized builds. +# No opt to give sane callstacks. +# +ifdef NS_TRACE_MALLOC +MOZ_OPTIMIZE_FLAGS=-Zi -Od -UDEBUG -DNDEBUG +OS_LDFLAGS = -DEBUG -PDB:NONE -OPT:REF -OPT:nowin98 +endif # NS_TRACE_MALLOC + +endif # MOZ_DEBUG +endif # WINNT && !GNU_CC + +# +# Build using PIC by default +# Do not use PIC if not building a shared lib (see exceptions below) +# + +ifndef BUILD_STATIC_LIBS +_ENABLE_PIC=1 +endif +ifneq (,$(FORCE_SHARED_LIB)$(FORCE_USE_PIC)) +_ENABLE_PIC=1 +endif + +# In Firefox, all components are linked into either libxul or the static +# meta-component, and should be compiled with PIC. +ifdef MOZ_META_COMPONENT +_ENABLE_PIC=1 +endif + +# If module is going to be merged into the nsStaticModule, +# make sure that the entry points are translated and +# the module is built static. + +ifdef IS_COMPONENT +ifdef EXPORT_LIBRARY +ifneq (,$(BUILD_STATIC_LIBS)) +ifdef MODULE_NAME +DEFINES += -DXPCOM_TRANSLATE_NSGM_ENTRY_POINT=1 +FORCE_STATIC_LIB=1 +endif +endif +endif +endif + +# Determine if module being compiled is destined +# to be merged into libxul + +ifdef MOZ_ENABLE_LIBXUL +ifdef LIBXUL_LIBRARY +ifdef IS_COMPONENT +ifdef MODULE_NAME +DEFINES += -DXPCOM_TRANSLATE_NSGM_ENTRY_POINT=1 +else +$(error Component makefile does not specify MODULE_NAME.) +endif +endif +FORCE_STATIC_LIB=1 +_ENABLE_PIC=1 +SHORT_LIBNAME= +endif +endif + +# If we are building this component into an extension/xulapp, it cannot be +# statically linked. In the future we may want to add a xulapp meta-component +# build option. + +ifdef XPI_NAME +_ENABLE_PIC=1 +ifdef IS_COMPONENT +EXPORT_LIBRARY= +FORCE_STATIC_LIB= +FORCE_SHARED_LIB=1 +endif +endif + +# +# Disable PIC if necessary +# + +ifndef _ENABLE_PIC +DSO_CFLAGS= +ifeq ($(OS_ARCH)_$(HAVE_GCC3_ABI),Darwin_1) +DSO_PIC_CFLAGS=-mdynamic-no-pic +else +DSO_PIC_CFLAGS= +endif +endif + +ifndef SHARED_LIBRARY_NAME +ifdef LIBRARY_NAME +SHARED_LIBRARY_NAME=$(LIBRARY_NAME) +endif +endif + +ifndef STATIC_LIBRARY_NAME +ifdef LIBRARY_NAME +STATIC_LIBRARY_NAME=$(LIBRARY_NAME) +endif +endif + +# This comes from configure +ifdef MOZ_PROFILE_GUIDED_OPTIMIZE_DISABLE +NO_PROFILE_GUIDED_OPTIMIZE = 1 +endif + +# Enable profile-based feedback +ifndef NO_PROFILE_GUIDED_OPTIMIZE +ifdef MOZ_PROFILE_GENERATE +# No sense in profiling tools +ifndef INTERNAL_TOOLS +OS_CFLAGS += $(PROFILE_GEN_CFLAGS) +OS_CXXFLAGS += $(PROFILE_GEN_CFLAGS) +OS_LDFLAGS += $(PROFILE_GEN_LDFLAGS) +ifeq (WINNT,$(OS_ARCH)) +AR_FLAGS += -LTCG +endif +endif # INTERNAL_TOOLS +endif # MOZ_PROFILE_GENERATE + +ifdef MOZ_PROFILE_USE +ifndef INTERNAL_TOOLS +OS_CFLAGS += $(PROFILE_USE_CFLAGS) +OS_CXXFLAGS += $(PROFILE_USE_CFLAGS) +OS_LDFLAGS += $(PROFILE_USE_LDFLAGS) +ifeq (WINNT,$(OS_ARCH)) +AR_FLAGS += -LTCG +endif +endif # INTERNAL_TOOLS +endif # MOZ_PROFILE_USE +endif # NO_PROFILE_GUIDED_OPTIMIZE + + +# Does the makefile specifies the internal XPCOM API linkage? +ifneq (,$(MOZILLA_INTERNAL_API)$(LIBXUL_LIBRARY)) +DEFINES += -DMOZILLA_INTERNAL_API +endif + +# Force XPCOM/widget/gfx methods to be _declspec(dllexport) when we're +# building libxul libraries +ifdef MOZ_ENABLE_LIBXUL +ifdef LIBXUL_LIBRARY +DEFINES += \ + -D_IMPL_NS_COM \ + -DEXPORT_XPT_API \ + -DEXPORT_XPTC_API \ + -D_IMPL_NS_GFX \ + -D_IMPL_NS_WIDGET \ + -DIMPL_XREAPI \ + -DIMPL_NS_NET \ + -DIMPL_THEBES \ + $(NULL) + +ifndef MOZ_NATIVE_ZLIB +DEFINES += -DZLIB_INTERNAL +endif +endif +endif + +# Force _all_ exported methods to be |_declspec(dllexport)| when we're +# building them into the executable. + +ifeq (,$(filter-out WINNT WINCE OS2, $(OS_ARCH))) +ifdef BUILD_STATIC_LIBS +DEFINES += \ + -D_IMPL_NS_GFX \ + -D_IMPL_NS_MSG_BASE \ + -D_IMPL_NS_WIDGET \ + $(NULL) +endif +endif + +# Flags passed to make-jars.pl + +MAKE_JARS_FLAGS = \ + -t $(topsrcdir) \ + -f $(MOZ_CHROME_FILE_FORMAT) \ + $(NULL) + +ifdef USE_EXTENSION_MANIFEST +MAKE_JARS_FLAGS += -e +endif + +ifdef BOTH_MANIFESTS +MAKE_JARS_FLAGS += --both-manifests +endif + +TAR_CREATE_FLAGS = -cvhf + +ifeq ($(OS_ARCH),BSD_OS) +TAR_CREATE_FLAGS = -cvLf +endif + +ifeq ($(OS_ARCH),OS2) +TAR_CREATE_FLAGS = -cvf +endif + +# +# Personal makefile customizations go in these optional make include files. +# +MY_CONFIG := $(DEPTH)/config/myconfig.mk +MY_RULES := $(DEPTH)/config/myrules.mk + +# +# Default command macros; can be overridden in .mk. +# +CCC = $(CXX) +NFSPWD = $(CONFIG_TOOLS)/nfspwd +PURIFY = purify $(PURIFYOPTIONS) +QUANTIFY = quantify $(QUANTIFYOPTIONS) +ifdef CROSS_COMPILE +XPIDL_COMPILE = $(CYGWIN_WRAPPER) $(LIBXUL_DIST)/host/bin/host_xpidl$(HOST_BIN_SUFFIX) +XPIDL_LINK = $(CYGWIN_WRAPPER) $(LIBXUL_DIST)/host/bin/host_xpt_link$(HOST_BIN_SUFFIX) +else +XPIDL_COMPILE = $(CYGWIN_WRAPPER) $(LIBXUL_DIST)/bin/xpidl$(BIN_SUFFIX) +XPIDL_LINK = $(CYGWIN_WRAPPER) $(LIBXUL_DIST)/bin/xpt_link$(BIN_SUFFIX) +endif + +# Java macros +JAVA_GEN_DIR = _javagen +JAVA_DIST_DIR = $(DEPTH)/$(JAVA_GEN_DIR) +JAVA_IFACES_PKG_NAME = org/mozilla/interfaces + +INCLUDES = \ + $(LOCAL_INCLUDES) \ + -I$(srcdir) \ + -I. \ + -I$(DIST)/include -I$(DIST)/include/nsprpub \ + $(if $(LIBXUL_SDK),-I$(LIBXUL_SDK)/include -I$(LIBXUL_SDK)/include/nsprpub) \ + $(OS_INCLUDES) \ + $(NULL) + +include $(topsrcdir)/config/static-checking-config.mk + +ifdef MOZ_SHARK +OS_CFLAGS += -F/System/Library/PrivateFrameworks +OS_CXXFLAGS += -F/System/Library/PrivateFrameworks +OS_LDFLAGS += -F/System/Library/PrivateFrameworks -framework CHUD +endif # ifdef MOZ_SHARK + +CFLAGS = $(OS_CFLAGS) +CXXFLAGS = $(OS_CXXFLAGS) +LDFLAGS = $(OS_LDFLAGS) $(MOZ_FIX_LINK_PATHS) + +# Allow each module to override the *default* optimization settings +# by setting MODULE_OPTIMIZE_FLAGS if the developer has not given +# arguments to --enable-optimize +ifdef MOZ_OPTIMIZE +ifeq (1,$(MOZ_OPTIMIZE)) +ifdef MODULE_OPTIMIZE_FLAGS +CFLAGS += $(MODULE_OPTIMIZE_FLAGS) +CXXFLAGS += $(MODULE_OPTIMIZE_FLAGS) +else +CFLAGS += $(MOZ_OPTIMIZE_FLAGS) +CXXFLAGS += $(MOZ_OPTIMIZE_FLAGS) +endif # MODULE_OPTIMIZE_FLAGS +else +CFLAGS += $(MOZ_OPTIMIZE_FLAGS) +CXXFLAGS += $(MOZ_OPTIMIZE_FLAGS) +endif # MOZ_OPTIMIZE == 1 +LDFLAGS += $(MOZ_OPTIMIZE_LDFLAGS) +endif # MOZ_OPTIMIZE + +ifdef CROSS_COMPILE +HOST_CFLAGS += $(HOST_OPTIMIZE_FLAGS) +else +ifdef MOZ_OPTIMIZE +ifeq (1,$(MOZ_OPTIMIZE)) +ifdef MODULE_OPTIMIZE_FLAGS +HOST_CFLAGS += $(MODULE_OPTIMIZE_FLAGS) +else +HOST_CFLAGS += $(MOZ_OPTIMIZE_FLAGS) +endif # MODULE_OPTIMIZE_FLAGS +else +HOST_CFLAGS += $(MOZ_OPTIMIZE_FLAGS) +endif # MOZ_OPTIMIZE == 1 +endif # MOZ_OPTIMIZE +endif # CROSS_COMPILE + + +ifeq ($(OS_ARCH)_$(GNU_CC),WINNT_) +#// Currently, unless USE_STATIC_LIBS is defined, the multithreaded +#// DLL version of the RTL is used... +#// +#//------------------------------------------------------------------------ +ifdef USE_STATIC_LIBS +RTL_FLAGS=-MT # Statically linked multithreaded RTL +ifneq (,$(MOZ_DEBUG)$(NS_TRACE_MALLOC)) +ifndef MOZ_NO_DEBUG_RTL +RTL_FLAGS=-MTd # Statically linked multithreaded MSVC4.0 debug RTL +endif +endif # MOZ_DEBUG || NS_TRACE_MALLOC + +else # !USE_STATIC_LIBS + +RTL_FLAGS=-MD # Dynamically linked, multithreaded RTL +ifneq (,$(MOZ_DEBUG)$(NS_TRACE_MALLOC)) +ifndef MOZ_NO_DEBUG_RTL +RTL_FLAGS=-MDd # Dynamically linked, multithreaded MSVC4.0 debug RTL +endif +endif # MOZ_DEBUG || NS_TRACE_MALLOC +endif # USE_STATIC_LIBS +endif # WINNT && !GNU_CC + +ifeq ($(OS_ARCH),Darwin) +# Darwin doesn't cross-compile, so just set both types of flags here. +HOST_CMFLAGS += -fobjc-exceptions +HOST_CMMFLAGS += -fobjc-exceptions +OS_COMPILE_CMFLAGS += -fobjc-exceptions +OS_COMPILE_CMMFLAGS += -fobjc-exceptions +endif + +COMPILE_CFLAGS = $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(CFLAGS) $(RTL_FLAGS) $(OS_COMPILE_CFLAGS) +COMPILE_CXXFLAGS = $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(CXXFLAGS) $(RTL_FLAGS) $(OS_COMPILE_CXXFLAGS) +COMPILE_CMFLAGS = $(OS_COMPILE_CMFLAGS) +COMPILE_CMMFLAGS = $(OS_COMPILE_CMMFLAGS) + +ifndef CROSS_COMPILE +HOST_CFLAGS += $(RTL_FLAGS) +endif + +# +# Name of the binary code directories +# +# Override defaults + +# We need to know where to find the libraries we +# put on the link line for binaries, and should +# we link statically or dynamic? Assuming dynamic for now. + +ifneq (WINNT_,$(OS_ARCH)_$(GNU_CC)) +ifneq (,$(filter-out WINCE,$(OS_ARCH))) +LIBS_DIR = -L$(DIST)/bin -L$(DIST)/lib +ifdef LIBXUL_SDK +LIBS_DIR += -L$(LIBXUL_SDK)/bin -L$(LIBXUL_SDK)/lib +endif +endif +endif + +# Default location of include files +IDL_DIR = $(DIST)/idl + +XPIDL_FLAGS = -I$(srcdir) -I$(IDL_DIR) +ifdef LIBXUL_SDK +XPIDL_FLAGS += -I$(LIBXUL_SDK)/idl +endif + +SDK_LIB_DIR = $(DIST)/sdk/lib +SDK_BIN_DIR = $(DIST)/sdk/bin + +DEPENDENCIES = .md + +MOZ_COMPONENT_LIBS=$(XPCOM_LIBS) $(MOZ_COMPONENT_NSPR_LIBS) + +ifeq (xpconnect, $(findstring xpconnect, $(BUILD_MODULES))) +DEFINES += -DXPCONNECT_STANDALONE +endif + +ifeq ($(OS_ARCH),OS2) +ELF_DYNSTR_GC = echo +else +ELF_DYNSTR_GC = : +endif + +ifndef CROSS_COMPILE +ifdef USE_ELF_DYNSTR_GC +ifdef MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS +ELF_DYNSTR_GC = $(DEPTH)/config/elf-dynstr-gc +endif +endif +endif + +ifeq ($(OS_ARCH),Darwin) +ifdef NEXT_ROOT +export NEXT_ROOT +PBBUILD = NEXT_ROOT= $(PBBUILD_BIN) +else # NEXT_ROOT +PBBUILD = $(PBBUILD_BIN) +endif # NEXT_ROOT +PBBUILD_SETTINGS = GCC_VERSION="$(GCC_VERSION)" SYMROOT=build ARCHS="$(OS_TEST)" +ifdef MACOS_SDK_DIR +PBBUILD_SETTINGS += SDKROOT="$(MACOS_SDK_DIR)" +endif # MACOS_SDK_DIR +ifdef MACOSX_DEPLOYMENT_TARGET +export MACOSX_DEPLOYMENT_TARGET +PBBUILD_SETTINGS += MACOSX_DEPLOYMENT_TARGET="$(MACOSX_DEPLOYMENT_TARGET)" +endif # MACOSX_DEPLOYMENT_TARGET +ifdef MOZ_OPTIMIZE +ifeq (2,$(MOZ_OPTIMIZE)) +# Only override project defaults if the config specified explicit settings +PBBUILD_SETTINGS += GCC_MODEL_TUNING= OPTIMIZATION_CFLAGS="$(MOZ_OPTIMIZE_FLAGS)" +endif # MOZ_OPTIMIZE=2 +endif # MOZ_OPTIMIZE +ifeq (1,$(HAS_XCODE_2_1)) +# Xcode 2.1 puts its build products in a directory corresponding to the +# selected build style/configuration. +XCODE_PRODUCT_DIR = build/$(BUILDSTYLE) +else +XCODE_PRODUCT_DIR = build +endif # HAS_XCODE_2_1=1 +endif # OS_ARCH=Darwin + + +ifdef MOZ_NATIVE_MAKEDEPEND +MKDEPEND_DIR = +MKDEPEND = $(CYGWIN_WRAPPER) $(MOZ_NATIVE_MAKEDEPEND) +else +MKDEPEND_DIR = $(CONFIG_TOOLS)/mkdepend +MKDEPEND = $(CYGWIN_WRAPPER) $(MKDEPEND_DIR)/mkdepend$(BIN_SUFFIX) +endif + +# Set link flags according to whether we want a console. +ifdef MOZ_WINCONSOLE +ifeq ($(MOZ_WINCONSOLE),1) +ifeq ($(OS_ARCH),OS2) +BIN_FLAGS += -Zlinker -PM:VIO +endif +ifeq ($(OS_ARCH),WINNT) +ifdef GNU_CC +WIN32_EXE_LDFLAGS += -mconsole +else +WIN32_EXE_LDFLAGS += -SUBSYSTEM:CONSOLE +endif +endif +else # MOZ_WINCONSOLE +ifeq ($(OS_ARCH),OS2) +BIN_FLAGS += -Zlinker -PM:PM +endif +ifeq ($(OS_ARCH),WINNT) +ifdef GNU_CC +WIN32_EXE_LDFLAGS += -mwindows +else +WIN32_EXE_LDFLAGS += -SUBSYSTEM:WINDOWS +endif +endif +endif +endif + +# If we're building a component on MSVC, we don't want to generate an +# import lib, because that import lib will collide with the name of a +# static version of the same library. +ifeq ($(GNU_LD)$(OS_ARCH),WINNT) +ifdef IS_COMPONENT +LDFLAGS += -IMPLIB:fake.lib +DELETE_AFTER_LINK = fake.lib fake.exp +endif +endif + +# +# Include any personal overrides the user might think are needed. +# +-include $(topsrcdir)/$(MOZ_BUILD_APP)/app-config.mk +-include $(MY_CONFIG) + +###################################################################### +# Now test variables that might have been set or overridden by $(MY_CONFIG). + +DEFINES += -DOSTYPE=\"$(OS_CONFIG)\" +DEFINES += -DOSARCH=$(OS_ARCH) + +###################################################################### + +GARBAGE += $(DEPENDENCIES) $(MKDEPENDENCIES) $(MKDEPENDENCIES).bak core $(wildcard core.[0-9]*) $(wildcard *.err) $(wildcard *.pure) $(wildcard *_pure_*.o) Templates.DB + +ifeq ($(OS_ARCH),Darwin) +ifndef NSDISTMODE +NSDISTMODE=absolute_symlink +endif +PWD := $(CURDIR) +endif + +ifdef NSINSTALL_BIN +NSINSTALL = $(CYGWIN_WRAPPER) $(NSINSTALL_BIN) +else +ifeq (OS2,$(CROSS_COMPILE)$(OS_ARCH)) +NSINSTALL = $(MOZ_TOOLS_DIR)/nsinstall +else +NSINSTALL = $(CONFIG_TOOLS)/nsinstall$(HOST_BIN_SUFFIX) +endif # OS2 +endif # NSINSTALL_BIN + + +ifeq (,$(CROSS_COMPILE)$(filter-out WINNT OS2, $(OS_ARCH))) +INSTALL = $(NSINSTALL) +else +ifeq ($(NSDISTMODE),copy) +# copy files, but preserve source mtime +INSTALL = $(NSINSTALL) -t +else +ifeq ($(NSDISTMODE),absolute_symlink) +# install using absolute symbolic links +ifeq ($(OS_ARCH),Darwin) +INSTALL = $(NSINSTALL) -L $(PWD) +else +INSTALL = $(NSINSTALL) -L `$(NFSPWD)` +endif # Darwin +else +# install using relative symbolic links +INSTALL = $(NSINSTALL) -R +endif # absolute_symlink +endif # copy +endif # WINNT/OS2 + +# Use nsinstall in copy mode to install files on the system +SYSINSTALL = $(NSINSTALL) -t + +ifeq ($(OS_ARCH),WINNT) +ifneq (,$(CYGDRIVE_MOUNT)) +export CYGDRIVE_MOUNT +endif +endif + +# +# Localization build automation +# + +# Because you might wish to "make locales AB_CD=ab-CD", we don't hardcode +# MOZ_UI_LOCALE directly, but use an intermediate variable that can be +# overridden by the command line. (Besides, AB_CD is prettier). +AB_CD = $(MOZ_UI_LOCALE) + +ifndef L10NBASEDIR +L10NBASEDIR = $(error L10NBASEDIR not defined by configure) +endif + +EXPAND_LOCALE_SRCDIR = $(if $(filter en-US,$(AB_CD)),$(topsrcdir)/$(1)/en-US,$(L10NBASEDIR)/$(AB_CD)/$(subst /locales,,$(1))) + +ifdef relativesrcdir +LOCALE_SRCDIR = $(call EXPAND_LOCALE_SRCDIR,$(relativesrcdir)) +endif + +ifdef LOCALE_SRCDIR +# if LOCALE_MERGEDIR is set, use mergedir first, then the localization, +# and finally en-US +ifdef LOCALE_MERGEDIR +MAKE_JARS_FLAGS += -c $(LOCALE_MERGEDIR)/$(subst /locales,,$(relativesrcdir)) +endif +MAKE_JARS_FLAGS += -c $(LOCALE_SRCDIR) +ifdef LOCALE_MERGEDIR +MAKE_JARS_FLAGS += -c $(topsrcdir)/$(relativesrcdir)/en-US +endif +endif + +ifeq (,$(filter WINCE WINNT OS2,$(OS_ARCH))) +RUN_TEST_PROGRAM = $(DIST)/bin/run-mozilla.sh +endif + +ifeq ($(OS_ARCH),OS2) +RUN_TEST_PROGRAM = $(topsrcdir)/build/os2/test_os2.cmd "$(DIST)" +endif + +# +# Java macros +# + +# Make sure any compiled classes work with at least JVM 1.4 +JAVAC_FLAGS += -source 1.4 + +ifdef MOZ_DEBUG +JAVAC_FLAGS += -g +endif + +ifdef TIERS +DIRS += $(foreach tier,$(TIERS),$(tier_$(tier)_dirs)) +STATIC_DIRS += $(foreach tier,$(TIERS),$(tier_$(tier)_staticdirs)) +endif diff --git a/ape-server/deps/js/src/config/fastcwd.pl b/ape-server/deps/js/src/config/fastcwd.pl new file mode 100755 index 0000000..c327ccf --- /dev/null +++ b/ape-server/deps/js/src/config/fastcwd.pl @@ -0,0 +1,66 @@ +#!perl5 +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +sub fastcwd { + local($odev, $oino, $cdev, $cino, $tdev, $tino); + local(@path, $path); + local(*DIR); + + ($cdev, $cino) = stat('.'); + for (;;) { + ($odev, $oino) = ($cdev, $cino); + chdir('..'); + ($cdev, $cino) = stat('.'); + last if $odev == $cdev && $oino == $cino; + opendir(DIR, '.'); + for (;;) { + $_ = readdir(DIR); + next if $_ eq '.'; + next if $_ eq '..'; + + last unless $_; + ($tdev, $tino) = lstat($_); + last unless $tdev != $odev || $tino != $oino; + } + closedir(DIR); + unshift(@path, $_); + } + chdir($path = '/' . join('/', @path)); + $path; +} +1; diff --git a/ape-server/deps/js/src/config/gcc_hidden.h b/ape-server/deps/js/src/config/gcc_hidden.h new file mode 100755 index 0000000..58140c1 --- /dev/null +++ b/ape-server/deps/js/src/config/gcc_hidden.h @@ -0,0 +1,2 @@ +/* Begin all files as hidden visibility */ +#pragma GCC visibility push(hidden) diff --git a/ape-server/deps/js/src/config/insure.mk b/ape-server/deps/js/src/config/insure.mk new file mode 100755 index 0000000..fc796eb --- /dev/null +++ b/ape-server/deps/js/src/config/insure.mk @@ -0,0 +1,53 @@ +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +INCLUDED_INSURE_MK = 1 + +INSURE_MATCH_SCRIPT=$(topsrcdir)/build/autoconf/match-dir.sh + +INSURE_EXCLUDE=$(shell $(INSURE_MATCH_SCRIPT) $(MOZ_INSURE_EXCLUDE_DIRS)) + +INSURE_INCLUDE=$(shell $(INSURE_MATCH_SCRIPT) $(MOZ_INSURE_DIRS)) + +ifeq ($(INSURE_EXCLUDE),0) + +ifeq ($(INSURE_INCLUDE),1) +CC := $(MOZ_INSURE) +CXX := $(MOZ_INSURE) +endif # INSURE_INCLUDE == 1 + +endif # INSURE_EXCLUDE == 0 diff --git a/ape-server/deps/js/src/config/make-system-wrappers.pl b/ape-server/deps/js/src/config/make-system-wrappers.pl new file mode 100755 index 0000000..31c9af4 --- /dev/null +++ b/ape-server/deps/js/src/config/make-system-wrappers.pl @@ -0,0 +1,59 @@ +#!/usr/bin/perl +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# IBM Corporation. +# Portions created by the Initial Developer are Copyright (C) 2004 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Brian Ryner +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +$output_dir = shift; + +while () { + chomp; + if (-e "$output_dir/$_") { + next; + } + + if (/(.*)\/[^\/*]/) { + mkdir "$output_dir/$1"; + } + + open OUT, ">$output_dir/$_"; + print OUT "#pragma GCC system_header\n"; # suppress include_next warning + print OUT "#pragma GCC visibility push(default)\n"; + print OUT "#include_next \<$_\>\n"; + print OUT "#pragma GCC visibility pop\n"; + close OUT; +} + diff --git a/ape-server/deps/js/src/config/milestone.pl b/ape-server/deps/js/src/config/milestone.pl new file mode 100755 index 0000000..e3cee2a --- /dev/null +++ b/ape-server/deps/js/src/config/milestone.pl @@ -0,0 +1,112 @@ +#!/usr/bin/perl -w +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is the Win32 Version System. +# +# The Initial Developer of the Original Code is Netscape Communications Corporation +# Portions created by the Initial Developer are Copyright (C) 2002 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +use Getopt::Long; + +use strict; +use vars qw( + $OBJDIR + $SRCDIR + $TOPSRCDIR + $SCRIPTDIR + @TEMPLATE_FILE + $MILESTONE_FILE + $MILESTONE + $MILESTONE_NUM + @MILESTONE_PARTS + $MINI_VERSION + $MICRO_VERSION + $opt_debug + $opt_template + $opt_help + ); + +$SCRIPTDIR = $0; +$SCRIPTDIR =~ s/[^\/]*$//; +push(@INC,$SCRIPTDIR); + +require "Moz/Milestone.pm"; + +&GetOptions('topsrcdir=s' => \$TOPSRCDIR, 'srcdir=s' => \$SRCDIR, 'objdir=s' => \$OBJDIR, 'debug', 'help', 'template'); + +if (defined($opt_help)) { + &usage(); + exit; +} + +if (defined($opt_template)) { + @TEMPLATE_FILE = @ARGV; + if ($opt_debug) { + print("TEMPLATE_FILE = --@TEMPLATE_FILE--\n"); + } +} + +if (!defined($SRCDIR)) { $SRCDIR = '.'; } +if (!defined($OBJDIR)) { $OBJDIR = '.'; } + +$MILESTONE_FILE = "$TOPSRCDIR/config/milestone.txt"; +@MILESTONE_PARTS = (0, 0, 0, 0); + +# +# Grab milestone (top line of $MILESTONE_FILE that starts with a digit) +# +my $milestone = Moz::Milestone::getOfficialMilestone($MILESTONE_FILE); + +if (defined(@TEMPLATE_FILE)) { + my $TFILE; + + foreach $TFILE (@TEMPLATE_FILE) { + my $BUILT_FILE = "$OBJDIR/$TFILE"; + $TFILE = "$SRCDIR/$TFILE.tmpl"; + + if (-e $TFILE) { + + Moz::Milestone::build_file($TFILE,$BUILT_FILE); + + } else { + warn("$0: No such file $TFILE!\n"); + } + } +} else { + print "$milestone\n"; +} + +sub usage() { + print <s_value = NULL; + } + return (sp); +} + +pperror(tag, x0,x1,x2,x3,x4) + int tag,x0,x1,x2,x3,x4; +{ + warning("\"%s\", line %d: ", currentinc->i_file, currentfile->f_line); + warning(x0,x1,x2,x3,x4); +} + + +yyerror(s) + register char *s; +{ + fatalerr("Fatal error: %s\n", s); +} +#else /* not CPP */ + +#include "ifparser.h" +struct _parse_data { + struct filepointer *filep; + struct inclist *inc; + char *filename; + const char *line; +}; + +static const char * +my_if_errors (IfParser *ip, const char *cp, const char *expecting) +{ + struct _parse_data *pd = (struct _parse_data *) ip->data; + int lineno = pd->filep->f_line; + char *filename = pd->filename; + char prefix[300]; + int prefixlen; + int i; + + sprintf (prefix, "\"%s\":%d", filename, lineno); + prefixlen = strlen(prefix); + fprintf (stderr, "%s: %s", prefix, pd->line); + i = cp - pd->line; + if (i > 0 && pd->line[i-1] != '\n') { + putc ('\n', stderr); + } + for (i += prefixlen + 3; i > 0; i--) { + putc (' ', stderr); + } + fprintf (stderr, "^--- expecting %s\n", expecting); + return NULL; +} + + +#define MAXNAMELEN 256 + +static struct symtab ** +lookup_variable (IfParser *ip, const char *var, int len) +{ + char tmpbuf[MAXNAMELEN + 1]; + struct _parse_data *pd = (struct _parse_data *) ip->data; + + if (len > MAXNAMELEN) + return 0; + + strncpy (tmpbuf, var, len); + tmpbuf[len] = '\0'; + return isdefined (tmpbuf, pd->inc, NULL); +} + + +static int +my_eval_defined (IfParser *ip, const char *var, int len) +{ + if (lookup_variable (ip, var, len)) + return 1; + else + return 0; +} + +#define isvarfirstletter(ccc) (isalpha(ccc) || (ccc) == '_') + +static long +my_eval_variable (IfParser *ip, const char *var, int len) +{ + long val; + struct symtab **s; + + s = lookup_variable (ip, var, len); + if (!s) + return 0; + do { + var = (*s)->s_value; + if (!isvarfirstletter(*var) || !strcmp((*s)->s_name, var)) + break; + s = lookup_variable (ip, var, strlen(var)); + } while (s); + + var = ParseIfExpression(ip, var, &val); + if (var && *var) debug(4, ("extraneous: '%s'\n", var)); + return val; +} + +int +cppsetup(char *filename, + char *line, + struct filepointer *filep, + struct inclist *inc) +{ + IfParser ip; + struct _parse_data pd; + long val = 0; + + pd.filep = filep; + pd.inc = inc; + pd.line = line; + pd.filename = filename; + ip.funcs.handle_error = my_if_errors; + ip.funcs.eval_defined = my_eval_defined; + ip.funcs.eval_variable = my_eval_variable; + ip.data = (char *) &pd; + + (void) ParseIfExpression (&ip, line, &val); + if (val) + return IF; + else + return IFFALSE; +} +#endif /* CPP */ + diff --git a/ape-server/deps/js/src/config/mkdepend/def.h b/ape-server/deps/js/src/config/mkdepend/def.h new file mode 100755 index 0000000..d6e5f89 --- /dev/null +++ b/ape-server/deps/js/src/config/mkdepend/def.h @@ -0,0 +1,184 @@ +/* $Xorg: def.h,v 1.4 2001/02/09 02:03:16 xorgcvs Exp $ */ +/* + +Copyright (c) 1993, 1994, 1998 The Open Group. + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + +*/ +/* $XFree86: xc/config/makedepend/def.h,v 3.14 2003/01/17 17:09:49 tsi Exp $ */ + +#ifndef NO_X11 +#include +#include +#endif +#include +#include +#include +#include +#if 0 +#ifndef X_NOT_POSIX +#ifndef _POSIX_SOURCE +#define _POSIX_SOURCE +#endif +#endif +#endif +#include +#include +#include + +#define MAXDEFINES 512 +#define MAXFILES 1024 +#define MAXINCFILES 256 /* "-include" files */ +#define MAXDIRS 1024 +#define SYMTABINC 10 /* must be > 1 for define() to work right */ +#define TRUE 1 +#define FALSE 0 + +/* the following must match the directives table in main.c */ +#define IF 0 +#define IFDEF 1 +#define IFNDEF 2 +#define ELSE 3 +#define ENDIF 4 +#define DEFINE 5 +#define UNDEF 6 +#define INCLUDE 7 +#define LINE 8 +#define PRAGMA 9 +#define ERROR 10 +#define IDENT 11 +#define SCCS 12 +#define ELIF 13 +#define EJECT 14 +#define WARNING 15 +#define INCLUDENEXT 16 +#define IFFALSE 17 /* pseudo value --- never matched */ +#define ELIFFALSE 18 /* pseudo value --- never matched */ +#define INCLUDEDOT 19 /* pseudo value --- never matched */ +#define IFGUESSFALSE 20 /* pseudo value --- never matched */ +#define ELIFGUESSFALSE 21 /* pseudo value --- never matched */ +#define INCLUDENEXTDOT 22 /* pseudo value --- never matched */ + +#ifdef DEBUG +extern int _debugmask; +/* + * debug levels are: + * + * 0 show ifn*(def)*,endif + * 1 trace defined/!defined + * 2 show #include + * 3 show #include SYMBOL + * 4-6 unused + */ +#define debug(level,arg) { if (_debugmask & (1 << level)) warning arg; } +#else +#define debug(level,arg) /**/ +#endif /* DEBUG */ + +typedef unsigned char boolean; + +struct symtab { + char *s_name; + char *s_value; +}; + +/* possible i_flag */ +#define DEFCHECKED (1<<0) /* whether defines have been checked */ +#define NOTIFIED (1<<1) /* whether we have revealed includes */ +#define MARKED (1<<2) /* whether it's in the makefile */ +#define SEARCHED (1<<3) /* whether we have read this */ +#define FINISHED (1<<4) /* whether we are done reading this */ +#define INCLUDED_SYM (1<<5) /* whether #include SYMBOL was found + Can't use i_list if TRUE */ +struct inclist { + char *i_incstring; /* string from #include line */ + char *i_file; /* path name of the include file */ + struct inclist **i_list; /* list of files it itself includes */ + int i_listlen; /* length of i_list */ + struct symtab **i_defs; /* symbol table for this file and its + children when merged */ + int i_ndefs; /* current # defines */ + boolean *i_merged; /* whether we have merged child + defines */ + unsigned char i_flags; +}; + +struct filepointer { + char *f_name; + char *f_p; + char *f_base; + char *f_end; + long f_len; + long f_line; + long cmdinc_count; + char **cmdinc_list; + long cmdinc_line; +}; + +#include +#if defined(macII) && !defined(__STDC__) /* stdlib.h fails to define these */ +char *malloc(), *realloc(); +#endif /* macII */ + +char *copy(char *str); +int match(char *str, char **list); +char *base_name(char *file); +char *getnextline(struct filepointer *fp); +struct symtab **slookup(char *symbol, struct inclist *file); +struct symtab **isdefined(char *symbol, struct inclist *file, + struct inclist **srcfile); +struct symtab **fdefined(char *symbol, struct inclist *file, + struct inclist **srcfile); +struct filepointer *getfile(char *file); +void included_by(struct inclist *ip, + struct inclist *newfile); +struct inclist *newinclude(char *newfile, char *incstring); +void inc_clean (void); +struct inclist *inc_path(char *file, char *include, int type); + +void freefile(struct filepointer *fp); + +void define2(char *name, char *val, struct inclist *file); +void define(char *def, struct inclist *file); +void undefine(char *symbol, struct inclist *file); +int find_includes(struct filepointer *filep, + struct inclist *file, + struct inclist *file_red, + int recursion, boolean failOK); + +void recursive_pr_include(struct inclist *head, + char *file, char *base); +void add_include(struct filepointer *filep, + struct inclist *file, + struct inclist *file_red, + char *include, int type, + boolean failOK); + +int cppsetup(char *filename, + char *line, + struct filepointer *filep, + struct inclist *inc); + + +extern void fatalerr(char *, ...); +extern void warning(char *, ...); +extern void warning1(char *, ...); diff --git a/ape-server/deps/js/src/config/mkdepend/ifparser.c b/ape-server/deps/js/src/config/mkdepend/ifparser.c new file mode 100755 index 0000000..0b184c2 --- /dev/null +++ b/ape-server/deps/js/src/config/mkdepend/ifparser.c @@ -0,0 +1,551 @@ +/* + * $Xorg: ifparser.c,v 1.3 2000/08/17 19:41:50 cpqbld Exp $ + * + * Copyright 1992 Network Computing Devices, Inc. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Network Computing Devices may not be + * used in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Network Computing Devices makes + * no representations about the suitability of this software for any purpose. + * It is provided ``as is'' without express or implied warranty. + * + * NETWORK COMPUTING DEVICES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, + * IN NO EVENT SHALL NETWORK COMPUTING DEVICES BE LIABLE FOR ANY SPECIAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + * Author: Jim Fulton + * Network Computing Devices, Inc. + * + * Simple if statement processor + * + * This module can be used to evaluate string representations of C language + * if constructs. It accepts the following grammar: + * + * EXPRESSION := VALUE + * | VALUE BINOP EXPRESSION + * | VALUE '?' EXPRESSION ':' EXPRESSION + * + * VALUE := '(' EXPRESSION ')' + * | '!' VALUE + * | '-' VALUE + * | '+' VALUE + * | '~' VALUE + * | 'defined' '(' variable ')' + * | 'defined' variable + * | # variable '(' variable-list ')' + * | variable + * | number + * + * BINOP := '*' | '/' | '%' + * | '+' | '-' + * | '<<' | '>>' + * | '<' | '>' | '<=' | '>=' + * | '==' | '!=' + * | '&' | '^' | '|' + * | '&&' | '||' + * + * The normal C order of precedence is supported. + * + * + * External Entry Points: + * + * ParseIfExpression parse a string for #if + */ +/* $XFree86: xc/config/makedepend/ifparser.c,v 3.11 2002/09/23 01:48:08 tsi Exp $ */ + +#include "ifparser.h" +#include +#include +#include + +/**************************************************************************** + Internal Macros and Utilities for Parser + ****************************************************************************/ + +#define DO(val) if (!(val)) return NULL +#define CALLFUNC(ggg,fff) (*((ggg)->funcs.fff)) +#define SKIPSPACE(ccc) while (isspace(*ccc)) ccc++ +#define isvarfirstletter(ccc) (isalpha(ccc) || (ccc) == '_') + + +static const char * +parse_variable (IfParser *g, const char *cp, const char **varp) +{ + SKIPSPACE (cp); + + if (!isvarfirstletter (*cp)) + return CALLFUNC(g, handle_error) (g, cp, "variable name"); + + *varp = cp; + /* EMPTY */ + for (cp++; isalnum(*cp) || *cp == '_'; cp++) ; + return cp; +} + + +static const char * +parse_number (IfParser *g, const char *cp, long *valp) +{ + long base = 10; + SKIPSPACE (cp); + + if (!isdigit(*cp)) + return CALLFUNC(g, handle_error) (g, cp, "number"); + + *valp = 0; + + if (*cp == '0') { + cp++; + if ((*cp == 'x') || (*cp == 'X')) { + base = 16; + cp++; + } else { + base = 8; + } + } + + /* Ignore overflows and assume ASCII, what source is usually written in */ + while (1) { + int increment = -1; + if (base == 8) { + if ((*cp >= '0') && (*cp <= '7')) + increment = *cp++ - '0'; + } else if (base == 16) { + if ((*cp >= '0') && (*cp <= '9')) + increment = *cp++ - '0'; + else if ((*cp >= 'A') && (*cp <= 'F')) + increment = *cp++ - ('A' - 10); + else if ((*cp >= 'a') && (*cp <= 'f')) + increment = *cp++ - ('a' - 10); + } else { /* Decimal */ + if ((*cp >= '0') && (*cp <= '9')) + increment = *cp++ - '0'; + } + if (increment < 0) + break; + *valp = (*valp * base) + increment; + } + + /* Skip trailing qualifiers */ + while (*cp == 'U' || *cp == 'u' || *cp == 'L' || *cp == 'l') cp++; + return cp; +} + +static const char * +parse_character (IfParser *g, const char *cp, long *valp) +{ + char val; + + SKIPSPACE (cp); + if (*cp == '\\') + switch (cp[1]) { + case 'n': val = '\n'; break; + case 't': val = '\t'; break; + case 'v': val = '\v'; break; + case 'b': val = '\b'; break; + case 'r': val = '\r'; break; + case 'f': val = '\f'; break; + case 'a': val = '\a'; break; + case '\\': val = '\\'; break; + case '?': val = '\?'; break; + case '\'': val = '\''; break; + case '\"': val = '\"'; break; + case 'x': val = (char) strtol (cp + 2, NULL, 16); break; + default: val = (char) strtol (cp + 1, NULL, 8); break; + } + else + val = *cp; + while (*cp != '\'') cp++; + *valp = (long) val; + return cp; +} + +static const char * +parse_value (IfParser *g, const char *cp, long *valp) +{ + const char *var, *varend; + + *valp = 0; + + SKIPSPACE (cp); + if (!*cp) + return cp; + + switch (*cp) { + case '(': + DO (cp = ParseIfExpression (g, cp + 1, valp)); + SKIPSPACE (cp); + if (*cp != ')') + return CALLFUNC(g, handle_error) (g, cp, ")"); + + return cp + 1; /* skip the right paren */ + + case '!': + DO (cp = parse_value (g, cp + 1, valp)); + *valp = !(*valp); + return cp; + + case '-': + DO (cp = parse_value (g, cp + 1, valp)); + *valp = -(*valp); + return cp; + + case '+': + DO (cp = parse_value (g, cp + 1, valp)); + return cp; + + case '~': + DO (cp = parse_value (g, cp + 1, valp)); + *valp = ~(*valp); + return cp; + + case '#': + DO (cp = parse_variable (g, cp + 1, &var)); + SKIPSPACE (cp); + if (*cp != '(') + return CALLFUNC(g, handle_error) (g, cp, "("); + do { + DO (cp = parse_variable (g, cp + 1, &var)); + SKIPSPACE (cp); + } while (*cp && *cp != ')'); + if (*cp != ')') + return CALLFUNC(g, handle_error) (g, cp, ")"); + *valp = 1; /* XXX */ + return cp + 1; + + case '\'': + DO (cp = parse_character (g, cp + 1, valp)); + if (*cp != '\'') + return CALLFUNC(g, handle_error) (g, cp, "'"); + return cp + 1; + + case 'd': + if (strncmp (cp, "defined", 7) == 0 && !isalnum(cp[7])) { + int paren = 0; + int len; + + cp += 7; + SKIPSPACE (cp); + if (*cp == '(') { + paren = 1; + cp++; + } + DO (cp = parse_variable (g, cp, &var)); + len = cp - var; + SKIPSPACE (cp); + if (paren && *cp != ')') + return CALLFUNC(g, handle_error) (g, cp, ")"); + *valp = (*(g->funcs.eval_defined)) (g, var, len); + return cp + paren; /* skip the right paren */ + } + /* fall out */ + } + + if (isdigit(*cp)) { + DO (cp = parse_number (g, cp, valp)); + } else if (!isvarfirstletter(*cp)) + return CALLFUNC(g, handle_error) (g, cp, "variable or number"); + else { + DO (cp = parse_variable (g, cp, &var)); + varend = cp; + SKIPSPACE(cp); + if (*cp != '(') { + *valp = (*(g->funcs.eval_variable)) (g, var, varend - var); + } else { + do { + long dummy; + DO (cp = ParseIfExpression (g, cp + 1, &dummy)); + SKIPSPACE(cp); + if (*cp == ')') + break; + if (*cp != ',') + return CALLFUNC(g, handle_error) (g, cp, ","); + } while (1); + + *valp = 1; /* XXX */ + cp++; + } + } + + return cp; +} + + + +static const char * +parse_product (IfParser *g, const char *cp, long *valp) +{ + long rightval; + + DO (cp = parse_value (g, cp, valp)); + SKIPSPACE (cp); + + switch (*cp) { + case '*': + DO (cp = parse_product (g, cp + 1, &rightval)); + *valp = (*valp * rightval); + break; + + case '/': + DO (cp = parse_product (g, cp + 1, &rightval)); + if (rightval == 0) + return CALLFUNC(g, handle_error) (g, cp, "0"); + *valp = (*valp / rightval); + break; + + case '%': + DO (cp = parse_product (g, cp + 1, &rightval)); + *valp = (*valp % rightval); + break; + } + return cp; +} + + +static const char * +parse_sum (IfParser *g, const char *cp, long *valp) +{ + long rightval; + + DO (cp = parse_product (g, cp, valp)); + SKIPSPACE (cp); + + switch (*cp) { + case '+': + DO (cp = parse_sum (g, cp + 1, &rightval)); + *valp = (*valp + rightval); + break; + + case '-': + DO (cp = parse_sum (g, cp + 1, &rightval)); + *valp = (*valp - rightval); + break; + } + return cp; +} + + +static const char * +parse_shift (IfParser *g, const char *cp, long *valp) +{ + long rightval; + + DO (cp = parse_sum (g, cp, valp)); + SKIPSPACE (cp); + + switch (*cp) { + case '<': + if (cp[1] == '<') { + DO (cp = parse_shift (g, cp + 2, &rightval)); + *valp = (*valp << rightval); + } + break; + + case '>': + if (cp[1] == '>') { + DO (cp = parse_shift (g, cp + 2, &rightval)); + *valp = (*valp >> rightval); + } + break; + } + return cp; +} + + +static const char * +parse_inequality (IfParser *g, const char *cp, long *valp) +{ + long rightval; + + DO (cp = parse_shift (g, cp, valp)); + SKIPSPACE (cp); + + switch (*cp) { + case '<': + if (cp[1] == '=') { + DO (cp = parse_inequality (g, cp + 2, &rightval)); + *valp = (*valp <= rightval); + } else { + DO (cp = parse_inequality (g, cp + 1, &rightval)); + *valp = (*valp < rightval); + } + break; + + case '>': + if (cp[1] == '=') { + DO (cp = parse_inequality (g, cp + 2, &rightval)); + *valp = (*valp >= rightval); + } else { + DO (cp = parse_inequality (g, cp + 1, &rightval)); + *valp = (*valp > rightval); + } + break; + } + return cp; +} + + +static const char * +parse_equality (IfParser *g, const char *cp, long *valp) +{ + long rightval; + + DO (cp = parse_inequality (g, cp, valp)); + SKIPSPACE (cp); + + switch (*cp) { + case '=': + if (cp[1] == '=') + cp++; + DO (cp = parse_equality (g, cp + 1, &rightval)); + *valp = (*valp == rightval); + break; + + case '!': + if (cp[1] != '=') + break; + DO (cp = parse_equality (g, cp + 2, &rightval)); + *valp = (*valp != rightval); + break; + } + return cp; +} + + +static const char * +parse_band (IfParser *g, const char *cp, long *valp) +{ + long rightval; + + DO (cp = parse_equality (g, cp, valp)); + SKIPSPACE (cp); + + switch (*cp) { + case '&': + if (cp[1] != '&') { + DO (cp = parse_band (g, cp + 1, &rightval)); + *valp = (*valp & rightval); + } + break; + } + return cp; +} + + +static const char * +parse_bxor (IfParser *g, const char *cp, long *valp) +{ + long rightval; + + DO (cp = parse_band (g, cp, valp)); + SKIPSPACE (cp); + + switch (*cp) { + case '^': + DO (cp = parse_bxor (g, cp + 1, &rightval)); + *valp = (*valp ^ rightval); + break; + } + return cp; +} + + +static const char * +parse_bor (IfParser *g, const char *cp, long *valp) +{ + long rightval; + + DO (cp = parse_bxor (g, cp, valp)); + SKIPSPACE (cp); + + switch (*cp) { + case '|': + if (cp[1] != '|') { + DO (cp = parse_bor (g, cp + 1, &rightval)); + *valp = (*valp | rightval); + } + break; + } + return cp; +} + + +static const char * +parse_land (IfParser *g, const char *cp, long *valp) +{ + long rightval; + + DO (cp = parse_bor (g, cp, valp)); + SKIPSPACE (cp); + + switch (*cp) { + case '&': + if (cp[1] != '&') + return CALLFUNC(g, handle_error) (g, cp, "&&"); + DO (cp = parse_land (g, cp + 2, &rightval)); + *valp = (*valp && rightval); + break; + } + return cp; +} + + +static const char * +parse_lor (IfParser *g, const char *cp, long *valp) +{ + long rightval; + + DO (cp = parse_land (g, cp, valp)); + SKIPSPACE (cp); + + switch (*cp) { + case '|': + if (cp[1] != '|') + return CALLFUNC(g, handle_error) (g, cp, "||"); + DO (cp = parse_lor (g, cp + 2, &rightval)); + *valp = (*valp || rightval); + break; + } + return cp; +} + + +static const char * +parse_cond(IfParser *g, const char *cp, long *valp) +{ + long trueval, falseval; + + DO (cp = parse_lor (g, cp, valp)); + SKIPSPACE (cp); + + switch (*cp) { + case '?': + DO (cp = parse_cond (g, cp + 1, &trueval)); + SKIPSPACE (cp); + if (*cp != ':') + return CALLFUNC(g, handle_error) (g, cp, ":"); + DO (cp = parse_cond (g, cp + 1, &falseval)); + *valp = (*valp ? trueval : falseval); + break; + } + return cp; +} + + +/**************************************************************************** + External Entry Points + ****************************************************************************/ + +const char * +ParseIfExpression (IfParser *g, const char *cp, long *valp) +{ + return parse_cond (g, cp, valp); +} diff --git a/ape-server/deps/js/src/config/mkdepend/ifparser.h b/ape-server/deps/js/src/config/mkdepend/ifparser.h new file mode 100755 index 0000000..89d2a2f --- /dev/null +++ b/ape-server/deps/js/src/config/mkdepend/ifparser.h @@ -0,0 +1,83 @@ +/* + * $Xorg: ifparser.h,v 1.3 2000/08/17 19:41:51 cpqbld Exp $ + * + * Copyright 1992 Network Computing Devices, Inc. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Network Computing Devices may not be + * used in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Network Computing Devices makes + * no representations about the suitability of this software for any purpose. + * It is provided ``as is'' without express or implied warranty. + * + * NETWORK COMPUTING DEVICES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, + * IN NO EVENT SHALL NETWORK COMPUTING DEVICES BE LIABLE FOR ANY SPECIAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + * Author: Jim Fulton + * Network Computing Devices, Inc. + * + * Simple if statement processor + * + * This module can be used to evaluate string representations of C language + * if constructs. It accepts the following grammar: + * + * EXPRESSION := VALUE + * | VALUE BINOP EXPRESSION + * | VALUE '?' EXPRESSION ':' EXPRESSION + * + * VALUE := '(' EXPRESSION ')' + * | '!' VALUE + * | '-' VALUE + * | '~' VALUE + * | 'defined' '(' variable ')' + * | variable + * | number + * + * BINOP := '*' | '/' | '%' + * | '+' | '-' + * | '<<' | '>>' + * | '<' | '>' | '<=' | '>=' + * | '==' | '!=' + * | '&' | '^' | '|' + * | '&&' | '||' + * + * The normal C order of precedence is supported. + * + * + * External Entry Points: + * + * ParseIfExpression parse a string for #if + */ + +/* $XFree86: xc/config/makedepend/ifparser.h,v 3.5 2001/07/25 15:04:40 dawes Exp $ */ + +#include + +typedef int Bool; +#define False 0 +#define True 1 + +typedef struct _if_parser { + struct { /* functions */ + const char *(*handle_error) (struct _if_parser *, const char *, + const char *); + long (*eval_variable) (struct _if_parser *, const char *, int); + int (*eval_defined) (struct _if_parser *, const char *, int); + } funcs; + char *data; +} IfParser; + +const char *ParseIfExpression ( + IfParser *, + const char *, + long * +); + diff --git a/ape-server/deps/js/src/config/mkdepend/imakemdep.h b/ape-server/deps/js/src/config/mkdepend/imakemdep.h new file mode 100755 index 0000000..5c58351 --- /dev/null +++ b/ape-server/deps/js/src/config/mkdepend/imakemdep.h @@ -0,0 +1,733 @@ + +/* $XConsortium: imakemdep.h,v 1.83 95/04/07 19:47:46 kaleb Exp $ */ +/* $XFree86: xc/config/imake/imakemdep.h,v 3.12 1995/07/08 10:22:17 dawes Exp $ */ +/* + +Copyright (c) 1993, 1994 X Consortium + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from the X Consortium. + +*/ + + +/* + * This file contains machine-dependent constants for the imake utility. + * When porting imake, read each of the steps below and add in any necessary + * definitions. In general you should *not* edit ccimake.c or imake.c! + */ + +#ifdef CCIMAKE +/* + * Step 1: imake_ccflags + * Define any special flags that will be needed to get imake.c to compile. + * These will be passed to the compile along with the contents of the + * make variable BOOTSTRAPCFLAGS. + */ +#ifdef hpux +#ifdef hp9000s800 +#define imake_ccflags "-DSYSV" +#else +#define imake_ccflags "-Wc,-Nd4000,-Ns3000 -DSYSV" +#endif +#endif + +#if defined(macII) || defined(_AUX_SOURCE) +#define imake_ccflags "-DmacII -DSYSV" +#endif + +#ifdef stellar +#define imake_ccflags "-DSYSV" +#endif + +#if defined(USL) || defined(Oki) || defined(NCR) +#define imake_ccflags "-Xc -DSVR4" +#endif + +#ifdef sony +#if defined(SYSTYPE_SYSV) || defined(_SYSTYPE_SYSV) +#define imake_ccflags "-DSVR4" +#else +#include +#if NEWSOS < 41 +#define imake_ccflags "-Dbsd43 -DNOSTDHDRS" +#else +#if NEWSOS < 42 +#define imake_ccflags "-Dbsd43" +#endif +#endif +#endif +#endif + +#ifdef _CRAY +#define imake_ccflags "-DSYSV -DUSG" +#endif + +#if defined(_IBMR2) || defined(aix) +#define imake_ccflags "-Daix -DSYSV" +#endif + +#ifdef Mips +# if defined(SYSTYPE_BSD) || defined(BSD) || defined(BSD43) +# define imake_ccflags "-DBSD43" +# else +# define imake_ccflags "-DSYSV" +# endif +#endif + +#ifdef is68k +#define imake_ccflags "-Dluna -Duniosb" +#endif + +#ifdef SYSV386 +# ifdef SVR4 +# define imake_ccflags "-Xc -DSVR4" +# else +# define imake_ccflags "-DSYSV" +# endif +#endif + +#ifdef SVR4 +# ifdef i386 +# define imake_ccflags "-Xc -DSVR4" +# endif +#endif + +#ifdef SYSV +# ifdef i386 +# define imake_ccflags "-DSYSV" +# endif +#endif + +#ifdef __convex__ +#define imake_ccflags "-fn -tm c1" +#endif + +#ifdef apollo +#define imake_ccflags "-DX_NOT_POSIX" +#endif + +#ifdef WIN32 +#define imake_ccflags "-nologo -batch -D__STDC__" +#endif + +#ifdef __uxp__ +#define imake_ccflags "-DSVR4 -DANSICPP" +#endif + +#ifdef __sxg__ +#define imake_ccflags "-DSYSV -DUSG -DNOSTDHDRS" +#endif + +#ifdef sequent +#define imake_ccflags "-DX_NOT_STDC_ENV -DX_NOT_POSIX" +#endif + +#ifdef _SEQUENT_ +#define imake_ccflags "-DSYSV -DUSG" +#endif + +#if defined(SX) || defined(PC_UX) +#define imake_ccflags "-DSYSV" +#endif + +#ifdef nec_ews_svr2 +#define imake_ccflags "-DUSG" +#endif + +#if defined(nec_ews_svr4) || defined(_nec_ews_svr4) || defined(_nec_up) || defined(_nec_ft) +#define imake_ccflags "-DSVR4" +#endif + +#ifdef MACH +#define imake_ccflags "-DNOSTDHDRS" +#endif + +/* this is for OS/2 under EMX. This won't work with DOS */ +#if defined(__EMX__) +#define imake_ccflags "-DBSD43" +#endif + +#else /* not CCIMAKE */ +#ifndef MAKEDEPEND +/* + * Step 2: dup2 + * If your OS doesn't have a dup2() system call to duplicate one file + * descriptor onto another, define such a mechanism here (if you don't + * already fall under the existing category(ies). + */ +#if defined(SYSV) && !defined(_CRAY) && !defined(Mips) && !defined(_SEQUENT_) +#define dup2(fd1,fd2) ((fd1 == fd2) ? fd1 : (close(fd2), \ + fcntl(fd1, F_DUPFD, fd2))) +#endif + + +/* + * Step 3: FIXUP_CPP_WHITESPACE + * If your cpp collapses tabs macro expansions into a single space and + * replaces escaped newlines with a space, define this symbol. This will + * cause imake to attempt to patch up the generated Makefile by looking + * for lines that have colons in them (this is why the rules file escapes + * all colons). One way to tell if you need this is to see whether or not + * your Makefiles have no tabs in them and lots of @@ strings. + */ +#if defined(sun) || defined(SYSV) || defined(SVR4) || defined(hcx) || defined(WIN32) || (defined(AMOEBA) && defined(CROSS_COMPILE)) +#define FIXUP_CPP_WHITESPACE +#endif +#ifdef WIN32 +#define REMOVE_CPP_LEADSPACE +#define INLINE_SYNTAX +#define MAGIC_MAKE_VARS +#endif +#ifdef __minix_vmd +#define FIXUP_CPP_WHITESPACE +#endif + +/* + * Step 4: USE_CC_E, DEFAULT_CC, DEFAULT_CPP + * If you want to use cc -E instead of cpp, define USE_CC_E. + * If use cc -E but want a different compiler, define DEFAULT_CC. + * If the cpp you need is not in /lib/cpp, define DEFAULT_CPP. + */ +#ifdef hpux +#define USE_CC_E +#endif +#ifdef WIN32 +#define USE_CC_E +#define DEFAULT_CC "cl" +#endif +#ifdef apollo +#define DEFAULT_CPP "/usr/lib/cpp" +#endif +#if defined(_IBMR2) && !defined(DEFAULT_CPP) +#define DEFAULT_CPP "/usr/lpp/X11/Xamples/util/cpp/cpp" +#endif +#if defined(sun) && defined(SVR4) +#define DEFAULT_CPP "/usr/ccs/lib/cpp" +#endif +#ifdef __bsdi__ +#define DEFAULT_CPP "/usr/bin/cpp" +#endif +#ifdef __uxp__ +#define DEFAULT_CPP "/usr/ccs/lib/cpp" +#endif +#ifdef __sxg__ +#define DEFAULT_CPP "/usr/lib/cpp" +#endif +#ifdef _CRAY +#define DEFAULT_CPP "/lib/pcpp" +#endif +#if defined(__386BSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) +#define DEFAULT_CPP "/usr/libexec/cpp" +#endif +#ifdef MACH +#define USE_CC_E +#endif +#ifdef __minix_vmd +#define DEFAULT_CPP "/usr/lib/cpp" +#endif +#if defined(__EMX__) +/* expects cpp in PATH */ +#define DEFAULT_CPP "cpp" +#endif + +/* + * Step 5: cpp_argv + * The following table contains the flags that should be passed + * whenever a Makefile is being generated. If your preprocessor + * doesn't predefine any unique symbols, choose one and add it to the + * end of this table. Then, do the following: + * + * a. Use this symbol in Imake.tmpl when setting MacroFile. + * b. Put this symbol in the definition of BootstrapCFlags in your + * .cf file. + * c. When doing a make World, always add "BOOTSTRAPCFLAGS=-Dsymbol" + * to the end of the command line. + * + * Note that you may define more than one symbol (useful for platforms + * that support multiple operating systems). + */ + +#define ARGUMENTS 50 /* number of arguments in various arrays */ +char *cpp_argv[ARGUMENTS] = { + "cc", /* replaced by the actual program to exec */ + "-I.", /* add current directory to include path */ +#ifdef unix + "-Uunix", /* remove unix symbol so that filename unix.c okay */ +#endif +#if defined(__386BSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(MACH) +# ifdef __i386__ + "-D__i386__", +# endif +# ifdef __x86_64__ + "-D__x86_64__", +# endif +# ifdef __GNUC__ + "-traditional", +# endif +#endif +#ifdef M4330 + "-DM4330", /* Tektronix */ +#endif +#ifdef M4310 + "-DM4310", /* Tektronix */ +#endif +#if defined(macII) || defined(_AUX_SOURCE) + "-DmacII", /* Apple A/UX */ +#endif +#ifdef USL + "-DUSL", /* USL */ +#endif +#ifdef sony + "-Dsony", /* Sony */ +#if !defined(SYSTYPE_SYSV) && !defined(_SYSTYPE_SYSV) && NEWSOS < 42 + "-Dbsd43", +#endif +#endif +#ifdef _IBMR2 + "-D_IBMR2", /* IBM RS-6000 (we ensured that aix is defined above */ +#ifndef aix +#define aix /* allow BOOTSTRAPCFLAGS="-D_IBMR2" */ +#endif +#endif /* _IBMR2 */ +#ifdef aix + "-Daix", /* AIX instead of AOS */ +#ifndef ibm +#define ibm /* allow BOOTSTRAPCFLAGS="-Daix" */ +#endif +#endif /* aix */ +#ifdef ibm + "-Dibm", /* IBM PS/2 and RT under both AOS and AIX */ +#endif +#ifdef luna + "-Dluna", /* OMRON luna 68K and 88K */ +#ifdef luna1 + "-Dluna1", +#endif +#ifdef luna88k /* need not on UniOS-Mach Vers. 1.13 */ + "-traditional", /* for some older version */ +#endif /* instead of "-DXCOMM=\\#" */ +#ifdef uniosb + "-Duniosb", +#endif +#ifdef uniosu + "-Duniosu", +#endif +#endif /* luna */ +#ifdef _CRAY /* Cray */ + "-Ucray", +#endif +#ifdef Mips + "-DMips", /* Define and use Mips for Mips Co. OS/mach. */ +# if defined(SYSTYPE_BSD) || defined(BSD) || defined(BSD43) + "-DBSD43", /* Mips RISCOS supports two environments */ +# else + "-DSYSV", /* System V environment is the default */ +# endif +#endif /* Mips */ +#ifdef MOTOROLA + "-DMOTOROLA", /* Motorola Delta Systems */ +# ifdef SYSV + "-DSYSV", +# endif +# ifdef SVR4 + "-DSVR4", +# endif +#endif /* MOTOROLA */ +#ifdef i386 + "-Di386", +# ifdef SVR4 + "-DSVR4", +# endif +# ifdef SYSV + "-DSYSV", +# ifdef ISC + "-DISC", +# ifdef ISC40 + "-DISC40", /* ISC 4.0 */ +# else +# ifdef ISC202 + "-DISC202", /* ISC 2.0.2 */ +# else +# ifdef ISC30 + "-DISC30", /* ISC 3.0 */ +# else + "-DISC22", /* ISC 2.2.1 */ +# endif +# endif +# endif +# endif +# ifdef SCO + "-DSCO", +# ifdef SCO324 + "-DSCO324", +# endif +# endif +# endif +# ifdef ESIX + "-DESIX", +# endif +# ifdef ATT + "-DATT", +# endif +# ifdef DELL + "-DDELL", +# endif +#endif +#ifdef SYSV386 /* System V/386 folks, obsolete */ + "-Di386", +# ifdef SVR4 + "-DSVR4", +# endif +# ifdef ISC + "-DISC", +# ifdef ISC40 + "-DISC40", /* ISC 4.0 */ +# else +# ifdef ISC202 + "-DISC202", /* ISC 2.0.2 */ +# else +# ifdef ISC30 + "-DISC30", /* ISC 3.0 */ +# else + "-DISC22", /* ISC 2.2.1 */ +# endif +# endif +# endif +# endif +# ifdef SCO + "-DSCO", +# ifdef SCO324 + "-DSCO324", +# endif +# endif +# ifdef ESIX + "-DESIX", +# endif +# ifdef ATT + "-DATT", +# endif +# ifdef DELL + "-DDELL", +# endif +#endif +#ifdef __osf__ + "-D__osf__", +# ifdef __mips__ + "-D__mips__", +# endif +# ifdef __alpha + "-D__alpha", +# endif +# ifdef __i386__ + "-D__i386__", +# endif +# ifdef __GNUC__ + "-traditional", +# endif +#endif +#ifdef Oki + "-DOki", +#endif +#ifdef sun +#ifdef SVR4 + "-DSVR4", +#endif +#endif +#ifdef WIN32 + "-DWIN32", + "-nologo", + "-batch", + "-D__STDC__", +#endif +#ifdef NCR + "-DNCR", /* NCR */ +#endif +#ifdef linux + "-traditional", + "-Dlinux", +#endif +#ifdef __uxp__ + "-D__uxp__", +#endif +#ifdef __sxg__ + "-D__sxg__", +#endif +#ifdef nec_ews_svr2 + "-Dnec_ews_svr2", +#endif +#ifdef AMOEBA + "-DAMOEBA", +# ifdef CROSS_COMPILE + "-DCROSS_COMPILE", +# ifdef CROSS_i80386 + "-Di80386", +# endif +# ifdef CROSS_sparc + "-Dsparc", +# endif +# ifdef CROSS_mc68000 + "-Dmc68000", +# endif +# else +# ifdef i80386 + "-Di80386", +# endif +# ifdef sparc + "-Dsparc", +# endif +# ifdef mc68000 + "-Dmc68000", +# endif +# endif +#endif +#ifdef __minix_vmd + "-Dminix", +#endif + +#if defined(__EMX__) + "-traditional", + "-Demxos2", +#endif + +}; +#else /* else MAKEDEPEND */ +/* + * Step 6: predefs + * If your compiler and/or preprocessor define any specific symbols, add + * them to the the following table. The definition of struct symtab is + * in util/makedepend/def.h. + */ +struct symtab predefs[] = { +#ifdef apollo + {"apollo", "1"}, +#endif +#ifdef ibm032 + {"ibm032", "1"}, +#endif +#ifdef ibm + {"ibm", "1"}, +#endif +#ifdef aix + {"aix", "1"}, +#endif +#ifdef sun + {"sun", "1"}, +#endif +#ifdef sun2 + {"sun2", "1"}, +#endif +#ifdef sun3 + {"sun3", "1"}, +#endif +#ifdef sun4 + {"sun4", "1"}, +#endif +#ifdef sparc + {"sparc", "1"}, +#endif +#ifdef __sparc__ + {"__sparc__", "1"}, +#endif +#ifdef hpux + {"hpux", "1"}, +#endif +#ifdef __hpux + {"__hpux", "1"}, +#endif +#ifdef __hp9000s800 + {"__hp9000s800", "1"}, +#endif +#ifdef __hp9000s700 + {"__hp9000s700", "1"}, +#endif +#ifdef vax + {"vax", "1"}, +#endif +#ifdef VMS + {"VMS", "1"}, +#endif +#ifdef cray + {"cray", "1"}, +#endif +#ifdef CRAY + {"CRAY", "1"}, +#endif +#ifdef _CRAY + {"_CRAY", "1"}, +#endif +#ifdef att + {"att", "1"}, +#endif +#ifdef mips + {"mips", "1"}, +#endif +#ifdef __mips__ + {"__mips__", "1"}, +#endif +#ifdef ultrix + {"ultrix", "1"}, +#endif +#ifdef stellar + {"stellar", "1"}, +#endif +#ifdef mc68000 + {"mc68000", "1"}, +#endif +#ifdef mc68020 + {"mc68020", "1"}, +#endif +#ifdef __GNUC__ + {"__GNUC__", "1"}, +#endif +#if __STDC__ + {"__STDC__", "1"}, +#endif +#ifdef __HIGHC__ + {"__HIGHC__", "1"}, +#endif +#ifdef CMU + {"CMU", "1"}, +#endif +#ifdef luna + {"luna", "1"}, +#ifdef luna1 + {"luna1", "1"}, +#endif +#ifdef luna2 + {"luna2", "1"}, +#endif +#ifdef luna88k + {"luna88k", "1"}, +#endif +#ifdef uniosb + {"uniosb", "1"}, +#endif +#ifdef uniosu + {"uniosu", "1"}, +#endif +#endif +#ifdef ieeep754 + {"ieeep754", "1"}, +#endif +#ifdef is68k + {"is68k", "1"}, +#endif +#ifdef m68k + {"m68k", "1"}, +#endif +#ifdef m88k + {"m88k", "1"}, +#endif +#ifdef __m88k__ + {"__m88k__", "1"}, +#endif +#ifdef bsd43 + {"bsd43", "1"}, +#endif +#ifdef hcx + {"hcx", "1"}, +#endif +#ifdef sony + {"sony", "1"}, +#ifdef SYSTYPE_SYSV + {"SYSTYPE_SYSV", "1"}, +#endif +#ifdef _SYSTYPE_SYSV + {"_SYSTYPE_SYSV", "1"}, +#endif +#endif +#ifdef __OSF__ + {"__OSF__", "1"}, +#endif +#ifdef __osf__ + {"__osf__", "1"}, +#endif +#ifdef __alpha + {"__alpha", "1"}, +#endif +#ifdef __DECC + {"__DECC", "1"}, +#endif +#ifdef __decc + {"__decc", "1"}, +#endif +#ifdef __uxp__ + {"__uxp__", "1"}, +#endif +#ifdef __sxg__ + {"__sxg__", "1"}, +#endif +#ifdef _SEQUENT_ + {"_SEQUENT_", "1"}, + {"__STDC__", "1"}, +#endif +#ifdef __bsdi__ + {"__bsdi__", "1"}, +#endif +#ifdef nec_ews_svr2 + {"nec_ews_svr2", "1"}, +#endif +#ifdef nec_ews_svr4 + {"nec_ews_svr4", "1"}, +#endif +#ifdef _nec_ews_svr4 + {"_nec_ews_svr4", "1"}, +#endif +#ifdef _nec_up + {"_nec_up", "1"}, +#endif +#ifdef SX + {"SX", "1"}, +#endif +#ifdef nec + {"nec", "1"}, +#endif +#ifdef _nec_ft + {"_nec_ft", "1"}, +#endif +#ifdef PC_UX + {"PC_UX", "1"}, +#endif +#ifdef sgi + {"sgi", "1"}, +#endif +#ifdef __sgi + {"__sgi", "1"}, +#endif +#ifdef __FreeBSD__ + {"__FreeBSD__", "1"}, +#endif +#ifdef __NetBSD__ + {"__NetBSD__", "1"}, +#endif +#ifdef __OpenBSD__ + {"__OpenBSD__", "1"}, +#endif +#ifdef __EMX__ + {"__EMX__", "1"}, +#endif + /* add any additional symbols before this line */ + {NULL, NULL} +}; + +#endif /* MAKEDEPEND */ +#endif /* CCIMAKE */ diff --git a/ape-server/deps/js/src/config/mkdepend/include.c b/ape-server/deps/js/src/config/mkdepend/include.c new file mode 100755 index 0000000..3b89f26 --- /dev/null +++ b/ape-server/deps/js/src/config/mkdepend/include.c @@ -0,0 +1,337 @@ +/* $Xorg: include.c,v 1.4 2001/02/09 02:03:16 xorgcvs Exp $ */ +/* + +Copyright (c) 1993, 1994, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + +*/ +/* $XFree86: xc/config/makedepend/include.c,v 3.7 2001/12/14 19:53:20 dawes Exp $ */ + + +#include "def.h" + +#ifdef _MSC_VER +#include +static int +does_file_exist(char *file) +{ + WIN32_FILE_ATTRIBUTE_DATA data; + BOOL b = GetFileAttributesExA(file, GetFileExInfoStandard, &data); + if (!b) + return 0; + return (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0; +} +#else +static int +does_file_exist(char *file) +{ + struct stat sb; + return stat(file, &sb) == 0 && !S_ISDIR(sb.st_mode); +} +#endif + +extern struct inclist inclist[ MAXFILES ], + *inclistp, *inclistnext; +extern char *includedirs[ ], + **includedirsnext; +extern char *notdotdot[ ]; +extern boolean show_where_not; +extern boolean warn_multiple; + +static boolean +isdot(char *p) +{ + if(p && *p++ == '.' && *p++ == '\0') + return(TRUE); + return(FALSE); +} + +static boolean +isdotdot(char *p) +{ + if(p && *p++ == '.' && *p++ == '.' && *p++ == '\0') + return(TRUE); + return(FALSE); +} + +static boolean +issymbolic(char *dir, char *component) +{ +#ifdef S_IFLNK + struct stat st; + char buf[ BUFSIZ ], **pp; + + sprintf(buf, "%s%s%s", dir, *dir ? "/" : "", component); + for (pp=notdotdot; *pp; pp++) + if (strcmp(*pp, buf) == 0) + return (TRUE); + if (lstat(buf, &st) == 0 + && (st.st_mode & S_IFMT) == S_IFLNK) { + *pp++ = copy(buf); + if (pp >= ¬dotdot[ MAXDIRS ]) + fatalerr("out of .. dirs, increase MAXDIRS\n"); + return(TRUE); + } +#endif + return(FALSE); +} + +/* + * Occasionally, pathnames are created that look like .../x/../y + * Any of the 'x/..' sequences within the name can be eliminated. + * (but only if 'x' is not a symbolic link!!) + */ +static void +remove_dotdot(char *path) +{ + register char *end, *from, *to, **cp; + char *components[ MAXFILES ], + newpath[ BUFSIZ ]; + boolean component_copied; + + /* + * slice path up into components. + */ + to = newpath; + if (*path == '/') + *to++ = '/'; + *to = '\0'; + cp = components; + for (from=end=path; *end; end++) + if (*end == '/') { + while (*end == '/') + *end++ = '\0'; + if (*from) + *cp++ = from; + from = end; + } + *cp++ = from; + *cp = NULL; + + /* + * Recursively remove all 'x/..' component pairs. + */ + cp = components; + while(*cp) { + if (!isdot(*cp) && !isdotdot(*cp) && isdotdot(*(cp+1)) + && !issymbolic(newpath, *cp)) + { + char **fp = cp + 2; + char **tp = cp; + + do + *tp++ = *fp; /* move all the pointers down */ + while (*fp++); + if (cp != components) + cp--; /* go back and check for nested ".." */ + } else { + cp++; + } + } + /* + * Concatenate the remaining path elements. + */ + cp = components; + component_copied = FALSE; + while(*cp) { + if (component_copied) + *to++ = '/'; + component_copied = TRUE; + for (from = *cp; *from; ) + *to++ = *from++; + *to = '\0'; + cp++; + } + *to++ = '\0'; + + /* + * copy the reconstituted path back to our pointer. + */ + strcpy(path, newpath); +} + +/* + * Add an include file to the list of those included by 'file'. + */ +struct inclist * +newinclude(char *newfile, char *incstring) +{ + register struct inclist *ip; + + /* + * First, put this file on the global list of include files. + */ + ip = inclistp++; + if (inclistp == inclist + MAXFILES - 1) + fatalerr("out of space: increase MAXFILES\n"); + ip->i_file = copy(newfile); + + if (incstring == NULL) + ip->i_incstring = ip->i_file; + else + ip->i_incstring = copy(incstring); + + inclistnext = inclistp; + return(ip); +} + +void +included_by(struct inclist *ip, struct inclist *newfile) +{ + register int i; + + if (ip == NULL) + return; + /* + * Put this include file (newfile) on the list of files included + * by 'file'. If 'file' is NULL, then it is not an include + * file itself (i.e. was probably mentioned on the command line). + * If it is already on the list, don't stick it on again. + */ + if (ip->i_list == NULL) { + ip->i_list = (struct inclist **) + malloc(sizeof(struct inclist *) * ++ip->i_listlen); + ip->i_merged = (boolean *) + malloc(sizeof(boolean) * ip->i_listlen); + } else { + for (i=0; ii_listlen; i++) + if (ip->i_list[ i ] == newfile) { + i = strlen(newfile->i_file); + if (!(ip->i_flags & INCLUDED_SYM) && + !(i > 2 && + newfile->i_file[i-1] == 'c' && + newfile->i_file[i-2] == '.')) + { + /* only bitch if ip has */ + /* no #include SYMBOL lines */ + /* and is not a .c file */ + if (warn_multiple) + { + warning("%s includes %s more than once!\n", + ip->i_file, newfile->i_file); + warning1("Already have\n"); + for (i=0; ii_listlen; i++) + warning1("\t%s\n", ip->i_list[i]->i_file); + } + } + return; + } + ip->i_list = (struct inclist **) realloc(ip->i_list, + sizeof(struct inclist *) * ++ip->i_listlen); + ip->i_merged = (boolean *) + realloc(ip->i_merged, sizeof(boolean) * ip->i_listlen); + } + ip->i_list[ ip->i_listlen-1 ] = newfile; + ip->i_merged[ ip->i_listlen-1 ] = FALSE; +} + +void +inc_clean (void) +{ + register struct inclist *ip; + + for (ip = inclist; ip < inclistp; ip++) { + ip->i_flags &= ~MARKED; + } +} + +struct inclist * +inc_path(char *file, char *include, int type) +{ + static char path[ BUFSIZ ]; + register char **pp, *p; + register struct inclist *ip; + + /* + * Check all previously found include files for a path that + * has already been expanded. + */ + if ((type == INCLUDE) || (type == INCLUDEDOT)) + inclistnext = inclist; + ip = inclistnext; + + for (; ip->i_file; ip++) { + if ((strcmp(ip->i_incstring, include) == 0) && + !(ip->i_flags & INCLUDED_SYM)) { + inclistnext = ip + 1; + return ip; + } + } + + if (inclistnext == inclist) { + /* + * If the path was surrounded by "" or is an absolute path, + * then check the exact path provided. + */ + if ((type == INCLUDEDOT) || + (type == INCLUDENEXTDOT) || + (*include == '/')) { + if (does_file_exist(include)) + return newinclude(include, include); + if (show_where_not) + warning1("\tnot in %s\n", include); + } + + /* + * If the path was surrounded by "" see if this include file is + * in the directory of the file being parsed. + */ + if ((type == INCLUDEDOT) || (type == INCLUDENEXTDOT)) { + for (p=file+strlen(file); p>file; p--) + if (*p == '/') + break; + if (p == file) { + strcpy(path, include); + } else { + strncpy(path, file, (p-file) + 1); + path[ (p-file) + 1 ] = '\0'; + strcpy(path + (p-file) + 1, include); + } + remove_dotdot(path); + if (does_file_exist(path)) + return newinclude(path, include); + if (show_where_not) + warning1("\tnot in %s\n", path); + } + } + + /* + * Check the include directories specified. Standard include dirs + * should be at the end. + */ + if ((type == INCLUDE) || (type == INCLUDEDOT)) + includedirsnext = includedirs; + pp = includedirsnext; + + for (; *pp; pp++) { + sprintf(path, "%s/%s", *pp, include); + remove_dotdot(path); + if (does_file_exist(path)) { + includedirsnext = pp + 1; + return newinclude(path, include); + } + if (show_where_not) + warning1("\tnot in %s\n", path); + } + + return NULL; +} diff --git a/ape-server/deps/js/src/config/mkdepend/main.c b/ape-server/deps/js/src/config/mkdepend/main.c new file mode 100755 index 0000000..a86866f --- /dev/null +++ b/ape-server/deps/js/src/config/mkdepend/main.c @@ -0,0 +1,860 @@ +/* $Xorg: main.c,v 1.5 2001/02/09 02:03:16 xorgcvs Exp $ */ +/* + +Copyright (c) 1993, 1994, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + +*/ +/* $XFree86: xc/config/makedepend/main.c,v 3.32 2003/03/26 20:43:48 tsi Exp $ */ + +#include "def.h" +#ifdef hpux +#define sigvec sigvector +#endif /* hpux */ + +#ifdef X_POSIX_C_SOURCE +#define _POSIX_C_SOURCE X_POSIX_C_SOURCE +#include +#undef _POSIX_C_SOURCE +#else +#if defined(X_NOT_POSIX) || defined(_POSIX_SOURCE) +#include +#else +#define _POSIX_SOURCE +#include +#undef _POSIX_SOURCE +#endif +#endif + +#include + +#ifdef MINIX +#define USE_CHMOD 1 +#endif + +#ifdef DEBUG +int _debugmask; +#endif + +/* #define DEBUG_DUMP */ +#ifdef DEBUG_DUMP +#define DBG_PRINT(file, fmt, args) fprintf(file, fmt, args) +#else +#define DBG_PRINT(file, fmt, args) /* empty */ +#endif + +#define DASH_INC_PRE "#include \"" +#define DASH_INC_POST "\"" + +char *ProgramName; + +char *directives[] = { + "if", + "ifdef", + "ifndef", + "else", + "endif", + "define", + "undef", + "include", + "line", + "pragma", + "error", + "ident", + "sccs", + "elif", + "eject", + "warning", + "include_next", + NULL +}; + +#define MAKEDEPEND +#include "imakemdep.h" /* from config sources */ +#undef MAKEDEPEND + +struct inclist inclist[ MAXFILES ], + *inclistp = inclist, + *inclistnext = inclist, + maininclist; + +static char *filelist[ MAXFILES ]; +char *includedirs[ MAXDIRS + 1 ], + **includedirsnext = includedirs; +char *notdotdot[ MAXDIRS ]; +static int cmdinc_count = 0; +static char *cmdinc_list[ 2 * MAXINCFILES ]; +char *objprefix = ""; +char *objsuffix = OBJSUFFIX; +static char *startat = "# DO NOT DELETE"; +int width = 78; +static boolean append = FALSE; +boolean printed = FALSE; +boolean verbose = FALSE; +boolean show_where_not = FALSE; +/* Warn on multiple includes of same file */ +boolean warn_multiple = FALSE; + +static void setfile_cmdinc(struct filepointer *filep, long count, char **list); +static void redirect(char *line, char *makefile); + +static +#ifdef SIGNALRETURNSINT +int +#else +void +#endif +catch (int sig) +{ + fflush (stdout); + fatalerr ("got signal %d\n", sig); +} + +#if defined(USG) || (defined(i386) && defined(SYSV)) || defined(WIN32) || defined(__UNIXOS2__) || defined(Lynx_22) || defined(__CYGWIN__) +#define USGISH +#endif + +#ifndef USGISH +#ifdef X_NOT_POSIX +#define sigaction sigvec +#define sa_handler sv_handler +#define sa_mask sv_mask +#define sa_flags sv_flags +#endif +struct sigaction sig_act; +#endif /* USGISH */ + +int +main(int argc, char *argv[]) +{ + char **fp = filelist; + char **incp = includedirs; + char *p; + struct inclist *ip; + char *makefile = NULL; + struct filepointer *filecontent; + struct symtab *psymp = predefs; + char *endmarker = NULL; + char *defincdir = NULL; + char **undeflist = NULL; + int numundefs = 0, i; + register char offset; + + ProgramName = argv[0]; + + while (psymp->s_name) + { + define2(psymp->s_name, psymp->s_value, &maininclist); + psymp++; + } + if (argc == 2 && argv[1][0] == '@') { + struct stat ast; + int afd; + char *args; + char **nargv; + int nargc; + char quotechar = '\0'; + + nargc = 1; + if ((afd = open(argv[1]+1, O_RDONLY)) < 0) + fatalerr("cannot open \"%s\"\n", argv[1]+1); + fstat(afd, &ast); + args = (char *)malloc(ast.st_size + 1); + if ((ast.st_size = read(afd, args, ast.st_size)) < 0) + fatalerr("failed to read %s\n", argv[1]+1); + args[ast.st_size] = '\0'; + close(afd); + for (p = args; *p; p++) { + if (quotechar) { + if (quotechar == '\\' || + (*p == quotechar && p[-1] != '\\')) + quotechar = '\0'; + continue; + } + switch (*p) { + case '\\': + case '"': + case '\'': + quotechar = *p; + break; + case ' ': + case '\n': + *p = '\0'; + if (p > args && p[-1]) + nargc++; + break; + } + } + if (p[-1]) + nargc++; + nargv = (char **)malloc(nargc * sizeof(char *)); + nargv[0] = argv[0]; + argc = 1; + for (p = args; argc < nargc; p += strlen(p) + 1) + if (*p) nargv[argc++] = p; + argv = nargv; + } + for(argc--, argv++; argc; argc--, argv++) { + /* if looking for endmarker then check before parsing */ + if (endmarker && strcmp (endmarker, *argv) == 0) { + endmarker = NULL; + continue; + } + if (**argv != '-') { + /* treat +thing as an option for C++ */ + if (endmarker && **argv == '+') + continue; + *fp++ = argv[0]; + continue; + } + switch(argv[0][1]) { + case '-': + endmarker = &argv[0][2]; + if (endmarker[0] == '\0') endmarker = "--"; + break; + case 'D': + offset = 2; + if (argv[0][2] == '\0') { + argv++; + argc--; + offset = 0; + } + /* offset +1 here since first def letter + * cannot be `=` + */ + for (p = argv[0] + offset + 1; *p; p++) + if (*p == '=') { + *p = ' '; + break; + } + define(argv[0] + offset, &maininclist); + break; + case 'I': + if (incp >= includedirs + MAXDIRS) + fatalerr("Too many -I flags.\n"); + *incp++ = argv[0]+2; + if (**(incp-1) == '\0') { + *(incp-1) = *(++argv); + argc--; + } + break; + case 'U': + /* Undef's override all -D's so save them up */ + numundefs++; + if (numundefs == 1) + undeflist = malloc(sizeof(char *)); + else + undeflist = realloc(undeflist, + numundefs * sizeof(char *)); + offset = 2; + if (argv[0][2] == '\0') { + argv++; + argc--; + offset = 0; + } + undeflist[numundefs - 1] = argv[0] + offset; + break; + case 'Y': + defincdir = argv[0]+2; + break; + /* do not use if endmarker processing */ + case 'a': + if (endmarker) break; + append = TRUE; + break; + case 'w': + if (endmarker) break; + if (argv[0][2] == '\0') { + argv++; + argc--; + width = atoi(argv[0]); + } else + width = atoi(argv[0]+2); + break; + case 'o': + if (endmarker) break; + if (argv[0][2] == '\0') { + argv++; + argc--; + objsuffix = argv[0]; + } else + objsuffix = argv[0]+2; + break; + case 'p': + if (endmarker) break; + if (argv[0][2] == '\0') { + argv++; + argc--; + objprefix = argv[0]; + } else + objprefix = argv[0]+2; + break; + case 'v': + if (endmarker) break; + verbose = TRUE; +#ifdef DEBUG + if (argv[0][2]) + _debugmask = atoi(argv[0]+2); +#endif + break; + case 's': + if (endmarker) break; + startat = argv[0]+2; + if (*startat == '\0') { + startat = *(++argv); + argc--; + } + if (*startat != '#') + fatalerr("-s flag's value should start %s\n", + "with '#'."); + break; + case 'f': + if (endmarker) break; + makefile = argv[0]+2; + if (*makefile == '\0') { + makefile = *(++argv); + argc--; + } + break; + + case 'm': + warn_multiple = TRUE; + break; + + /* Ignore -O, -g so we can just pass ${CFLAGS} to + makedepend + */ + case 'O': + case 'g': + break; + case 'i': + if (strcmp(&argv[0][1],"include") == 0) { + char *buf; + if (argc<2) + fatalerr("option -include is a " + "missing its parameter\n"); + if (cmdinc_count >= MAXINCFILES) + fatalerr("Too many -include flags.\n"); + argc--; + argv++; + buf = malloc(strlen(DASH_INC_PRE) + + strlen(argv[0]) + + strlen(DASH_INC_POST) + 1); + if(!buf) + fatalerr("out of memory at " + "-include string\n"); + cmdinc_list[2 * cmdinc_count + 0] = argv[0]; + cmdinc_list[2 * cmdinc_count + 1] = buf; + cmdinc_count++; + break; + } + /* intentional fall through */ + default: + if (endmarker) break; + /* fatalerr("unknown opt = %s\n", argv[0]); */ + warning("ignoring option %s\n", argv[0]); + } + } + /* Now do the undefs from the command line */ + for (i = 0; i < numundefs; i++) + undefine(undeflist[i], &maininclist); + if (numundefs > 0) + free(undeflist); + + if (!defincdir) { +#ifdef PREINCDIR + if (incp >= includedirs + MAXDIRS) + fatalerr("Too many -I flags.\n"); + *incp++ = PREINCDIR; +#endif +#ifdef __UNIXOS2__ + { + char *emxinc = getenv("C_INCLUDE_PATH"); + /* can have more than one component */ + if (emxinc) { + char *beg, *end; + beg= (char*)strdup(emxinc); + for (;;) { + end = (char*)strchr(beg,';'); + if (end) *end = 0; + if (incp >= includedirs + MAXDIRS) + fatalerr("Too many include dirs\n"); + *incp++ = beg; + if (!end) break; + beg = end+1; + } + } + } +#else /* !__UNIXOS2__, does not use INCLUDEDIR at all */ + if (incp >= includedirs + MAXDIRS) + fatalerr("Too many -I flags.\n"); + *incp++ = INCLUDEDIR; +#endif + +#ifdef EXTRAINCDIR + if (incp >= includedirs + MAXDIRS) + fatalerr("Too many -I flags.\n"); + *incp++ = EXTRAINCDIR; +#endif + +#ifdef POSTINCDIR + if (incp >= includedirs + MAXDIRS) + fatalerr("Too many -I flags.\n"); + *incp++ = POSTINCDIR; +#endif + } else if (*defincdir) { + if (incp >= includedirs + MAXDIRS) + fatalerr("Too many -I flags.\n"); + *incp++ = defincdir; + } + + redirect(startat, makefile); + + /* + * catch signals. + */ +#ifdef USGISH +/* should really reset SIGINT to SIG_IGN if it was. */ +#ifdef SIGHUP + signal (SIGHUP, catch); +#endif + signal (SIGINT, catch); +#ifdef SIGQUIT + signal (SIGQUIT, catch); +#endif + signal (SIGILL, catch); +#ifdef SIGBUS + signal (SIGBUS, catch); +#endif + signal (SIGSEGV, catch); +#ifdef SIGSYS + signal (SIGSYS, catch); +#endif +#else + sig_act.sa_handler = catch; +#if defined(_POSIX_SOURCE) || !defined(X_NOT_POSIX) + sigemptyset(&sig_act.sa_mask); + sigaddset(&sig_act.sa_mask, SIGINT); + sigaddset(&sig_act.sa_mask, SIGQUIT); +#ifdef SIGBUS + sigaddset(&sig_act.sa_mask, SIGBUS); +#endif + sigaddset(&sig_act.sa_mask, SIGILL); + sigaddset(&sig_act.sa_mask, SIGSEGV); + sigaddset(&sig_act.sa_mask, SIGHUP); + sigaddset(&sig_act.sa_mask, SIGPIPE); +#ifdef SIGSYS + sigaddset(&sig_act.sa_mask, SIGSYS); +#endif +#else + sig_act.sa_mask = ((1<<(SIGINT -1)) + |(1<<(SIGQUIT-1)) +#ifdef SIGBUS + |(1<<(SIGBUS-1)) +#endif + |(1<<(SIGILL-1)) + |(1<<(SIGSEGV-1)) + |(1<<(SIGHUP-1)) + |(1<<(SIGPIPE-1)) +#ifdef SIGSYS + |(1<<(SIGSYS-1)) +#endif + ); +#endif /* _POSIX_SOURCE */ + sig_act.sa_flags = 0; + sigaction(SIGHUP, &sig_act, (struct sigaction *)0); + sigaction(SIGINT, &sig_act, (struct sigaction *)0); + sigaction(SIGQUIT, &sig_act, (struct sigaction *)0); + sigaction(SIGILL, &sig_act, (struct sigaction *)0); +#ifdef SIGBUS + sigaction(SIGBUS, &sig_act, (struct sigaction *)0); +#endif + sigaction(SIGSEGV, &sig_act, (struct sigaction *)0); +#ifdef SIGSYS + sigaction(SIGSYS, &sig_act, (struct sigaction *)0); +#endif +#endif /* USGISH */ + + /* + * now peruse through the list of files. + */ + for(fp=filelist; *fp; fp++) { + DBG_PRINT(stderr,"file: %s\n",*fp); + filecontent = getfile(*fp); + setfile_cmdinc(filecontent, cmdinc_count, cmdinc_list); + ip = newinclude(*fp, (char *)NULL); + + find_includes(filecontent, ip, ip, 0, FALSE); + freefile(filecontent); + recursive_pr_include(ip, ip->i_file, base_name(*fp)); + inc_clean(); + } + if (printed) + printf("\n"); + return 0; +} + +#ifdef __UNIXOS2__ +/* + * eliminate \r chars from file + */ +static int +elim_cr(char *buf, int sz) +{ + int i,wp; + for (i= wp = 0; if_name = file; + if ((fd = open(file, O_RDONLY)) < 0) { + warning("cannot open \"%s\"\n", file); + content->f_p = content->f_base = content->f_end = (char *)malloc(1); + *content->f_p = '\0'; + return(content); + } + fstat(fd, &st); + content->f_base = (char *)malloc(st.st_size+1); + if (content->f_base == NULL) + fatalerr("cannot allocate mem\n"); + if ((st.st_size = read(fd, content->f_base, st.st_size)) < 0) + fatalerr("failed to read %s\n", file); +#ifdef __UNIXOS2__ + st.st_size = elim_cr(content->f_base,st.st_size); +#endif + close(fd); + content->f_len = st.st_size+1; + content->f_p = content->f_base; + content->f_end = content->f_base + st.st_size; + *content->f_end = '\0'; + content->f_line = 0; + content->cmdinc_count = 0; + content->cmdinc_list = NULL; + content->cmdinc_line = 0; + return(content); +} + +void +setfile_cmdinc(struct filepointer* filep, long count, char** list) +{ + filep->cmdinc_count = count; + filep->cmdinc_list = list; + filep->cmdinc_line = 0; +} + +void +freefile(struct filepointer *fp) +{ + free(fp->f_base); + free(fp); +} + +char *copy(char *str) +{ + char *p = (char *)malloc(strlen(str) + 1); + + strcpy(p, str); + return(p); +} + +int +match(char *str, char **list) +{ + int i; + + for (i=0; *list; i++, list++) + if (strcmp(str, *list) == 0) + return(i); + return(-1); +} + +/* + * Get the next line. We only return lines beginning with '#' since that + * is all this program is ever interested in. + */ +char *getnextline(struct filepointer *filep) +{ + char *p, /* walking pointer */ + *eof, /* end of file pointer */ + *bol; /* beginning of line pointer */ + int lineno; /* line number */ + boolean whitespace = FALSE; + + /* + * Fake the "-include" line files in form of #include to the + * start of each file. + */ + if (filep->cmdinc_line < filep->cmdinc_count) { + char *inc = filep->cmdinc_list[2 * filep->cmdinc_line + 0]; + char *buf = filep->cmdinc_list[2 * filep->cmdinc_line + 1]; + filep->cmdinc_line++; + sprintf(buf,"%s%s%s",DASH_INC_PRE,inc,DASH_INC_POST); + DBG_PRINT(stderr,"%s\n",buf); + return(buf); + } + + p = filep->f_p; + eof = filep->f_end; + if (p >= eof) + return((char *)NULL); + lineno = filep->f_line; + + for (bol = p--; ++p < eof; ) { + if ((bol == p) && ((*p == ' ') || (*p == '\t'))) + { + /* Consume leading white-spaces for this line */ + while (((p+1) < eof) && ((*p == ' ') || (*p == '\t'))) + { + p++; + bol++; + } + whitespace = TRUE; + } + + if (*p == '/' && (p+1) < eof && *(p+1) == '*') { + /* Consume C comments */ + *(p++) = ' '; + *(p++) = ' '; + while (p < eof && *p) { + if (*p == '*' && (p+1) < eof && *(p+1) == '/') { + *(p++) = ' '; + *(p++) = ' '; + break; + } + if (*p == '\n') + lineno++; + *(p++) = ' '; + } + --p; + } + else if (*p == '/' && (p+1) < eof && *(p+1) == '/') { + /* Consume C++ comments */ + *(p++) = ' '; + *(p++) = ' '; + while (p < eof && *p) { + if (*p == '\\' && (p+1) < eof && + *(p+1) == '\n') { + *(p++) = ' '; + lineno++; + } + else if (*p == '?' && (p+3) < eof && + *(p+1) == '?' && + *(p+2) == '/' && + *(p+3) == '\n') { + *(p++) = ' '; + *(p++) = ' '; + *(p++) = ' '; + lineno++; + } + else if (*p == '\n') + break; /* to process end of line */ + *(p++) = ' '; + } + --p; + } + else if (*p == '\\' && (p+1) < eof && *(p+1) == '\n') { + /* Consume backslash line terminations */ + *(p++) = ' '; + *p = ' '; + lineno++; + } + else if (*p == '?' && (p+3) < eof && + *(p+1) == '?' && *(p+2) == '/' && *(p+3) == '\n') { + /* Consume trigraph'ed backslash line terminations */ + *(p++) = ' '; + *(p++) = ' '; + *(p++) = ' '; + *p = ' '; + lineno++; + } + else if (*p == '\n') { + lineno++; + if (*bol == '#') { + char *cp; + + *(p++) = '\0'; + /* punt lines with just # (yacc generated) */ + for (cp = bol+1; + *cp && (*cp == ' ' || *cp == '\t'); cp++); + if (*cp) goto done; + --p; + } + bol = p+1; + whitespace = FALSE; + } + } + if (*bol != '#') + bol = NULL; +done: + if (bol && whitespace) { + warning("%s: non-portable whitespace encountered at line %d\n", + filep->f_name, lineno); + } + filep->f_p = p; + filep->f_line = lineno; +#ifdef DEBUG_DUMP + if (bol) + DBG_PRINT(stderr,"%s\n",bol); +#endif + return(bol); +} + +/* + * Strip the file name down to what we want to see in the Makefile. + * It will have objprefix and objsuffix around it. + */ +char *base_name(char *file) +{ + char *p; + + file = copy(file); + for(p=file+strlen(file); p>file && *p != '.'; p--) ; + + if (*p == '.') + *p = '\0'; + return(file); +} + +#if defined(USG) && !defined(CRAY) && !defined(SVR4) && !defined(__UNIXOS2__) && !defined(clipper) && !defined(__clipper__) +int rename (char *from, char *to) +{ + (void) unlink (to); + if (link (from, to) == 0) { + unlink (from); + return 0; + } else { + return -1; + } +} +#endif /* USGISH */ + +void +redirect(char *line, char *makefile) +{ + struct stat st; + FILE *fdin, *fdout; + char backup[ BUFSIZ ], + buf[ BUFSIZ ]; + boolean found = FALSE; + int len; + + /* + * if makefile is "-" then let it pour onto stdout. + */ + if (makefile && *makefile == '-' && *(makefile+1) == '\0') { + puts(line); + return; + } + + /* + * use a default makefile is not specified. + */ + if (!makefile) { + if (stat("Makefile", &st) == 0) + makefile = "Makefile"; + else if (stat("makefile", &st) == 0) + makefile = "makefile"; + else + fatalerr("[mM]akefile is not present\n"); + } + else + stat(makefile, &st); + if ((fdin = fopen(makefile, "r")) == NULL) + fatalerr("cannot open \"%s\"\n", makefile); + sprintf(backup, "%s.bak", makefile); + unlink(backup); +#if defined(WIN32) || defined(__UNIXOS2__) || defined(__CYGWIN__) + fclose(fdin); +#endif + if (rename(makefile, backup) < 0) + fatalerr("cannot rename %s to %s\n", makefile, backup); +#if defined(WIN32) || defined(__UNIXOS2__) || defined(__CYGWIN__) + if ((fdin = fopen(backup, "r")) == NULL) + fatalerr("cannot open \"%s\"\n", backup); +#endif + if ((fdout = freopen(makefile, "w", stdout)) == NULL) + fatalerr("cannot open \"%s\"\n", backup); + len = strlen(line); + while (!found && fgets(buf, BUFSIZ, fdin)) { + if (*buf == '#' && strncmp(line, buf, len) == 0) + found = TRUE; + fputs(buf, fdout); + } + if (!found) { + if (verbose) + warning("Adding new delimiting line \"%s\" and dependencies...\n", + line); + puts(line); /* same as fputs(fdout); but with newline */ + } else if (append) { + while (fgets(buf, BUFSIZ, fdin)) { + fputs(buf, fdout); + } + } + fflush(fdout); +#if defined(USGISH) || defined(_SEQUENT_) || defined(USE_CHMOD) + chmod(makefile, st.st_mode); +#else + fchmod(fileno(fdout), st.st_mode); +#endif /* USGISH */ +} + +void +fatalerr(char *msg, ...) +{ + va_list args; + fprintf(stderr, "%s: error: ", ProgramName); + va_start(args, msg); + vfprintf(stderr, msg, args); + va_end(args); + exit (1); +} + +void +warning(char *msg, ...) +{ + va_list args; + fprintf(stderr, "%s: warning: ", ProgramName); + va_start(args, msg); + vfprintf(stderr, msg, args); + va_end(args); +} + +void +warning1(char *msg, ...) +{ + va_list args; + va_start(args, msg); + vfprintf(stderr, msg, args); + va_end(args); +} diff --git a/ape-server/deps/js/src/config/mkdepend/mkdepend.man b/ape-server/deps/js/src/config/mkdepend/mkdepend.man new file mode 100755 index 0000000..595c87e --- /dev/null +++ b/ape-server/deps/js/src/config/mkdepend/mkdepend.man @@ -0,0 +1,382 @@ +.\" $Xorg: mkdepend.man,v 1.5 2001/02/09 02:03:16 xorgcvs Exp $ +.\" Copyright (c) 1993, 1994, 1998 The Open Group +.\" +.\" Permission to use, copy, modify, distribute, and sell this software and its +.\" documentation for any purpose is hereby granted without fee, provided that +.\" the above copyright notice appear in all copies and that both that +.\" copyright notice and this permission notice appear in supporting +.\" documentation. +.\" +.\" The above copyright notice and this permission notice shall be included in +.\" all copies or substantial portions of the Software. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +.\" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +.\" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +.\" THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +.\" WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +.\" OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +.\" SOFTWARE. +.\" +.\" Except as contained in this notice, the name of The Open Group shall not +.\" be used in advertising or otherwise to promote the sale, use or other +.\" dealing in this Software without prior written authorization from The +.\" Open Group. +.\" +.\" $XFree86: xc/config/makedepend/mkdepend.man,v 1.7 2002/12/14 02:39:45 dawes Exp $ +.\" +.TH MAKEDEPEND 1 __xorgversion__ +.UC 4 +.SH NAME +makedepend \- create dependencies in makefiles +.SH SYNOPSIS +.B makedepend +[ +.BI \-D name\fB=\fPdef +] [ +.BI \-D name +] [ +.BI \-I includedir +] [ +.BI \-Y includedir +] [ +.B \-a +] [ +.BI \-f makefile +] [ +.BI \-include \ file +] [ +.BI \-o objsuffix +] [ +.BI \-p objprefix +] [ +.BI \-s string +] [ +.BI \-w width +] [ +.B \-v +] [ +.B \-m +] [ +\-\^\- +.I otheroptions +\-\^\- +] +.I sourcefile +\&.\|.\|. +.br +.SH DESCRIPTION +The +.B makedepend +program reads each +.I sourcefile +in sequence and parses it like a C-preprocessor, +processing all +.I #include, +.I #define, +.I #undef, +.I #ifdef, +.I #ifndef, +.I #endif, +.I #if, +.I #elif +and +.I #else +directives so that it can correctly tell which +.I #include, +directives would be used in a compilation. +Any +.I #include, +directives can reference files having other +.I #include +directives, and parsing will occur in these files as well. +.PP +Every file that a +.I sourcefile +includes, +directly or indirectly, +is what +.B makedepend +calls a \fIdependency.\fP +These dependencies are then written to a +.I makefile +in such a way that +.B make(1) +will know which object files must be recompiled when a dependency has changed. +.PP +By default, +.B makedepend +places its output in the file named +.I makefile +if it exists, otherwise +.I Makefile. +An alternate makefile may be specified with the +.B \-f +option. +It first searches the makefile for +the line +.sp +\& # DO NOT DELETE THIS LINE \-\^\- make depend depends on it. +.sp +or one provided with the +.B \-s +option, +as a delimiter for the dependency output. +If it finds it, it will delete everything +following this to the end of the makefile +and put the output after this line. +If it doesn't find it, the program +will append the string to the end of the makefile +and place the output following that. +For each +.I sourcefile +appearing on the command line, +.B makedepend +puts lines in the makefile of the form +.sp + sourcefile.o:\0dfile .\|.\|. +.sp +Where \fIsourcefile.o\fP is the name from the command +line with its suffix replaced with ``.o'', +and \fIdfile\fP is a dependency discovered in a +.I #include +directive while parsing +.I sourcefile +or one of the files it included. +.SH EXAMPLE +Normally, +.B makedepend +will be used in a makefile target so that typing ``make depend'' will +bring the dependencies up to date for the makefile. +For example, +.nf + SRCS\0=\0file1.c\0file2.c\0.\|.\|. + CFLAGS\0=\0\-O\0\-DHACK\0\-I\^.\^.\^/foobar\0\-xyz + depend: + makedepend\0\-\^\-\0$(CFLAGS)\0\-\^\-\0$(SRCS) +.fi +.SH OPTIONS +The program +will ignore any option that it does not understand so that you may use +the same arguments that you would for +.B cc(1). +.TP 5 +.B \-D\fIname\fP=\fIdef\fP \fRor\fP \-D\fIname\fP +Define. +This places a definition for +.I name +in +.B makedepend's +symbol table. +Without +.I =def\| +the symbol becomes defined as ``1''. +.TP 5 +.B \-I\fIincludedir\fP +Include directory. +This option tells +.B makedepend +to prepend +.I includedir +to its list of directories to search when it encounters +a +.I #include +directive. +By default, +.B makedepend +only searches the standard include directories (usually /usr/include +and possibly a compiler-dependent directory). +.TP 5 +.B \-Y\fIincludedir\fP +Replace all of the standard include directories with the single specified +include directory; you can omit the +.I includedir +to simply prevent searching the standard include directories. +.TP 5 +.B \-a +Append the dependencies to the end of the file instead of replacing them. +.TP 5 +.B \-f\fImakefile\fP +Filename. +This allows you to specify an alternate makefile in which +.B makedepend +can place its output. +Specifying ``\-'' as the file name (i.e., \fB\-f\-\fP) sends the +output to standard output instead of modifying an existing file. +.TP 5 +.B \-include \fIfile\fP +Process file as input, and include all the resulting output +before processing the regular input file. This has the same +affect as if the specified file is an include statement that +appears before the very first line of the regular input file. +.TP 5 +.B \-o\fIobjsuffix\fP +Object file suffix. +Some systems may have object files whose suffix is something other +than ``.o''. +This option allows you to specify another suffix, such as +``.b'' with +.I \-o.b +or ``:obj'' +with +.I \-o:obj +and so forth. +.TP 5 +.B \-p\fIobjprefix\fP +Object file prefix. +The prefix is prepended to the name of the object file. This is +usually used to designate a different directory for the object file. +The default is the empty string. +.TP 5 +.B \-s\fIstring\fP +Starting string delimiter. +This option permits you to specify +a different string for +.B makedepend +to look for in the makefile. +.TP 5 +.B \-w\fIwidth\fP +Line width. +Normally, +.B makedepend +will ensure that every output line that it writes will be no wider than +78 characters for the sake of readability. +This option enables you to change this width. +.TP 5 +.B \-v +Verbose operation. +This option causes +.B makedepend +to emit the list of files included by each input file. +.TP 5 +.B \-m +Warn about multiple inclusion. +This option causes +.B makedepend +to produce a warning if any input file includes another file more than +once. In previous versions of +.B makedepend +this was the default behavior; the default has been changed to better +match the behavior of the C compiler, which does not consider multiple +inclusion to be an error. This option is provided for backward +compatibility, and to aid in debugging problems related to multiple +inclusion. +.TP 5 +.B "\-\^\- \fIoptions\fP \-\^\-" +If +.B makedepend +encounters a double hyphen (\-\^\-) in the argument list, +then any unrecognized argument following it +will be silently ignored; a second double hyphen terminates this +special treatment. +In this way, +.B makedepend +can be made to safely ignore esoteric compiler arguments that might +normally be found in a CFLAGS +.B make +macro (see the +.B EXAMPLE +section above). +All options that +.B makedepend +recognizes and appear between the pair of double hyphens +are processed normally. +.SH ALGORITHM +The approach used in this program enables it to run an order of magnitude +faster than any other ``dependency generator'' I have ever seen. +Central to this performance are two assumptions: +that all files compiled by a single +makefile will be compiled with roughly the same +.I \-I +and +.I \-D +options; +and that most files in a single directory will include largely the +same files. +.PP +Given these assumptions, +.B makedepend +expects to be called once for each makefile, with +all source files that are maintained by the +makefile appearing on the command line. +It parses each source and include +file exactly once, maintaining an internal symbol table +for each. +Thus, the first file on the command line will take an amount of time +proportional to the amount of time that a normal C preprocessor takes. +But on subsequent files, if it encounters an include file +that it has already parsed, it does not parse it again. +.PP +For example, +imagine you are compiling two files, +.I file1.c +and +.I file2.c, +they each include the header file +.I header.h, +and the file +.I header.h +in turn includes the files +.I def1.h +and +.I def2.h. +When you run the command +.sp + makedepend\0file1.c\0file2.c +.sp +.B makedepend +will parse +.I file1.c +and consequently, +.I header.h +and then +.I def1.h +and +.I def2.h. +It then decides that the dependencies for this file are +.sp + file1.o:\0header.h\0def1.h\0def2.h +.sp +But when the program parses +.I file2.c +and discovers that it, too, includes +.I header.h, +it does not parse the file, +but simply adds +.I header.h, +.I def1.h +and +.I def2.h +to the list of dependencies for +.I file2.o. +.SH "SEE ALSO" +cc(1), make(1) +.SH BUGS +.B makedepend +parses, but does not currently evaluate, the SVR4 #predicate(token-list) +preprocessor expression; such expressions are simply assumed to be true. +This may cause the wrong +.I #include +directives to be evaluated. +.PP +Imagine you are parsing two files, +say +.I file1.c +and +.I file2.c, +each includes the file +.I def.h. +The list of files that +.I def.h +includes might truly be different when +.I def.h +is included by +.I file1.c +than when it is included by +.I file2.c. +But once +.B makedepend +arrives at a list of dependencies for a file, +it is cast in concrete. +.SH AUTHOR +Todd Brunhoff, Tektronix, Inc. and MIT Project Athena diff --git a/ape-server/deps/js/src/config/mkdepend/parse.c b/ape-server/deps/js/src/config/mkdepend/parse.c new file mode 100755 index 0000000..968d2c4 --- /dev/null +++ b/ape-server/deps/js/src/config/mkdepend/parse.c @@ -0,0 +1,686 @@ +/* $Xorg: parse.c,v 1.6 2001/02/09 02:03:16 xorgcvs Exp $ */ +/* + +Copyright (c) 1993, 1994, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + +*/ +/* $XFree86: xc/config/makedepend/parse.c,v 1.12 2002/02/26 05:09:10 tsi Exp $ */ + +#include "def.h" + +extern char *directives[]; +extern struct inclist inclist[ MAXFILES ], + *inclistnext, + maininclist; +extern char *includedirs[ ], + **includedirsnext; + +static int deftype (char *line, struct filepointer *filep, + struct inclist *file_red, struct inclist *file, + int parse_it); +static int zero_value(char *filename, char *exp, struct filepointer *filep, + struct inclist *file_red); +static int merge2defines(struct inclist *file1, struct inclist *file2); + +static int +gobble(struct filepointer *filep, struct inclist *file, + struct inclist *file_red) +{ + char *line; + int type; + + while ((line = getnextline(filep))) { + switch(type = deftype(line, filep, file_red, file, FALSE)) { + case IF: + case IFFALSE: + case IFGUESSFALSE: + case IFDEF: + case IFNDEF: + type = gobble(filep, file, file_red); + while ((type == ELIF) || (type == ELIFFALSE) || + (type == ELIFGUESSFALSE)) + type = gobble(filep, file, file_red); + if (type == ELSE) + (void)gobble(filep, file, file_red); + break; + case ELSE: + case ENDIF: + debug(0,("%s, line %d: #%s\n", + file->i_file, filep->f_line, + directives[type])); + return(type); + case DEFINE: + case UNDEF: + case INCLUDE: + case INCLUDEDOT: + case PRAGMA: + case ERROR: + case IDENT: + case SCCS: + case EJECT: + case WARNING: + case INCLUDENEXT: + case INCLUDENEXTDOT: + break; + case ELIF: + case ELIFFALSE: + case ELIFGUESSFALSE: + return(type); + case -1: + warning("%s", file_red->i_file); + if (file_red != file) + warning1(" (reading %s)", file->i_file); + warning1(", line %d: unknown directive == \"%s\"\n", + filep->f_line, line); + break; + } + } + return(-1); +} + +/* + * Decide what type of # directive this line is. + */ +static int +deftype (char *line, struct filepointer *filep, + struct inclist *file_red, struct inclist *file, int parse_it) +{ + register char *p; + char *directive, savechar, *q; + register int ret; + + /* + * Parse the directive... + */ + directive=line+1; + while (*directive == ' ' || *directive == '\t') + directive++; + + p = directive; + while ((*p == '_') || (*p >= 'a' && *p <= 'z')) + p++; + savechar = *p; + *p = '\0'; + ret = match(directive, directives); + *p = savechar; + + /* If we don't recognize this compiler directive or we happen to just + * be gobbling up text while waiting for an #endif or #elif or #else + * in the case of an #elif we must check the zero_value and return an + * ELIF or an ELIFFALSE. + */ + + if (ret == ELIF && !parse_it) + { + while (*p == ' ' || *p == '\t') + p++; + /* + * parse an expression. + */ + debug(0,("%s, line %d: #elif %s ", + file->i_file, filep->f_line, p)); + ret = zero_value(file->i_file, p, filep, file_red); + if (ret != IF) + { + debug(0,("false...\n")); + if (ret == IFFALSE) + return(ELIFFALSE); + else + return(ELIFGUESSFALSE); + } + else + { + debug(0,("true...\n")); + return(ELIF); + } + } + + if (ret < 0 || ! parse_it) + return(ret); + + /* + * now decide how to parse the directive, and do it. + */ + while (*p == ' ' || *p == '\t') + p++; + q = p + strlen(p); + do { + q--; + } while (*q == ' ' || *q == '\t'); + q[1] = '\0'; + switch (ret) { + case IF: + /* + * parse an expression. + */ + ret = zero_value(file->i_file, p, filep, file_red); + debug(0,("%s, line %d: %s #if %s\n", + file->i_file, filep->f_line, ret?"false":"true", p)); + break; + case IFDEF: + case IFNDEF: + debug(0,("%s, line %d: #%s %s\n", + file->i_file, filep->f_line, directives[ret], p)); + case UNDEF: + /* + * separate the name of a single symbol. + */ + while (isalnum(*p) || *p == '_') + *line++ = *p++; + *line = '\0'; + break; + case INCLUDE: + case INCLUDENEXT: + debug(2,("%s, line %d: #include%s %s\n", + file->i_file, filep->f_line, + (ret == INCLUDE) ? "" : "_next", p)); + + /* Support ANSI macro substitution */ + while (1) { + struct symtab **sym; + + if (!*p || *p == '"' || *p == '<') + break; + + sym = isdefined(p, file_red, NULL); + if (!sym) + break; + + p = (*sym)->s_value; + debug(3,("%s : #includes SYMBOL %s = %s\n", + file->i_incstring, + (*sym) -> s_name, + (*sym) -> s_value)); + /* mark file as having included a 'soft include' */ + file->i_flags |= INCLUDED_SYM; + } + + /* + * Separate the name of the include file. + */ + while (*p && *p != '"' && *p != '<') + p++; + if (! *p) + return(-2); + if (*p++ == '"') { + if (ret == INCLUDE) + ret = INCLUDEDOT; + else + ret = INCLUDENEXTDOT; + while (*p && *p != '"') + *line++ = *p++; + } else + while (*p && *p != '>') + *line++ = *p++; + *line = '\0'; + break; + case DEFINE: + /* + * copy the definition back to the beginning of the line. + */ + strcpy (line, p); + break; + case ELSE: + case ENDIF: + case ELIF: + case PRAGMA: + case ERROR: + case IDENT: + case SCCS: + case EJECT: + case WARNING: + debug(0,("%s, line %d: #%s\n", + file->i_file, filep->f_line, directives[ret])); + /* + * nothing to do. + */ + break; + } + return(ret); +} + +struct symtab ** +fdefined(char *symbol, struct inclist *file, struct inclist **srcfile) +{ + struct inclist **ip; + struct symtab **val; + int i; + static int recurse_lvl = 0; + + if (file->i_flags & DEFCHECKED) + return(NULL); + debug(2,("Looking for %s in %s\n", symbol, file->i_file)); + file->i_flags |= DEFCHECKED; + if ((val = slookup(symbol, file))) + debug(1,("%s defined in %s as %s\n", + symbol, file->i_file, (*val)->s_value)); + if (val == NULL && file->i_list) + { + for (ip = file->i_list, i=0; i < file->i_listlen; i++, ip++) + if (file->i_merged[i]==FALSE) { + val = fdefined(symbol, *ip, srcfile); + file->i_merged[i]=merge2defines(file,*ip); + if (val!=NULL) break; + } + } + else if (val != NULL && srcfile != NULL) *srcfile = file; + recurse_lvl--; + file->i_flags &= ~DEFCHECKED; + + return(val); +} + +struct symtab ** +isdefined(char *symbol, struct inclist *file, struct inclist **srcfile) +{ + struct symtab **val; + + if ((val = slookup(symbol, &maininclist))) { + debug(1,("%s defined on command line\n", symbol)); + if (srcfile != NULL) *srcfile = &maininclist; + return(val); + } + if ((val = fdefined(symbol, file, srcfile))) + return(val); + debug(1,("%s not defined in %s\n", symbol, file->i_file)); + return(NULL); +} + +/* + * Return type based on if the #if expression evaluates to 0 + */ +static int +zero_value(char *filename, + char *exp, + struct filepointer *filep, + struct inclist *file_red) +{ + if (cppsetup(filename, exp, filep, file_red)) + return(IFFALSE); + else + return(IF); +} + +void +define2(char *name, char *val, struct inclist *file) +{ + int first, last, below; + register struct symtab **sp = NULL, **dest; + struct symtab *stab; + + /* Make space if it's needed */ + if (file->i_defs == NULL) + { + file->i_defs = (struct symtab **) + malloc(sizeof (struct symtab*) * SYMTABINC); + file->i_ndefs = 0; + } + else if (!(file->i_ndefs % SYMTABINC)) + file->i_defs = (struct symtab **) + realloc(file->i_defs, + sizeof(struct symtab*)*(file->i_ndefs+SYMTABINC)); + + if (file->i_defs == NULL) + fatalerr("malloc()/realloc() failure in insert_defn()\n"); + + below = first = 0; + last = file->i_ndefs - 1; + while (last >= first) + { + /* Fast inline binary search */ + register char *s1; + register char *s2; + register int middle = (first + last) / 2; + + /* Fast inline strchr() */ + s1 = name; + s2 = file->i_defs[middle]->s_name; + while (*s1++ == *s2++) + if (s2[-1] == '\0') break; + + /* If exact match, set sp and break */ + if (*--s1 == *--s2) + { + sp = file->i_defs + middle; + break; + } + + /* If name > i_defs[middle] ... */ + if (*s1 > *s2) + { + below = first; + first = middle + 1; + } + /* else ... */ + else + { + below = last = middle - 1; + } + } + + /* Search is done. If we found an exact match to the symbol name, + just replace its s_value */ + if (sp != NULL) + { + debug(1,("redefining %s from %s to %s in file %s\n", + name, (*sp)->s_value, val, file->i_file)); + free((*sp)->s_value); + (*sp)->s_value = copy(val); + return; + } + + sp = file->i_defs + file->i_ndefs++; + dest = file->i_defs + below + 1; + while (sp > dest) + { + *sp = sp[-1]; + sp--; + } + stab = (struct symtab *) malloc(sizeof (struct symtab)); + if (stab == NULL) + fatalerr("malloc()/realloc() failure in insert_defn()\n"); + + debug(1,("defining %s to %s in file %s\n", name, val, file->i_file)); + stab->s_name = copy(name); + stab->s_value = copy(val); + *sp = stab; +} + +void +define(char *def, struct inclist *file) +{ + char *val; + + /* Separate symbol name and its value */ + val = def; + while (isalnum(*val) || *val == '_') + val++; + if (*val) + *val++ = '\0'; + while (*val == ' ' || *val == '\t') + val++; + + if (!*val) + val = "1"; + define2(def, val, file); +} + +struct symtab ** +slookup(char *symbol, struct inclist *file) +{ + register int first = 0; + register int last = file->i_ndefs - 1; + + if (file) while (last >= first) + { + /* Fast inline binary search */ + register char *s1; + register char *s2; + register int middle = (first + last) / 2; + + /* Fast inline strchr() */ + s1 = symbol; + s2 = file->i_defs[middle]->s_name; + while (*s1++ == *s2++) + if (s2[-1] == '\0') break; + + /* If exact match, we're done */ + if (*--s1 == *--s2) + { + return file->i_defs + middle; + } + + /* If symbol > i_defs[middle] ... */ + if (*s1 > *s2) + { + first = middle + 1; + } + /* else ... */ + else + { + last = middle - 1; + } + } + return(NULL); +} + +static int +merge2defines(struct inclist *file1, struct inclist *file2) +{ + int i; + + if ((file1==NULL) || (file2==NULL) || + !(file2->i_flags & FINISHED)) + return 0; + + for (i=0; i < file2->i_listlen; i++) + if (file2->i_merged[i]==FALSE) + return 0; + + { + int first1 = 0; + int last1 = file1->i_ndefs - 1; + + int first2 = 0; + int last2 = file2->i_ndefs - 1; + + int first=0; + struct symtab** i_defs = NULL; + int deflen=file1->i_ndefs+file2->i_ndefs; + + debug(2,("merging %s into %s\n", + file2->i_file, file1->i_file)); + + if (deflen>0) + { + /* make sure deflen % SYMTABINC == 0 is still true */ + deflen += (SYMTABINC - deflen % SYMTABINC) % SYMTABINC; + i_defs=(struct symtab**) + malloc(deflen*sizeof(struct symtab*)); + if (i_defs==NULL) return 0; + } + + while ((last1 >= first1) && (last2 >= first2)) + { + char *s1=file1->i_defs[first1]->s_name; + char *s2=file2->i_defs[first2]->s_name; + + if (strcmp(s1,s2) < 0) + i_defs[first++]=file1->i_defs[first1++]; + else if (strcmp(s1,s2) > 0) + i_defs[first++]=file2->i_defs[first2++]; + else /* equal */ + { + i_defs[first++]=file2->i_defs[first2++]; + first1++; + } + } + while (last1 >= first1) + { + i_defs[first++]=file1->i_defs[first1++]; + } + while (last2 >= first2) + { + i_defs[first++]=file2->i_defs[first2++]; + } + + if (file1->i_defs) free(file1->i_defs); + file1->i_defs=i_defs; + file1->i_ndefs=first; + + return 1; + } +} + +void +undefine(char *symbol, struct inclist *file) +{ + register struct symtab **ptr; + struct inclist *srcfile; + while ((ptr = isdefined(symbol, file, &srcfile)) != NULL) + { + srcfile->i_ndefs--; + for (; ptr < srcfile->i_defs + srcfile->i_ndefs; ptr++) + *ptr = ptr[1]; + } +} + +int +find_includes(struct filepointer *filep, struct inclist *file, + struct inclist *file_red, int recursion, boolean failOK) +{ + struct inclist *inclistp; + char **includedirsp; + register char *line; + register int type; + boolean recfailOK; + + while ((line = getnextline(filep))) { + switch(type = deftype(line, filep, file_red, file, TRUE)) { + case IF: + doif: + type = find_includes(filep, file, + file_red, recursion+1, failOK); + while ((type == ELIF) || (type == ELIFFALSE) || + (type == ELIFGUESSFALSE)) + type = gobble(filep, file, file_red); + if (type == ELSE) + gobble(filep, file, file_red); + break; + case IFFALSE: + case IFGUESSFALSE: + doiffalse: + if (type == IFGUESSFALSE || type == ELIFGUESSFALSE) + recfailOK = TRUE; + else + recfailOK = failOK; + type = gobble(filep, file, file_red); + if (type == ELSE) + find_includes(filep, file, + file_red, recursion+1, recfailOK); + else + if (type == ELIF) + goto doif; + else + if ((type == ELIFFALSE) || (type == ELIFGUESSFALSE)) + goto doiffalse; + break; + case IFDEF: + case IFNDEF: + if ((type == IFDEF && isdefined(line, file_red, NULL)) + || (type == IFNDEF && !isdefined(line, file_red, NULL))) { + debug(1,(type == IFNDEF ? + "line %d: %s !def'd in %s via %s%s\n" : "", + filep->f_line, line, + file->i_file, file_red->i_file, ": doit")); + type = find_includes(filep, file, + file_red, recursion+1, failOK); + while (type == ELIF || type == ELIFFALSE || type == ELIFGUESSFALSE) + type = gobble(filep, file, file_red); + if (type == ELSE) + gobble(filep, file, file_red); + } + else { + debug(1,(type == IFDEF ? + "line %d: %s !def'd in %s via %s%s\n" : "", + filep->f_line, line, + file->i_file, file_red->i_file, ": gobble")); + type = gobble(filep, file, file_red); + if (type == ELSE) + find_includes(filep, file, + file_red, recursion+1, failOK); + else if (type == ELIF) + goto doif; + else if (type == ELIFFALSE || type == ELIFGUESSFALSE) + goto doiffalse; + } + break; + case ELSE: + case ELIFFALSE: + case ELIFGUESSFALSE: + case ELIF: + if (!recursion) + gobble(filep, file, file_red); + case ENDIF: + if (recursion) + return(type); + case DEFINE: + define(line, file); + break; + case UNDEF: + if (!*line) { + warning("%s", file_red->i_file); + if (file_red != file) + warning1(" (reading %s)", file->i_file); + warning1(", line %d: incomplete undef == \"%s\"\n", + filep->f_line, line); + break; + } + undefine(line, file_red); + break; + case INCLUDE: + case INCLUDEDOT: + case INCLUDENEXT: + case INCLUDENEXTDOT: + inclistp = inclistnext; + includedirsp = includedirsnext; + debug(2,("%s, reading %s, includes %s\n", + file_red->i_file, file->i_file, line)); + add_include(filep, file, file_red, line, type, failOK); + inclistnext = inclistp; + includedirsnext = includedirsp; + break; + case ERROR: + case WARNING: + warning("%s", file_red->i_file); + if (file_red != file) + warning1(" (reading %s)", file->i_file); + warning1(", line %d: %s\n", + filep->f_line, line); + break; + + case PRAGMA: + case IDENT: + case SCCS: + case EJECT: + break; + case -1: + warning("%s", file_red->i_file); + if (file_red != file) + warning1(" (reading %s)", file->i_file); + warning1(", line %d: unknown directive == \"%s\"\n", + filep->f_line, line); + break; + case -2: + warning("%s", file_red->i_file); + if (file_red != file) + warning1(" (reading %s)", file->i_file); + warning1(", line %d: incomplete include == \"%s\"\n", + filep->f_line, line); + break; + } + } + file->i_flags |= FINISHED; + debug(2,("finished with %s\n", file->i_file)); + return(-1); +} diff --git a/ape-server/deps/js/src/config/mkdepend/pr.c b/ape-server/deps/js/src/config/mkdepend/pr.c new file mode 100755 index 0000000..e864793 --- /dev/null +++ b/ape-server/deps/js/src/config/mkdepend/pr.c @@ -0,0 +1,124 @@ +/* $Xorg: pr.c,v 1.4 2001/02/09 02:03:16 xorgcvs Exp $ */ +/* + +Copyright (c) 1993, 1994, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + +*/ +/* $XFree86: xc/config/makedepend/pr.c,v 1.5 2001/12/14 19:53:21 dawes Exp $ */ + +#include "def.h" + +extern struct inclist inclist[ MAXFILES ], + *inclistp; +extern char *objprefix; +extern char *objsuffix; +extern int width; +extern boolean printed; +extern boolean verbose; +extern boolean show_where_not; + +void +add_include(struct filepointer *filep, struct inclist *file, + struct inclist *file_red, char *include, int type, + boolean failOK) +{ + register struct inclist *newfile; + register struct filepointer *content; + + /* + * First decide what the pathname of this include file really is. + */ + newfile = inc_path(file->i_file, include, type); + if (newfile == NULL) { + if (failOK) + return; + if (file != file_red) + warning("%s (reading %s, line %d): ", + file_red->i_file, file->i_file, filep->f_line); + else + warning("%s, line %d: ", file->i_file, filep->f_line); + warning1("cannot find include file \"%s\"\n", include); + show_where_not = TRUE; + newfile = inc_path(file->i_file, include, type); + show_where_not = FALSE; + } + + if (newfile) { + included_by(file, newfile); + if (!(newfile->i_flags & SEARCHED)) { + newfile->i_flags |= SEARCHED; + content = getfile(newfile->i_file); + find_includes(content, newfile, file_red, 0, failOK); + freefile(content); + } + } +} + +static void +pr(struct inclist *ip, char *file, char *base) +{ + static char *lastfile; + static int current_len; + register int len, i; + char buf[ BUFSIZ ]; + + printed = TRUE; + len = strlen(ip->i_file)+1; + if (current_len + len > width || file != lastfile) { + lastfile = file; + sprintf(buf, "\n%s%s%s: %s", objprefix, base, objsuffix, + ip->i_file); + len = current_len = strlen(buf); + } + else { + buf[0] = ' '; + strcpy(buf+1, ip->i_file); + current_len += len; + } + fwrite(buf, len, 1, stdout); + + /* + * If verbose is set, then print out what this file includes. + */ + if (! verbose || ip->i_list == NULL || ip->i_flags & NOTIFIED) + return; + ip->i_flags |= NOTIFIED; + lastfile = NULL; + printf("\n# %s includes:", ip->i_file); + for (i=0; ii_listlen; i++) + printf("\n#\t%s", ip->i_list[ i ]->i_incstring); +} + +void +recursive_pr_include(struct inclist *head, char *file, char *base) +{ + int i; + + if (head->i_flags & MARKED) + return; + head->i_flags |= MARKED; + if (head->i_file != file) + pr(head, file, base); + for (i=0; ii_listlen; i++) + recursive_pr_include(head->i_list[ i ], file, base); +} diff --git a/ape-server/deps/js/src/config/myconfig.mk b/ape-server/deps/js/src/config/myconfig.mk new file mode 100755 index 0000000..e69de29 diff --git a/ape-server/deps/js/src/config/myrules.mk b/ape-server/deps/js/src/config/myrules.mk new file mode 100755 index 0000000..e69de29 diff --git a/ape-server/deps/js/src/config/nfspwd.pl b/ape-server/deps/js/src/config/nfspwd.pl new file mode 100755 index 0000000..2f0e4fb --- /dev/null +++ b/ape-server/deps/js/src/config/nfspwd.pl @@ -0,0 +1,50 @@ +#! perl +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +require "fastcwd.pl"; + +$_ = &fastcwd; +if (m@^/[uh]/@o || s@^/tmp_mnt/@/@o) { + print("$_\n"); +} elsif ((($user, $rest) = m@^/usr/people/(\w+)/(.*)@o) + && readlink("/u/$user") eq "/usr/people/$user") { + print("/u/$user/$rest\n"); +} else { + chop($host = `hostname`); + print("/h/$host$_\n"); +} diff --git a/ape-server/deps/js/src/config/nsinstall.c b/ape-server/deps/js/src/config/nsinstall.c new file mode 100755 index 0000000..b6ca0e3 --- /dev/null +++ b/ape-server/deps/js/src/config/nsinstall.c @@ -0,0 +1,481 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* +** Netscape portable install command. +** +** Brendan Eich, 7/20/95 +*/ +#include /* OSF/1 requires this before grp.h, so put it first */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pathsub.h" + +#ifdef HAVE_GETOPT_H +#include +#endif + +#ifdef SUNOS4 +#include "sunos4.h" +#endif + +#ifdef NEXTSTEP +#include +#endif + +#ifdef __QNX__ +#include +#endif + +#ifdef NEED_S_ISLNK +#if !defined(S_ISLNK) && defined(S_IFLNK) +#define S_ISLNK(a) (((a) & S_IFMT) == S_IFLNK) +#endif +#endif + +#ifndef _DIRECTORY_SEPARATOR +#define _DIRECTORY_SEPARATOR "/" +#endif /* _DIRECTORY_SEPARATOR */ + +#ifdef NEED_FCHMOD_PROTO +extern int fchmod(int fildes, mode_t mode); +#endif + +static void +usage(void) +{ + fprintf(stderr, + "usage: %s [-C cwd] [-L linkprefix] [-m mode] [-o owner] [-g group]\n" + " %*s [-DdltR] file [file ...] directory\n", + program, (int) strlen(program), ""); + exit(2); +} + +static int +mkdirs(char *path, mode_t mode) +{ + char *cp; + struct stat sb; + int res; + int l; + + /* strip trailing "/." */ + l = strlen(path); + if(l > 1 && path[l - 1] == '.' && path[l - 2] == '/') + path[l - 2] = 0; + + while (*path == '/' && path[1] == '/') + path++; + for (cp = strrchr(path, '/'); cp && cp != path && *(cp - 1) == '/'; cp--); + if (cp && cp != path) { + *cp = '\0'; + if ((lstat(path, &sb) < 0 || !S_ISDIR(sb.st_mode)) && + mkdirs(path, mode) < 0) { + return -1; + } + *cp = '/'; + } + + res = mkdir(path, mode); + if ((res != 0) && (errno == EEXIST)) + return 0; + else + return res; +} + +static uid_t +touid(char *owner) +{ + struct passwd *pw; + uid_t uid; + char *cp; + + pw = getpwnam(owner); + if (pw) + return pw->pw_uid; + uid = strtol(owner, &cp, 0); + if (uid == 0 && cp == owner) + fail("cannot find uid for %s", owner); + return uid; +} + +static gid_t +togid(char *group) +{ + struct group *gr; + gid_t gid; + char *cp; + + gr = getgrnam(group); + if (gr) + return gr->gr_gid; + gid = strtol(group, &cp, 0); + if (gid == 0 && cp == group) + fail("cannot find gid for %s", group); + return gid; +} + +static void +copyfile( char *name, char *toname, mode_t mode, char *group, char *owner, + int dotimes, uid_t uid, gid_t gid ) +{ + int fromfd, tofd = -1, cc, wc, exists; + char buf[BUFSIZ], *bp; + struct stat sb, tosb; + struct utimbuf utb; + + exists = (lstat(toname, &tosb) == 0); + + fromfd = open(name, O_RDONLY); + if (fromfd < 0 || fstat(fromfd, &sb) < 0) + fail("cannot access %s", name); + if (exists) { + if (S_ISREG(tosb.st_mode)) { + /* See if we can open it. This is more reliable than 'access'. */ + tofd = open(toname, O_CREAT | O_WRONLY, 0666); + } + if (tofd < 0) { + (void) (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname); + } + } + if (tofd < 0) { + tofd = open(toname, O_CREAT | O_WRONLY, 0666); + if (tofd < 0) + fail("cannot create %s", toname); + } + + bp = buf; + while ((cc = read(fromfd, bp, sizeof buf)) > 0) + { + while ((wc = write(tofd, bp, (unsigned int)cc)) > 0) + { + if ((cc -= wc) == 0) + break; + bp += wc; + } + if (wc < 0) + fail("cannot write to %s", toname); + } + if (cc < 0) + fail("cannot read from %s", name); + + if (ftruncate(tofd, sb.st_size) < 0) + fail("cannot truncate %s", toname); +#if !defined(VMS) + if (dotimes) + { + utb.actime = sb.st_atime; + utb.modtime = sb.st_mtime; + if (utime(toname, &utb) < 0) + fail("cannot set times of %s", toname); + } +#ifdef HAVE_FCHMOD + if (fchmod(tofd, mode) < 0) +#else + if (chmod(toname, mode) < 0) +#endif + fail("cannot change mode of %s", toname); +#endif + if ((owner || group) && fchown(tofd, uid, gid) < 0) + fail("cannot change owner of %s", toname); + + /* Must check for delayed (NFS) write errors on close. */ + if (close(tofd) < 0) + fail("cannot write to %s", toname); + close(fromfd); +#if defined(VMS) + if (chmod(toname, (mode & (S_IREAD | S_IWRITE))) < 0) + fail("cannot change mode of %s", toname); + if (dotimes) + { + utb.actime = sb.st_atime; + utb.modtime = sb.st_mtime; + if (utime(toname, &utb) < 0) + fail("cannot set times of %s", toname); + } +#endif +} + +static void +copydir( char *from, char *to, mode_t mode, char *group, char *owner, + int dotimes, uid_t uid, gid_t gid) +{ + int i; + DIR *dir; + struct dirent *ep; + struct stat sb; + char *base, *destdir, *direntry, *destentry; + + base = xbasename(from); + + /* create destination directory */ + destdir = xmalloc((unsigned int)(strlen(to) + 1 + strlen(base) + 1)); + sprintf(destdir, "%s%s%s", to, _DIRECTORY_SEPARATOR, base); + if (mkdirs(destdir, mode) != 0) { + fail("cannot make directory %s\n", destdir); + return; + } + + dir = opendir(from); + + direntry = xmalloc((unsigned int)PATH_MAX); + destentry = xmalloc((unsigned int)PATH_MAX); + + while ((ep = readdir(dir))) + { + if (strcmp(ep->d_name, ".") == 0 || strcmp(ep->d_name, "..") == 0) + continue; + + sprintf(direntry, "%s/%s", from, ep->d_name); + sprintf(destentry, "%s%s%s", destdir, _DIRECTORY_SEPARATOR, ep->d_name); + + if (stat(direntry, &sb) == 0 && S_ISDIR(sb.st_mode)) + copydir( direntry, destdir, mode, group, owner, dotimes, uid, gid ); + else + copyfile( direntry, destentry, mode, group, owner, dotimes, uid, gid ); + } + + free(direntry); + free(destentry); + closedir(dir); +} + +int +main(int argc, char **argv) +{ + int onlydir, dodir, dolink, dorelsymlink, dotimes, opt, len, lplen, tdlen, bnlen, exists, fromfd, tofd, cc, wc; + mode_t mode = 0755; + char *linkprefix, *owner, *group, *cp, *cwd, *todir, *toname, *name, *base, *linkname, *bp, buf[BUFSIZ]; + uid_t uid; + gid_t gid; + struct stat sb, tosb, fromsb; + struct utimbuf utb; + + program = argv[0]; + cwd = linkname = linkprefix = owner = group = 0; + onlydir = dodir = dolink = dorelsymlink = dotimes = lplen = 0; + + while ((opt = getopt(argc, argv, "C:DdlL:Rm:o:g:t")) != EOF) { + switch (opt) { + case 'C': + cwd = optarg; + break; + case 'D': + onlydir = 1; + break; + case 'd': + dodir = 1; + break; + case 'l': + dolink = 1; + break; + case 'L': + linkprefix = optarg; + lplen = strlen(linkprefix); + dolink = 1; + break; + case 'R': + dolink = dorelsymlink = 1; + break; + case 'm': + mode = strtoul(optarg, &cp, 8); + if (mode == 0 && cp == optarg) + usage(); + break; + case 'o': + owner = optarg; + break; + case 'g': + group = optarg; + break; + case 't': + dotimes = 1; + break; + default: + usage(); + } + } + + argc -= optind; + argv += optind; + if (argc < 2 - onlydir) + usage(); + + todir = argv[argc-1]; + if ((stat(todir, &sb) < 0 || !S_ISDIR(sb.st_mode)) && + mkdirs(todir, 0777) < 0) { + fail("cannot make directory %s", todir); + } + if (onlydir) + return 0; + + if (!cwd) { +#ifndef NEEDS_GETCWD +#ifndef GETCWD_CANT_MALLOC + cwd = getcwd(0, PATH_MAX); +#else + cwd = malloc(PATH_MAX + 1); + cwd = getcwd(cwd, PATH_MAX); +#endif +#else + cwd = malloc(PATH_MAX + 1); + cwd = getwd(cwd); +#endif + } + + xchdir(todir); +#ifndef NEEDS_GETCWD +#ifndef GETCWD_CANT_MALLOC + todir = getcwd(0, PATH_MAX); +#else + todir = malloc(PATH_MAX + 1); + todir = getcwd(todir, PATH_MAX); +#endif +#else + todir = malloc(PATH_MAX + 1); + todir = getwd(todir); +#endif + tdlen = strlen(todir); + xchdir(cwd); + tdlen = strlen(todir); + + uid = owner ? touid(owner) : (uid_t)(-1); + gid = group ? togid(group) : (gid_t)(-1); + + while (--argc > 0) { + name = *argv++; + len = strlen(name); + base = xbasename(name); + bnlen = strlen(base); + toname = xmalloc((unsigned int)(tdlen + 1 + bnlen + 1)); + sprintf(toname, "%s%s%s", todir, _DIRECTORY_SEPARATOR, base); + exists = (lstat(toname, &tosb) == 0); + + if (dodir) { + /* -d means create a directory, always */ + if (exists && !S_ISDIR(tosb.st_mode)) { + (void) unlink(toname); + exists = 0; + } + if (!exists && mkdir(toname, mode) < 0) + fail("cannot make directory %s", toname); + if ((owner || group) && chown(toname, uid, gid) < 0) + fail("cannot change owner of %s", toname); + } else if (dolink) { + if (access(name, R_OK) != 0) { + fail("cannot access %s", name); + } + if (*name == '/') { + /* source is absolute pathname, link to it directly */ + linkname = 0; + } else { + if (linkprefix) { + /* -L implies -l and prefixes names with a $cwd arg. */ + len += lplen + 1; + linkname = xmalloc((unsigned int)(len + 1)); + sprintf(linkname, "%s/%s", linkprefix, name); + } else if (dorelsymlink) { + /* Symlink the relative path from todir to source name. */ + linkname = xmalloc(PATH_MAX); + + if (*todir == '/') { + /* todir is absolute: skip over common prefix. */ + lplen = relatepaths(todir, cwd, linkname); + strcpy(linkname + lplen, name); + } else { + /* todir is named by a relative path: reverse it. */ + reversepath(todir, name, len, linkname); + xchdir(cwd); + } + + len = strlen(linkname); + } + name = linkname; + } + + /* Check for a pre-existing symlink with identical content. */ + if ((exists && (!S_ISLNK(tosb.st_mode) || + readlink(toname, buf, sizeof buf) != len || + strncmp(buf, name, (unsigned int)len) != 0)) || + ((stat(name, &fromsb) == 0) && + (fromsb.st_mtime > tosb.st_mtime))) { + (void) (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname); + exists = 0; + } + if (!exists && symlink(name, toname) < 0) + fail("cannot make symbolic link %s", toname); +#ifdef HAVE_LCHOWN + if ((owner || group) && lchown(toname, uid, gid) < 0) + fail("cannot change owner of %s", toname); +#endif + + if (linkname) { + free(linkname); + linkname = 0; + } + } else { + /* Copy from name to toname, which might be the same file. */ + if( stat(name, &sb) == 0 && S_IFDIR & sb.st_mode ) + { + /* then is directory: must explicitly create destination dir */ + /* and manually copy files over */ + copydir( name, todir, mode, group, owner, dotimes, uid, gid ); + } + else + { + copyfile(name, toname, mode, group, owner, dotimes, uid, gid); + } + } + + free(toname); + } + + free(cwd); + free(todir); + return 0; +} diff --git a/ape-server/deps/js/src/config/nsinstall.exe.manifest b/ape-server/deps/js/src/config/nsinstall.exe.manifest new file mode 100755 index 0000000..62290f9 --- /dev/null +++ b/ape-server/deps/js/src/config/nsinstall.exe.manifest @@ -0,0 +1,17 @@ + + + +nsinstall + + + + + + + + diff --git a/ape-server/deps/js/src/config/nsinstall.py b/ape-server/deps/js/src/config/nsinstall.py new file mode 100755 index 0000000..7c990c6 --- /dev/null +++ b/ape-server/deps/js/src/config/nsinstall.py @@ -0,0 +1,155 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla. +# +# The Initial Developer of the Original Code is +# the Mozilla Foundation. +# Portions created by the Initial Developer are Copyright (C) 2007 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Axel Hecht +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# This is a partial python port of nsinstall. +# It's intended to be used when there's no natively compile nsinstall +# available, and doesn't intend to be fully equivalent. +# Its major use is for l10n repackaging on systems that don't have +# a full build environment set up. +# The basic limitation is, it doesn't even try to link and ignores +# all related options. + +from optparse import OptionParser +import os +import os.path +import sys +import shutil + +def nsinstall(argv): + usage = "usage: %prog [options] arg1 [arg2 ...] target-directory" + p = OptionParser(usage=usage) + + p.add_option('-D', action="store_true", + help="Create a single directory only") + p.add_option('-t', action="store_true", + help="Preserve time stamp") + p.add_option('-m', action="store", + help="Set mode", metavar="mode") + p.add_option('-d', action="store_true", + help="Create directories in target") + p.add_option('-R', action="store_true", + help="Use relative symbolic links (ignored)") + p.add_option('-l', action="store_true", + help="Create link (ignored)") + p.add_option('-L', action="store", metavar="linkprefix", + help="Link prefix (ignored)") + + # The remaining arguments are not used in our tree, thus they're not + # implented. + def BadArg(option, opt, value, parser): + parser.error('option not supported: %s' % opt) + + p.add_option('-C', action="callback", metavar="CWD", + callback=BadArg, + help="NOT SUPPORTED") + p.add_option('-o', action="callback", callback=BadArg, + help="Set owner (NOT SUPPORTED)", metavar="owner") + p.add_option('-g', action="callback", callback=BadArg, + help="Set group (NOT SUPPORTED)", metavar="group") + + (options, args) = p.parse_args(argv) + + if options.m: + # mode is specified + try: + options.m = int(options.m, 8) + except: + sys.stderr.write('nsinstall: ' + options.m + ' is not a valid mode\n') + return 1 + + # just create one directory? + if options.D: + if len(args) != 1: + return 1 + if os.path.exists(args[0]): + if not os.path.isdir(args[0]): + sys.stderr.write('nsinstall: ' + args[0] + ' is not a directory\n') + sys.exit(1) + if options.m: + os.chmod(args[0], options.m) + sys.exit() + if options.m: + os.makedirs(args[0], options.m) + else: + os.makedirs(args[0]) + return 0 + + # nsinstall arg1 [...] directory + if len(args) < 2: + p.error('not enough arguments') + + def copy_all_entries(entries, target): + for e in entries: + dest = os.path.join(target, + os.path.basename(os.path.normpath(e))) + handleTarget(e, dest) + if options.m: + os.chmod(dest, options.m) + + # set up handler + if options.d: + # we're supposed to create directories + def handleTarget(srcpath, targetpath): + # target directory was already created, just use mkdir + os.mkdir(targetpath) + else: + # we're supposed to copy files + def handleTarget(srcpath, targetpath): + if os.path.isdir(srcpath): + if not os.path.exists(targetpath): + os.mkdir(targetpath) + entries = [os.path.join(srcpath, e) for e in os.listdir(srcpath)] + copy_all_entries(entries, targetpath) + # options.t is not relevant for directories + if options.m: + os.chmod(targetpath, options.m) + elif options.t: + shutil.copy2(srcpath, targetpath) + else: + shutil.copy(srcpath, targetpath) + + # the last argument is the target directory + target = args.pop() + # ensure target directory + if not os.path.isdir(target): + os.makedirs(target) + + copy_all_entries(args, target) + return 0 + +if __name__ == '__main__': + sys.exit(nsinstall(sys.argv[1:])) diff --git a/ape-server/deps/js/src/config/nsinstall_win.c b/ape-server/deps/js/src/config/nsinstall_win.c new file mode 100755 index 0000000..d9b723b --- /dev/null +++ b/ape-server/deps/js/src/config/nsinstall_win.c @@ -0,0 +1,711 @@ +/* + * The nsinstall command for Win32 + * + * Our gmake makefiles use the nsinstall command to create the + * object directories or installing headers and libs. This code was originally + * taken from shmsdos.c + */ + +#include +#include +#include +#include +#include +#pragma hdrstop + +/* + * sh_FileFcn -- + * + * A function that operates on a file. The pathname is either + * absolute or relative to the current directory, and contains + * no wildcard characters such as * and ?. Additional arguments + * can be passed to the function via the arg pointer. + */ + +typedef BOOL (*sh_FileFcn)( + wchar_t *pathName, + WIN32_FIND_DATA *fileData, + void *arg); + +static int shellCp (wchar_t **pArgv); +static int shellNsinstall (wchar_t **pArgv); +static int shellMkdir (wchar_t **pArgv); +static BOOL sh_EnumerateFiles(const wchar_t *pattern, const wchar_t *where, + sh_FileFcn fileFcn, void *arg, int *nFiles); +static const char *sh_GetLastErrorMessage(void); +static BOOL sh_DoCopy(wchar_t *srcFileName, DWORD srcFileAttributes, + wchar_t *dstFileName, DWORD dstFileAttributes, + int force, int recursive); + +#define LONGPATH_PREFIX L"\\\\?\\" +#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0])) +#define STR_LEN(a) (ARRAY_LEN(a) - 1) + +/* changes all forward slashes in token to backslashes */ +void changeForwardSlashesToBackSlashes ( wchar_t *arg ) +{ + if ( arg == NULL ) + return; + + while ( *arg ) { + if ( *arg == '/' ) + *arg = '\\'; + arg++; + } +} + +int wmain(int argc, wchar_t *argv[ ]) +{ + return shellNsinstall ( argv + 1 ); +} + +static int +shellNsinstall (wchar_t **pArgv) +{ + int retVal = 0; /* exit status */ + int dirOnly = 0; /* 1 if and only if -D is specified */ + wchar_t **pSrc; + wchar_t **pDst; + + /* + * Process the command-line options. We ignore the + * options except for -D. Some options, such as -m, + * are followed by an argument. We need to skip the + * argument too. + */ + while ( *pArgv && **pArgv == '-' ) { + wchar_t c = (*pArgv)[1]; /* The char after '-' */ + + if ( c == 'D' ) { + dirOnly = 1; + } else if ( c == 'm' ) { + pArgv++; /* skip the next argument */ + } + pArgv++; + } + + if ( !dirOnly ) { + /* There are files to install. Get source files */ + if ( *pArgv ) { + pSrc = pArgv++; + } else { + fprintf( stderr, "nsinstall: not enough arguments\n"); + return 3; + } + } + + /* Get to last token to find destination directory */ + if ( *pArgv ) { + pDst = pArgv++; + if ( dirOnly && *pArgv ) { + fprintf( stderr, "nsinstall: too many arguments with -D\n"); + return 3; + } + } else { + fprintf( stderr, "nsinstall: not enough arguments\n"); + return 3; + } + while ( *pArgv ) + pDst = pArgv++; + + retVal = shellMkdir ( pDst ); + if ( retVal ) + return retVal; + if ( !dirOnly ) + retVal = shellCp ( pSrc ); + return retVal; +} + +static int +shellMkdir (wchar_t **pArgv) +{ + int retVal = 0; /* assume valid return */ + wchar_t *arg; + wchar_t *pArg; + wchar_t path[_MAX_PATH]; + wchar_t tmpPath[_MAX_PATH]; + wchar_t *pTmpPath = tmpPath; + + /* All the options are simply ignored in this implementation */ + while ( *pArgv && **pArgv == '-' ) { + if ( (*pArgv)[1] == 'm' ) { + pArgv++; /* skip the next argument (mode) */ + } + pArgv++; + } + + while ( *pArgv ) { + arg = *pArgv; + changeForwardSlashesToBackSlashes ( arg ); + pArg = arg; + pTmpPath = tmpPath; + while ( 1 ) { + /* create part of path */ + while ( *pArg ) { + *pTmpPath++ = *pArg++; + if ( *pArg == '\\' ) + break; + } + *pTmpPath = '\0'; + + /* check if directory already exists */ + _wgetcwd ( path, _MAX_PATH ); + if ( _wchdir ( tmpPath ) == -1 && + _wmkdir ( tmpPath ) == -1 && // might have hit EEXIST + _wchdir ( tmpPath ) == -1) { // so try again + char buf[2048]; + _snprintf(buf, 2048, "Could not create the directory: %S", + tmpPath); + perror ( buf ); + retVal = 3; + break; + } else { + // get back to the cwd + _wchdir ( path ); + } + if ( *pArg == '\0' ) /* complete path? */ + break; + /* loop for next directory */ + } + + pArgv++; + } + return retVal; +} + +static const char * +sh_GetLastErrorMessage() +{ + static char buf[128]; + + FormatMessageA( + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* default language */ + buf, + sizeof(buf), + NULL + ); + return buf; +} + +/* + * struct sh_FileData -- + * + * A pointer to the sh_FileData structure is passed into sh_RecordFileData, + * which will fill in the fields. + */ + +struct sh_FileData { + wchar_t pathName[_MAX_PATH]; + DWORD dwFileAttributes; +}; + +/* + * sh_RecordFileData -- + * + * Record the pathname and attributes of the file in + * the sh_FileData structure pointed to by arg. + * + * Always return TRUE (successful completion). + * + * This function is intended to be passed into sh_EnumerateFiles + * to see if a certain pattern expands to exactly one file/directory, + * and if so, record its pathname and attributes. + */ + +static BOOL +sh_RecordFileData(wchar_t *pathName, WIN32_FIND_DATA *findData, void *arg) +{ + struct sh_FileData *fData = (struct sh_FileData *) arg; + + wcscpy(fData->pathName, pathName); + fData->dwFileAttributes = findData->dwFileAttributes; + return TRUE; +} + +static BOOL +sh_DoCopy(wchar_t *srcFileName, + DWORD srcFileAttributes, + wchar_t *dstFileName, + DWORD dstFileAttributes, + int force, + int recursive +) +{ + if (dstFileAttributes != 0xFFFFFFFF) { + if ((dstFileAttributes & FILE_ATTRIBUTE_READONLY) && force) { + dstFileAttributes &= ~FILE_ATTRIBUTE_READONLY; + SetFileAttributes(dstFileName, dstFileAttributes); + } + } + + if (srcFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + fprintf(stderr, "nsinstall: %ls is a directory\n", + srcFileName); + return FALSE; + } else { + DWORD r; + wchar_t longSrc[1004] = LONGPATH_PREFIX; + wchar_t longDst[1004] = LONGPATH_PREFIX; + r = GetFullPathName(srcFileName, 1000, longSrc + STR_LEN(LONGPATH_PREFIX), NULL); + if (!r) { + fprintf(stderr, "nsinstall: couldn't get full path of %ls: %s\n", + srcFileName, sh_GetLastErrorMessage()); + return FALSE; + } + r = GetFullPathName(dstFileName, 1000, longDst + ARRAY_LEN(LONGPATH_PREFIX) - 1, NULL); + if (!r) { + fprintf(stderr, "nsinstall: couldn't get full path of %ls: %s\n", + dstFileName, sh_GetLastErrorMessage()); + return FALSE; + } + + if (!CopyFile(longSrc, longDst, FALSE)) { + fprintf(stderr, "nsinstall: cannot copy %ls to %ls: %s\n", + srcFileName, dstFileName, sh_GetLastErrorMessage()); + return FALSE; + } + } + return TRUE; +} + +/* + * struct sh_CpCmdArg -- + * + * A pointer to the sh_CpCmdArg structure is passed into sh_CpFileCmd. + * The sh_CpCmdArg contains information about the cp command, and + * provide a buffer for constructing the destination file name. + */ + +struct sh_CpCmdArg { + int force; /* -f option, ok to overwrite an existing + * read-only destination file */ + int recursive; /* -r or -R option, recursively copy + * directories. Note: this field is not used + * by nsinstall and should always be 0. */ + wchar_t *dstFileName; /* a buffer for constructing the destination + * file name */ + wchar_t *dstFileNameMarker; /* points to where in the dstFileName buffer + * we should write the file component of the + * destination file */ +}; + +/* + * sh_CpFileCmd -- + * + * Copy a file to the destination directory + * + * This function is intended to be passed into sh_EnumerateFiles to + * copy all the files specified by the pattern to the destination + * directory. + * + * Return TRUE if the file is successfully copied, and FALSE otherwise. + */ + +static BOOL +sh_CpFileCmd(wchar_t *pathName, WIN32_FIND_DATA *findData, void *cpArg) +{ + BOOL retVal = TRUE; + struct sh_CpCmdArg *arg = (struct sh_CpCmdArg *) cpArg; + + wcscpy(arg->dstFileNameMarker, findData->cFileName); + return sh_DoCopy(pathName, findData->dwFileAttributes, + arg->dstFileName, GetFileAttributes(arg->dstFileName), + arg->force, arg->recursive); +} + +static int +shellCp (wchar_t **pArgv) +{ + int retVal = 0; + wchar_t **pSrc; + wchar_t **pDst; + struct sh_CpCmdArg arg; + struct sh_FileData dstData; + int dstIsDir = 0; + int n; + + arg.force = 0; + arg.recursive = 0; + arg.dstFileName = dstData.pathName; + arg.dstFileNameMarker = 0; + + while (*pArgv && **pArgv == '-') { + wchar_t *p = *pArgv; + + while (*(++p)) { + if (*p == 'f') { + arg.force = 1; + } + } + pArgv++; + } + + /* the first source file */ + if (*pArgv) { + pSrc = pArgv++; + } else { + fprintf(stderr, "nsinstall: not enough arguments\n"); + return 3; + } + + /* get to the last token to find destination */ + if (*pArgv) { + pDst = pArgv++; + } else { + fprintf(stderr, "nsinstall: not enough arguments\n"); + return 3; + } + while (*pArgv) { + pDst = pArgv++; + } + + /* + * The destination pattern must unambiguously expand to exactly + * one file or directory. + */ + + changeForwardSlashesToBackSlashes(*pDst); + sh_EnumerateFiles(*pDst, *pDst, sh_RecordFileData, &dstData, &n); + assert(n >= 0); + if (n == 1) { + /* + * Is the destination a file or directory? + */ + + if (dstData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + dstIsDir = 1; + } + } else if (n > 1) { + fprintf(stderr, "nsinstall: %ls: ambiguous destination file " + "or directory\n", *pDst); + return 3; + } else { + /* + * n == 0, meaning that destination file or directory does + * not exist. In this case the destination file directory + * name must be fully specified. + */ + + wchar_t *p; + + for (p = *pDst; *p; p++) { + if (*p == '*' || *p == '?') { + fprintf(stderr, "nsinstall: %ls: No such file or directory\n", + *pDst); + return 3; + } + } + + /* + * Do not include the trailing \, if any, unless it is a root + * directory (\ or X:\). + */ + + if (p > *pDst && p[-1] == '\\' && p != *pDst + 1 && p[-2] != ':') { + p[-1] = '\0'; + } + wcscpy(dstData.pathName, *pDst); + dstData.dwFileAttributes = 0xFFFFFFFF; + } + + /* + * If there are two or more source files, the destination has + * to be a directory. + */ + + if (pDst - pSrc > 1 && !dstIsDir) { + fprintf(stderr, "nsinstall: cannot copy more than" + " one file to the same destination file\n"); + return 3; + } + + if (dstIsDir) { + arg.dstFileNameMarker = arg.dstFileName + wcslen(arg.dstFileName); + + /* + * Now arg.dstFileNameMarker is pointing to the null byte at the + * end of string. We want to make sure that there is a \ at the + * end of string, and arg.dstFileNameMarker should point right + * after that \. + */ + + if (arg.dstFileNameMarker[-1] != '\\') { + *(arg.dstFileNameMarker++) = '\\'; + } + } + + if (!dstIsDir) { + struct sh_FileData srcData; + + assert(pDst - pSrc == 1); + changeForwardSlashesToBackSlashes(*pSrc); + sh_EnumerateFiles(*pSrc, *pSrc, sh_RecordFileData, &srcData, &n); + if (n == 0) { + fprintf(stderr, "nsinstall: %ls: No such file or directory\n", + *pSrc); + retVal = 3; + } else if (n > 1) { + fprintf(stderr, "nsinstall: cannot copy more than one file or " + "directory to the same destination\n"); + retVal = 3; + } else { + assert(n == 1); + if (sh_DoCopy(srcData.pathName, srcData.dwFileAttributes, + dstData.pathName, dstData.dwFileAttributes, + arg.force, arg.recursive) == FALSE) { + retVal = 3; + } + } + return retVal; + } + + for ( ; *pSrc != *pDst; pSrc++) { + BOOL rv; + + changeForwardSlashesToBackSlashes(*pSrc); + rv = sh_EnumerateFiles(*pSrc, *pSrc, sh_CpFileCmd, &arg, &n); + if (rv == FALSE) { + retVal = 3; + } else { + if (n == 0) { + fprintf(stderr, "nsinstall: %ls: No such file or directory\n", + *pSrc); + retVal = 3; + } + } + } + + return retVal; +} + +/* + * sh_EnumerateFiles -- + * + * Enumerate all the files in the specified pattern, which is a pathname + * containing possibly wildcard characters such as * and ?. fileFcn + * is called on each file, passing the expanded file name, a pointer + * to the file's WIN32_FILE_DATA, and the arg pointer. + * + * It is assumed that there are no wildcard characters before the + * character pointed to by 'where'. + * + * On return, *nFiles stores the number of files enumerated. *nFiles is + * set to this number whether sh_EnumerateFiles or 'fileFcn' succeeds + * or not. + * + * Return TRUE if the files are successfully enumerated and all + * 'fileFcn' invocations succeeded. Return FALSE if something went + * wrong. + */ + +static BOOL sh_EnumerateFiles( + const wchar_t *pattern, + const wchar_t *where, + sh_FileFcn fileFcn, + void *arg, + int *nFiles + ) +{ + WIN32_FIND_DATA fileData; + HANDLE hSearch; + const wchar_t *src; + wchar_t *dst; + wchar_t fileName[_MAX_PATH]; + wchar_t *fileNameMarker = fileName; + wchar_t *oldFileNameMarker; + BOOL hasWildcard = FALSE; + BOOL retVal = TRUE; + BOOL patternEndsInDotStar = FALSE; + BOOL patternEndsInDot = FALSE; /* a special case of + * patternEndsInDotStar */ + int numDotsInPattern; + int len; + + /* + * Windows expands patterns ending in ".", ".*", ".**", etc. + * differently from the glob expansion on Unix. For example, + * both "foo." and "foo.*" match "foo", and "*.*" matches + * everything, including filenames with no dots. So we need + * to throw away extra files returned by the FindNextFile() + * function. We require that a matched filename have at least + * the number of dots in the pattern. + */ + len = wcslen(pattern); + if (len >= 2) { + /* Start from the end of pattern and go backward */ + const wchar_t *p = &pattern[len - 1]; + + /* We can have zero or more *'s */ + while (p >= pattern && *p == '*') { + p--; + } + if (p >= pattern && *p == '.') { + patternEndsInDotStar = TRUE; + if (p == &pattern[len - 1]) { + patternEndsInDot = TRUE; + } + p--; + numDotsInPattern = 1; + while (p >= pattern && *p != '\\') { + if (*p == '.') { + numDotsInPattern++; + } + p--; + } + } + } + + *nFiles = 0; + + /* + * Copy pattern to fileName, but only up to and not including + * the first \ after the first wildcard letter. + * + * Make fileNameMarker point to one of the following: + * - the start of fileName, if fileName does not contain any \. + * - right after the \ before the first wildcard letter, if there is + * a wildcard character. + * - right after the last \, if there is no wildcard character. + */ + + dst = fileName; + src = pattern; + while (src < where) { + if (*src == '\\') { + oldFileNameMarker = fileNameMarker; + fileNameMarker = dst + 1; + } + *(dst++) = *(src++); + } + + while (*src && *src != '*' && *src != '?') { + if (*src == '\\') { + oldFileNameMarker = fileNameMarker; + fileNameMarker = dst + 1; + } + *(dst++) = *(src++); + } + + if (*src) { + /* + * Must have seen the first wildcard letter + */ + + hasWildcard = TRUE; + while (*src && *src != '\\') { + *(dst++) = *(src++); + } + } + + /* Now src points to either null or \ */ + + assert(*src == '\0' || *src == '\\'); + assert(hasWildcard || *src == '\0'); + *dst = '\0'; + + /* + * If the pattern does not contain any wildcard characters, then + * we don't need to go the FindFirstFile route. + */ + + if (!hasWildcard) { + /* + * See if it is the root directory, \, or X:\. + */ + + assert(!wcscmp(fileName, pattern)); + assert(wcslen(fileName) >= 1); + if (dst[-1] == '\\' && (dst == fileName + 1 || dst[-2] == ':')) { + fileData.cFileName[0] = '\0'; + } else { + /* + * Do not include the trailing \, if any + */ + + if (dst[-1] == '\\') { + assert(*fileNameMarker == '\0'); + dst[-1] = '\0'; + fileNameMarker = oldFileNameMarker; + } + wcscpy(fileData.cFileName, fileNameMarker); + } + fileData.dwFileAttributes = GetFileAttributes(fileName); + if (fileData.dwFileAttributes == 0xFFFFFFFF) { + return TRUE; + } + *nFiles = 1; + return (*fileFcn)(fileName, &fileData, arg); + } + + hSearch = FindFirstFile(fileName, &fileData); + if (hSearch == INVALID_HANDLE_VALUE) { + return retVal; + } + + do { + if (!wcscmp(fileData.cFileName, L".") + || !wcscmp(fileData.cFileName, L"..")) { + /* + * Skip over . and .. + */ + + continue; + } + + if (patternEndsInDotStar) { + int nDots = 0; + wchar_t *p = fileData.cFileName; + while (*p) { + if (*p == '.') { + nDots++; + } + p++; + } + /* Now p points to the null byte at the end of file name */ + if (patternEndsInDot && (p == fileData.cFileName + || p[-1] != '.')) { + /* + * File name does not end in dot. Skip this file. + * Note: windows file name probably cannot end in dot, + * but we do this check anyway. + */ + continue; + } + if (nDots < numDotsInPattern) { + /* + * Not enough dots in file name. Must be an extra + * file in matching .* pattern. Skip this file. + */ + continue; + } + } + + wcscpy(fileNameMarker, fileData.cFileName); + if (*src && *(src + 1)) { + /* + * More to go. Recurse. + */ + + int n; + + assert(*src == '\\'); + where = fileName + wcslen(fileName); + wcscat(fileName, src); + sh_EnumerateFiles(fileName, where, fileFcn, arg, &n); + *nFiles += n; + } else { + assert(wcschr(fileName, '*') == NULL); + assert(wcschr(fileName, '?') == NULL); + (*nFiles)++; + if ((*fileFcn)(fileName, &fileData, arg) == FALSE) { + retVal = FALSE; + } + } + } while (FindNextFile(hSearch, &fileData)); + + FindClose(hSearch); + return retVal; +} diff --git a/ape-server/deps/js/src/config/pathsub.c b/ape-server/deps/js/src/config/pathsub.c new file mode 100755 index 0000000..8384e5f --- /dev/null +++ b/ape-server/deps/js/src/config/pathsub.c @@ -0,0 +1,247 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* +** Pathname subroutines. +** +** Brendan Eich, 8/29/95 +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pathsub.h" + +#ifdef USE_REENTRANT_LIBC +#include +#endif + +#ifdef SUNOS4 +#include "sunos4.h" +#endif + +#ifndef D_INO +#define D_INO d_ino +#endif + +char *program; + +void +fail(char *format, ...) +{ + int error; + va_list ap; + +#ifdef USE_REENTRANT_LIBC + R_STRERROR_INIT_R(); +#endif + + error = errno; + fprintf(stderr, "%s: ", program); + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + if (error) { + +#ifdef USE_REENTRANT_LIBC + R_STRERROR_R(errno); + fprintf(stderr, ": %s", r_strerror_r); +#else + fprintf(stderr, ": %s", strerror(errno)); +#endif + } + + putc('\n', stderr); + exit(1); +} + +char * +getcomponent(char *path, char *name) +{ + if (*path == '\0') + return 0; + if (*path == '/') { + *name++ = '/'; + } else { + do { + *name++ = *path++; + } while (*path != '/' && *path != '\0'); + } + *name = '\0'; + while (*path == '/') + path++; + return path; +} + +#ifdef LAME_READDIR +#include +/* +** The static buffer in Unixware's readdir is too small. +*/ +struct dirent *readdir(DIR *d) +{ + static struct dirent *buf = NULL; + + if(buf == NULL) + buf = (struct dirent *) malloc(sizeof(struct dirent) + MAXPATHLEN); + return(readdir_r(d, buf)); +} +#endif + +char * +ino2name(ino_t ino, char *dir) +{ + DIR *dp; + struct dirent *ep; + char *name; + + dp = opendir(".."); + if (!dp) + fail("cannot read parent directory"); + for (;;) { + if (!(ep = readdir(dp))) + fail("cannot find current directory"); + if (ep->D_INO == ino) + break; + } + name = xstrdup(ep->d_name); + closedir(dp); + return name; +} + +void * +xmalloc(size_t size) +{ + void *p = malloc(size); + if (!p) + fail("cannot allocate %u bytes", size); + return p; +} + +char * +xstrdup(char *s) +{ + return strcpy(xmalloc(strlen(s) + 1), s); +} + +char * +xbasename(char *path) +{ + char *cp; + + while ((cp = strrchr(path, '/')) && cp[1] == '\0') + *cp = '\0'; + if (!cp) return path; + return cp + 1; +} + +void +xchdir(char *dir) +{ + if (chdir(dir) < 0) + fail("cannot change directory to %s", dir); +} + +int +relatepaths(char *from, char *to, char *outpath) +{ + char *cp, *cp2; + int len; + char buf[NAME_MAX]; + + assert(*from == '/' && *to == '/'); + for (cp = to, cp2 = from; *cp == *cp2; cp++, cp2++) + if (*cp == '\0') + break; + while (cp[-1] != '/') + cp--, cp2--; + if (cp - 1 == to) { + /* closest common ancestor is /, so use full pathname */ + len = strlen(strcpy(outpath, to)); + if (outpath[len] != '/') { + outpath[len++] = '/'; + outpath[len] = '\0'; + } + } else { + len = 0; + while ((cp2 = getcomponent(cp2, buf)) != 0) { + strcpy(outpath + len, "../"); + len += 3; + } + while ((cp = getcomponent(cp, buf)) != 0) { + sprintf(outpath + len, "%s/", buf); + len += strlen(outpath + len); + } + } + return len; +} + +void +reversepath(char *inpath, char *name, int len, char *outpath) +{ + char *cp, *cp2; + char buf[NAME_MAX]; + struct stat sb; + + cp = strcpy(outpath + PATH_MAX - (len + 1), name); + cp2 = inpath; + while ((cp2 = getcomponent(cp2, buf)) != 0) { + if (strcmp(buf, ".") == 0) + continue; + if (strcmp(buf, "..") == 0) { + if (stat(".", &sb) < 0) + fail("cannot stat current directory"); + name = ino2name(sb.st_ino, ".."); + len = strlen(name); + cp -= len + 1; + strcpy(cp, name); + cp[len] = '/'; + free(name); + xchdir(".."); + } else { + cp -= 3; + strncpy(cp, "../", 3); + xchdir(buf); + } + } + strcpy(outpath, cp); +} diff --git a/ape-server/deps/js/src/config/pathsub.h b/ape-server/deps/js/src/config/pathsub.h new file mode 100755 index 0000000..ab1a7fe --- /dev/null +++ b/ape-server/deps/js/src/config/pathsub.h @@ -0,0 +1,74 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef pathsub_h___ +#define pathsub_h___ +/* +** Pathname subroutines. +** +** Brendan Eich, 8/29/95 +*/ +#include +#include + +#ifndef PATH_MAX +#define PATH_MAX 1024 +#endif + +/* + * Just prevent stupidity + */ +#undef NAME_MAX +#define NAME_MAX 256 + +extern char *program; + +extern void fail(char *format, ...); +extern char *getcomponent(char *path, char *name); +extern char *ino2name(ino_t ino, char *dir); +extern void *xmalloc(size_t size); +extern char *xstrdup(char *s); +extern char *xbasename(char *path); +extern void xchdir(char *dir); + +/* Relate absolute pathnames from and to returning the result in outpath. */ +extern int relatepaths(char *from, char *to, char *outpath); + +/* XXX changes current working directory -- caveat emptor */ +extern void reversepath(char *inpath, char *name, int len, char *outpath); + +#endif /* pathsub_h___ */ diff --git a/ape-server/deps/js/src/config/preprocessor.pl b/ape-server/deps/js/src/config/preprocessor.pl new file mode 100755 index 0000000..3da6654 --- /dev/null +++ b/ape-server/deps/js/src/config/preprocessor.pl @@ -0,0 +1,671 @@ +#!/usr/bin/perl -w +# -*- Mode: perl; tab-width: 4; indent-tabs-mode: nil; -*- +# +# Preprocessor +# Version 1.1 +# +# Copyright (c) 2002, 2003, 2004 by Ian Hickson +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +# Thanks to bryner and bsmedberg for suggestions. +# Thanks to jon rekai for a patch to not require File::Spec 0.8. + +use strict; + +# takes as arguments the files to process +# defaults to stdin +# output to stdout + +my $stack = new stack; +my $marker = '#'; + +# command line arguments +my @includes; +while ($_ = $ARGV[0], defined($_) && /^-./) { + shift; + last if /^--$/os; + if (/^-D(.*)$/os) { + for ($1) { + if (/^([\w\.]+)=(.*)$/os) { + $stack->define($1, $2); + } elsif (/^([\w\.]+)$/os) { + $stack->define($1, 1); + } else { + die "$0: invalid argument to -D: $_\n"; + } + } + } elsif (/^-F(.*)$/os) { + for ($1) { + if (/^(\w+)$/os) { + $stack->filter($1, 1); + } else { + die "$0: invalid argument to -F: $_\n"; + } + } + } elsif (/^-I(.*)$/os) { + push(@includes, $1); + } elsif (/^-E$/os) { + foreach (keys %ENV) { + # define all variables that have valid names + $stack->define($_, $ENV{$_}) unless m/\W/; + } + } elsif (/^-d$/os) { + $stack->{'dependencies'} = 1; + } elsif (/^--line-endings=crlf$/os) { + $stack->{'lineEndings'} = "\x0D\x0A"; + } elsif (/^--line-endings=cr$/os) { + $stack->{'lineEndings'} = "\x0D"; + } elsif (/^--line-endings=lf$/os) { + $stack->{'lineEndings'} = "\x0A"; + } elsif (/^--line-endings=(.+)$/os) { + die "$0: unrecognised line ending: $1\n"; + } elsif (/^--marker=(.)$/os) { + $marker = $1; + } else { + die "$0: invalid argument: $_\n"; + } +} +unshift(@ARGV, '-') unless @ARGV; +unshift(@ARGV, @includes); + +# do the work +foreach (@ARGV) { include($stack, $_); } +exit(0); + +######################################################################## + +package main; +use File::Spec; +use File::Spec::Unix; # on all platforms, because the #include syntax is unix-based + +# Note: Ideally we would use File::Spec 0.8. When this becomes +# possible, add "0.8" to the first "use" line above, then replace +# occurrences of "::_0_8::" with "->" below. And remove the code for +# File::Spec 0.8 much lower down the file. + +sub include { + my($stack, $filename) = @_; + my $directory = $stack->{'variables'}->{'DIRECTORY'}; + if ($filename ne '-') { + $filename = File::Spec::_0_8::rel2abs($filename, $directory); + # splitpath expects forward-slash paths on windows, so we have to + # change the slashes if using Activestate Perl. + $filename =~ s?\\?/?g if "$^O" eq "MSWin32"; + my($volume, $path) = File::Spec::_0_8::splitpath($filename); + $directory = File::Spec::_0_8::catpath($volume, $path, ''); + } + local $stack->{'variables'}->{'DIRECTORY'} = $directory; + local $stack->{'variables'}->{'FILE'} = $filename; + local $stack->{'variables'}->{'LINE'} = 0; + local *FILE; + open(FILE, $filename) or die "Couldn't open $filename: $!\n"; + my $lineout = 0; + while () { + # on cygwin, line endings are screwed up, so normalise them. + s/[\x0D\x0A]+$/\n/os if ($^O eq 'msys' || $^O eq 'cygwin' || "$^O" eq "MSWin32"); + $stack->newline; + if (/^\Q$marker\E([a-z]+)\n?$/os) { # argumentless processing instruction + process($stack, $1); + } elsif (/^\Q$marker\E([a-z]+)\s(.*?)\n?$/os) { # processing instruction with arguments + process($stack, $1, $2); + } elsif (/^\Q$marker\E/os) { # comment + # ignore it + } elsif ($stack->enabled) { + next if $stack->{'dependencies'}; + + # set the current line number in JavaScript if necessary + my $linein = $stack->{'variables'}->{'LINE'}; + if (++$lineout != $linein) { + if ($filename =~ /\.js(|\.in)$/o) { + $stack->print("//\@line $linein \"$filename\"\n") + } + $lineout = $linein; + } + + # print it, including any newlines + $stack->print(filtered($stack, $_)); + } + } + close(FILE); +} + +sub process { + my($stack, $instruction, @arguments) = @_; + my $method = 'preprocessor'->can($instruction); + if (not defined($method)) { + fatal($stack, 'unknown instruction', $instruction); + } + eval { &$method($stack, @arguments) }; + if ($@) { + fatal($stack, "error evaluating $instruction:", $@); + } +} + +sub filtered { + my($stack, $text) = @_; + foreach my $filter (sort keys %{$stack->{'filters'}}) { + next unless $stack->{'filters'}->{$filter}; + my $method = 'filter'->can($filter); + if (not defined($method)) { + fatal($stack, 'unknown filter', $filter); + } + $text = eval { &$method($stack, $text) }; + if ($@) { + fatal($stack, "error using $filter:", $@); + } + } + return $text; +} + +sub fatal { + my $stack = shift; + my $filename = $stack->{'variables'}->{'FILE'}; + local $" = ' '; + print STDERR "$0:$filename:$.: @_\n"; + exit(1); +} + + +######################################################################## + +package stack; + +# condition evaluated just prior to this context was false +use constant COND_FALSE => 0; + +# condition evaluated just prior to this context was true +use constant COND_TRUE => 1; + +# some prior condition at this level already evaluated to true (or a +# parent condition evaluated to false or must be ignored), so we're +# ignoring all remaining conditions at current level (and nested +# conditions, too) +use constant COND_COMPLETED => 2; + +sub new { + return bless { + 'variables' => { + # %ENV, + 'LINE' => 0, # the line number in the source file + 'DIRECTORY' => '', # current directory + 'FILE' => '', # source filename + '1' => 1, # for convenience (the constant '1' is thus true) + }, + 'filters' => { + # filters + }, + 'values' => [], # the value of the last condition evaluated at the nth level + 'lastConditionState' => [], # whether the condition in the nth-level context was true, false, or not applicable + 'conditionState' => COND_TRUE, + 'dependencies' => 0, # whether we are showing dependencies + 'lineEndings' => "\n", # default to platform conventions + }; +} + +sub newline { + my $self = shift; + $self->{'variables'}->{'LINE'}++; +} + +sub define { + my $self = shift; + my($variable, $value) = @_; + die "not a valid variable name: '$variable'\n" if $variable =~ m/[^\w\.]/; + $self->{'variables'}->{$variable} = $value; +} + +sub defined { + my $self = shift; + my($variable) = @_; + die "not a valid variable name: '$variable'\n" if $variable =~ m/[^\w\.]/; + return defined($self->{'variables'}->{$variable}); +} + +sub undefine { + my $self = shift; + my($variable) = @_; + die "not a valid variable name: '$variable'\n" if $variable =~ m/[^\w\.]/; + delete($self->{'variables'}->{$variable}); +} + +sub get { + my $self = shift; + my($variable, $required) = @_; + die "not a valid variable name: '$variable'\n" if $variable =~ m/[^\w\.]/; + my $value = $self->{'variables'}->{$variable}; + if (defined($value)) { + return $value; + } else { + die "variable '$variable' is not defined\n" if $required; + return ''; + } +} + +sub replace { + my $self = shift; + my ($value) = @_; + + ${$self->{'values'}}[-1] = $value; + $self->{'conditionState'} = $self->{'conditionState'} != COND_FALSE + ? COND_COMPLETED + : $value ? COND_TRUE : COND_FALSE; +} + +sub push { + my $self = shift; + my($value) = @_; + + push(@{$self->{'values'}}, $value); + my $lastCondition = $self->{'conditionState'}; + push(@{$self->{'lastConditionState'}}, $lastCondition); + $self->{'conditionState'} = $lastCondition != COND_TRUE + ? COND_COMPLETED + : $value ? COND_TRUE : COND_FALSE; +} + +sub pop { + my $self = shift; + $self->{'conditionState'} = pop(@{$self->{'lastConditionState'}}); + return pop(@{$self->{'values'}}); +} + +sub enabled { + my $self = shift; + return $self->{'conditionState'} == COND_TRUE; +} + +sub disabled { + my $self = shift; + return $self->{'conditionState'} != COND_TRUE; +} + +sub filter { + my $self = shift; + my($filter, $value) = @_; + die "not a valid filter name: '$filter'\n" if $filter =~ m/\W/; + $self->{'filters'}->{$filter} = $value; +} + +sub expand { + my $self = shift; + my($line) = @_; + $line =~ s/__(\w+)__/$self->get($1)/gose; + return $line; +} + +sub print { + my $self = shift; + return if $self->{'dependencies'}; + foreach my $line (@_) { + if (chomp $line) { + CORE::print("$line$self->{'lineEndings'}"); + } else { + CORE::print($line); + } + } +} + +sub visit { + my $self = shift; + my($filename) = @_; + my $directory = $stack->{'variables'}->{'DIRECTORY'}; + $filename = File::Spec::_0_8::abs2rel(File::Spec::_0_8::rel2abs($filename, $directory)); + CORE::print("$filename\n"); +} + +######################################################################## + +package preprocessor; + +sub define { + my $stack = shift; + return if $stack->disabled; + die "argument expected\n" unless @_; + my $argument = shift; + for ($argument) { + /^(\w+)\s(.*)$/os && do { + return $stack->define($1, $2); + }; + /^(\w+)$/os && do { + return $stack->define($1, 1); + }; + die "invalid argument: '$_'\n"; + } +} + +sub undef { + my $stack = shift; + return if $stack->disabled; + die "argument expected\n" unless @_; + $stack->undefine(@_); +} + +sub ifdef { + my $stack = shift; + my $variable = shift; + my $replace = defined(shift); + die "argument expected\n" unless defined($variable); + if ($replace) { + $stack->replace($stack->defined($variable)); + } else { + $stack->push($stack->defined($variable)); + } +} + +sub ifndef { + my $stack = shift; + my $variable = shift; + my $replace = defined(shift); + die "argument expected\n" unless defined($variable); + if ($replace) { + $stack->replace(not $stack->defined($variable)); + } else { + $stack->push(not $stack->defined($variable)); + } +} + +sub if { + my $stack = shift; + die "argument expected\n" unless @_; + my $argument = shift; + my $replace = defined(shift); + for ($argument) { + /^(\w+)==(.*)$/os && do { + # equality + if ($replace) { + return $stack->replace($stack->get($1) eq $2); + } else { + return $stack->push($stack->get($1) eq $2); + } + }; + /^(\w+)!=(.*)$/os && do { + # inequality + if ($replace) { + return $stack->replace($stack->get($1) ne $2); + } else { + return $stack->push($stack->get($1) ne $2); + } + }; + /^(\w+)$/os && do { + # true value + if ($replace) { + return $stack->replace($stack->get($1)); + } else { + return $stack->push($stack->get($1)); + } + }; + /^!(\w+)$/os && do { + # false value + if ($replace) { + return $stack->replace(not $stack->get($1)); + } else { + return $stack->push(not $stack->get($1)); + } + }; + die "invalid argument: '$_'\n"; + } +} + +sub else { + my $stack = shift; + die "argument unexpected\n" if @_; + $stack->replace(1); +} + +sub elif { + my $stack = shift; + die "argument expected\n" unless @_; + &if($stack, @_, 1); +} + +sub elifdef { + my $stack = shift; + die "argument expected\n" unless @_; + &ifdef($stack, @_, 1); +} + +sub elifndef { + my $stack = shift; + die "argument expected\n" unless @_; + &ifndef($stack, @_, 1); +} + +sub endif { + my $stack = shift; + die "argument unexpected\n" if @_; + $stack->pop; +} + +sub error { + my $stack = shift; + return if $stack->disabled; + die "argument expected\n" unless @_; + my $line = $stack->expand(@_); + die "$line\n"; +} + +sub expand { + my $stack = shift; + return if $stack->disabled; + die "argument expected\n" unless @_; + my $line = $stack->expand(@_); + $stack->print("$line\n"); +} + +sub literal { + my $stack = shift; + return if $stack->disabled; + die "argument expected\n" unless @_; + my $line = shift; + $stack->print("$line\n"); +} + +sub include { + my $stack = shift; + return if $stack->disabled; + die "argument expected\n" unless @_; + my $filename = File::Spec::_0_8::catpath(File::Spec::_0_8::splitpath(@_)); + if ($stack->{'dependencies'}) { + $stack->visit($filename); + } else { + main::include($stack, $filename); + } +} + +sub includesubst { + my ($stack, $filename) = @_; + return if $stack->disabled; + die "argument expected\n" unless $filename; + $filename =~ s/@(\w+)@/$stack->get($1, 1)/gose; + $filename = File::Spec::_0_8::catpath(File::Spec::_0_8::splitpath($filename)); + if ($stack->{'dependencies'}) { + $stack->visit($filename); + } else { + main::include($stack, $filename); + } +} + +sub filter { + my $stack = shift; + return if $stack->disabled; + die "argument expected\n" unless @_; + foreach (split(/\s/os, shift)) { + $stack->filter($_, 1); + } +} + +sub unfilter { + my $stack = shift; + return if $stack->disabled; + die "argument expected\n" unless @_; + foreach (split(/\s/os, shift)) { + $stack->filter($_, 0); + } +} + + +######################################################################## + +package filter; + +sub emptyLines { + my($stack, $text) = @_; + $text = "" if $text eq "\n"; + return $text; +} + +sub spaces { + my($stack, $text) = @_; + $text =~ s/ +/ /gos; # middle spaces + $text =~ s/^ //gos; # start spaces + $text =~ s/ (\n?)$/$1/gos; # end spaces + return $text; +} + +sub slashslash { + my($stack, $text) = @_; + $text =~ s|//.*?(\n?)$|$1|gos; + return $text; +} + +sub substitution { + my($stack, $text) = @_; + $text =~ s/@(\w+)@/$stack->get($1, 1)/gose; + return $text; +} + +sub attemptSubstitution { + my($stack, $text) = @_; + $text =~ s/@(\w+)@/$stack->get($1, 0)/gose; + return $text; +} + +######################################################################## + +######################################################################## +# This code is from File::Spec::Unix 0.8. +# It is not considered a part of the preprocessor.pl source file +# This code is licensed under the same license as File::Spec itself. + +package File::Spec::_0_8; + +use Cwd; + +sub rel2abs { + my ($path, $base) = @_; + if ( ! File::Spec->file_name_is_absolute( $path ) ) { + if ( !defined( $base ) || $base eq '' ) { + $base = cwd() ; + } elsif ( ! File::Spec->file_name_is_absolute( $base ) ) { + $base = rel2abs( $base ); + } else { + $base = File::Spec->canonpath( $base ); + } + $path = File::Spec->catdir( $base, $path ); + } + return File::Spec->canonpath( $path ); +} + +sub splitdir { + return split m|/|, $_[1], -1; # Preserve trailing fields +} + +sub splitpath { + my ($path, $nofile) = @_; + + my ($volume,$directory,$file) = ('','',''); + + if ( $nofile ) { + $directory = $path; + } + else { + $path =~ m|^ ( (?: .* / (?: \.\.?\Z(?!\n) )? )? ) ([^/]*) |xs; + $directory = $1; + $file = $2; + } + + return ($volume,$directory,$file); +} + +sub catpath { + my ($volume,$directory,$file) = @_; + + if ( $directory ne '' && + $file ne '' && + substr( $directory, -1 ) ne '/' && + substr( $file, 0, 1 ) ne '/' + ) { + $directory .= "/$file" ; + } + else { + $directory .= $file ; + } + + return $directory ; +} + +sub abs2rel { + my($path,$base) = @_; + + # Clean up $path + if ( ! File::Spec->file_name_is_absolute( $path ) ) { + $path = rel2abs( $path ) ; + } + else { + $path = File::Spec->canonpath( $path ) ; + } + + # Figure out the effective $base and clean it up. + if ( !defined( $base ) || $base eq '' ) { + $base = cwd(); + } + elsif ( ! File::Spec->file_name_is_absolute( $base ) ) { + $base = rel2abs( $base ) ; + } + else { + $base = File::Spec->canonpath( $base ) ; + } + + # Now, remove all leading components that are the same + my @pathchunks = File::Spec::_0_8::splitdir( $path); + my @basechunks = File::Spec::_0_8::splitdir( $base); + + while (@pathchunks && @basechunks && $pathchunks[0] eq $basechunks[0]) { + shift @pathchunks ; + shift @basechunks ; + } + + $path = CORE::join( '/', @pathchunks ); + $base = CORE::join( '/', @basechunks ); + + # $base now contains the directories the resulting relative path + # must ascend out of before it can descend to $path_directory. So, + # replace all names with $parentDir + $base =~ s|[^/]+|..|g ; + + # Glue the two together, using a separator if necessary, and preventing an + # empty result. + if ( $path ne '' && $base ne '' ) { + $path = "$base/$path" ; + } else { + $path = "$base$path" ; + } + + return File::Spec->canonpath( $path ) ; +} + +# End code from File::Spec::Unix 0.8. +######################################################################## diff --git a/ape-server/deps/js/src/config/rules.mk b/ape-server/deps/js/src/config/rules.mk new file mode 100755 index 0000000..2c38fe3 --- /dev/null +++ b/ape-server/deps/js/src/config/rules.mk @@ -0,0 +1,2287 @@ +# vim:set ts=8 sw=8 sts=8 noet: +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Chase Phillips +# Benjamin Smedberg +# Jeff Walden +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +ifndef topsrcdir +$(error topsrcdir was not set)) +endif + +ifndef MOZILLA_DIR +MOZILLA_DIR = $(topsrcdir) +endif + +ifndef INCLUDED_CONFIG_MK +include $(topsrcdir)/config/config.mk +endif + +ifndef INCLUDED_VERSION_MK +include $(topsrcdir)/config/version.mk +endif + +ifdef SDK_XPIDLSRCS +XPIDLSRCS += $(SDK_XPIDLSRCS) +endif +ifdef SDK_HEADERS +EXPORTS += $(SDK_HEADERS) +endif + +REPORT_BUILD = @echo $(notdir $<) + +ifeq ($(OS_ARCH),OS2) +EXEC = +else +EXEC = exec +endif + +# Don't copy xulrunner files at install time, when using system xulrunner +ifdef SYSTEM_LIBXUL + SKIP_COPY_XULRUNNER=1 +endif + +# ELOG prints out failed command when building silently (gmake -s). +ifneq (,$(findstring -s,$(MAKEFLAGS))) + ELOG := $(EXEC) sh $(BUILD_TOOLS)/print-failed-commands.sh +else + ELOG := +endif + +ifeq (,$(filter-out WINNT WINCE,$(OS_ARCH))) +ifndef GNU_CC +_LIBNAME_RELATIVE_PATHS=1 +endif +endif + +ifeq (,$(filter-out WINNT WINCE,$(OS_ARCH))) +_VPATH_SRCS = $(abspath $<) +else +_VPATH_SRCS = $< +endif + +# Add $(DIST)/lib to VPATH so that -lfoo dependencies are followed +VPATH += $(DIST)/lib +ifdef LIBXUL_SDK +VPATH += $(LIBXUL_SDK)/lib +endif + +# EXPAND_LIBNAME - $(call EXPAND_LIBNAME,foo) +# expands to foo.lib on platforms with import libs and -lfoo otherwise + +# EXPAND_LIBNAME_PATH - $(call EXPAND_LIBNAME_PATH,foo,dir) +# expands to dir/foo.lib on platforms with import libs and +# -Ldir -lfoo otherwise + +# EXPAND_MOZLIBNAME - $(call EXPAND_MOZLIBNAME,foo) +# expands to $(DIST)/lib/foo.lib on platforms with import libs and +# -lfoo otherwise + +ifdef _LIBNAME_RELATIVE_PATHS +EXPAND_LIBNAME = $(foreach lib,$(1),$(LIB_PREFIX)$(lib).$(LIB_SUFFIX)) +EXPAND_LIBNAME_PATH = $(foreach lib,$(1),$(2)/$(LIB_PREFIX)$(lib).$(LIB_SUFFIX)) +EXPAND_MOZLIBNAME = $(foreach lib,$(1),$(DIST)/lib/$(LIB_PREFIX)$(lib).$(LIB_SUFFIX)) +else +EXPAND_LIBNAME = $(addprefix -l,$(1)) +EXPAND_LIBNAME_PATH = -L$(2) $(addprefix -l,$(1)) +EXPAND_MOZLIBNAME = $(addprefix -l,$(1)) +endif + +ifdef EXTRA_DSO_LIBS +EXTRA_DSO_LIBS := $(call EXPAND_MOZLIBNAME,$(EXTRA_DSO_LIBS)) +endif + +################################################################################ +# Testing frameworks support +################################################################################ + +ifdef ENABLE_TESTS + +ifdef XPCSHELL_TESTS +ifndef MODULE +$(error Must define MODULE when defining XPCSHELL_TESTS.) +endif + +testxpcobjdir = $(DEPTH)/_tests/xpcshell + +# Test file installation +ifneq (,$(filter WINNT os2-emx,$(HOST_OS_ARCH))) +# Windows and OS/2 nsinstall can't recursively copy directories, so use nsinstall.py +TEST_INSTALLER = $(PYTHON) $(topsrcdir)/config/nsinstall.py +else +TEST_INSTALLER = $(INSTALL) +endif + +define _INSTALL_TESTS +$(TEST_INSTALLER) $(wildcard $(srcdir)/$(dir)/*) $(testxpcobjdir)/$(MODULE)/$(dir) + +endef # do not remove the blank line! + +SOLO_FILE ?= $(error Specify a test filename in SOLO_FILE when using check-interactive or check-one) + +libs:: + $(foreach dir,$(XPCSHELL_TESTS),$(_INSTALL_TESTS)) + $(PERL) -I$(MOZILLA_DIR)/config $(MOZILLA_DIR)/config/build-list.pl \ + $(testxpcobjdir)/all-test-dirs.list \ + $(addprefix $(MODULE)/,$(XPCSHELL_TESTS)) + +testxpcsrcdir = $(topsrcdir)/testing/xpcshell + +# Execute all tests in the $(XPCSHELL_TESTS) directories. +# See also testsuite-targets.mk 'xpcshell-tests' target for global execution. +xpcshell-tests: + $(PYTHON) -u $(topsrcdir)/config/pythonpath.py \ + -I$(topsrcdir)/build \ + $(testxpcsrcdir)/runxpcshelltests.py \ + --symbols-path=$(DIST)/crashreporter-symbols \ + $(DIST)/bin/xpcshell \ + $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(MODULE)/$(dir)) + +# Execute a single test, specified in $(SOLO_FILE), but don't automatically +# start the test. Instead, present the xpcshell prompt so the user can +# attach a debugger and then start the test. +check-interactive: + $(PYTHON) -u $(topsrcdir)/config/pythonpath.py \ + -I$(topsrcdir)/build \ + $(testxpcsrcdir)/runxpcshelltests.py \ + --symbols-path=$(DIST)/crashreporter-symbols \ + --test-path=$(SOLO_FILE) \ + --interactive \ + $(DIST)/bin/xpcshell \ + $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(MODULE)/$(dir)) + +# Execute a single test, specified in $(SOLO_FILE) +check-one: + $(PYTHON) -u $(topsrcdir)/config/pythonpath.py \ + -I$(topsrcdir)/build \ + $(testxpcsrcdir)/runxpcshelltests.py \ + --symbols-path=$(DIST)/crashreporter-symbols \ + --test-path=$(SOLO_FILE) \ + $(DIST)/bin/xpcshell \ + $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(MODULE)/$(dir)) + +endif # XPCSHELL_TESTS + +ifdef CPP_UNIT_TESTS + +# Compile the tests to $(DIST)/bin. Make lots of niceties available by default +# through TestHarness.h, by modifying the list of includes and the libs against +# which stuff links. +CPPSRCS += $(CPP_UNIT_TESTS) +SIMPLE_PROGRAMS += $(CPP_UNIT_TESTS:.cpp=$(BIN_SUFFIX)) +INCLUDES += -I$(DIST)/include/testing +LIBS += $(XPCOM_GLUE_LDOPTS) $(NSPR_LIBS) + +# ...and run them the usual way +check:: + @$(EXIT_ON_ERROR) \ + for f in $(subst .cpp,,$(CPP_UNIT_TESTS)); do \ + XPCOM_DEBUG_BREAK=stack-and-abort $(RUN_TEST_PROGRAM) $(DIST)/bin/$$f; \ + done + +endif # CPP_UNIT_TESTS + +.PHONY: check xpcshell-tests check-interactive check-one + +endif # ENABLE_TESTS + + +# +# Library rules +# +# If BUILD_STATIC_LIBS or FORCE_STATIC_LIB is set, build a static library. +# Otherwise, build a shared library. +# + +ifndef LIBRARY +ifdef STATIC_LIBRARY_NAME +ifeq (OS2,$(OS_ARCH)) +ifdef SHORT_LIBNAME +STATIC_LIBRARY_NAME := $(SHORT_LIBNAME) +SHARED_LIBRARY_NAME := $(SHORT_LIBNAME) +endif +endif +LIBRARY := $(LIB_PREFIX)$(STATIC_LIBRARY_NAME).$(LIB_SUFFIX) +endif # STATIC_LIBRARY_NAME +endif # LIBRARY + +ifndef HOST_LIBRARY +ifdef HOST_LIBRARY_NAME +HOST_LIBRARY := $(LIB_PREFIX)$(HOST_LIBRARY_NAME).$(LIB_SUFFIX) +endif +endif + +ifdef LIBRARY +ifneq (_1,$(FORCE_SHARED_LIB)_$(BUILD_STATIC_LIBS)) +ifdef MKSHLIB + +ifdef LIB_IS_C_ONLY +MKSHLIB = $(MKCSHLIB) +endif + +ifdef MAKE_FRAMEWORK +SHARED_LIBRARY := $(SHARED_LIBRARY_NAME) +else +SHARED_LIBRARY := $(DLL_PREFIX)$(SHARED_LIBRARY_NAME)$(DLL_SUFFIX) +endif + +ifeq ($(OS_ARCH),OS2) +DEF_FILE := $(SHARED_LIBRARY:.dll=.def) +endif + +ifneq (,$(filter OS2 WINNT WINCE,$(OS_ARCH))) +IMPORT_LIBRARY := $(LIB_PREFIX)$(SHARED_LIBRARY_NAME).$(IMPORT_LIB_SUFFIX) +endif + +ifdef MOZ_ENABLE_LIBXUL +EMBED_MANIFEST_AT=2 +endif + +endif # MKSHLIB +endif # FORCE_SHARED_LIB && !BUILD_STATIC_LIBS +endif # LIBRARY + +ifeq (,$(BUILD_STATIC_LIBS)$(FORCE_STATIC_LIB)) +LIBRARY := $(NULL) +endif + +ifeq (_1,$(FORCE_SHARED_LIB)_$(BUILD_STATIC_LIBS)) +SHARED_LIBRARY := $(NULL) +DEF_FILE := $(NULL) +IMPORT_LIBRARY := $(NULL) +endif + +ifdef FORCE_STATIC_LIB +ifndef FORCE_SHARED_LIB +SHARED_LIBRARY := $(NULL) +DEF_FILE := $(NULL) +IMPORT_LIBRARY := $(NULL) +endif +endif + +ifdef FORCE_SHARED_LIB +ifndef FORCE_STATIC_LIB +LIBRARY := $(NULL) +endif +endif + +ifdef JAVA_LIBRARY_NAME +JAVA_LIBRARY := $(JAVA_LIBRARY_NAME).jar +endif + +ifeq (,$(filter-out WINNT WINCE,$(OS_ARCH))) +ifndef GNU_CC + +# +# Unless we're building SIMPLE_PROGRAMS, all C++ files share a PDB file per +# directory. For parallel builds, this PDB file is shared and locked by +# MSPDBSRV.EXE, starting with MSVC8 SP1. If you're using MSVC 7.1 or MSVC8 +# without SP1, don't do parallel builds. +# +# The final PDB for libraries and programs is created by the linker and uses +# a different name from the single PDB file created by the compiler. See +# bug 462740. +# + +ifdef SIMPLE_PROGRAMS +COMPILE_PDBFILE = $(basename $(@F)).pdb +else +COMPILE_PDBFILE = generated.pdb +endif + +LINK_PDBFILE = $(basename $(@F)).pdb +ifdef MOZ_DEBUG +CODFILE=$(basename $(@F)).cod +endif + +ifdef MOZ_MAPINFO +ifdef SHARED_LIBRARY_NAME +MAPFILE=$(SHARED_LIBRARY_NAME).map +else +MAPFILE=$(basename $(@F)).map +endif # SHARED_LIBRARY_NAME +endif # MOZ_MAPINFO + +ifdef DEFFILE +OS_LDFLAGS += -DEF:$(call normalizepath,$(DEFFILE)) +EXTRA_DEPS += $(DEFFILE) +endif + +ifdef MAPFILE +OS_LDFLAGS += -MAP:$(MAPFILE) +endif + +endif # !GNU_CC + +ifdef ENABLE_CXX_EXCEPTIONS +ifdef GNU_CC +CXXFLAGS += -fexceptions +else +ifeq (,$(filter-out 1200 1300 1310,$(_MSC_VER))) +CXXFLAGS += -GX +else +CXXFLAGS += -EHsc +endif # _MSC_VER +endif # GNU_CC +endif # ENABLE_CXX_EXCEPTIONS +endif # WINNT + +ifeq ($(SOLARIS_SUNPRO_CXX),1) +CXXFLAGS += -features=extensions -D__FUNCTION__=__func__ +ifeq (86,$(findstring 86,$(OS_TEST))) +OS_LDFLAGS += -M $(topsrcdir)/config/solaris_ia32.map +endif # x86 +endif # Solaris Sun Studio C++ + +ifeq (,$(filter-out WINNT WINCE,$(HOST_OS_ARCH))) +HOST_PDBFILE=$(basename $(@F)).pdb +endif + +ifndef TARGETS +TARGETS = $(LIBRARY) $(SHARED_LIBRARY) $(PROGRAM) $(SIMPLE_PROGRAMS) $(HOST_LIBRARY) $(HOST_PROGRAM) $(HOST_SIMPLE_PROGRAMS) $(JAVA_LIBRARY) +endif + +ifndef OBJS +_OBJS = \ + $(JRI_STUB_CFILES) \ + $(addsuffix .$(OBJ_SUFFIX), $(JMC_GEN)) \ + $(CSRCS:.c=.$(OBJ_SUFFIX)) \ + $(patsubst %.cc,%.$(OBJ_SUFFIX),$(CPPSRCS:.cpp=.$(OBJ_SUFFIX))) \ + $(CMSRCS:.m=.$(OBJ_SUFFIX)) \ + $(CMMSRCS:.mm=.$(OBJ_SUFFIX)) \ + $(ASFILES:.$(ASM_SUFFIX)=.$(OBJ_SUFFIX)) +OBJS = $(strip $(_OBJS)) +endif + +ifndef HOST_OBJS +_HOST_OBJS = \ + $(addprefix host_,$(HOST_CSRCS:.c=.$(OBJ_SUFFIX))) \ + $(addprefix host_,$(patsubst %.cc,%.$(OBJ_SUFFIX),$(HOST_CPPSRCS:.cpp=.$(OBJ_SUFFIX)))) \ + $(addprefix host_,$(HOST_CMSRCS:.m=.$(OBJ_SUFFIX))) \ + $(addprefix host_,$(HOST_CMMSRCS:.mm=.$(OBJ_SUFFIX))) +HOST_OBJS = $(strip $(_HOST_OBJS)) +endif + +LIBOBJS := $(addprefix \", $(OBJS)) +LIBOBJS := $(addsuffix \", $(LIBOBJS)) + +ifndef MOZ_AUTO_DEPS +ifneq (,$(OBJS)$(XPIDLSRCS)$(SIMPLE_PROGRAMS)) +MDDEPFILES = $(addprefix $(MDDEPDIR)/,$(OBJS:.$(OBJ_SUFFIX)=.pp)) +ifndef NO_GEN_XPT +MDDEPFILES += $(addprefix $(MDDEPDIR)/,$(XPIDLSRCS:.idl=.xpt)) +endif +endif +endif + +ALL_TRASH = \ + $(GARBAGE) $(TARGETS) $(OBJS) $(PROGOBJS) LOGS TAGS a.out \ + $(filter-out $(ASFILES),$(OBJS:.$(OBJ_SUFFIX)=.s)) $(OBJS:.$(OBJ_SUFFIX)=.ii) \ + $(OBJS:.$(OBJ_SUFFIX)=.i) \ + $(HOST_PROGOBJS) $(HOST_OBJS) $(IMPORT_LIBRARY) $(DEF_FILE)\ + $(EXE_DEF_FILE) so_locations _gen _stubs $(wildcard *.res) $(wildcard *.RES) \ + $(wildcard *.pdb) $(CODFILE) $(MAPFILE) $(IMPORT_LIBRARY) \ + $(SHARED_LIBRARY:$(DLL_SUFFIX)=.exp) $(wildcard *.ilk) \ + $(PROGRAM:$(BIN_SUFFIX)=.exp) $(SIMPLE_PROGRAMS:$(BIN_SUFFIX)=.exp) \ + $(PROGRAM:$(BIN_SUFFIX)=.lib) $(SIMPLE_PROGRAMS:$(BIN_SUFFIX)=.lib) \ + $(SIMPLE_PROGRAMS:$(BIN_SUFFIX)=.$(OBJ_SUFFIX)) \ + $(wildcard gts_tmp_*) $(LIBRARY:%.a=.%.timestamp) +ALL_TRASH_DIRS = \ + $(GARBAGE_DIRS) /no-such-file + +ifdef QTDIR +GARBAGE += $(MOCSRCS) +endif + +ifdef SIMPLE_PROGRAMS +GARBAGE += $(SIMPLE_PROGRAMS:%=%.$(OBJ_SUFFIX)) +endif + +ifdef HOST_SIMPLE_PROGRAMS +GARBAGE += $(HOST_SIMPLE_PROGRAMS:%=%.$(OBJ_SUFFIX)) +endif + +# +# the Solaris WorkShop template repository cache. it occasionally can get +# out of sync, so targets like clobber should kill it. +# +ifeq ($(SOLARIS_SUNPRO_CXX),1) +GARBAGE_DIRS += SunWS_cache +endif + +ifeq ($(OS_ARCH),OpenVMS) +GARBAGE += $(wildcard *.*_defines) +ifdef SHARED_LIBRARY +VMS_SYMVEC_FILE = $(SHARED_LIBRARY:$(DLL_SUFFIX)=_symvec.opt) +ifdef MOZ_DEBUG +VMS_SYMVEC_FILE_MODULE = $(topsrcdir)/build/unix/vms/$(notdir $(SHARED_LIBRARY:$(DLL_SUFFIX)=_dbg_symvec.opt)) +else +VMS_SYMVEC_FILE_MODULE = $(topsrcdir)/build/unix/vms/$(notdir $(SHARED_LIBRARY:$(DLL_SUFFIX)=_symvec.opt)) +endif +VMS_SYMVEC_FILE_COMP = $(topsrcdir)/build/unix/vms/component_symvec.opt +GARBAGE += $(VMS_SYMVEC_FILE) +ifdef IS_COMPONENT +DSO_LDOPTS := $(filter-out -auto_symvec,$(DSO_LDOPTS)) $(VMS_SYMVEC_FILE) +endif +endif +endif + +XPIDL_GEN_DIR = _xpidlgen + +ifdef MOZ_UPDATE_XTERM +# Its good not to have a newline at the end of the titlebar string because it +# makes the make -s output easier to read. Echo -n does not work on all +# platforms, but we can trick sed into doing it. +UPDATE_TITLE = sed -e "s!Y!$(1) in $(shell $(BUILD_TOOLS)/print-depth-path.sh)/$(2)!" $(MOZILLA_DIR)/config/xterm.str; +endif + +define SUBMAKE # $(call SUBMAKE,target,directory) +@$(UPDATE_TITLE) ++@$(MAKE) $(if $(2),-C $(2)) $(1) + +endef # The extra line is important here! don't delete it + +ifneq (,$(strip $(DIRS))) +LOOP_OVER_DIRS = \ + $(foreach dir,$(DIRS),$(call SUBMAKE,$@,$(dir))) +endif + +# we only use this for the makefiles target and other stuff that doesn't matter +ifneq (,$(strip $(PARALLEL_DIRS))) +LOOP_OVER_PARALLEL_DIRS = \ + $(foreach dir,$(PARALLEL_DIRS),$(call SUBMAKE,$@,$(dir))) +endif + +ifneq (,$(strip $(STATIC_DIRS))) +LOOP_OVER_STATIC_DIRS = \ + $(foreach dir,$(STATIC_DIRS),$(call SUBMAKE,$@,$(dir))) +endif + +ifneq (,$(strip $(TOOL_DIRS))) +LOOP_OVER_TOOL_DIRS = \ + $(foreach dir,$(TOOL_DIRS),$(call SUBMAKE,$@,$(dir))) +endif + +ifdef PARALLEL_DIRS +# create a bunch of fake targets for order-only processing +PARALLEL_DIRS_export = $(addsuffix _export,$(PARALLEL_DIRS)) +PARALLEL_DIRS_libs = $(addsuffix _libs,$(PARALLEL_DIRS)) +PARALLEL_DIRS_tools = $(addsuffix _tools,$(PARALLEL_DIRS)) + +.PHONY: $(PARALLEL_DIRS_export) $(PARALLEL_DIRS_libs) $(PARALLEL_DIRS_tools) +endif + +# +# Now we can differentiate between objects used to build a library, and +# objects used to build an executable in the same directory. +# +ifndef PROGOBJS +PROGOBJS = $(OBJS) +endif + +ifndef HOST_PROGOBJS +HOST_PROGOBJS = $(HOST_OBJS) +endif + +# MAKE_DIRS: List of directories to build while looping over directories. +# A Makefile that needs $(MDDEPDIR) created but doesn't set any of these +# variables we know to check can just set NEED_MDDEPDIR explicitly. +ifneq (,$(OBJS)$(XPIDLSRCS)$(SIMPLE_PROGRAMS)$(NEED_MDDEPDIR)) +MAKE_DIRS += $(CURDIR)/$(MDDEPDIR) +GARBAGE_DIRS += $(MDDEPDIR) +endif + +# +# Tags: emacs (etags), vi (ctags) +# TAG_PROGRAM := ctags -L - +# +TAG_PROGRAM = xargs etags -a + +# +# Turn on C++ linking if we have any .cpp or .mm files +# (moved this from config.mk so that config.mk can be included +# before the CPPSRCS are defined) +# +ifneq ($(CPPSRCS)$(CMMSRCS),) +CPP_PROG_LINK = 1 +endif + +# +# Make sure to wrap static libs inside linker specific flags to turn on & off +# inclusion of all symbols inside the static libs +# +ifndef NO_LD_ARCHIVE_FLAGS +ifdef SHARED_LIBRARY_LIBS +EXTRA_DSO_LDOPTS := $(MKSHLIB_FORCE_ALL) $(SHARED_LIBRARY_LIBS) $(MKSHLIB_UNFORCE_ALL) $(EXTRA_DSO_LDOPTS) +endif +endif + +# +# This will strip out symbols that the component should not be +# exporting from the .dynsym section. +# +ifdef IS_COMPONENT +EXTRA_DSO_LDOPTS += $(MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS) +endif # IS_COMPONENT + +# +# Enforce the requirement that MODULE_NAME must be set +# for components in static builds +# +ifdef IS_COMPONENT +ifdef EXPORT_LIBRARY +ifndef FORCE_SHARED_LIB +ifndef MODULE_NAME +$(error MODULE_NAME is required for components which may be used in static builds) +endif +endif +endif +endif + +# +# MacOS X specific stuff +# + +ifeq ($(OS_ARCH),Darwin) +ifdef SHARED_LIBRARY +ifdef IS_COMPONENT +EXTRA_DSO_LDOPTS += -bundle +else +EXTRA_DSO_LDOPTS += -dynamiclib -install_name @executable_path/$(SHARED_LIBRARY) -compatibility_version 1 -current_version 1 -single_module +endif +endif +endif + +# +# On NetBSD a.out systems, use -Bsymbolic. This fixes what would otherwise be +# fatal symbol name clashes between components. +# +ifeq ($(OS_ARCH),NetBSD) +ifeq ($(DLL_SUFFIX),.so.1.0) +ifdef IS_COMPONENT +EXTRA_DSO_LDOPTS += -Wl,-Bsymbolic +endif +endif +endif + +ifeq ($(OS_ARCH),FreeBSD) +ifdef IS_COMPONENT +EXTRA_DSO_LDOPTS += -Wl,-Bsymbolic +endif +endif + +ifeq ($(OS_ARCH),NetBSD) +ifneq (,$(filter arc cobalt hpcmips mipsco newsmips pmax sgimips,$(OS_TEST))) +ifeq ($(MODULE),layout) +OS_CFLAGS += -Wa,-xgot +OS_CXXFLAGS += -Wa,-xgot +endif +endif +endif + +ifeq ($(OS_ARCH),Linux) +ifneq (,$(filter mips mipsel,$(OS_TEST))) +ifeq ($(MODULE),layout) +OS_CFLAGS += -Wa,-xgot +OS_CXXFLAGS += -Wa,-xgot +endif +endif +endif + +# +# HP-UXBeOS specific section: for COMPONENTS only, add -Bsymbolic flag +# which uses internal symbols first +# +ifeq ($(OS_ARCH),HP-UX) +ifdef IS_COMPONENT +ifeq ($(GNU_CC)$(GNU_CXX),) +EXTRA_DSO_LDOPTS += -Wl,-Bsymbolic +ifneq ($(HAS_EXTRAEXPORTS),1) +MKSHLIB += -Wl,+eNSGetModule -Wl,+eerrno +MKCSHLIB += +eNSGetModule +eerrno +ifneq ($(OS_TEST),ia64) +MKSHLIB += -Wl,+e_shlInit +MKCSHLIB += +e_shlInit +endif # !ia64 +endif # !HAS_EXTRAEXPORTS +endif # non-gnu compilers +endif # IS_COMPONENT +endif # HP-UX + +ifeq ($(OS_ARCH),AIX) +ifdef IS_COMPONENT +ifneq ($(HAS_EXTRAEXPORTS),1) +MKSHLIB += -bE:$(MOZILLA_DIR)/build/unix/aix.exp -bnoexpall +MKCSHLIB += -bE:$(MOZILLA_DIR)/build/unix/aix.exp -bnoexpall +endif # HAS_EXTRAEXPORTS +endif # IS_COMPONENT +endif # AIX + +# +# OSF1: add -B symbolic flag for components +# +ifeq ($(OS_ARCH),OSF1) +ifdef IS_COMPONENT +ifeq ($(GNU_CC)$(GNU_CXX),) +EXTRA_DSO_LDOPTS += -B symbolic +endif +endif +endif + +# +# Linux: add -Bsymbolic flag for components +# +ifeq ($(OS_ARCH),Linux) +ifdef IS_COMPONENT +EXTRA_DSO_LDOPTS += -Wl,-Bsymbolic +endif +endif + +# +# MINGW32 +# +ifeq ($(OS_ARCH),WINNT) +ifdef GNU_CC +ifndef IS_COMPONENT +DSO_LDOPTS += -Wl,--out-implib -Wl,$(IMPORT_LIBRARY) +endif +endif +endif + +ifeq ($(USE_TVFS),1) +IFLAGS1 = -rb +IFLAGS2 = -rb +else +IFLAGS1 = -m 644 +IFLAGS2 = -m 755 +endif + +ifeq (_WINNT,$(GNU_CC)_$(OS_ARCH)) +OUTOPTION = -Fo# eol +else +OUTOPTION = -o # eol +endif # WINNT && !GNU_CC +ifneq (,$(filter WINCE,$(OS_ARCH))) +OUTOPTION = -Fo# eol +endif + +ifeq ($(OS_TARGET), WINCE) +OUTOPTION = -Fo# eol +HOST_OUTOPTION = -Fo# eol +else + +ifeq (,$(CROSS_COMPILE)) +HOST_OUTOPTION = $(OUTOPTION) +else +HOST_OUTOPTION = -o # eol +endif + +endif +################################################################################ + +# SUBMAKEFILES: List of Makefiles for next level down. +# This is used to update or create the Makefiles before invoking them. +SUBMAKEFILES += $(addsuffix /Makefile, $(DIRS) $(TOOL_DIRS) $(PARALLEL_DIRS)) + +# The root makefile doesn't want to do a plain export/libs, because +# of the tiers and because of libxul. Suppress the default rules in favor +# of something else. Makefiles which use this var *must* provide a sensible +# default rule before including rules.mk +ifndef SUPPRESS_DEFAULT_RULES +ifdef TIERS +default all alldep:: + $(foreach tier,$(TIERS),$(call SUBMAKE,tier_$(tier))) +else + +default all:: +ifneq (,$(strip $(STATIC_DIRS))) + $(foreach dir,$(STATIC_DIRS),$(call SUBMAKE,,$(dir))) +endif + $(MAKE) export + $(MAKE) libs + $(MAKE) tools + +# Do depend as well +alldep:: + $(MAKE) export + $(MAKE) depend + $(MAKE) libs + $(MAKE) tools + +endif # TIERS +endif # SUPPRESS_DEFAULT_RULES + +ifeq ($(filter s,$(MAKEFLAGS)),) +ECHO := echo +QUIET := +else +ECHO := true +QUIET := -q +endif + +MAKE_TIER_SUBMAKEFILES = +$(if $(tier_$*_dirs),$(MAKE) $(addsuffix /Makefile,$(tier_$*_dirs))) + +export_tier_%: + @$(ECHO) "$@" + @$(MAKE_TIER_SUBMAKEFILES) + $(foreach dir,$(tier_$*_dirs),$(call SUBMAKE,export,$(dir))) + +libs_tier_%: + @$(ECHO) "$@" + @$(MAKE_TIER_SUBMAKEFILES) + $(foreach dir,$(tier_$*_dirs),$(call SUBMAKE,libs,$(dir))) + +tools_tier_%: + @$(ECHO) "$@" + @$(MAKE_TIER_SUBMAKEFILES) + $(foreach dir,$(tier_$*_dirs),$(call SUBMAKE,tools,$(dir))) + +$(foreach tier,$(TIERS),tier_$(tier)):: + @$(ECHO) "$@: $($@_staticdirs) $($@_dirs)" + $(foreach dir,$($@_staticdirs),$(call SUBMAKE,,$(dir))) + $(MAKE) export_$@ + $(MAKE) libs_$@ + +# Do everything from scratch +everything:: + $(MAKE) clean + $(MAKE) alldep + +# Add dummy depend target for tinderboxes +depend:: + +# Target to only regenerate makefiles +makefiles: $(SUBMAKEFILES) +ifneq (,$(DIRS)$(TOOL_DIRS)$(PARALLEL_DIRS)) + $(LOOP_OVER_PARALLEL_DIRS) + $(LOOP_OVER_DIRS) + $(LOOP_OVER_TOOL_DIRS) +endif + +ifdef PARALLEL_DIRS +export:: $(PARALLEL_DIRS_export) + +$(PARALLEL_DIRS_export): %_export: %/Makefile + +@$(call SUBMAKE,export,$*) +endif + +export:: $(SUBMAKEFILES) $(MAKE_DIRS) $(if $(XPIDLSRCS),$(IDL_DIR)) + $(LOOP_OVER_DIRS) + $(LOOP_OVER_TOOL_DIRS) + +ifdef PARALLEL_DIRS +tools:: $(PARALLEL_DIRS_tools) + +$(PARALLEL_DIRS_tools): %_tools: %/Makefile + +@$(call SUBMAKE,tools,$*) +endif + +tools:: $(SUBMAKEFILES) $(MAKE_DIRS) + $(LOOP_OVER_DIRS) +ifneq (,$(strip $(TOOL_DIRS))) + $(foreach dir,$(TOOL_DIRS),$(call SUBMAKE,libs,$(dir))) +endif + +# +# Rule to create list of libraries for final link +# +export:: +ifdef LIBRARY_NAME +ifdef EXPORT_LIBRARY +ifdef IS_COMPONENT +ifdef BUILD_STATIC_LIBS + @$(PERL) -I$(MOZILLA_DIR)/config $(MOZILLA_DIR)/config/build-list.pl $(FINAL_LINK_COMPS) $(STATIC_LIBRARY_NAME) +ifdef MODULE_NAME + @$(PERL) -I$(MOZILLA_DIR)/config $(MOZILLA_DIR)/config/build-list.pl $(FINAL_LINK_COMP_NAMES) $(MODULE_NAME) +endif +endif # BUILD_STATIC_LIBS +else # !IS_COMPONENT + $(PERL) -I$(MOZILLA_DIR)/config $(MOZILLA_DIR)/config/build-list.pl $(FINAL_LINK_LIBS) $(STATIC_LIBRARY_NAME) +endif # IS_COMPONENT +endif # EXPORT_LIBRARY +endif # LIBRARY_NAME + +# Create dependencies on static (and shared EXTRA_DSO_LIBS) libraries +LIBS_DEPS = $(filter %.$(LIB_SUFFIX), $(LIBS)) +HOST_LIBS_DEPS = $(filter %.$(LIB_SUFFIX), $(HOST_LIBS)) +DSO_LDOPTS_DEPS = $(EXTRA_DSO_LIBS) $(filter %.$(LIB_SUFFIX), $(EXTRA_DSO_LDOPTS)) + +ifndef _LIBNAME_RELATIVE_PATHS + +LIBS_DEPS += $(filter -l%, $(LIBS)) +HOST_LIBS_DEPS += $(filter -l%, $(HOST_LIBS)) +DSO_LDOPTS_DEPS += $(filter -l%, $(EXTRA_DSO_LDOPTS)) + +_LIBDIRS = $(patsubst -L%,%,$(filter -L%, $(LIBS) $(HOST_LIBS) $(EXTRA_DSO_LDOPTS))) +ifneq (,$(_LIBDIRS)) +vpath $(LIB_PREFIX)%.$(LIB_SUFFIX) $(_LIBDIRS) +ifdef IMPORT_LIB_SUFFIX +vpath $(LIB_PREFIX)%.$(IMPORT_LIB_SUFFIX) $(_LIBDIRS) +endif # IMPORT_LIB_SUFFIX +vpath $(DLL_PREFIX)%$(DLL_SUFFIX) $(_LIBDIRS) +endif # _LIBDIRS + +endif # _LIBNAME_RELATIVE_PATHS + +# Dependancies which, if modified, should cause everything to rebuild +GLOBAL_DEPS += Makefile Makefile.in $(DEPTH)/config/autoconf.mk $(topsrcdir)/config/config.mk + +############################################## +ifdef PARALLEL_DIRS +libs:: $(PARALLEL_DIRS_libs) + +$(PARALLEL_DIRS_libs): %_libs: %/Makefile + +@$(call SUBMAKE,libs,$*) +endif + +libs:: $(SUBMAKEFILES) $(MAKE_DIRS) $(HOST_LIBRARY) $(LIBRARY) $(SHARED_LIBRARY) $(IMPORT_LIBRARY) $(HOST_PROGRAM) $(PROGRAM) $(HOST_SIMPLE_PROGRAMS) $(SIMPLE_PROGRAMS) $(JAVA_LIBRARY) +ifndef NO_DIST_INSTALL +ifdef LIBRARY +ifdef EXPORT_LIBRARY # Stage libs that will be linked into a static build +ifdef IS_COMPONENT + $(INSTALL) $(IFLAGS1) $(LIBRARY) $(DEPTH)/staticlib/components +else + $(INSTALL) $(IFLAGS1) $(LIBRARY) $(DEPTH)/staticlib +endif +endif # EXPORT_LIBRARY +ifdef DIST_INSTALL +ifdef IS_COMPONENT + $(error Shipping static component libs makes no sense.) +else + $(INSTALL) $(IFLAGS1) $(LIBRARY) $(DIST)/lib +endif +endif # DIST_INSTALL +endif # LIBRARY +ifdef SHARED_LIBRARY +ifdef IS_COMPONENT + $(INSTALL) $(IFLAGS2) $(SHARED_LIBRARY) $(FINAL_TARGET)/components + $(ELF_DYNSTR_GC) $(FINAL_TARGET)/components/$(SHARED_LIBRARY) + @$(PERL) -I$(MOZILLA_DIR)/config $(MOZILLA_DIR)/config/build-list.pl $(FINAL_TARGET)/components/components.list $(SHARED_LIBRARY) +ifdef BEOS_ADDON_WORKAROUND + ( cd $(FINAL_TARGET)/components && $(CC) -nostart -o $(SHARED_LIBRARY).stub $(SHARED_LIBRARY) ) +endif +else # ! IS_COMPONENT +ifneq (,$(filter OS2 WINNT WINCE,$(OS_ARCH))) + $(INSTALL) $(IFLAGS2) $(IMPORT_LIBRARY) $(DIST)/lib +else + $(INSTALL) $(IFLAGS2) $(SHARED_LIBRARY) $(DIST)/lib +endif + $(INSTALL) $(IFLAGS2) $(SHARED_LIBRARY) $(FINAL_TARGET) +ifdef BEOS_ADDON_WORKAROUND + ( cd $(FINAL_TARGET) && $(CC) -nostart -o $(SHARED_LIBRARY).stub $(SHARED_LIBRARY) ) +endif +endif # IS_COMPONENT +endif # SHARED_LIBRARY +ifdef PROGRAM + $(INSTALL) $(IFLAGS2) $(PROGRAM) $(FINAL_TARGET) +endif +ifdef SIMPLE_PROGRAMS + $(INSTALL) $(IFLAGS2) $(SIMPLE_PROGRAMS) $(FINAL_TARGET) +endif +ifdef HOST_PROGRAM + $(INSTALL) $(IFLAGS2) $(HOST_PROGRAM) $(DIST)/host/bin +endif +ifdef HOST_SIMPLE_PROGRAMS + $(INSTALL) $(IFLAGS2) $(HOST_SIMPLE_PROGRAMS) $(DIST)/host/bin +endif +ifdef HOST_LIBRARY + $(INSTALL) $(IFLAGS1) $(HOST_LIBRARY) $(DIST)/host/lib +endif +ifdef JAVA_LIBRARY +ifdef IS_COMPONENT + $(INSTALL) $(IFLAGS1) $(JAVA_LIBRARY) $(FINAL_TARGET)/components +else + $(INSTALL) $(IFLAGS1) $(JAVA_LIBRARY) $(FINAL_TARGET) +endif +endif # JAVA_LIBRARY +endif # !NO_DIST_INSTALL + $(LOOP_OVER_DIRS) + +############################################## + +ifndef NO_PROFILE_GUIDED_OPTIMIZE +ifdef MOZ_PROFILE_USE +ifeq ($(OS_ARCH)_$(GNU_CC)$(INTERNAL_TOOLS), WINNT_) +# When building with PGO, we have to make sure to re-link +# in the MOZ_PROFILE_USE phase if we linked in the +# MOZ_PROFILE_GENERATE phase. We'll touch this pgo.relink +# file in the link rule in the GENERATE phase to indicate +# that we need a relink. +ifdef SHARED_LIBRARY +$(SHARED_LIBRARY): pgo.relink +endif +ifdef PROGRAM +$(PROGRAM): pgo.relink +endif + +# In the second pass, we need to merge the pgc files into the pgd file. +# The compiler would do this for us automatically if they were in the right +# place, but they're in dist/bin. +ifneq (,$(SHARED_LIBRARY)$(PROGRAM)) +export:: +ifdef PROGRAM + $(PYTHON) $(topsrcdir)/build/win32/pgomerge.py \ + $(PROGRAM:$(BIN_SUFFIX)=) $(DIST)/bin +endif +ifdef SHARED_LIBRARY + $(PYTHON) $(topsrcdir)/build/win32/pgomerge.py \ + $(SHARED_LIBRARY_NAME) $(DIST)/bin +endif +endif # SHARED_LIBRARY || PROGRAM +endif # WINNT_ +endif # MOZ_PROFILE_GENERATE || MOZ_PROFILE_USE +endif # NO_PROFILE_GUIDED_OPTIMIZE + +############################################## + +checkout: + $(MAKE) -C $(topsrcdir) -f client.mk checkout + +clean clobber realclean clobber_all:: $(SUBMAKEFILES) + -rm -f $(ALL_TRASH) + -rm -rf $(ALL_TRASH_DIRS) + $(foreach dir,$(PARALLEL_DIRS) $(DIRS) $(STATIC_DIRS) $(TOOL_DIRS),-$(call SUBMAKE,$@,$(dir))) + +distclean:: $(SUBMAKEFILES) + $(foreach dir,$(PARALLEL_DIRS) $(DIRS) $(STATIC_DIRS) $(TOOL_DIRS),-$(call SUBMAKE,$@,$(dir))) + -rm -rf $(ALL_TRASH_DIRS) + -rm -f $(ALL_TRASH) \ + Makefile .HSancillary \ + $(wildcard *.$(OBJ_SUFFIX)) $(wildcard *.ho) $(wildcard host_*.o*) \ + $(wildcard *.$(LIB_SUFFIX)) $(wildcard *$(DLL_SUFFIX)) \ + $(wildcard *.$(IMPORT_LIB_SUFFIX)) +ifeq ($(OS_ARCH),OS2) + -rm -f $(PROGRAM:.exe=.map) +endif + +alltags: + rm -f TAGS + find $(topsrcdir) -name dist -prune -o \( -name '*.[hc]' -o -name '*.cp' -o -name '*.cpp' -o -name '*.idl' \) -print | $(TAG_PROGRAM) + +# +# PROGRAM = Foo +# creates OBJS, links with LIBS to create Foo +# +$(PROGRAM): $(PROGOBJS) $(LIBS_DEPS) $(EXTRA_DEPS) $(EXE_DEF_FILE) $(RESFILE) $(GLOBAL_DEPS) +ifeq (WINCE,$(OS_ARCH)) + $(LD) -NOLOGO -OUT:$@ $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(PROGOBJS) $(RESFILE) $(LIBS) $(EXTRA_LIBS) $(OS_LIBS) +else +ifeq (_WINNT,$(GNU_CC)_$(OS_ARCH)) + $(LD) -NOLOGO -OUT:$@ -PDB:$(LINK_PDBFILE) $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(PROGOBJS) $(RESFILE) $(LIBS) $(EXTRA_LIBS) $(OS_LIBS) +ifdef MSMANIFEST_TOOL + @if test -f $@.manifest; then \ + if test -f "$(srcdir)/$@.manifest"; then \ + mt.exe -NOLOGO -MANIFEST "$(win_srcdir)/$@.manifest" $@.manifest -OUTPUTRESOURCE:$@\;1; \ + else \ + mt.exe -NOLOGO -MANIFEST $@.manifest -OUTPUTRESOURCE:$@\;1; \ + fi; \ + rm -f $@.manifest; \ + fi +endif # MSVC with manifest tool +ifdef MOZ_PROFILE_GENERATE +# touch it a few seconds into the future to work around FAT's +# 2-second granularity + touch -t `date +%Y%m%d%H%M.%S -d "now+5seconds"` pgo.relink +endif +else # !WINNT || GNU_CC +ifeq ($(CPP_PROG_LINK),1) + $(CCC) -o $@ $(CXXFLAGS) $(WRAP_MALLOC_CFLAGS) $(PROGOBJS) $(RESFILE) $(WIN32_EXE_LDFLAGS) $(SOLARIS_JEMALLOC_LDFLAGS) $(LDFLAGS) $(LIBS_DIR) $(LIBS) $(OS_LIBS) $(EXTRA_LIBS) $(BIN_FLAGS) $(WRAP_MALLOC_LIB) $(EXE_DEF_FILE) +else # ! CPP_PROG_LINK + $(CC) -o $@ $(CFLAGS) $(PROGOBJS) $(RESFILE) $(WIN32_EXE_LDFLAGS) $(SOLARIS_JEMALLOC_LDFLAGS) $(LDFLAGS) $(LIBS_DIR) $(LIBS) $(OS_LIBS) $(EXTRA_LIBS) $(BIN_FLAGS) $(EXE_DEF_FILE) +endif # CPP_PROG_LINK +endif # WINNT && !GNU_CC +endif # WINCE + +ifdef ENABLE_STRIP + $(STRIP) $@ +endif +ifdef MOZ_POST_PROGRAM_COMMAND + $(MOZ_POST_PROGRAM_COMMAND) $@ +endif +ifeq ($(OS_ARCH),BeOS) +ifdef BEOS_PROGRAM_RESOURCE + xres -o $@ $(BEOS_PROGRAM_RESOURCE) + mimeset $@ +endif +endif # BeOS + +$(HOST_PROGRAM): $(HOST_PROGOBJS) $(HOST_LIBS_DEPS) $(HOST_EXTRA_DEPS) $(GLOBAL_DEPS) +ifeq (WINCE,$(OS_ARCH)) + $(HOST_LD) -NOLOGO -OUT:$@ $(HOST_OBJS) $(WIN32_EXE_LDFLAGS) $(HOST_LIBS) $(HOST_EXTRA_LIBS) +else +ifeq (_WINNT,$(GNU_CC)_$(HOST_OS_ARCH)) + $(HOST_LD) -NOLOGO -OUT:$@ -PDB:$(HOST_PDBFILE) $(HOST_OBJS) $(WIN32_EXE_LDFLAGS) $(HOST_LIBS) $(HOST_EXTRA_LIBS) +ifdef MSMANIFEST_TOOL + @if test -f $@.manifest; then \ + if test -f "$(srcdir)/$@.manifest"; then \ + mt.exe -NOLOGO -MANIFEST "$(win_srcdir)/$@.manifest" $@.manifest -OUTPUTRESOURCE:$@\;1; \ + else \ + mt.exe -NOLOGO -MANIFEST $@.manifest -OUTPUTRESOURCE:$@\;1; \ + fi; \ + rm -f $@.manifest; \ + fi +endif # MSVC with manifest tool +else +ifeq ($(CPP_PROG_LINK),1) + $(HOST_CXX) -o $@ $(HOST_CXXFLAGS) $(HOST_LDFLAGS) $(HOST_PROGOBJS) $(HOST_LIBS) $(HOST_EXTRA_LIBS) +else + $(HOST_CC) -o $@ $(HOST_CFLAGS) $(HOST_LDFLAGS) $(HOST_PROGOBJS) $(HOST_LIBS) $(HOST_EXTRA_LIBS) +endif # CPP_PROG_LINK +endif +endif + +# +# This is an attempt to support generation of multiple binaries +# in one directory, it assumes everything to compile Foo is in +# Foo.o (from either Foo.c or Foo.cpp). +# +# SIMPLE_PROGRAMS = Foo Bar +# creates Foo.o Bar.o, links with LIBS to create Foo, Bar. +# +$(SIMPLE_PROGRAMS): %$(BIN_SUFFIX): %.$(OBJ_SUFFIX) $(LIBS_DEPS) $(EXTRA_DEPS) $(GLOBAL_DEPS) +ifeq (WINCE,$(OS_ARCH)) + $(LD) -nologo -entry:main -out:$@ $< $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(LIBS) $(EXTRA_LIBS) $(OS_LIBS) +else +ifeq (_WINNT,$(GNU_CC)_$(OS_ARCH)) + $(LD) -nologo -out:$@ -pdb:$(LINK_PDBFILE) $< $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(LIBS) $(EXTRA_LIBS) $(OS_LIBS) +ifdef MSMANIFEST_TOOL + @if test -f $@.manifest; then \ + mt.exe -NOLOGO -MANIFEST $@.manifest -OUTPUTRESOURCE:$@\;1; \ + rm -f $@.manifest; \ + fi +endif # MSVC with manifest tool +else +ifeq ($(CPP_PROG_LINK),1) + $(CCC) $(WRAP_MALLOC_CFLAGS) $(CXXFLAGS) -o $@ $< $(WIN32_EXE_LDFLAGS) $(SOLARIS_JEMALLOC_LDFLAGS) $(LDFLAGS) $(LIBS_DIR) $(LIBS) $(OS_LIBS) $(EXTRA_LIBS) $(WRAP_MALLOC_LIB) $(BIN_FLAGS) +else + $(CC) $(WRAP_MALLOC_CFLAGS) $(CFLAGS) $(OUTOPTION)$@ $< $(WIN32_EXE_LDFLAGS) $(SOLARIS_JEMALLOC_LDFLAGS) $(LDFLAGS) $(LIBS_DIR) $(LIBS) $(OS_LIBS) $(EXTRA_LIBS) $(WRAP_MALLOC_LIB) $(BIN_FLAGS) +endif # CPP_PROG_LINK +endif # WINNT && !GNU_CC +endif # WINCE + +ifdef ENABLE_STRIP + $(STRIP) $@ +endif +ifdef MOZ_POST_PROGRAM_COMMAND + $(MOZ_POST_PROGRAM_COMMAND) $@ +endif + +$(HOST_SIMPLE_PROGRAMS): host_%$(HOST_BIN_SUFFIX): host_%.$(OBJ_SUFFIX) $(HOST_LIBS_DEPS) $(HOST_EXTRA_DEPS) $(GLOBAL_DEPS) +ifeq (WINCE,$(OS_ARCH)) + $(HOST_LD) -NOLOGO -OUT:$@ $(WIN32_EXE_LDFLAGS) $< $(HOST_LIBS) $(HOST_EXTRA_LIBS) +else +ifeq (WINNT_,$(HOST_OS_ARCH)_$(GNU_CC)) + $(HOST_LD) -NOLOGO -OUT:$@ -PDB:$(HOST_PDBFILE) $< $(WIN32_EXE_LDFLAGS) $(HOST_LIBS) $(HOST_EXTRA_LIBS) +else +ifneq (,$(HOST_CPPSRCS)$(USE_HOST_CXX)) + $(HOST_CXX) $(HOST_OUTOPTION)$@ $(HOST_CXXFLAGS) $(INCLUDES) $< $(HOST_LIBS) $(HOST_EXTRA_LIBS) +else + $(HOST_CC) $(HOST_OUTOPTION)$@ $(HOST_CFLAGS) $(INCLUDES) $< $(HOST_LIBS) $(HOST_EXTRA_LIBS) +endif +endif +endif + +# +# Purify target. Solaris/sparc only to start. +# Purify does not recognize "egcs" or "c++" so we go with +# "gcc" and "g++" for now. +# +pure: $(PROGRAM) +ifeq ($(CPP_PROG_LINK),1) + $(PURIFY) $(CCC) -o $^.pure $(CXXFLAGS) $(PROGOBJS) $(LDFLAGS) $(LIBS_DIR) $(LIBS) $(OS_LIBS) $(EXTRA_LIBS) +else + $(PURIFY) $(CC) -o $^.pure $(CFLAGS) $(PROGOBJS) $(LDFLAGS) $(LIBS_DIR) $(LIBS) $(OS_LIBS) $(EXTRA_LIBS) +endif +ifndef NO_DIST_INSTALL + $(INSTALL) $(IFLAGS2) $^.pure $(FINAL_TARGET) +endif + +quantify: $(PROGRAM) +ifeq ($(CPP_PROG_LINK),1) + $(QUANTIFY) $(CCC) -o $^.quantify $(CXXFLAGS) $(PROGOBJS) $(LDFLAGS) $(LIBS_DIR) $(LIBS) $(OS_LIBS) $(EXTRA_LIBS) +else + $(QUANTIFY) $(CC) -o $^.quantify $(CFLAGS) $(PROGOBJS) $(LDFLAGS) $(LIBS_DIR) $(LIBS) $(OS_LIBS) $(EXTRA_LIBS) +endif +ifndef NO_DIST_INSTALL + $(INSTALL) $(IFLAGS2) $^.quantify $(FINAL_TARGET) +endif + +# +# This allows us to create static versions of the shared libraries +# that are built using other static libraries. Confused...? +# +ifdef SHARED_LIBRARY_LIBS +ifeq (,$(GNU_LD)$(filter-out OS2 WINNT WINCE, $(OS_ARCH))) +ifneq (,$(BUILD_STATIC_LIBS)$(FORCE_STATIC_LIB)) +LOBJS += $(SHARED_LIBRARY_LIBS) +endif +else +ifneq (,$(filter OSF1 BSD_OS FreeBSD NetBSD OpenBSD SunOS Darwin,$(OS_ARCH))) +CLEANUP1 := | egrep -v '(________64ELEL_|__.SYMDEF)' +CLEANUP2 := rm -f ________64ELEL_ __.SYMDEF +else +CLEANUP2 := true +endif +SUB_LOBJS = $(shell for lib in $(SHARED_LIBRARY_LIBS); do $(AR_LIST) $${lib} $(CLEANUP1); done;) +endif +endif +ifdef MOZILLA_PROBE_LIBS +PROBE_LOBJS = $(shell for lib in $(MOZILLA_PROBE_LIBS); do $(AR_LIST) $${lib} $(CLEANUP1); done;) +endif +ifdef DTRACE_PROBE_OBJ +EXTRA_DEPS += $(DTRACE_PROBE_OBJ) +endif + +$(LIBRARY): $(OBJS) $(LOBJS) $(SHARED_LIBRARY_LIBS) $(EXTRA_DEPS) $(GLOBAL_DEPS) + rm -f $@ +ifneq (,$(GNU_LD)$(filter-out OS2 WINNT WINCE, $(OS_ARCH))) +ifdef SHARED_LIBRARY_LIBS + @rm -f $(SUB_LOBJS) + @for lib in $(SHARED_LIBRARY_LIBS); do $(AR_EXTRACT) $${lib}; $(CLEANUP2); done +endif +endif + $(AR) $(AR_FLAGS) $(OBJS) $(LOBJS) $(SUB_LOBJS) + $(RANLIB) $@ + @rm -f foodummyfilefoo $(SUB_LOBJS) + +ifeq (,$(filter-out WINNT WINCE, $(OS_ARCH))) +$(IMPORT_LIBRARY): $(SHARED_LIBRARY) +endif + +ifeq ($(OS_ARCH),OS2) +$(DEF_FILE): $(OBJS) $(SHARED_LIBRARY_LIBS) + rm -f $@ + echo LIBRARY $(SHARED_LIBRARY_NAME) INITINSTANCE TERMINSTANCE > $@ + echo PROTMODE >> $@ + echo CODE LOADONCALL MOVEABLE DISCARDABLE >> $@ + echo DATA PRELOAD MOVEABLE MULTIPLE NONSHARED >> $@ + echo EXPORTS >> $@ +ifeq ($(IS_COMPONENT),1) +ifeq ($(HAS_EXTRAEXPORTS),1) +ifndef MOZ_OS2_USE_DECLSPEC + $(FILTER) $(OBJS) $(SHARED_LIBRARY_LIBS) >> $@ +endif +else + echo _NSGetModule >> $@ +endif +else +ifndef MOZ_OS2_USE_DECLSPEC + $(FILTER) $(OBJS) $(SHARED_LIBRARY_LIBS) >> $@ +endif +endif + $(ADD_TO_DEF_FILE) + +ifdef MOZ_OS2_USE_DECLSPEC +$(IMPORT_LIBRARY): $(SHARED_LIBRARY) +else +$(IMPORT_LIBRARY): $(DEF_FILE) +endif + rm -f $@ + $(IMPLIB) $@ $^ + $(RANLIB) $@ +endif # OS/2 + +$(HOST_LIBRARY): $(HOST_OBJS) Makefile + rm -f $@ + $(HOST_AR) $(HOST_AR_FLAGS) $(HOST_OBJS) + $(HOST_RANLIB) $@ + +ifdef NO_LD_ARCHIVE_FLAGS +SUB_SHLOBJS = $(SUB_LOBJS) +endif + +ifdef HAVE_DTRACE +ifndef XP_MACOSX +ifdef DTRACE_PROBE_OBJ +ifndef DTRACE_LIB_DEPENDENT +$(DTRACE_PROBE_OBJ): $(OBJS) + dtrace -G -C -32 -s $(MOZILLA_DTRACE_SRC) -o $(DTRACE_PROBE_OBJ) $(OBJS) +endif +endif +endif +endif + +# On Darwin (Mac OS X), dwarf2 debugging uses debug info left in .o files, +# so instead of deleting .o files after repacking them into a dylib, we make +# symlinks back to the originals. The symlinks are a no-op for stabs debugging, +# so no need to conditionalize on OS version or debugging format. + +$(SHARED_LIBRARY): $(OBJS) $(LOBJS) $(DEF_FILE) $(RESFILE) $(SHARED_LIBRARY_LIBS) $(EXTRA_DEPS) $(DSO_LDOPTS_DEPS) $(GLOBAL_DEPS) +ifndef INCREMENTAL_LINKER + rm -f $@ +endif +ifeq ($(OS_ARCH),OpenVMS) + @if test ! -f $(VMS_SYMVEC_FILE); then \ + if test -f $(VMS_SYMVEC_FILE_MODULE); then \ + echo Creating specific component options file $(VMS_SYMVEC_FILE); \ + cp $(VMS_SYMVEC_FILE_MODULE) $(VMS_SYMVEC_FILE); \ + fi; \ + fi +ifdef IS_COMPONENT + @if test ! -f $(VMS_SYMVEC_FILE); then \ + echo Creating generic component options file $(VMS_SYMVEC_FILE); \ + cp $(VMS_SYMVEC_FILE_COMP) $(VMS_SYMVEC_FILE); \ + fi +endif +endif # OpenVMS +ifdef NO_LD_ARCHIVE_FLAGS +ifdef SHARED_LIBRARY_LIBS + @rm -f $(SUB_SHLOBJS) + @for lib in $(SHARED_LIBRARY_LIBS); do $(AR_EXTRACT) $${lib}; $(CLEANUP2); done +ifeq ($(OS_ARCH),Darwin) + @echo Making symlinks to the original object files in the archive libraries $(SHARED_LIBRARY_LIBS) + @for lib in $(SHARED_LIBRARY_LIBS); do \ + libdir=`echo $$lib|sed -e 's,/[^/]*\.a,,'`; \ + ofiles=`$(AR_LIST) $${lib}`; \ + for ofile in $$ofiles; do \ + if [ -f $$libdir/$$ofile ]; then \ + rm -f $$ofile; \ + ln -s $$libdir/$$ofile $$ofile; \ + fi; \ + done; \ + done +endif +endif # SHARED_LIBRARY_LIBS +endif # NO_LD_ARCHIVE_FLAGS +ifdef DTRACE_LIB_DEPENDENT + @rm -f $(PROBE_LOBJS) + @for lib in $(MOZILLA_PROBE_LIBS); do $(AR_EXTRACT) $${lib}; $(CLEANUP2); done +ifndef XP_MACOSX + dtrace -G -C -32 -s $(MOZILLA_DTRACE_SRC) -o $(DTRACE_PROBE_OBJ) $(PROBE_LOBJS) +endif + @for lib in $(MOZILLA_PROBE_LIBS); do \ + ofiles=`$(AR_LIST) $${lib}`; \ + $(AR_DELETE) $${lib} $$ofiles; \ + done + $(MKSHLIB) $(SHLIB_LDSTARTFILE) $(OBJS) $(LOBJS) $(SUB_SHLOBJS) $(DTRACE_PROBE_OBJ) $(PROBE_LOBJS) $(RESFILE) $(LDFLAGS) $(EXTRA_DSO_LDOPTS) $(OS_LIBS) $(EXTRA_LIBS) $(DEF_FILE) $(SHLIB_LDENDFILE) + @rm -f $(PROBE_LOBJS) + @rm -f $(DTRACE_PROBE_OBJ) + @for lib in $(MOZILLA_PROBE_LIBS); do \ + if [ -L $${lib} ]; then rm -f `readlink $${lib}`; fi; \ + done + @rm -f $(MOZILLA_PROBE_LIBS) + +else # ! DTRACE_LIB_DEPENDENT + $(MKSHLIB) $(SHLIB_LDSTARTFILE) $(OBJS) $(DTRACE_PROBE_OBJ) $(LOBJS) $(SUB_SHLOBJS) $(RESFILE) $(LDFLAGS) $(EXTRA_DSO_LDOPTS) $(OS_LIBS) $(EXTRA_LIBS) $(DEF_FILE) $(SHLIB_LDENDFILE) +endif # DTRACE_LIB_DEPENDENT + +ifeq (_WINNT,$(GNU_CC)_$(OS_ARCH)) +ifdef MSMANIFEST_TOOL +ifdef EMBED_MANIFEST_AT + @if test -f $@.manifest; then \ + mt.exe -NOLOGO -MANIFEST $@.manifest -OUTPUTRESOURCE:$@\;$(EMBED_MANIFEST_AT); \ + rm -f $@.manifest; \ + fi +endif # EMBED_MANIFEST_AT +endif # MSVC with manifest tool +ifdef MOZ_PROFILE_GENERATE + touch -t `date +%Y%m%d%H%M.%S -d "now+5seconds"` pgo.relink +endif +endif # WINNT && !GCC +ifneq ($(OS_ARCH),Darwin) + @rm -f $(SUB_SHLOBJS) +endif # Darwin + @rm -f foodummyfilefoo $(DELETE_AFTER_LINK) + chmod +x $@ +ifdef ENABLE_STRIP + $(STRIP) $@ +endif +ifdef MOZ_POST_DSO_LIB_COMMAND + $(MOZ_POST_DSO_LIB_COMMAND) $@ +endif + +ifdef MOZ_AUTO_DEPS +ifdef COMPILER_DEPEND +ifeq ($(SOLARIS_SUNPRO_CC),1) +_MDDEPFILE = $(MDDEPDIR)/$(@F).pp + +define MAKE_DEPS_AUTO_CC +if test -d $(@D); then \ + echo "Building deps for $< using Sun Studio cc"; \ + $(CC) $(COMPILE_CFLAGS) -xM $< >$(_MDDEPFILE) ; \ +fi +endef +define MAKE_DEPS_AUTO_CXX +if test -d $(@D); then \ + echo "Building deps for $< using Sun Studio CC"; \ + $(CXX) $(COMPILE_CXXFLAGS) -xM $< >$(_MDDEPFILE) ; \ +fi +endef +endif # Sun Studio on Solaris +else # COMPILER_DEPEND +# +# Generate dependencies on the fly +# +_MDDEPFILE = $(MDDEPDIR)/$(@F).pp + +define MAKE_DEPS_AUTO +if test -d $(@D); then \ + echo "Building deps for $<"; \ + $(MKDEPEND) -o'.$(OBJ_SUFFIX)' -f- $(DEFINES) $(ACDEFINES) $(INCLUDES) $< 2>/dev/null | sed -e "s|^[^ ]*/||" > $(_MDDEPFILE) ; \ +fi +endef + +MAKE_DEPS_AUTO_CC = $(MAKE_DEPS_AUTO) +MAKE_DEPS_AUTO_CXX = $(MAKE_DEPS_AUTO) + +endif # COMPILER_DEPEND + +endif # MOZ_AUTO_DEPS + +ifdef MOZ_MEMORY +ifeq ($(OS_ARCH),SunOS) +SOLARIS_JEMALLOC_LDFLAGS = $(call EXPAND_LIBNAME_PATH,jemalloc,$(DIST)/lib) +endif +endif + +# Rules for building native targets must come first because of the host_ prefix +host_%.$(OBJ_SUFFIX): %.c $(GLOBAL_DEPS) + $(REPORT_BUILD) + $(ELOG) $(HOST_CC) $(HOST_OUTOPTION)$@ -c $(HOST_CFLAGS) $(INCLUDES) $(NSPR_CFLAGS) $(_VPATH_SRCS) + +host_%.$(OBJ_SUFFIX): %.cpp $(GLOBAL_DEPS) + $(REPORT_BUILD) + $(ELOG) $(HOST_CXX) $(HOST_OUTOPTION)$@ -c $(HOST_CXXFLAGS) $(INCLUDES) $(NSPR_CFLAGS) $(_VPATH_SRCS) + +host_%.$(OBJ_SUFFIX): %.cc $(GLOBAL_DEPS) + $(REPORT_BUILD) + $(ELOG) $(HOST_CXX) $(HOST_OUTOPTION)$@ -c $(HOST_CXXFLAGS) $(INCLUDES) $(NSPR_CFLAGS) $(_VPATH_SRCS) + +host_%.$(OBJ_SUFFIX): %.m $(GLOBAL_DEPS) + $(REPORT_BUILD) + $(ELOG) $(HOST_CC) $(HOST_OUTOPTION)$@ -c $(HOST_CFLAGS) $(HOST_CMFLAGS) $(INCLUDES) $(NSPR_CFLAGS) $(_VPATH_SRCS) + +host_%.$(OBJ_SUFFIX): %.mm $(GLOBAL_DEPS) + $(REPORT_BUILD) + $(ELOG) $(HOST_CXX) $(HOST_OUTOPTION)$@ -c $(HOST_CXXFLAGS) $(HOST_CMMFLAGS) $(INCLUDES) $(NSPR_CFLAGS) $(_VPATH_SRCS) + +%:: %.c $(GLOBAL_DEPS) + $(REPORT_BUILD) + @$(MAKE_DEPS_AUTO_CC) + $(ELOG) $(CC) $(CFLAGS) $(LDFLAGS) $(OUTOPTION)$@ $(_VPATH_SRCS) + +%.$(OBJ_SUFFIX): %.c $(GLOBAL_DEPS) + $(REPORT_BUILD) + @$(MAKE_DEPS_AUTO_CC) + $(ELOG) $(CC) $(OUTOPTION)$@ -c $(COMPILE_CFLAGS) $(_VPATH_SRCS) + +moc_%.cpp: %.h $(GLOBAL_DEPS) + $(MOC) $< $(OUTOPTION)$@ + +ifdef ASFILES +# The AS_DASH_C_FLAG is needed cause not all assemblers (Solaris) accept +# a '-c' flag. +%.$(OBJ_SUFFIX): %.$(ASM_SUFFIX) $(GLOBAL_DEPS) + $(AS) -o $@ $(ASFLAGS) $(AS_DASH_C_FLAG) $(_VPATH_SRCS) +endif + +%.$(OBJ_SUFFIX): %.S $(GLOBAL_DEPS) + $(AS) -o $@ $(ASFLAGS) -c $< + +%:: %.cpp $(GLOBAL_DEPS) + @$(MAKE_DEPS_AUTO_CXX) + $(CCC) $(OUTOPTION)$@ $(CXXFLAGS) $(_VPATH_SRCS) $(LDFLAGS) + +# +# Please keep the next two rules in sync. +# +%.$(OBJ_SUFFIX): %.cc $(GLOBAL_DEPS) + $(REPORT_BUILD) + @$(MAKE_DEPS_AUTO_CXX) + $(ELOG) $(CCC) $(OUTOPTION)$@ -c $(COMPILE_CXXFLAGS) $(_VPATH_SRCS) + +%.$(OBJ_SUFFIX): %.cpp $(GLOBAL_DEPS) + $(REPORT_BUILD) + @$(MAKE_DEPS_AUTO_CXX) +ifdef STRICT_CPLUSPLUS_SUFFIX + echo "#line 1 \"$*.cpp\"" | cat - $*.cpp > t_$*.cc + $(ELOG) $(CCC) -o $@ -c $(COMPILE_CXXFLAGS) t_$*.cc + rm -f t_$*.cc +else + $(ELOG) $(CCC) $(OUTOPTION)$@ -c $(COMPILE_CXXFLAGS) $(_VPATH_SRCS) +endif #STRICT_CPLUSPLUS_SUFFIX + +$(OBJ_PREFIX)%.$(OBJ_SUFFIX): %.mm $(GLOBAL_DEPS) + $(REPORT_BUILD) + @$(MAKE_DEPS_AUTO_CXX) + $(ELOG) $(CCC) -o $@ -c $(COMPILE_CXXFLAGS) $(COMPILE_CMMFLAGS) $(_VPATH_SRCS) + +$(OBJ_PREFIX)%.$(OBJ_SUFFIX): %.m $(GLOBAL_DEPS) + $(REPORT_BUILD) + @$(MAKE_DEPS_AUTO_CC) + $(ELOG) $(CC) -o $@ -c $(COMPILE_CFLAGS) $(COMPILE_CMFLAGS) $(_VPATH_SRCS) + +%.s: %.cpp + $(CCC) -S $(COMPILE_CXXFLAGS) $(_VPATH_SRCS) + +%.s: %.cc + $(CCC) -S $(COMPILE_CXXFLAGS) $(_VPATH_SRCS) + +%.s: %.c + $(CC) -S $(COMPILE_CFLAGS) $(_VPATH_SRCS) + +%.i: %.cpp + $(CCC) -C -E $(COMPILE_CXXFLAGS) $(_VPATH_SRCS) > $*.i + +%.i: %.cc + $(CCC) -C -E $(COMPILE_CXXFLAGS) $(_VPATH_SRCS) > $*.i + +%.i: %.c + $(CC) -C -E $(COMPILE_CFLAGS) $(_VPATH_SRCS) > $*.i + +%.i: %.mm + $(CCC) -C -E $(COMPILE_CXXFLAGS) $(COMPILE_CMMFLAGS) $(_VPATH_SRCS) > $*.i + +%.res: %.rc + @echo Creating Resource file: $@ +ifeq ($(OS_ARCH),OS2) + $(RC) $(RCFLAGS:-D%=-d %) -i $(subst /,\,$(srcdir)) -r $< $@ +else +ifdef GNU_CC + $(RC) $(RCFLAGS) $(filter-out -U%,$(DEFINES)) $(INCLUDES:-I%=--include-dir %) $(OUTOPTION)$@ $(_VPATH_SRCS) +else + $(RC) $(RCFLAGS) -r $(DEFINES) $(INCLUDES) $(OUTOPTION)$@ $(_VPATH_SRCS) +endif +endif + +# need 3 separate lines for OS/2 +%:: %.pl + rm -f $@ + cp $< $@ + chmod +x $@ + +%:: %.sh + rm -f $@; cp $< $@; chmod +x $@ + +# Cancel these implicit rules +# +%: %,v + +%: RCS/%,v + +%: s.% + +%: SCCS/s.% + +############################################################################### +# Java rules +############################################################################### +ifneq (,$(filter OS2 WINNT WINCE,$(OS_ARCH))) +SEP := ; +else +SEP := : +endif + +EMPTY := +SPACE := $(EMPTY) $(EMPTY) + +# Cygwin and MSYS have their own special path form, but javac expects the source +# and class paths to be in the DOS form (i.e. e:/builds/...). This function +# does the appropriate conversion on Windows, but is a noop on other systems. +ifeq (,$(filter-out WINNT WINCE, $(HOST_OS_ARCH))) +ifdef CYGWIN_WRAPPER +normalizepath = $(foreach p,$(1),$(shell cygpath -m $(p))) +else +# assume MSYS +# We use 'pwd -W' to get DOS form of the path. However, since the given path +# could be a file or a non-existent path, we cannot call 'pwd -W' directly +# on the path. Instead, we extract the root path (i.e. "c:/"), call 'pwd -W' +# on it, then merge with the rest of the path. +root-path = $(shell echo $(1) | sed -e "s|\(/[^/]*\)/\?\(.*\)|\1|") +non-root-path = $(shell echo $(1) | sed -e "s|\(/[^/]*\)/\?\(.*\)|\2|") +normalizepath = $(foreach p,$(1),$(if $(filter /%,$(1)),$(patsubst %/,%,$(shell cd $(call root-path,$(1)) && pwd -W))/$(call non-root-path,$(1)),$(1))) +endif +else +normalizepath = $(1) +endif + +_srcdir = $(call normalizepath,$(srcdir)) +ifdef JAVA_SOURCEPATH +SP = $(subst $(SPACE),$(SEP),$(call normalizepath,$(strip $(JAVA_SOURCEPATH)))) +_JAVA_SOURCEPATH = ".$(SEP)$(_srcdir)$(SEP)$(SP)" +else +_JAVA_SOURCEPATH = ".$(SEP)$(_srcdir)" +endif + +ifdef JAVA_CLASSPATH +CP = $(subst $(SPACE),$(SEP),$(call normalizepath,$(strip $(JAVA_CLASSPATH)))) +_JAVA_CLASSPATH = ".$(SEP)$(CP)" +else +_JAVA_CLASSPATH = . +endif + +_JAVA_DIR = _java +$(_JAVA_DIR):: + $(NSINSTALL) -D $@ + +$(_JAVA_DIR)/%.class: %.java $(GLOBAL_DEPS) $(_JAVA_DIR) + $(CYGWIN_WRAPPER) $(JAVAC) $(JAVAC_FLAGS) -classpath $(_JAVA_CLASSPATH) \ + -sourcepath $(_JAVA_SOURCEPATH) -d $(_JAVA_DIR) $(_VPATH_SRCS) + +$(JAVA_LIBRARY): $(addprefix $(_JAVA_DIR)/,$(JAVA_SRCS:.java=.class)) $(GLOBAL_DEPS) + $(JAR) cf $@ -C $(_JAVA_DIR) . + +GARBAGE_DIRS += $(_JAVA_DIR) + +############################################################################### +# Update Makefiles +############################################################################### + +# In GNU make 3.80, makefiles must use the /cygdrive syntax, even if we're +# processing them with AS perl. See bug 232003 +ifdef AS_PERL +CYGWIN_TOPSRCDIR = -nowrap -p $(topsrcdir) -wrap +endif + +# Note: Passing depth to make-makefile is optional. +# It saves the script some work, though. +Makefile: Makefile.in + @$(PERL) $(AUTOCONF_TOOLS)/make-makefile -t $(topsrcdir) -d $(DEPTH) $(CYGWIN_TOPSRCDIR) + +ifdef SUBMAKEFILES +# VPATH does not work on some machines in this case, so add $(srcdir) +$(SUBMAKEFILES): % : $(srcdir)/%.in + $(PERL) $(AUTOCONF_TOOLS)/make-makefile -t $(topsrcdir) -d $(DEPTH) $(CYGWIN_TOPSRCDIR) $@ +endif + +ifdef AUTOUPDATE_CONFIGURE +$(topsrcdir)/configure: $(topsrcdir)/configure.in + (cd $(topsrcdir) && $(AUTOCONF)) && (cd $(DEPTH) && ./config.status --recheck) +endif + +############################################################################### +# Bunch of things that extend the 'export' rule (in order): +############################################################################### + +################################################################################ +# Copy each element of EXPORTS to $(DIST)/include + +ifdef MOZ_JAVAXPCOM +ifneq ($(XPIDLSRCS),) +$(JAVA_DIST_DIR):: + $(NSINSTALL) -D $@ +endif +endif + +ifneq ($(XPI_NAME),) +$(FINAL_TARGET): + $(NSINSTALL) -D $@ + +export:: $(FINAL_TARGET) +endif + +ifndef NO_DIST_INSTALL +ifneq (,$(EXPORTS)) +export:: $(EXPORTS) + $(INSTALL) $(IFLAGS1) $^ $(DIST)/include +endif +endif # NO_DIST_INSTALL + +define EXPORT_NAMESPACE_RULE +ifndef NO_DIST_INSTALL +export:: $(EXPORTS_$(namespace)) + $(INSTALL) $(IFLAGS1) $$^ $(DIST)/include/$(namespace) +endif # NO_DIST_INSTALL +endef + +$(foreach namespace,$(EXPORTS_NAMESPACES),$(eval $(EXPORT_NAMESPACE_RULE))) + +################################################################################ +# Copy each element of PREF_JS_EXPORTS + +ifdef GRE_MODULE +PREF_DIR = greprefs +else +ifneq (,$(XPI_NAME)$(LIBXUL_SDK)) +PREF_DIR = defaults/preferences +else +PREF_DIR = defaults/pref +endif +endif + +ifneq ($(PREF_JS_EXPORTS),) +# on win32, pref files need CRLF line endings... see bug 206029 +ifeq (WINNT,$(OS_ARCH)) +PREF_PPFLAGS = --line-endings=crlf +endif + +ifndef NO_DIST_INSTALL +$(FINAL_TARGET)/$(PREF_DIR): + $(NSINSTALL) -D $@ + +libs:: $(FINAL_TARGET)/$(PREF_DIR) $(PREF_JS_EXPORTS) + $(EXIT_ON_ERROR) \ + for i in $(PREF_JS_EXPORTS); do \ + dest=$(FINAL_TARGET)/$(PREF_DIR)/`basename $$i`; \ + $(RM) -f $$dest; \ + $(PYTHON) $(topsrcdir)/config/Preprocessor.py $(PREF_PPFLAGS) $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) $$i > $$dest; \ + done +endif +endif + +################################################################################ +# Copy each element of AUTOCFG_JS_EXPORTS to $(FINAL_TARGET)/defaults/autoconfig + +ifneq ($(AUTOCFG_JS_EXPORTS),) +$(FINAL_TARGET)/defaults/autoconfig:: + $(NSINSTALL) -D $@ + +ifndef NO_DIST_INSTALL +export:: $(AUTOCFG_JS_EXPORTS) $(FINAL_TARGET)/defaults/autoconfig + $(INSTALL) $(IFLAGS1) $^ +endif + +endif +################################################################################ +# Export the elements of $(XPIDLSRCS) +# generating .h and .xpt files and moving them to the appropriate places. + +ifneq ($(XPIDLSRCS),) + +export:: $(patsubst %.idl,$(XPIDL_GEN_DIR)/%.h, $(XPIDLSRCS)) + +ifndef XPIDL_MODULE +XPIDL_MODULE = $(MODULE) +endif + +ifeq ($(XPIDL_MODULE),) # we need $(XPIDL_MODULE) to make $(XPIDL_MODULE).xpt +export:: FORCE + @echo + @echo "*** Error processing XPIDLSRCS:" + @echo "Please define MODULE or XPIDL_MODULE when defining XPIDLSRCS," + @echo "so we have a module name to use when creating MODULE.xpt." + @echo; sleep 2; false +endif + +$(IDL_DIR):: + $(NSINSTALL) -D $@ + +# generate .h files from into $(XPIDL_GEN_DIR), then export to $(DIST)/include; +# warn against overriding existing .h file. +$(XPIDL_GEN_DIR)/.done: + @if test ! -d $(XPIDL_GEN_DIR); then echo Creating $(XPIDL_GEN_DIR)/.done; rm -rf $(XPIDL_GEN_DIR); mkdir $(XPIDL_GEN_DIR); fi + @touch $@ + +# don't depend on $(XPIDL_GEN_DIR), because the modification date changes +# with any addition to the directory, regenerating all .h files -> everything. + +$(XPIDL_GEN_DIR)/%.h: %.idl $(XPIDL_COMPILE) $(XPIDL_GEN_DIR)/.done + $(REPORT_BUILD) + $(ELOG) $(XPIDL_COMPILE) -m header -w $(XPIDL_FLAGS) -o $(XPIDL_GEN_DIR)/$* $(_VPATH_SRCS) + @if test -n "$(findstring $*.h, $(EXPORTS))"; \ + then echo "*** WARNING: file $*.h generated from $*.idl overrides $(srcdir)/$*.h"; else true; fi + +ifndef NO_GEN_XPT +# generate intermediate .xpt files into $(XPIDL_GEN_DIR), then link +# into $(XPIDL_MODULE).xpt and export it to $(FINAL_TARGET)/components. +$(XPIDL_GEN_DIR)/%.xpt: %.idl $(XPIDL_COMPILE) $(XPIDL_GEN_DIR)/.done + $(REPORT_BUILD) + $(ELOG) $(XPIDL_COMPILE) -m typelib -w $(XPIDL_FLAGS) -e $@ -d $(MDDEPDIR)/$*.pp $(_VPATH_SRCS) + +# no need to link together if XPIDLSRCS contains only XPIDL_MODULE +ifneq ($(XPIDL_MODULE).idl,$(strip $(XPIDLSRCS))) +$(XPIDL_GEN_DIR)/$(XPIDL_MODULE).xpt: $(patsubst %.idl,$(XPIDL_GEN_DIR)/%.xpt,$(XPIDLSRCS)) $(GLOBAL_DEPS) $(XPIDL_LINK) + $(XPIDL_LINK) $(XPIDL_GEN_DIR)/$(XPIDL_MODULE).xpt $(patsubst %.idl,$(XPIDL_GEN_DIR)/%.xpt,$(XPIDLSRCS)) +endif # XPIDL_MODULE.xpt != XPIDLSRCS + +libs:: $(XPIDL_GEN_DIR)/$(XPIDL_MODULE).xpt +ifndef NO_DIST_INSTALL + $(INSTALL) $(IFLAGS1) $(XPIDL_GEN_DIR)/$(XPIDL_MODULE).xpt $(FINAL_TARGET)/components +endif + +endif # NO_GEN_XPT + +GARBAGE_DIRS += $(XPIDL_GEN_DIR) + +endif # XPIDLSRCS + +ifneq ($(XPIDLSRCS),) +# export .idl files to $(IDL_DIR) +ifndef NO_DIST_INSTALL +export:: $(XPIDLSRCS) $(IDL_DIR) + $(INSTALL) $(IFLAGS1) $^ + +export:: $(patsubst %.idl,$(XPIDL_GEN_DIR)/%.h, $(XPIDLSRCS)) $(DIST)/include + $(INSTALL) $(IFLAGS1) $^ +endif # NO_DIST_INSTALL + +endif # XPIDLSRCS + + + +# +# General rules for exporting idl files. +# +# WORK-AROUND ONLY, for mozilla/tools/module-deps/bootstrap.pl build. +# Bug to fix idl dependency problems w/o this extra build pass is +# http://bugzilla.mozilla.org/show_bug.cgi?id=145777 +# +$(IDL_DIR):: + $(NSINSTALL) -D $@ + +export-idl:: $(SUBMAKEFILES) $(MAKE_DIRS) + +ifneq ($(XPIDLSRCS),) +ifndef NO_DIST_INSTALL +export-idl:: $(XPIDLSRCS) $(IDL_DIR) + $(INSTALL) $(IFLAGS1) $^ +endif +endif + $(LOOP_OVER_PARALLEL_DIRS) + $(LOOP_OVER_DIRS) + $(LOOP_OVER_TOOL_DIRS) + +ifdef MOZ_JAVAXPCOM +ifneq ($(XPIDLSRCS),) + +JAVA_XPIDLSRCS = $(XPIDLSRCS) + +# A single IDL file can contain multiple interfaces, which result in multiple +# Java interface files. So use hidden dependency files. +JAVADEPFILES = $(addprefix $(JAVA_GEN_DIR)/.,$(JAVA_XPIDLSRCS:.idl=.java.pp)) + +$(JAVA_GEN_DIR): + $(NSINSTALL) -D $@ +GARBAGE_DIRS += $(JAVA_GEN_DIR) + +# generate .java files into _javagen/[package name dirs] +_JAVA_GEN_DIR = $(JAVA_GEN_DIR)/$(JAVA_IFACES_PKG_NAME) +$(_JAVA_GEN_DIR): + $(NSINSTALL) -D $@ + +$(JAVA_GEN_DIR)/.%.java.pp: %.idl $(XPIDL_COMPILE) $(_JAVA_GEN_DIR) + $(REPORT_BUILD) + $(ELOG) $(XPIDL_COMPILE) -m java -w -I$(srcdir) -I$(IDL_DIR) -o $(_JAVA_GEN_DIR)/$* $(_VPATH_SRCS) + @touch $@ + +# "Install" generated Java interfaces. We segregate them based on the XPI_NAME. +# If XPI_NAME is not set, install into the "default" directory. +ifneq ($(XPI_NAME),) +JAVA_INSTALL_DIR = $(JAVA_DIST_DIR)/$(XPI_NAME) +else +JAVA_INSTALL_DIR = $(JAVA_DIST_DIR)/default +endif + +$(JAVA_INSTALL_DIR): + $(NSINSTALL) -D $@ + +export:: $(JAVA_DIST_DIR) $(JAVADEPFILES) $(JAVA_INSTALL_DIR) + (cd $(JAVA_GEN_DIR) && tar $(TAR_CREATE_FLAGS) - .) | (cd $(JAVA_INSTALL_DIR) && tar -xf -) + +endif # XPIDLSRCS +endif # MOZ_JAVAXPCOM + +################################################################################ +# Copy each element of EXTRA_COMPONENTS to $(FINAL_TARGET)/components +ifdef EXTRA_COMPONENTS +libs:: $(EXTRA_COMPONENTS) +ifndef NO_DIST_INSTALL + $(INSTALL) $(IFLAGS1) $^ $(FINAL_TARGET)/components + @$(PERL) -I$(MOZILLA_DIR)/config $(MOZILLA_DIR)/config/build-list.pl $(FINAL_TARGET)/components/components.list $(notdir $^) +endif + +endif + +ifdef EXTRA_PP_COMPONENTS +libs:: $(EXTRA_PP_COMPONENTS) +ifndef NO_DIST_INSTALL + $(EXIT_ON_ERROR) \ + $(NSINSTALL) -D $(FINAL_TARGET)/components; \ + for i in $^; do \ + fname=`basename $$i`; \ + dest=$(FINAL_TARGET)/components/$${fname}; \ + $(RM) -f $$dest; \ + $(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) $$i > $$dest; \ + $(PERL) -I$(MOZILLA_DIR)/config $(MOZILLA_DIR)/config/build-list.pl $(FINAL_TARGET)/components/components.list $$fname; \ + done +endif + +endif + +################################################################################ +# Copy each element of EXTRA_JS_MODULES to $(FINAL_TARGET)/modules +ifdef EXTRA_JS_MODULES +libs:: $(EXTRA_JS_MODULES) +ifndef NO_DIST_INSTALL + $(INSTALL) $(IFLAGS1) $^ $(FINAL_TARGET)/modules +endif + +endif + +ifdef EXTRA_PP_JS_MODULES +libs:: $(EXTRA_PP_JS_MODULES) +ifndef NO_DIST_INSTALL + $(EXIT_ON_ERROR) \ + $(NSINSTALL) -D $(FINAL_TARGET)/modules; \ + for i in $^; do \ + dest=$(FINAL_TARGET)/modules/`basename $$i`; \ + $(RM) -f $$dest; \ + $(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) $$i > $$dest; \ + done +endif + +endif + +################################################################################ +# SDK + +ifneq (,$(SDK_LIBRARY)) +$(SDK_LIB_DIR):: + $(NSINSTALL) -D $@ + +ifndef NO_DIST_INSTALL +libs:: $(SDK_LIBRARY) $(SDK_LIB_DIR) + $(INSTALL) $(IFLAGS2) $^ +endif + +endif # SDK_LIBRARY + +ifneq (,$(SDK_BINARY)) +$(SDK_BIN_DIR):: + $(NSINSTALL) -D $@ + +ifndef NO_DIST_INSTALL +libs:: $(SDK_BINARY) $(SDK_BIN_DIR) + $(INSTALL) $(IFLAGS2) $^ +endif + +endif # SDK_BINARY + +################################################################################ +# CHROME PACKAGING + +JAR_MANIFEST := $(srcdir)/jar.mn + +chrome:: + $(MAKE) realchrome + $(LOOP_OVER_PARALLEL_DIRS) + $(LOOP_OVER_DIRS) + $(LOOP_OVER_TOOL_DIRS) + +$(FINAL_TARGET)/chrome: + $(NSINSTALL) -D $@ + +ifneq (,$(wildcard $(JAR_MANIFEST))) +ifndef NO_DIST_INSTALL +libs realchrome:: $(CHROME_DEPS) $(FINAL_TARGET)/chrome + $(PYTHON) $(MOZILLA_DIR)/config/JarMaker.py \ + $(QUIET) -j $(FINAL_TARGET)/chrome \ + $(MAKE_JARS_FLAGS) $(XULPPFLAGS) $(DEFINES) $(ACDEFINES) \ + $(JAR_MANIFEST) +endif +endif + +ifneq ($(DIST_FILES),) +$(DIST)/bin: + $(NSINSTALL) -D $@ + +libs:: $(DIST_FILES) $(DIST)/bin + @$(EXIT_ON_ERROR) \ + for f in $(DIST_FILES); do \ + dest=$(FINAL_TARGET)/`basename $$f`; \ + $(RM) -f $$dest; \ + $(PYTHON) $(MOZILLA_DIR)/config/Preprocessor.py \ + $(XULAPP_DEFINES) $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) \ + $(srcdir)/$$f > $$dest; \ + done +endif + +ifneq ($(DIST_CHROME_FILES),) +libs:: $(DIST_CHROME_FILES) + @$(EXIT_ON_ERROR) \ + for f in $(DIST_CHROME_FILES); do \ + dest=$(FINAL_TARGET)/chrome/`basename $$f`; \ + $(RM) -f $$dest; \ + $(PYTHON) $(MOZILLA_DIR)/config/Preprocessor.py \ + $(XULAPP_DEFINES) $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) \ + $(srcdir)/$$f > $$dest; \ + done +endif + +ifneq ($(XPI_PKGNAME),) +libs realchrome:: +ifdef STRIP_XPI +ifndef MOZ_DEBUG + @echo "Stripping $(XPI_PKGNAME) package directory..." + @echo $(FINAL_TARGET) + @cd $(FINAL_TARGET) && find . ! -type d \ + ! -name "*.js" \ + ! -name "*.xpt" \ + ! -name "*.gif" \ + ! -name "*.jpg" \ + ! -name "*.png" \ + ! -name "*.xpm" \ + ! -name "*.txt" \ + ! -name "*.rdf" \ + ! -name "*.sh" \ + ! -name "*.properties" \ + ! -name "*.dtd" \ + ! -name "*.html" \ + ! -name "*.xul" \ + ! -name "*.css" \ + ! -name "*.xml" \ + ! -name "*.jar" \ + ! -name "*.dat" \ + ! -name "*.tbl" \ + ! -name "*.src" \ + ! -name "*.reg" \ + $(PLATFORM_EXCLUDE_LIST) \ + -exec $(STRIP) $(STRIP_FLAGS) {} >/dev/null 2>&1 \; +endif +endif + @echo "Packaging $(XPI_PKGNAME).xpi..." + cd $(FINAL_TARGET) && $(ZIP) -qr ../$(XPI_PKGNAME).xpi * +endif + +ifdef INSTALL_EXTENSION_ID +ifndef XPI_NAME +$(error XPI_NAME must be set for INSTALL_EXTENSION_ID) +endif + +libs:: + $(RM) -rf "$(DIST)/bin/extensions/$(INSTALL_EXTENSION_ID)" + $(NSINSTALL) -D "$(DIST)/bin/extensions/$(INSTALL_EXTENSION_ID)" + cd $(FINAL_TARGET) && tar $(TAR_CREATE_FLAGS) - . | (cd "../../bin/extensions/$(INSTALL_EXTENSION_ID)" && tar -xf -) + +endif + +ifneq (,$(filter flat symlink,$(MOZ_CHROME_FILE_FORMAT))) +_JAR_REGCHROME_DISABLE_JAR=1 +else +_JAR_REGCHROME_DISABLE_JAR=0 +endif + +REGCHROME = $(PERL) -I$(MOZILLA_DIR)/config $(MOZILLA_DIR)/config/add-chrome.pl \ + $(if $(filter gtk2,$(MOZ_WIDGET_TOOLKIT)),-x) \ + $(if $(CROSS_COMPILE),-o $(OS_ARCH)) $(FINAL_TARGET)/chrome/installed-chrome.txt \ + $(_JAR_REGCHROME_DISABLE_JAR) + +REGCHROME_INSTALL = $(PERL) -I$(MOZILLA_DIR)/config $(MOZILLA_DIR)/config/add-chrome.pl \ + $(if $(filter gtk2,$(MOZ_WIDGET_TOOLKIT)),-x) \ + $(if $(CROSS_COMPILE),-o $(OS_ARCH)) $(DESTDIR)$(mozappdir)/chrome/installed-chrome.txt \ + $(_JAR_REGCHROME_DISABLE_JAR) + + +############################################################################# +# Dependency system +############################################################################# +ifdef COMPILER_DEPEND +depend:: + @echo "$(MAKE): No need to run depend target.\ + Using compiler-based depend." 1>&2 +ifeq ($(GNU_CC)$(GNU_CXX),) +# Non-GNU compilers + @echo "`echo '$(MAKE):'|sed 's/./ /g'`"\ + '(Compiler-based depend was turned on by "--enable-md".)' 1>&2 +else +# GNU compilers + @space="`echo '$(MAKE): '|sed 's/./ /g'`";\ + echo "$$space"'Since you are using a GNU compiler,\ + it is on by default.' 1>&2; \ + echo "$$space"'To turn it off, pass --disable-md to configure.' 1>&2 +endif + +else # ! COMPILER_DEPEND + +ifndef MOZ_AUTO_DEPS + +define MAKE_DEPS_NOAUTO + $(MKDEPEND) -w1024 -o'.$(OBJ_SUFFIX)' -f- $(DEFINES) $(ACDEFINES) $(INCLUDES) $< 2>/dev/null | sed -e "s|^[^ ]*/||" > $@ +endef + +$(MDDEPDIR)/%.pp: %.c + $(REPORT_BUILD) + @$(MAKE_DEPS_NOAUTO) + +$(MDDEPDIR)/%.pp: %.cpp + $(REPORT_BUILD) + @$(MAKE_DEPS_NOAUTO) + +$(MDDEPDIR)/%.pp: %.s + $(REPORT_BUILD) + @$(MAKE_DEPS_NOAUTO) + +ifneq (,$(OBJS)$(XPIDLSRCS)$(SIMPLE_PROGRAMS)) +depend:: $(SUBMAKEFILES) $(MAKE_DIRS) $(MDDEPFILES) +else +depend:: $(SUBMAKEFILES) +endif + $(LOOP_OVER_PARALLEL_DIRS) + $(LOOP_OVER_DIRS) + $(LOOP_OVER_TOOL_DIRS) + +dependclean:: $(SUBMAKEFILES) + rm -f $(MDDEPFILES) + $(LOOP_OVER_PARALLEL_DIRS) + $(LOOP_OVER_DIRS) + $(LOOP_OVER_TOOL_DIRS) + +endif # MOZ_AUTO_DEPS + +endif # COMPILER_DEPEND + + +############################################################################# +# MDDEPDIR is the subdirectory where all the dependency files are placed. +# This uses a make rule (instead of a macro) to support parallel +# builds (-jN). If this were done in the LOOP_OVER_DIRS macro, two +# processes could simultaneously try to create the same directory. +# +# We use $(CURDIR) in the rule's target to ensure that we don't find +# a dependency directory in the source tree via VPATH (perhaps from +# a previous build in the source tree) and thus neglect to create a +# dependency directory in the object directory, where we really need +# it. + +$(CURDIR)/$(MDDEPDIR): + @if test ! -d $@; then echo Creating $@; rm -rf $@; mkdir $@; else true; fi + +ifneq (,$(filter-out all chrome default export realchrome tools clean clobber clobber_all distclean realclean,$(MAKECMDGOALS))) +ifneq (,$(OBJS)$(XPIDLSRCS)$(SIMPLE_PROGRAMS)) +MDDEPEND_FILES := $(strip $(wildcard $(MDDEPDIR)/*.pp)) + +ifneq (,$(MDDEPEND_FILES)) +# The script mddepend.pl checks the dependencies and writes to stdout +# one rule to force out-of-date objects. For example, +# foo.o boo.o: FORCE +# The script has an advantage over including the *.pp files directly +# because it handles the case when header files are removed from the build. +# 'make' would complain that there is no way to build missing headers. +ALL_PP_RESULTS = $(shell $(PERL) $(BUILD_TOOLS)/mddepend.pl - $(MDDEPEND_FILES)) +$(eval $(ALL_PP_RESULTS)) +endif + +endif +endif +############################################################################# + +-include $(topsrcdir)/$(MOZ_BUILD_APP)/app-rules.mk +-include $(MY_RULES) + +# +# This speeds up gmake's processing if these files don't exist. +# +$(MY_CONFIG) $(MY_RULES): + @touch $@ + +# +# Generate Emacs tags in a file named TAGS if ETAGS was set in $(MY_CONFIG) +# or in $(MY_RULES) +# +ifdef ETAGS +ifneq ($(CSRCS)$(CPPSRCS)$(HEADERS),) +all:: TAGS +TAGS:: $(CSRCS) $(CPPSRCS) $(HEADERS) + $(ETAGS) $(CSRCS) $(CPPSRCS) $(HEADERS) +endif +endif + +################################################################################ +# Special gmake rules. +################################################################################ + + +# +# Disallow parallel builds with MSVC < 8 +# +ifneq (,$(filter 1200 1300 1310,$(_MSC_VER))) +.NOTPARALLEL: +endif + +# +# Re-define the list of default suffixes, so gmake won't have to churn through +# hundreds of built-in suffix rules for stuff we don't need. +# +.SUFFIXES: + +# +# Fake targets. Always run these rules, even if a file/directory with that +# name already exists. +# +.PHONY: all alltags boot checkout chrome realchrome clean clobber clobber_all export install libs makefiles realclean run_apprunner tools $(DIRS) $(TOOL_DIRS) FORCE + +# Used as a dependency to force targets to rebuild +FORCE: + +# Delete target if error occurs when building target +.DELETE_ON_ERROR: + +# Properly set LIBPATTERNS for the platform +.LIBPATTERNS = $(if $(IMPORT_LIB_SUFFIX),$(LIB_PREFIX)%.$(IMPORT_LIB_SUFFIX)) $(LIB_PREFIX)%.$(LIB_SUFFIX) $(DLL_PREFIX)%$(DLL_SUFFIX) + +tags: TAGS + +TAGS: $(SUBMAKEFILES) $(CSRCS) $(CPPSRCS) $(wildcard *.h) + -etags $(CSRCS) $(CPPSRCS) $(wildcard *.h) + $(LOOP_OVER_PARALLEL_DIRS) + $(LOOP_OVER_DIRS) + +echo-variable-%: + @echo "$($*)" + +echo-tiers: + @echo $(TIERS) + +echo-tier-dirs: + @$(foreach tier,$(TIERS),echo '$(tier):'; echo ' dirs: $(tier_$(tier)_dirs)'; echo ' staticdirs: $(tier_$(tier)_staticdirs)'; ) + +echo-dirs: + @echo $(DIRS) + +echo-module: + @echo $(MODULE) + +echo-requires: + @echo $(REQUIRES) + +echo-depth-path: + @$(topsrcdir)/build/unix/print-depth-path.sh + +echo-module-name: + @$(topsrcdir)/build/package/rpm/print-module-name.sh + +echo-module-filelist: + @$(topsrcdir)/build/package/rpm/print-module-filelist.sh + +showtargs: +ifneq (,$(filter $(PROGRAM) $(HOST_PROGRAM) $(SIMPLE_PROGRAMS) $(HOST_LIBRARY) $(LIBRARY) $(SHARED_LIBRARY),$(TARGETS))) + @echo -------------------------------------------------------------------------------- + @echo "PROGRAM = $(PROGRAM)" + @echo "SIMPLE_PROGRAMS = $(SIMPLE_PROGRAMS)" + @echo "LIBRARY = $(LIBRARY)" + @echo "SHARED_LIBRARY = $(SHARED_LIBRARY)" + @echo "SHARED_LIBRARY_LIBS = $(SHARED_LIBRARY_LIBS)" + @echo "LIBS = $(LIBS)" + @echo "DEF_FILE = $(DEF_FILE)" + @echo "IMPORT_LIBRARY = $(IMPORT_LIBRARY)" + @echo "STATIC_LIBS = $(STATIC_LIBS)" + @echo "SHARED_LIBS = $(SHARED_LIBS)" + @echo "EXTRA_DSO_LIBS = $(EXTRA_DSO_LIBS)" + @echo "EXTRA_DSO_LDOPTS = $(EXTRA_DSO_LDOPTS)" + @echo "DEPENDENT_LIBS = $(DEPENDENT_LIBS)" + @echo -------------------------------------------------------------------------------- +endif + $(LOOP_OVER_PARALLEL_DIRS) + $(LOOP_OVER_DIRS) + +showbuild: + @echo "MOZ_BUILD_ROOT = $(MOZ_BUILD_ROOT)" + @echo "MOZ_WIDGET_TOOLKIT = $(MOZ_WIDGET_TOOLKIT)" + @echo "CC = $(CC)" + @echo "CXX = $(CXX)" + @echo "CCC = $(CCC)" + @echo "CPP = $(CPP)" + @echo "LD = $(LD)" + @echo "AR = $(AR)" + @echo "IMPLIB = $(IMPLIB)" + @echo "FILTER = $(FILTER)" + @echo "MKSHLIB = $(MKSHLIB)" + @echo "MKCSHLIB = $(MKCSHLIB)" + @echo "RC = $(RC)" + @echo "CFLAGS = $(CFLAGS)" + @echo "OS_CFLAGS = $(OS_CFLAGS)" + @echo "COMPILE_CFLAGS = $(COMPILE_CFLAGS)" + @echo "CXXFLAGS = $(CXXFLAGS)" + @echo "OS_CXXFLAGS = $(OS_CXXFLAGS)" + @echo "COMPILE_CXXFLAGS = $(COMPILE_CXXFLAGS)" + @echo "COMPILE_CMFLAGS = $(COMPILE_CMFLAGS)" + @echo "COMPILE_CMMFLAGS = $(COMPILE_CMMFLAGS)" + @echo "LDFLAGS = $(LDFLAGS)" + @echo "OS_LDFLAGS = $(OS_LDFLAGS)" + @echo "DSO_LDOPTS = $(DSO_LDOPTS)" + @echo "OS_INCLUDES = $(OS_INCLUDES)" + @echo "OS_LIBS = $(OS_LIBS)" + @echo "EXTRA_LIBS = $(EXTRA_LIBS)" + @echo "BIN_FLAGS = $(BIN_FLAGS)" + @echo "INCLUDES = $(INCLUDES)" + @echo "DEFINES = $(DEFINES)" + @echo "ACDEFINES = $(ACDEFINES)" + @echo "BIN_SUFFIX = $(BIN_SUFFIX)" + @echo "LIB_SUFFIX = $(LIB_SUFFIX)" + @echo "DLL_SUFFIX = $(DLL_SUFFIX)" + @echo "IMPORT_LIB_SUFFIX = $(IMPORT_LIB_SUFFIX)" + @echo "INSTALL = $(INSTALL)" + +showhost: + @echo "HOST_CC = $(HOST_CC)" + @echo "HOST_CXX = $(HOST_CXX)" + @echo "HOST_CFLAGS = $(HOST_CFLAGS)" + @echo "HOST_LDFLAGS = $(HOST_LDFLAGS)" + @echo "HOST_LIBS = $(HOST_LIBS)" + @echo "HOST_EXTRA_LIBS = $(HOST_EXTRA_LIBS)" + @echo "HOST_EXTRA_DEPS = $(HOST_EXTRA_DEPS)" + @echo "HOST_PROGRAM = $(HOST_PROGRAM)" + @echo "HOST_OBJS = $(HOST_OBJS)" + @echo "HOST_PROGOBJS = $(HOST_PROGOBJS)" + @echo "HOST_LIBRARY = $(HOST_LIBRARY)" + +showbuildmods:: + @echo "Build Modules = $(BUILD_MODULES)" + @echo "Module dirs = $(BUILD_MODULE_DIRS)" + +documentation: + @cd $(DEPTH) + $(DOXYGEN) $(DEPTH)/config/doxygen.cfg + +ifdef ENABLE_TESTS +check:: $(SUBMAKEFILES) $(MAKE_DIRS) + $(LOOP_OVER_PARALLEL_DIRS) + $(LOOP_OVER_DIRS) + $(LOOP_OVER_TOOL_DIRS) +endif + + +FREEZE_VARIABLES = \ + CSRCS \ + CPPSRCS \ + EXPORTS \ + XPIDLSRCS \ + DIRS \ + LIBRARY \ + MODULE \ + REQUIRES \ + SHORT_LIBNAME \ + TIERS \ + $(NULL) + +$(foreach var,$(FREEZE_VARIABLES),$(eval $(var)_FROZEN := '$($(var))')) + +CHECK_FROZEN_VARIABLES = $(foreach var,$(FREEZE_VARIABLES), \ + $(if $(subst $($(var)_FROZEN),,'$($(var))'),$(error Makefile variable '$(var)' changed value after including rules.mk. Was $($(var)_FROZEN), now $($(var)).))) + +libs export libs:: + $(CHECK_FROZEN_VARIABLES) diff --git a/ape-server/deps/js/src/config/solaris_ia32.map b/ape-server/deps/js/src/config/solaris_ia32.map new file mode 100755 index 0000000..712b2d8 --- /dev/null +++ b/ape-server/deps/js/src/config/solaris_ia32.map @@ -0,0 +1 @@ +hwcap_1 = OVERRIDE; diff --git a/ape-server/deps/js/src/config/static-checking-config.mk b/ape-server/deps/js/src/config/static-checking-config.mk new file mode 100755 index 0000000..27ca255 --- /dev/null +++ b/ape-server/deps/js/src/config/static-checking-config.mk @@ -0,0 +1,21 @@ +DEHYDRA_SCRIPT = $(topsrcdir)/config/static-checking.js + +DEHYDRA_MODULES = \ + $(NULL) + +TREEHYDRA_MODULES = \ + $(topsrcdir)/jsstack.js \ + $(NULL) + +DEHYDRA_ARGS = \ + --topsrcdir=$(topsrcdir) \ + --objdir=$(DEPTH) \ + --dehydra-modules=$(subst $(NULL) ,$(COMMA),$(strip $(DEHYDRA_MODULES))) \ + --treehydra-modules=$(subst $(NULL) ,$(COMMA),$(strip $(TREEHYDRA_MODULES))) \ + $(NULL) + +DEHYDRA_FLAGS = -fplugin=$(DEHYDRA_PATH) -fplugin-arg='$(DEHYDRA_SCRIPT) $(DEHYDRA_ARGS)' + +ifdef DEHYDRA_PATH +OS_CXXFLAGS += $(DEHYDRA_FLAGS) +endif diff --git a/ape-server/deps/js/src/config/static-checking.js b/ape-server/deps/js/src/config/static-checking.js new file mode 100755 index 0000000..7a64eea --- /dev/null +++ b/ape-server/deps/js/src/config/static-checking.js @@ -0,0 +1,108 @@ +/** + * A script for GCC-dehydra to analyze the Mozilla codebase and catch + * patterns that are incorrect, but which cannot be detected by a compiler. */ + +/** + * Activate Treehydra outparams analysis if running in Treehydra. + */ + +function treehydra_enabled() { + return this.hasOwnProperty('TREE_CODE'); +} + +include('unstable/getopt.js'); +[options, args] = getopt(); + +sys.include_path.push(options.topsrcdir); + +include('string-format.js'); + +let modules = []; + +function LoadModules(modulelist) +{ + if (modulelist == "") + return; + + let modulenames = modulelist.split(','); + for each (let modulename in modulenames) { + let module = { __proto__: this }; + include(modulename, module); + modules.push(module); + } +} + +LoadModules(options['dehydra-modules']); +if (treehydra_enabled()) + LoadModules(options['treehydra-modules']); + +function process_type(c) +{ + for each (let module in modules) + if (module.hasOwnProperty('process_type')) + module.process_type(c); +} + +function hasAttribute(c, attrname) +{ + var attr; + + if (c.attributes === undefined) + return false; + + for each (attr in c.attributes) + if (attr.name == 'user' && attr.value[0] == attrname) + return true; + + return false; +} + +// This is useful for detecting method overrides +function signaturesMatch(m1, m2) +{ + if (m1.shortName != m2.shortName) + return false; + + if ((!!m1.isVirtual) != (!!m2.isVirtual)) + return false; + + if (m1.isStatic != m2.isStatic) + return false; + + let p1 = m1.type.parameters; + let p2 = m2.type.parameters; + + if (p1.length != p2.length) + return false; + + for (let i = 0; i < p1.length; ++i) + if (p1[i] !== p2[i]) + return false; + + return true; +} + +const forward_functions = [ + 'process_type', + 'process_tree_type', + 'process_decl', + 'process_tree_decl', + 'process_function', + 'process_tree', + 'process_cp_pre_genericize', + 'input_end' +]; + +function setup_forwarding(n) +{ + this[n] = function() { + for each (let module in modules) { + if (module.hasOwnProperty(n)) { + module[n].apply(this, arguments); + } + } + } +} + +for each (let n in forward_functions) + setup_forwarding(n); diff --git a/ape-server/deps/js/src/config/string-format.js b/ape-server/deps/js/src/config/string-format.js new file mode 100755 index 0000000..352a5f3 --- /dev/null +++ b/ape-server/deps/js/src/config/string-format.js @@ -0,0 +1,61 @@ +String.prototype.format = function string_format() { + // there are two modes of operation... unnamed indices are read in order; + // named indices using %(name)s. The two styles cannot be mixed. + // Unnamed indices can be passed as either a single argument to this function, + // multiple arguments to this function, or as a single array argument + let curindex = 0; + let d; + + if (arguments.length > 1) { + d = arguments; + } + else + d = arguments[0]; + + function r(s, key, type) { + if (type == '%') + return '%'; + + let v; + if (key == "") { + if (curindex == -1) + throw Error("Cannot mix named and positional indices in string formatting."); + + if (curindex == 0 && (!(d instanceof Object) || !(0 in d))) { + v = d; + } + else if (!(curindex in d)) + throw Error("Insufficient number of items in format, requesting item %i".format(curindex)); + else { + v = d[curindex]; + } + + ++curindex; + } + else { + key = key.slice(1, -1); + if (curindex > 0) + throw Error("Cannot mix named and positional indices in string formatting."); + curindex = -1; + + if (!(key in d)) + throw Error("Key '%s' not present during string substitution.".format(key)); + v = d[key]; + } + switch (type) { + case "s": + if (v === undefined) + return ""; + return v.toString(); + case "r": + return uneval(v); + case "i": + return parseInt(v); + case "f": + return Number(v); + default: + throw Error("Unexpected format character '%s'.".format(type)); + } + } + return this.replace(/%(\([^)]+\))?(.)/g, r); +}; diff --git a/ape-server/deps/js/src/config/system-headers b/ape-server/deps/js/src/config/system-headers new file mode 100755 index 0000000..a57b07b --- /dev/null +++ b/ape-server/deps/js/src/config/system-headers @@ -0,0 +1,1020 @@ +A4Stuff.h +activscp.h +AEDataModel.h +AEObjects.h +AEPackObject.h +AERegistry.h +AEUtils.h +afxcmn.h +afxcoll.h +afxcview.h +afxdisp.h +afxdtctl.h +afxext.h +afxmt.h +afxpriv.h +afxtempl.h +afxwin.h +algorithm +Aliases.h +all.h +alloca.h +alloc.h +alsa/asoundlib.h +alsa/pcm.h +alsa/mixer.h +ansi_parms.h +a.out.h +app/Cursor.h +Appearance.h +AppFileInfo.h +AppKit.h +AppleEvents.h +Application.h +app/Message.h +app/MessageRunner.h +arpa/inet.h +arpa/nameser.h +asm/sigcontext.h +asm/signal.h +ASRegistry.h +assert.h +atk/atk.h +atlbase.h +atlcom.h +atlconv.h +atlctl.cpp +atlctl.h +ATLCTL.H +atlhost.h +atlimpl.cpp +atlwin.cpp +ATSTypes.h +ATSUnicode.h +Balloons.h +base/pblock.h +base/PCR_Base.h +base/session.h +basetyps.h +be/app/Application.h +Beep.h +be/kernel/image.h +be/kernel/OS.h +bfd.h +Bitmap.h +blapi.h +bsd/libc.h +bsd/syscall.h +bstring.h +builtin.h +Button.h +byteswap.h +#if MOZ_ENABLE_LIBXUL!=1 +#define WRAP_CAIRO_HEADERS +#endif +#if MOZ_TREE_CAIRO!=1 +#define WRAP_CAIRO_HEADERS +#endif +#ifdef WRAP_CAIRO_HEADERS +cairo.h +cairo-atsui.h +cairo-beos.h +cairo-ft.h +cairo-glitz.h +cairo-os2.h +cairo-pdf.h +cairo-ps.h +cairo-quartz.h +cairo-win32.h +cairo-xlib.h +cairo-xlib-xrender.h +cairo-directfb.h +cairo-qpainter.h +#endif +dfiff.h +ffi.h +fusion/reactor.h +fusion/property.h +fusion/conf.h +fusion/build.h +fusion/hash.h +fusion/shm/shm.h +fusion/shm/shm_internal.h +fusion/shm/pool.h +fusion/ref.h +fusion/fusion_internal.h +fusion/lock.h +fusion/types.h +fusion/vector.h +fusion/call.h +fusion/shmalloc.h +fusion/protocol.h +fusion/fusion.h +fusion/arena.h +fusion/object.h +directfbgl.h +directfb_version.h +directfb.h +directfb_util.h +directfb_keynames.h +dgiff.h +direct/util.h +direct/memcpy.h +direct/interface.h +direct/conf.h +direct/tree.h +direct/signals.h +direct/build.h +direct/interface_implementation.h +direct/utf8.h +direct/serial.h +direct/hash.h +direct/direct.h +direct/clock.h +direct/types.h +direct/mem.h +direct/thread.h +direct/debug.h +direct/stream.h +direct/messages.h +direct/trace.h +direct/modules.h +direct/log.h +direct/system.h +direct/list.h +dfb_types.h +directfb_strings.h +directfb_keyboard.h +callconv.h +Carbon/Carbon.h +CarbonEvents.h +Carbon.h +cassert +c_asm.h +cctype +cderr.h +cerrno +cert.h +CFBase.h +CFBundle.h +CFData.h +CFDictionary.h +cf.h +CFNumber.h +CFPlugIn.h +CFPreferences.h +CFString.h +CFURL.h +CGAffineTransform.h +CheckBox.h +climits +Clipboard.h +cmplrs/stsupport.h +Cocoa/Cocoa.h +CodeFragments.h +comdef.h +commctrl.h +COMMCTRL.H +commdlg.h +compat.h +condapi.h +ConditionalMacros.h +config.h +conio.h +console.h +ControlDefinitions.h +Controls.h +CoreFoundation/CoreFoundation.h +CoreServices/CoreServices.h +CPalmRec.cpp +Cpalmrec.h +CPCatgry.cpp +CPDbBMgr.h +CPString.cpp +CPString.h +crtdbg.h +crt_externs.h +crypt.h +cstdio +cstdlib +cstring +ctime +ctype.h +curl/curl.h +curl/easy.h +curl/types.h +curses.h +cxxabi.h +DateTimeUtils.h +dbus/dbus.h +dbus/dbus-glib.h +dbus/dbus-glib-lowlevel.h +ddeml.h +Debug.h +dem.h +descrip.h +Devices.h +Dialogs.h +direct.h +dirent.h +DiskInit.h +dlfcn.h +dlgs.h +dl.h +docobj.h +dos/dosextens.h +dos.h +Drag.h +DriverServices.h +DriverSynchronization.h +DropInPanel.h +dvidef.h +elf.h +endian.h +Entry.h +errno.h +Errors.h +Events.h +exdisp.h +ExDisp.h +exe386.h +execinfo.h +extras.h +fabdef.h +fcntl.h +features.h +fibdef.h +File.h +filehdr.h +files.h +Files.h +FindDirectory.h +Finder.h +FinderRegistry.h +FixMath.h +float.h +fnmatch.h +Folders.h +fontconfig/fontconfig.h +fontconfig/fcfreetype.h +Font.h +Fonts.h +fp.h +fpieee.h +frame/log.h +frame/req.h +freetype/freetype.h +freetype/ftcache.h +freetype/ftglyph.h +freetype/ftsynth.h +freetype/ftoutln.h +freetype/ttnameid.h +freetype/tttables.h +freetype/t1tables.h +fribidi/fribidi.h +FSp_fopen.h +fstream.h +ft2build.h +fts.h +gconf/gconf-client.h +Gdiplus.h +gdk/gdk.h +gdk/gdkkeysyms.h +gdk/gdkprivate.h +gdk/gdkx.h +gdk/gdkdirectfb.h +gdk-pixbuf/gdk-pixbuf.h +Gestalt.h +getopt.h +glibconfig.h +glib.h +glib-object.h +gmodule.h +gnome.h +gnu/libc-version.h +grp.h +gssapi_generic.h +gssapi/gssapi_generic.h +gssapi/gssapi.h +gssapi.h +gtk/gtk.h +gtk/gtkprinter.h +gtk/gtkprintjob.h +gtk/gtkprintunixdialog.h +HIToolbox/HIToolbox.h +hlink.h +htiface.h +ia64/sys/inline.h +Icons.h +iconv.h +IDL.h +ieeefp.h +ifaddrs.h +image.h +imagehlp.h +imm.h +initguid.h +InterfaceDefs.h +InternetConfig.h +IntlResources.h +ints.h +intshcut.h +inttypes.h +iodef.h +io.h +IOKit/IOKitLib.h +IOKit/IOMessage.h +IOKit/pwr_mgt/IOPMLib.h +iomanip +iostream +iostream.h +jar.h +JavaControl.h +JavaEmbedding/JavaControl.h +JavaVM/jni.h +JManager.h +JNIEnvTests.h +jni.h +#if MOZ_NATIVE_JPEG==1 +jpeglib.h +#endif +JVMManagerTests.h +Kerberos/Kerberos.h +kernel/image.h +kernel/OS.h +key.h +keyt.h +keythi.h +LAction.h +langinfo.h +LApplication.h +LArray.h +LArrayIterator.h +LAttachable.h +LAttachment.h +LaunchServices.h +lber.h +LBroadcaster.h +LButton.h +lcache.h +LCaption.h +LCheckBox.h +LCicnButton.h +LClipboard.h +LCommander.h +LComparator.h +LControl.h +ldap.h +ldaplog.h +ldappr.h +ldap_ssl.h +LDataStream.h +ldfcn.h +LDialogBox.h +ldif.h +LDocApplication.h +LDocument.h +LDragAndDrop.h +LDragTask.h +LEditField.h +LEditText.h +LEventDispatcher.h +LFile.h +LFileStream.h +LFileTypeList.h +LFocusBox.h +LGrafPortView.h +LHandleStream.h +libc_r.h +libelf.h +libelf/libelf.h +libgen.h +libgnome/gnome-url.h +libgnome/libgnome.h +libgnomeui/gnome-icon-lookup.h +libgnomeui/gnome-icon-theme.h +libgnomeui/gnome-ui-init.h +libgnomevfs/gnome-vfs-file-info.h +libgnomevfs/gnome-vfs.h +libgnomevfs/gnome-vfs-init.h +libgnomevfs/gnome-vfs-mime.h +libgnomevfs/gnome-vfs-mime-handlers.h +libgnomevfs/gnome-vfs-mime-utils.h +libgnomevfs/gnome-vfs-ops.h +libgnomevfs/gnome-vfs-standard-callbacks.h +libIDL/IDL.h +lib$routines.h +libnotify/notify.h +limits +limits.h +link.h +linux/kernel.h +linux/limits.h +linux/rtc.h +linux/version.h +list +List.h +Lists.h +LListBox.h +LListener.h +LMenuBar.h +LMenu.h +LModelDirector.h +LModelObject.h +LModelProperty.h +loader.h +locale +locale.h +LOffscreenView.h +logkeys.h +logstrng.h +Looper.h +LowMem.h +LPane.h +LPeriodical.h +LPicture.h +LPlaceHolder.h +LPrintout.h +LProgressBar.h +LPushButton.h +LRadioGroup.h +LRadioGroupView.h +LRunArray.h +LScroller.h +LSharable.h +LSingleDoc.h +LStaticText.h +LStdControl.h +LStream.h +LString.h +LTabGroup.h +LTabGroupView.h +LTableArrayStorage.h +LTableMonoGeometry.h +LTableSingleSelector.h +LTableView.h +LTextEditView.h +LTextTableView.h +LUndoer.h +LVariableArray.h +LView.h +LWindow.h +m68881.h +MacErrors.h +MacHeadersCarbon.h +machine/ansi.h +machine/builtins.h +machine/clock.h +machine/endian.h +machine/frame.h +machine/inline.h +machine/limits.h +machine/signal.h +machine/trap.h +mach/mach_host.h +mach/mach_init.h +mach/mach_interface.h +mach/mach_port.h +mach-o/dyld.h +MacLocales.h +MacMemory.h +MacTCP.h +MacTypes.h +MacWindows.h +malloc.h +map +mapicode.h +mapidefs.h +mapiguid.h +mapi.h +mapitags.h +mapiutil.h +mapix.h +Math64.h +math.h +mbstring.h +mem.h +memory +memory.h +Memory.h +MenuBar.h +Menu.h +Menus.h +Message.h +Mime.h +MixedMode.h +mlang.h +mmsystem.h +model.h +Movies.h +mpw/errno.h +mshtmhst.h +mshtml.h +mswsock.h +Multiprocessing.h +mutex.h +Navigation.h +ncompat.h +ncurses.h +netCore.h +netdb.h +net/if.h +netinet/in.h +netinet/in_systm.h +netinet/tcp.h +new +newexe.h +new.h +nl_types.h +NodeInfo.h +nss.h +nssilock.h +objbase.h +objidl.h +Objsafe.h +ojiapitests.h +ole2.h +oleidl.h +OpenTptInternet.h +OpenTransport.h +os2.h +OS.h +osreldate.h +OSUtils.h +Packages.h +Palettes.h +PALM_CMN.H +pango-engine.h +pango-glyph.h +pango-modules.h +pango/pangocairo.h +pango/pangofc-decoder.h +pango/pangofc-font.h +pango/pangofc-fontmap.h +pango/pango-break.h +pango/pango-fontmap.h +pango/pango.h +pango/pangoxft.h +pango/pango-utils.h +pango-types.h +pascal.h +Patches.h +Path.h +pcfs/pc_dir.h +Pgenerr.h +PGenErr.h +Ph.h +photon/Pg.h +photon/PhProto.h +photon/PhRender.h +photon/PpProto.h +photon/PtProgress.h +photon/PtServer.h +photon/PtWebClient.h +photon/PxImage.h +pk11func.h +pk11pub.h +pkcs11t.h +PLStringFuncs.h +PMApplication.h +pmddim.h +poll.h +Polygon.h +portable.h +Power.h +PP_ClassHeaders.cp +PP_Constants.h +PPCToolbox.h +PP_DebugHeaders.cp +PP_KeyCodes.h +PP_Macros.h +PP_Messages.h +PP_Prefix.h +PP_Resources.h +PP_Types.h +Printing.h +Print/PMPrintingDialogExtensions.h +private/qucomextra_p.h +Processes.h +process.h +Process.h +proto/dos.h +proto/exec.h +psap.h +Pt.h +pthread.h +pwd.h +Python.h +QDOffscreen.h +Quickdraw.h +QuickDraw.h +QuickTimeComponents.h +quipu/attr.h +rasdlg.h +raserror.h +ras.h +regex.h +Region.h +resolv.h +Resources.h +Retrace.h +rld_interface.h +rmsdef.h +Roster.h +rpc.h +rpcproxy.h +rpc/types.h +sane/sane.h +sane/sanei.h +sane/saneopts.h +Scrap.h +Screen.h +Script.h +ScrollBar.h +seccomon.h +sec.h +secmod.h +secmodt.h +secrng.h +security.h +secutil.h +semaphore.h +servprov.h +setjmp.h +SFNTLayoutTypes.h +SFNTTypes.h +share.h +shellapi.h +shlguid.h +shlobj.h +sigcontext.h +signal.h +SimpleGameSound.h +SIOUX.h +size_t.h +someincludefile.h +Sound.h +sqlite3.h +ssdef.h +sslerr.h +ssl.h +sslproto.h +sstream +stack +StandardFile.h +starlet.h +stat.h +statreg.cpp +statreg.h +stdarg.h +stdbool.h +stddef.h +stdint.h +stdio.h +stdlib.h +storage/FindDirectory.h +StorageKit.h +string +StringCompare.h +string.h +String.h +strings.h +Strings.h +StringView.h +stropts.h +strstrea.h +structs.h +stsdef.h +SupportDefs.h +support/String.h +support/SupportDefs.h +support/TLS.h +svrcore.h +symconst.h +sym.h +synch.h +syncmgr.h +sys/atomic_op.h +sys/bitypes.h +sys/byteorder.h +syscall.h +sys/cdefs.h +sys/cfgodm.h +sys/elf.h +sys/endian.h +sys/errno.h +sys/fault.h +sys/fcntl.h +sys/file.h +sys/filio.h +sys/frame.h +sys/immu.h +sys/inttypes.h +sys/ioccom.h +sys/ioctl.h +sys/ipc.h +sys/ldr.h +sys/link.h +sys/locking.h +syslog.h +sys/lwp.h +sys/machine.h +sys/mman.h +sys/mmu.h +sys/mount.h +sys/mpctl.h +sys/param.h +sys/pda.h +sys/poll.h +sys/ppc.h +sys/prctl.h +sys/priv.h +sys/procfs.h +sys/pstat.h +sys/ptrace.h +sys/queue.h +sys/quota.h +sys/reg.h +sys/regset.h +sys/resource.h +sys/sched.h +sys/select.h +sys/sem.h +sys/sendfile.h +sys/shm.h +sys/siginfo.h +sys/signal.h +sys/socket.h +sys/sockio.h +sys/sparc/frame.h +sys/stack.h +sys/statfs.h +sys/stat.h +sys/statvfs.h +sys/syscall.h +sys/sysctl.h +sys/sysinfo.h +sys/sysmp.h +sys/syssgi.h +sys/systeminfo.h +sys/timeb.h +sys/time.h +sys/times.h +sys/ttycom.h +sys/types.h +sys/ucontext.h +sys/uio.h +sys/un.h +sys/unistd.h +sys/utsname.h +sys/vfs.h +sys/wait.h +tables.h +TArray.h +TArrayIterator.h +task.h +tchar.h +TCHAR.H +termios.h +TextCommon.h +TextEdit.h +TextEncodingConverter.h +TextServices.h +TextUtils.h +TextView.h +th/PCR_Th.h +thread.h +ThreadManagerTests.h +Threads.h +time.h +Timer.h +tlhelp32.h +ToolUtils.h +tr1/functional +trace.h +Traps.h +typeinfo +types.h +Types.h +UAppleEventsMgr.h +UAttachments.h +ucontext.h +uconv.h +UCursor.h +ucx$inetdef.h +UDebugging.h +UDesktop.h +UDrawingState.h +UDrawingUtils.h +UEnvironment.h +UEventMgr.h +UException.h +UExtractFromAEDesc.h +UGWorld.h +UKeyFilters.h +ulocks.h +ulserrno.h +UMemoryMgr.h +UModalDialogs.h +UNavServicesDialogs.h +UnicodeBlockObjects.h +UnicodeConverter.h +UnicodeUtilities.h +unidef.h +unikbd.h +unistd.h +unix.h +unixio.h +unixlib.h +unknwn.h +UPrinting.h +UQuickTime.h +UReanimator.h +URegions.h +URegistrar.h +UResourceMgr.h +urlhist.h +urlmon.h +UScrap.h +UScreenPort.h +UTCUtils.h +UTETextAction.h +UTEViewTextAction.h +UTextEdit.h +UTextTraits.h +utility +utime.h +UWindows.h +values.h +varargs.h +vcclr.h +vector +View.h +Volume.h +wab.h +wait.h +wchar.h +winbase.h +win/compobj.h +windef.h +Window.h +windows.h +Windows.h +windowsx.h +Wininet.h +winnls.h +winperf.h +winreg.h +Winreg.h +winsock2.h +winsock.h +winspool.h +winsvc.h +winuser.h +winver.h +wmem.h +workbench/startup.h +wtypes.h +wx/image.h +wx/listctrl.h +wx/log.h +wx/toolbar.h +wx/wx.h +wx/xrc/xmlres.h +X11/cursorfont.h +X11/extensions/Print.h +X11/extensions/shape.h +X11/extensions/XIElib.h +X11/extensions/XShm.h +X11/extensions/Xrender.h +X11/extensions/Xdamage.h +X11/extensions/Xcomposite.h +X11/Intrinsic.h +X11/keysymdef.h +X11/keysym.h +X11/Shell.h +X11/StringDefs.h +X11/Xatom.h +X11/Xft/Xft.h +X11/Xfuncproto.h +X11/X.h +X11/XKBlib.h +X11/Xlib.h +X11/Xlibint.h +X11/Xlocale.h +X11/Xos.h +X11/Xutil.h +xpt_struct.h +xpt_xdr.h +zmouse.h +sslt.h +smime.h +cms.h +sechash.h +secoidt.h +certdb.h +secerr.h +nssb64.h +secasn1.h +secder.h +certt.h +ocsp.h +keyhi.h +cryptohi.h +crmf.h +pk11pqg.h +cmmf.h +base64.h +secdert.h +secitem.h +secmime.h +nssckbi.h +pkcs12.h +p12plcy.h +pk11sdr.h +nspr.h +pratom.h +prbit.h +prclist.h +prcmon.h +prcountr.h +prcvar.h +prdtoa.h +prenv.h +prerr.h +prerror.h +prinet.h +prinit.h +prinrval.h +prio.h +pripcsem.h +prlink.h +prlock.h +prlog.h +prlong.h +prmem.h +prmon.h +prmwait.h +prnetdb.h +prolock.h +prpdce.h +prprf.h +prproces.h +prrng.h +prrwlock.h +prshm.h +prshma.h +prsystem.h +prthread.h +prtime.h +prtpool.h +prtrace.h +prtypes.h +prvrsion.h +plbase64.h +plerror.h +plgetopt.h +plresolv.h +plstr.h +plarenas.h +plarena.h +plhash.h +#if MOZ_NATIVE_PNG==1 +png.h +#endif +#if MOZ_NATIVE_ZLIB==1 +zlib.h +#endif +#if MOZ_ENABLE_LIBXUL!=1 +#if BUILD_STATIC_LIBS!=1 +#define WRAP_LCMS_HEADERS +#endif +#endif +#ifdef WRAP_LCMS_HEADERS +icc34.h +lcms.h +#endif +#ifdef MOZ_ENABLE_STARTUP_NOTIFICATION +libsn/sn.h +libsn/sn-common.h +libsn/sn-launchee.h +libsn/sn-launcher.h +libsn/sn-monitor.h +libsn/sn-util.h +#endif +#if MOZ_NATIVE_HUNSPELL==1 +hunspell.hxx +#endif +#if MOZ_NATIVE_BZ2==1 +bzlib.h +#endif +#ifdef MOZ_PLATFORM_HILDON +hildon-uri.h +hildon-mime.h +#endif +#ifdef NS_OSSO +libosso.h +#endif +#ifdef MOZ_ENABLE_GIO +gio/gio.h +#endif diff --git a/ape-server/deps/js/src/config/version.mk b/ape-server/deps/js/src/config/version.mk new file mode 100755 index 0000000..3877ff1 --- /dev/null +++ b/ape-server/deps/js/src/config/version.mk @@ -0,0 +1,85 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is the Win32 Version System. +# +# The Initial Developer of the Original Code is Netscape Communications Corporation +# Portions created by the Initial Developer are Copyright (C) 2002 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +ifndef INCLUDED_VERSION_MK +INCLUDED_VERSION_MK=1 + +# Windows gmake build: +# Build default .rc file if $(RESFILE) isn't defined. +# TODO: +# PBI : Private build info. Not used currently. +# Guessing the best way would be to set an env var. +# BINARY : Binary name. Not used currently. +ifeq ($(MOZ_WIDGET_TOOLKIT),windows) +ifndef RESFILE +RCFILE=./module.rc +RESFILE=./module.res +_RC_STRING = -QUIET 1 -DEPTH $(DEPTH) -TOPSRCDIR $(topsrcdir) -OBJDIR . -SRCDIR $(srcdir) -DISPNAME $(MOZ_APP_DISPLAYNAME) -APPVERSION $(MOZ_APP_VERSION) +ifdef MOZILLA_OFFICIAL +_RC_STRING += -OFFICIAL 1 +endif +ifdef MOZ_DEBUG +_RC_STRING += -DEBUG 1 +endif +ifdef MODULE +_RC_STRING += -MODNAME $(MODULE) +endif +ifdef PROGRAM +_RC_STRING += -BINARY $(PROGRAM) +else +ifdef _PROGRAM +_RC_STRING += -BINARY $(_PROGRAM) +else +ifdef SHARED_LIBRARY +_RC_STRING += -BINARY $(SHARED_LIBRARY) +endif +endif +endif +ifdef RCINCLUDE +_RC_STRING += -RCINCLUDE $(srcdir)/$(RCINCLUDE) +endif + +GARBAGE += $(RESFILE) $(RCFILE) + +#dummy target so $(RCFILE) doesn't become the default =P +all:: + +$(RCFILE): $(RCINCLUDE) $(topsrcdir)/config/version_win.pl + $(PERL) $(topsrcdir)/config/version_win.pl $(_RC_STRING) + +endif # RESFILE +endif # Windows + +endif diff --git a/ape-server/deps/js/src/config/version_win.pl b/ape-server/deps/js/src/config/version_win.pl new file mode 100755 index 0000000..2e5b880 --- /dev/null +++ b/ape-server/deps/js/src/config/version_win.pl @@ -0,0 +1,436 @@ +#!/usr/bin/perl -w + +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is the Win32 Version System. +# +# The Initial Developer of the Original Code is Brian Bober +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +#use diagnostics; +require strict; +my $dir = $0; +$dir =~ s/[^\/]*$//; +push(@INC, "$dir"); +require "Moz/Milestone.pm"; +use Getopt::Long; +use Getopt::Std; +use POSIX; + +# Calculate the number of days since Jan. 1, 2000 from a buildid string +sub daysFromBuildID +{ + my ($buildid,) = @_; + + my ($y, $m, $d, $h) = ($buildid =~ /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$/); + $d || die("Unrecognized buildid string."); + + my $secondstodays = 60 * 60 * 24; + return sprintf("%d", + (POSIX::mktime(00, 00, 00, $d, $m - 1, $y - 1900) - + POSIX::mktime(00, 00, 00, 01, 00, 100)) / $secondstodays); +} + +#Creates version resource file + +#Paramaters are passed on the command line: + +#Example: -MODNAME nsToolkitCompsModule -DEBUG=1 + +# DEBUG - Mozilla's global debug variable - tells if its debug version +# OFFICIAL - tells Mozilla is building a milestone or nightly +# MSTONE - tells which milestone is being built; +# OBJDIR - Holds the object directory; +# MODNAME - tells what the name of the module is like nsBMPModule +# DEPTH - Holds the path to the root obj dir +# TOPSRCDIR - Holds the path to the root mozilla dir +# SRCDIR - Holds module.ver and source +# BINARY - Holds the name of the binary file +# DISPNAME - Holds the display name of the built application +# APPVERSION - Holds the version string of the built application +# RCINCLUDE - Holds the name of the RC File to include or "" +# QUIET - Turns off output + +#Description and Comment come from module.ver +#Bug 23560 +#http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/rc_7x2d.asp + +#Get next .ver file entry +sub getNextEntry +{ + while () + { + my $mline = $_; + ($mline) = split(/#/,$mline); + my ($entry, $value)=split(/=/,$mline,2); + if (defined($entry)) + { + if (defined($value)) + { + $entry =~ s/^\s*(.*?)\s*$/$1/; + $value =~ s/^\s*(.*?)\s*$/$1/; + return ($entry,$value); + } + } + } + return undef; +} + +my ($quiet,$objdir,$debug,$official,$milestone,$buildid,$module,$binary,$depth,$rcinclude,$srcdir,$fileversion,$productversion); + +GetOptions( "QUIET" => \$quiet, + "DEBUG=s" => \$debug, + "OFFICIAL=s" => \$official, + "MSTONE=s" => \$milestone, + "MODNAME=s" => \$module, + "BINARY=s" => \$binary, + "DISPNAME=s" => \$displayname, + "APPVERSION=s" => \$appversion, + "SRCDIR=s" => \$srcdir, + "TOPSRCDIR=s" => \$topsrcdir, + "DEPTH=s" => \$depth, + "RCINCLUDE=s" => \$rcinclude, + "OBJDIR=s" => \$objdir); +if (!defined($debug)) {$debug="";} +if (!defined($official)) {$official="";} +if (!defined($milestone)) {$milestone="";} +if (!defined($module)) {$module="";} +if (!defined($binary)) {$binary="";} +if (!defined($displayname)) {$displayname="Mozilla";} +if (!defined($appversion)) {$appversion=$milestone;} +if (!defined($depth)) {$depth=".";} +if (!defined($rcinclude)) {$rcinclude="";} +if (!defined($objdir)) {$objdir=".";} +if (!defined($srcdir)) {$srcdir=".";} +if (!defined($topsrcdir)) {$topsrcdir=".";} +my $mfversion = "Personal"; +my $mpversion = "Personal"; +my @fileflags = ("0"); +my $comment=""; +my $description=""; +if (!defined($module)) +{ + $module = $binary; + ($module) = split(/\./,$module); +} + +my $bufferstr=" "; + +my $MILESTONE_FILE = "$topsrcdir/config/milestone.txt"; +my $BUILDID_FILE = "$depth/config/buildid"; + +#Read module.ver file +#Version file overrides for WIN32: +#WIN32_MODULE_COMMENT +#WIN32_MODULE_DESCRIPTION +#WIN32_MODULE_FILEVERSION +#WIN32_MODULE_COMPANYNAME +#WIN32_MODULE_FILEVERSION_STRING +#WIN32_MODULE_NAME +#WIN32_MODULE_COPYRIGHT +#WIN32_MODULE_TRADEMARKS +#WIN32_MODULE_ORIGINAL_FILENAME +#WIN32_MODULE_PRODUCTNAME +#WIN32_MODULE_PRODUCTVERSION +#WIN32_MODULE_PRODUCTVERSION_STRING + +#Override values obtained from the .ver file +my $override_comment; +my $override_description; +my $override_fileversion; +my $override_company; +my $override_mfversion; +my $override_module; +my $override_copyright; +my $override_trademarks; +my $override_filename; +my $override_productname; +my $override_productversion; +my $override_mpversion; +if (open(VERFILE, "<$srcdir/module.ver")) +{ + + my ($a,$b) = getNextEntry(); + while (defined($a)) + { + if ($a eq "WIN32_MODULE_COMMENT") { $override_comment = $b; } + if ($a eq "WIN32_MODULE_DESCRIPTION") { $override_description = $b; } + if ($a eq "WIN32_MODULE_FILEVERSION") { $override_fileversion = $b; } + if ($a eq "WIN32_MODULE_COMPANYNAME") { $override_company = $b; } + if ($a eq "WIN32_MODULE_FILEVERSION_STRING") { $override_mfversion = $b; } + if ($a eq "WIN32_MODULE_NAME") { $override_module = $b; } + if ($a eq "WIN32_MODULE_COPYRIGHT") { $override_copyright = $b; } + if ($a eq "WIN32_MODULE_TRADEMARKS") { $override_trademarks = $b; } + if ($a eq "WIN32_MODULE_ORIGINAL_FILENAME") { $override_filename = $b; } + if ($a eq "WIN32_MODULE_PRODUCTNAME") { $override_productname = $b; } + if ($a eq "WIN32_MODULE_PRODUCTVERSION") { $override_productversion = $b; } + if ($a eq "WIN32_MODULE_PRODUCTVERSION_STRING") { $override_mpversion = $b; } + ($a,$b) = getNextEntry(); + } + close(VERFILE) +} +else +{ + if (!$quiet || $quiet ne "1") { print "$bufferstr" . "WARNING: No module.ver file included ($module, $binary). Default values used\n"; } +} +#Get rid of trailing and leading whitespace +$debug =~ s/^\s*(.*)\s*$/$1/; +$comment =~ s/^\s*(.*)\s*$/$1/; +$official =~ s/^\s*(.*)\s*$/$1/; +$milestone =~ s/^\s*(.*)\s*$/$1/; +$description =~ s/^\s*(.*)\s*$/$1/; +$module =~ s/^\s*(.*)\s*$/$1/; +$depth =~ s/^\s*(.*)\s*$/$1/; +$binary =~ s/^\s*(.*)\s*$/$1/; +$displayname =~ s/^\s*(.*)\s*$/$1/; + +open(BUILDID, "<", $BUILDID_FILE) || die("Couldn't open buildid file: $BUILDID_FILE"); +$buildid = ; +$buildid =~ s/\s*$//; +close BUILDID; + +my $daycount = daysFromBuildID($buildid); + +if ($milestone eq "") { + $milestone = Moz::Milestone::getOfficialMilestone($MILESTONE_FILE); +} + +$mfversion = $mpversion = $milestone; +if ($appversion eq "") { + $appversion = $milestone; +} + +if ($debug eq "1") +{ + push @fileflags, "VS_FF_DEBUG"; + $mpversion .= " Debug"; + $mfversion .= " Debug"; +} + +if ($official ne "1") { + push @fileflags, "VS_FF_PRIVATEBUILD"; +} + +if ($milestone =~ /[a-z]/) { + push @fileflags, "VS_FF_PRERELEASE"; +} + +my @mstone = split(/\./,$milestone); +$mstone[1] =~s/\D.*$//; +if (!$mstone[2]) { + $mstone[2] = "0"; +} +else { + $mstone[2] =~s/\D.*$//; +} +$fileversion = $productversion="$mstone[0],$mstone[1],$mstone[2],$daycount"; + +my @appver = split(/\./,$appversion); +for ($j = 1; $j < 4; $j++) +{ + if (!$appver[$j]) { + $appver[$j] = "0"; + } + else { + $appver[$j] =~s/\D.*$//; + } +} +my $winappversion = "$appver[0],$appver[1],$appver[2],$appver[3]"; + +my $copyright = "License: MPL 1.1/GPL 2.0/LGPL 2.1"; +my $company = "Mozilla Foundation"; +my $trademarks = "Mozilla"; +my $productname = $displayname; + + +if (defined($override_comment)){$override_comment =~ s/\@MOZ_APP_DISPLAYNAME\@/$displayname/g; $comment=$override_comment;} +if (defined($override_description)){$override_description =~ s/\@MOZ_APP_DISPLAYNAME\@/$displayname/g; $description=$override_description;} +if (defined($override_fileversion)){$override_fileversion =~ s/\@MOZ_APP_WINVERSION\@/$winappversion/g; $fileversion=$override_fileversion;} +if (defined($override_mfversion)){$override_mfversion =~ s/\@MOZ_APP_VERSION\@/$appversion/g; $mfversion=$override_mfversion;} +if (defined($override_company)){$company=$override_company;} +if (defined($override_module)){$override_module =~ s/\@MOZ_APP_DISPLAYNAME\@/$displayname/g; $module=$override_module;} +if (defined($override_copyright)){$override_copyright =~ s/\@MOZ_APP_DISPLAYNAME\@/$displayname/g; $copyright=$override_copyright;} +if (defined($override_trademarks)){$override_trademarks =~ s/\@MOZ_APP_DISPLAYNAME\@/$displayname/g; $trademarks=$override_trademarks;} +if (defined($override_filename)){$binary=$override_filename;} +if (defined($override_productname)){$override_productname =~ s/\@MOZ_APP_DISPLAYNAME\@/$displayname/g; $productname=$override_productname;} +if (defined($override_productversion)){$override_productversion =~ s/\@MOZ_APP_WINVERSION\@/$winappversion/g; $productversion=$override_productversion;} +if (defined($override_mpversion)){$override_mpversion =~ s/\@MOZ_APP_VERSION\@/$appversion/g; $mpversion=$override_mpversion;} + + +#Override section + +open(RCFILE, ">$objdir/module.rc") || die("Can't edit module.rc - It must be locked.\n"); +print RCFILE qq{ +// ***** BEGIN LICENSE BLOCK ***** +// Version: MPL 1.1/GPL 2.0/LGPL 2.1 +// +// The contents of this file are subject to the Mozilla Public License Version +// 1.1 (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// http://www.mozilla.org/MPL/ +// +// Software distributed under the License is distributed on an "AS IS" basis, +// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +// for the specific language governing rights and limitations under the +// License. +// +// The Original Code is the Win32 Version System. +// +// The Initial Developer of the Original Code is Brian Bober +// Portions created by the Initial Developer are Copyright (C) 2001 +// the Initial Developer. All Rights Reserved. +// +// Contributor(s): +// +// Alternatively, the contents of this file may be used under the terms of +// either the GNU General Public License Version 2 or later (the "GPL"), or +// the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +// in which case the provisions of the GPL or the LGPL are applicable instead +// of those above. If you wish to allow use of your version of this file only +// under the terms of either the GPL or the LGPL, and not to allow others to +// use your version of this file under the terms of the MPL, indicate your +// decision by deleting the provisions above and replace them with the notice +// and other provisions required by the GPL or the LGPL. If you do not delete +// the provisions above, a recipient may use your version of this file under +// the terms of any one of the MPL, the GPL or the LGPL. +// +// ***** END LICENSE BLOCK ***** + +#include + +// Note: if you contain versioning information in an included +// RC script, it will be discarded +// Use module.ver to explicitly set these values + +// Do not edit this file. Changes won't affect the build. + +}; + +my $versionlevel=0; +my $insideversion=0; +if (open(RCINCLUDE, "<$rcinclude")) +{ + print RCFILE "// From included resource $rcinclude\n"; +# my $mstring=""; + while () + { + $_ =~ s/\@MOZ_APP_DISPLAYNAME\@/$displayname/g; + print RCFILE $_; +# my $instr=$_; +# chomp($instr); +# $mstring .= "$instr\;"; + } + close(RCINCLUDE); +# $mstring =~ s/\/\*.*\*\///g; +# my @mlines = split(/\;/,$mstring); +# for(@mlines) +# { +# my ($nocomment)=split(/\/\//,$_); +# if (defined($nocomment) && $nocomment ne "") +# { +# my ($firststring,$secondstring) = split(/\s+/,$nocomment); +# if (!defined($firststring)) {$firststring="";} +# if (!defined($secondstring)) {$secondstring="";} +# if ($secondstring eq "VERSIONINFO") +# { +#if (!$quiet || $quiet ne "1") { +# print "$bufferstr" . "WARNING: Included RC file ($rcinclude, $module, $binary)\n"; +# print "$bufferstr" . "WARNING: contains versioning information that will be discarded\n"; +# print "$bufferstr" . "WARNING: Remove it and use relevant overrides (in module.ver)\n"; +#} +# $versionlevel = 0; +# $insideversion = 1; +# } +# if ($firststring eq "BEGIN") { $versionlevel++; } +# if ($secondstring eq "END") +# { +# $versionlevel--; +# if ($insideversion==1 && $versionlevel==0) {$versionlevel=0;} +# } +# my $includecheck = $firststring . $secondstring; +# $includecheck =~ s/<|>/"/g; +# $includecheck = lc($includecheck); +# if ($includecheck ne "#include\"winver.h\"") +# { +# if ($insideversion == 0 && $versionlevel == 0) +# { +# print RCFILE "$nocomment\n"; +# } +# } +# } +# } + +} + +my $fileflags = join(' | ', @fileflags); + +print RCFILE qq{ + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +1 VERSIONINFO + FILEVERSION $fileversion + PRODUCTVERSION $productversion + FILEFLAGSMASK 0x3fL + FILEFLAGS $fileflags + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "000004b0" + BEGIN + VALUE "Comments", "$comment" + VALUE "LegalCopyright", "$copyright" + VALUE "CompanyName", "$company" + VALUE "FileDescription", "$description" + VALUE "FileVersion", "$mfversion" + VALUE "ProductVersion", "$mpversion" + VALUE "InternalName", "$module" + VALUE "LegalTrademarks", "$trademarks" + VALUE "OriginalFilename", "$binary" + VALUE "ProductName", "$productname" + VALUE "BuildID", "$buildid" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1200 + END +END + +}; +close(RCFILE); diff --git a/ape-server/deps/js/src/configure b/ape-server/deps/js/src/configure new file mode 100755 index 0000000..a6efbc4 --- /dev/null +++ b/ape-server/deps/js/src/configure @@ -0,0 +1,14016 @@ +#! /bin/sh + +if test `uname -s | grep -c MINGW 2>/dev/null` != "0"; then + msyshost=1 +fi + +# Guess values for system-dependent variables and create Makefiles. +# Generated automatically using autoconf version 2.13 +# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc. +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. + +# Defaults: +ac_help= +ac_default_prefix=/usr/local +# Any additions from configure.in: +ac_help="$ac_help + --with-dist-dir=DIR Use DIR as 'dist' staging area. DIR may be + relative to the top of SpiderMonkey build tree, + or absolute." +ac_help="$ac_help + --disable-compile-environment + Disable compiler/library checks." +ac_help="$ac_help + --with-symbian-sdk=SYMBIAN_SDK_DIR + The path to the Symbian SDK" +ac_help="$ac_help + --enable-macos-target=VER (default=10.4) + Set the minimum MacOS version needed at runtime" +ac_help="$ac_help + --with-macos-sdk=dir Location of platform SDK to use (Mac OS X only)" +ac_help="$ac_help + --with-x use the X Window System" +ac_help="$ac_help + --disable-os2-high-mem Disable high-memory support on OS/2" +ac_help="$ac_help + --disable-windows-mobile-components + Disable Windows Mobile specific components from CE build" +ac_help="$ac_help + --disable-jit Disable JIT support" +ac_help="$ac_help + --enable-dtrace build with dtrace support if available (default=no)" +ac_help="$ac_help + --with-pthreads Force use of system pthread library with NSPR " +ac_help="$ac_help + --with-system-nspr Use an NSPR that is already built and installed. + Use the 'nspr-config' script in the current path, + or look for the script in the directories given with + --with-nspr-exec-prefix or --with-nspr-prefix. + (Those flags are only checked if you specify + --with-system-nspr.)" +ac_help="$ac_help + --with-nspr-cflags=FLAGS Pass FLAGS to CC when building code that uses NSPR. + Use this when there's no accurate nspr-config + script available. This is the case when building + SpiderMonkey as part of the Mozilla tree: the + top-level configure script computes NSPR flags + that accomodate the quirks of that environment." +ac_help="$ac_help + --with-nspr-libs=LIBS Pass LIBS to LD when linking code that uses NSPR. + See --with-nspr-cflags for more details." +ac_help="$ac_help + --with-nspr-prefix=PFX Prefix where NSPR is installed" +ac_help="$ac_help + --with-nspr-exec-prefix=PFX + Exec prefix where NSPR is installed" +ac_help="$ac_help + --with-arm-kuser Use kuser helpers (Linux/ARM only -- requires kernel 2.6.13 or later)" +ac_help="$ac_help + --enable-ui-locale=ab-CD + Select the user interface locale (default: en-US)" +ac_help="$ac_help + --disable-tests Do not build test libraries & programs" +ac_help="$ac_help + --enable-debug[=DBG] Enable building with developer debug info + (Using compiler flags DBG)" +ac_help="$ac_help + --disable-optimize Disable compiler optimization + --enable-optimize=[OPT] Specify compiler optimization flags [OPT=-O]" +ac_help="$ac_help + --enable-debug-modules Enable/disable debug info for specific modules" +ac_help="$ac_help + --enable-debugger-info-modules + Enable/disable debugger info for specific modules" +ac_help="$ac_help + --enable-narcissus Build with Narcissus code enabled" +ac_help="$ac_help + --enable-trace-malloc Enable malloc tracing" +ac_help="$ac_help + --enable-jemalloc Replace memory allocator with jemalloc" +ac_help="$ac_help + --enable-wrap-malloc Wrap malloc calls (gnu linker only)" +ac_help="$ac_help + --with-wrap-malloc=DIR Location of malloc wrapper library" +ac_help="$ac_help + --enable-tracevis Enable TraceVis tracing tool (default=no)" +ac_help="$ac_help + --enable-valgrind Enable Valgrind integration hooks (default=no)" +ac_help="$ac_help + --enable-jprof Enable jprof profiling tool (needs mozilla/tools/jprof)" +ac_help="$ac_help + --enable-shark Enable shark remote profiling (needs CHUD framework)" +ac_help="$ac_help + --enable-callgrind Enable callgrind profiling" +ac_help="$ac_help + --enable-vtune Enable vtune profiling" +ac_help="$ac_help + --enable-gczeal Enable zealous GCing" +ac_help="$ac_help + --with-static-checking=path/to/gcc_dehydra.so + Enable static checking of code using GCC-dehydra" +ac_help="$ac_help + --enable-strip Enable stripping of libs & executables " +ac_help="$ac_help + --enable-install-strip Enable stripping of libs & executables when packaging " +ac_help="$ac_help + --enable-timeline Enable timeline services " +ac_help="$ac_help + --enable-insure Enable insure++ instrumentation (linux only)" +ac_help="$ac_help + --with-insure-dirs=DIRS + Dirs to instrument with insure " +ac_help="$ac_help + --with-insure-exclude-dirs=DIRS + Dirs to not instrument with insure " +ac_help="$ac_help + --enable-quantify Enable Quantify support (Windows only) " +ac_help="$ac_help + --enable-xterm-updates Update XTERM titles with current command." +ac_help="$ac_help + --enable-long-long-warning + Warn about use of non-ANSI long long type" +ac_help="$ac_help + --disable-profile-guided-optimization + Don't build with PGO even if called via make profiledbuild" +ac_help="$ac_help + --disable-pedantic Issue all warnings demanded by strict ANSI C " +ac_help="$ac_help + --enable-cpp-rtti Enable C++ RTTI " +ac_help="$ac_help + --enable-cpp-exceptions Enable C++ exceptions " +ac_help="$ac_help + --disable-auto-deps Do not automatically generate dependency info" +ac_help="$ac_help + --disable-md Do not use compiler-based dependencies " +ac_help="$ac_help + --enable-static Enable building of internal static libs" +ac_help="$ac_help + --enable-readline Link js shell to system readline library" +ac_help="$ac_help + --with-sync-build-files=DIR + Check that files in 'config' and 'build' match + their originals in 'DIR/config' and 'DIR/build'. + This helps keep the SpiderMonkey build machinery + in sync with Mozilla's, on which it is based." +ac_help="$ac_help + --enable-threadsafe Enable support for multiple threads." + +# Initialize some variables set by options. +# The variables have the same names as the options, with +# dashes changed to underlines. +build=NONE +cache_file=./config.cache +exec_prefix=NONE +host=NONE +no_create= +nonopt=NONE +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +target=NONE +verbose= +x_includes=NONE +x_libraries=NONE +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +# Initialize some other variables. +subdirs= +MFLAGS= MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} +# Maximum number of lines to put in a shell here document. +ac_max_here_lines=12 + +ac_prev= +for ac_option +do + + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + case "$ac_option" in + -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) ac_optarg= ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case "$ac_option" in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir="$ac_optarg" ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build="$ac_optarg" ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file="$ac_optarg" ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir="$ac_optarg" ;; + + -disable-* | --disable-*) + ac_feature=`echo $ac_option|sed -e 's/-*disable-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + eval "enable_${ac_feature}=no" ;; + + -enable-* | --enable-*) + ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "enable_${ac_feature}='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix="$ac_optarg" ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he) + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat << EOF +Usage: configure [options] [host] +Options: [defaults in brackets after descriptions] +Configuration: + --cache-file=FILE cache test results in FILE + --help print this message + --no-create do not create output files + --quiet, --silent do not print \`checking...' messages + --version print the version of autoconf that created configure +Directory and file names: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [same as prefix] + --bindir=DIR user executables in DIR [EPREFIX/bin] + --sbindir=DIR system admin executables in DIR [EPREFIX/sbin] + --libexecdir=DIR program executables in DIR [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data in DIR + [PREFIX/share] + --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data in DIR + [PREFIX/com] + --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var] + --libdir=DIR object code libraries in DIR [EPREFIX/lib] + --includedir=DIR C header files in DIR [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include] + --infodir=DIR info documentation in DIR [PREFIX/info] + --mandir=DIR man documentation in DIR [PREFIX/man] + --srcdir=DIR find the sources in DIR [configure dir or ..] + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM + run sed PROGRAM on installed program names +EOF + cat << EOF +Host type: + --build=BUILD configure for building on BUILD [BUILD=HOST] + --host=HOST configure for HOST [guessed] + --target=TARGET configure for TARGET [TARGET=HOST] +Features and packages: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --x-includes=DIR X include files are in DIR + --x-libraries=DIR X library files are in DIR +EOF + if test -n "$ac_help"; then + echo "--enable and --with options recognized:$ac_help" + fi + exit 0 ;; + + -host | --host | --hos | --ho) + ac_prev=host ;; + -host=* | --host=* | --hos=* | --ho=*) + host="$ac_optarg" ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir="$ac_optarg" ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir="$ac_optarg" ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir="$ac_optarg" ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir="$ac_optarg" ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir="$ac_optarg" ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir="$ac_optarg" ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir="$ac_optarg" ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix="$ac_optarg" ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix="$ac_optarg" ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix="$ac_optarg" ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name="$ac_optarg" ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir="$ac_optarg" ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir="$ac_optarg" ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site="$ac_optarg" ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir="$ac_optarg" ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir="$ac_optarg" ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target="$ac_optarg" ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers) + echo "configure generated by autoconf version 2.13" + exit 0 ;; + + -with-* | --with-*) + ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "with_${ac_package}='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`echo $ac_option|sed -e 's/-*without-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + eval "with_${ac_package}=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes="$ac_optarg" ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries="$ac_optarg" ;; + + -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; } + ;; + + *) + if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then + echo "configure: warning: $ac_option: invalid host type" 1>&2 + fi + if test "x$nonopt" != xNONE; then + { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } + fi + nonopt="$ac_option" + ;; + + esac +done + +if test -n "$ac_prev"; then + { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; } +fi + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +# File descriptor usage: +# 0 standard input +# 1 file creation +# 2 errors and warnings +# 3 some systems may open it to /dev/tty +# 4 used on the Kubota Titan +# 6 checking for... messages and results +# 5 compiler messages saved in config.log +if test "$silent" = yes; then + exec 6>/dev/null +else + exec 6>&1 +fi +exec 5>./config.log + +echo "\ +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. +" 1>&5 + +# Strip out --no-create and --no-recursion so they do not pile up. +# Also quote any args containing shell metacharacters. +ac_configure_args= +for ac_arg +do + case "$ac_arg" in + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) ;; + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*) + ac_configure_args="$ac_configure_args '$ac_arg'" ;; + *) ac_configure_args="$ac_configure_args $ac_arg" ;; + esac +done + +# NLS nuisances. +# Only set these to C if already set. These must not be set unconditionally +# because not all systems understand e.g. LANG=C (notably SCO). +# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'! +# Non-C LC_CTYPE values break the ctype check. +if test "${LANG+set}" = set; then LANG=C; export LANG; fi +if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi +if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi +if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo > confdefs.h + +# A filename unique to this package, relative to the directory that +# configure is in, which we can look for to find out if srcdir is correct. +ac_unique_file=jsapi.h + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_prog=$0 + ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'` + test "x$ac_confdir" = "x$ac_prog" && ac_confdir=. + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; } + else + { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; } + fi +fi +srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'` + +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + echo "loading site script $ac_site_file" + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + echo "loading cache $cache_file" + . $cache_file +else + echo "creating cache $cache_file" + > $cache_file +fi + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +ac_exeext= +ac_objext=o +if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then + # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu. + if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then + ac_n= ac_c=' +' ac_t=' ' + else + ac_n=-n ac_c= ac_t= + fi +else + ac_n= ac_c='\c' ac_t= +fi + + +ac_aux_dir= +for ac_dir in ${srcdir}/build/autoconf $srcdir/${srcdir}/build/autoconf; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { echo "configure: error: can not find install-sh or install.sh in ${srcdir}/build/autoconf $srcdir/${srcdir}/build/autoconf" 1>&2; exit 1; } +fi +ac_config_guess=$ac_aux_dir/config.guess +ac_config_sub=$ac_aux_dir/config.sub +ac_configure=$ac_aux_dir/configure # This should be Cygnus configure. + + + +# Do some error checking and defaulting for the host and target type. +# The inputs are: +# configure --host=HOST --target=TARGET --build=BUILD NONOPT +# +# The rules are: +# 1. You are not allowed to specify --host, --target, and nonopt at the +# same time. +# 2. Host defaults to nonopt. +# 3. If nonopt is not specified, then host defaults to the current host, +# as determined by config.guess. +# 4. Target and build default to nonopt. +# 5. If nonopt is not specified, then target and build default to host. + +# The aliases save the names the user supplied, while $host etc. +# will get canonicalized. +case $host---$target---$nonopt in +NONE---*---* | *---NONE---* | *---*---NONE) ;; +*) { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } ;; +esac + + +# Make sure we can run config.sub. +if ${CONFIG_SHELL-/bin/sh} $ac_config_sub sun4 >/dev/null 2>&1; then : +else { echo "configure: error: can not run $ac_config_sub" 1>&2; exit 1; } +fi + +echo $ac_n "checking host system type""... $ac_c" 1>&6 +echo "configure:718: checking host system type" >&5 + +host_alias=$host +case "$host_alias" in +NONE) + case $nonopt in + NONE) + if host_alias=`${CONFIG_SHELL-/bin/sh} $ac_config_guess`; then : + else { echo "configure: error: can not guess host type; you must specify one" 1>&2; exit 1; } + fi ;; + *) host_alias=$nonopt ;; + esac ;; +esac + +host=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $host_alias` +host_cpu=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +host_vendor=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +host_os=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` +echo "$ac_t""$host" 1>&6 + +echo $ac_n "checking target system type""... $ac_c" 1>&6 +echo "configure:739: checking target system type" >&5 + +target_alias=$target +case "$target_alias" in +NONE) + case $nonopt in + NONE) target_alias=$host_alias ;; + *) target_alias=$nonopt ;; + esac ;; +esac + +target=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $target_alias` +target_cpu=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +target_vendor=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +target_os=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` +echo "$ac_t""$target" 1>&6 + +echo $ac_n "checking build system type""... $ac_c" 1>&6 +echo "configure:757: checking build system type" >&5 + +build_alias=$build +case "$build_alias" in +NONE) + case $nonopt in + NONE) build_alias=$host_alias ;; + *) build_alias=$nonopt ;; + esac ;; +esac + +build=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $build_alias` +build_cpu=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +build_vendor=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +build_os=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` +echo "$ac_t""$build" 1>&6 + +test "$host_alias" != "$target_alias" && + test "$program_prefix$program_suffix$program_transform_name" = \ + NONENONEs,x,x, && + program_prefix=${target_alias}- + +TARGET_CPU="${target_cpu}" +TARGET_VENDOR="${target_vendor}" +TARGET_OS="${target_os}" + +CFLAGS="${CFLAGS=}" +CPPFLAGS="${CPPFLAGS=}" +CXXFLAGS="${CXXFLAGS=}" +LDFLAGS="${LDFLAGS=}" +HOST_CFLAGS="${HOST_CFLAGS=}" +HOST_CXXFLAGS="${HOST_CXXFLAGS=}" +HOST_LDFLAGS="${HOST_LDFLAGS=}" + +_SUBDIR_CC="$CC" +_SUBDIR_CXX="$CXX" +_SUBDIR_CFLAGS="$CFLAGS" +_SUBDIR_CPPFLAGS="$CPPFLAGS" +_SUBDIR_CXXFLAGS="$CXXFLAGS" +_SUBDIR_LDFLAGS="$LDFLAGS" +_SUBDIR_HOST_CC="$HOST_CC" +_SUBDIR_HOST_CFLAGS="$HOST_CFLAGS" +_SUBDIR_HOST_CXXFLAGS="$HOST_CXXFLAGS" +_SUBDIR_HOST_LDFLAGS="$HOST_LDFLAGS" +_SUBDIR_CONFIG_ARGS="$ac_configure_args" + +NSPR_VERSION=4 + +PERL_VERSION=5.006 +WINDRES_VERSION=2.14.90 +W32API_VERSION=3.8 + +MSMANIFEST_TOOL= + +MISSING_X= +for ac_prog in mawk gawk nawk awk +do +# Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:817: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_AWK'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_AWK="$ac_prog" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +AWK="$ac_cv_prog_AWK" +if test -n "$AWK"; then + echo "$ac_t""$AWK" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +test -n "$AWK" && break +done + + +USE_PTHREADS= +_PTHREAD_LDFLAGS="" + +_topsrcdir=`cd \`dirname $0\`; pwd` +_objdir=`pwd` + +if test "$_topsrcdir" != "$_objdir" +then + # Check for a couple representative files in the source tree + _conflict_files= + for file in $_topsrcdir/Makefile $_topsrcdir/config/autoconf.mk; do + if test -f $file; then + _conflict_files="$_conflict_files $file" + fi + done + if test "$_conflict_files"; then + echo "***" + echo "* Your source tree contains these files:" + for file in $_conflict_files; do + echo "* $file" + done + cat 1>&2 <<-EOF + * This indicates that you previously built in the source tree. + * A source tree build can confuse the separate objdir build. + * + * To clean up the source tree: + * 1. cd $_topsrcdir + * 2. gmake distclean + *** + EOF + exit 1 + break + fi +fi +MOZ_BUILD_ROOT=`pwd` + + +# Check whether --with-dist-dir or --without-dist-dir was given. +if test "${with_dist_dir+set}" = set; then + withval="$with_dist_dir" + TOP_DIST=$withval +else + TOP_DIST=dist +fi + + + +if test -z "$CROSS_COMPILE"; then +case "$target" in +*-cygwin*|*-mingw*|*-msvc*|*-mks*) + if test -z "$CC"; then CC=cl; fi + if test -z "$CXX"; then CXX=cl; fi + if test -z "$CPP"; then CPP="cl -E -nologo"; fi + if test -z "$CXXCPP"; then CXXCPP="cl -TP -E -nologo"; ac_cv_prog_CXXCPP="$CXXCPP"; fi + if test -z "$LD"; then LD=link; fi + if test -z "$AS"; then + case "${target_cpu}" in + i*86) + AS=ml; + ;; + x86_64) + AS=ml64; + ;; + esac + fi + if test -z "$MIDL"; then MIDL=midl; fi + ;; +*-darwin*) + if test -z "$CC"; then CC=gcc-4.2; fi + if test -z "$CXX"; then CXX=g++-4.2; fi + ;; +esac +fi + +COMPILE_ENVIRONMENT=1 +# Check whether --enable-compile-environment or --disable-compile-environment was given. +if test "${enable_compile_environment+set}" = set; then + enableval="$enable_compile_environment" + if test "$enableval" = "yes"; then + COMPILE_ENVIRONMENT=1 + elif test "$enableval" = "no"; then + COMPILE_ENVIRONMENT= + else + { echo "configure: error: Option, compile-environment, does not take an argument ($enableval)." 1>&2; exit 1; } + fi +fi + + + +if test "$COMPILE_ENVIRONMENT"; then + +if test "$target" != "$host"; then + echo "cross compiling from $host to $target" + + _SAVE_CC="$CC" + _SAVE_CFLAGS="$CFLAGS" + _SAVE_LDFLAGS="$LDFLAGS" + + echo $ac_n "checking for host c compiler""... $ac_c" 1>&6 +echo "configure:946: checking for host c compiler" >&5 + for ac_prog in $HOST_CC gcc cc /usr/ucb/cc cl icc +do +# Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:952: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_HOST_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$HOST_CC"; then + ac_cv_prog_HOST_CC="$HOST_CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_HOST_CC="$ac_prog" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +HOST_CC="$ac_cv_prog_HOST_CC" +if test -n "$HOST_CC"; then + echo "$ac_t""$HOST_CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +test -n "$HOST_CC" && break +done +test -n "$HOST_CC" || HOST_CC="""" + + if test -z "$HOST_CC"; then + { echo "configure: error: no acceptable c compiler found in \$PATH" 1>&2; exit 1; } + fi + echo "$ac_t""$HOST_CC" 1>&6 + echo $ac_n "checking for host c++ compiler""... $ac_c" 1>&6 +echo "configure:987: checking for host c++ compiler" >&5 + for ac_prog in $HOST_CXX $CCC c++ g++ gcc CC cxx cc++ cl icc +do +# Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:993: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_HOST_CXX'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$HOST_CXX"; then + ac_cv_prog_HOST_CXX="$HOST_CXX" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_HOST_CXX="$ac_prog" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +HOST_CXX="$ac_cv_prog_HOST_CXX" +if test -n "$HOST_CXX"; then + echo "$ac_t""$HOST_CXX" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +test -n "$HOST_CXX" && break +done +test -n "$HOST_CXX" || HOST_CXX="""" + + if test -z "$HOST_CXX"; then + { echo "configure: error: no acceptable c++ compiler found in \$PATH" 1>&2; exit 1; } + fi + echo "$ac_t""$HOST_CXX" 1>&6 + + if test -z "$HOST_CFLAGS"; then + HOST_CFLAGS="$CFLAGS" + fi + if test -z "$HOST_CXXFLAGS"; then + HOST_CXXFLAGS="$CXXFLAGS" + fi + if test -z "$HOST_LDFLAGS"; then + HOST_LDFLAGS="$LDFLAGS" + fi + for ac_prog in $HOST_RANLIB ranlib +do +# Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1042: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_HOST_RANLIB'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$HOST_RANLIB"; then + ac_cv_prog_HOST_RANLIB="$HOST_RANLIB" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy=":" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_HOST_RANLIB="$ac_prog" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +HOST_RANLIB="$ac_cv_prog_HOST_RANLIB" +if test -n "$HOST_RANLIB"; then + echo "$ac_t""$HOST_RANLIB" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +test -n "$HOST_RANLIB" && break +done +test -n "$HOST_RANLIB" || HOST_RANLIB="ranlib" + + for ac_prog in $HOST_AR ar +do +# Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1077: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_HOST_AR'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$HOST_AR"; then + ac_cv_prog_HOST_AR="$HOST_AR" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy=":" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_HOST_AR="$ac_prog" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +HOST_AR="$ac_cv_prog_HOST_AR" +if test -n "$HOST_AR"; then + echo "$ac_t""$HOST_AR" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +test -n "$HOST_AR" && break +done +test -n "$HOST_AR" || HOST_AR="ar" + + CC="$HOST_CC" + CFLAGS="$HOST_CFLAGS" + LDFLAGS="$HOST_LDFLAGS" + + echo $ac_n "checking whether the host c compiler ($HOST_CC $HOST_CFLAGS $HOST_LDFLAGS) works""... $ac_c" 1>&6 +echo "configure:1112: checking whether the host c compiler ($HOST_CC $HOST_CFLAGS $HOST_LDFLAGS) works" >&5 + cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_prog_hostcc_works=1 echo "$ac_t""yes" 1>&6 +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + { echo "configure: error: installation or configuration problem: host compiler $HOST_CC cannot create executables." 1>&2; exit 1; } +fi +rm -f conftest* + + CC="$HOST_CXX" + CFLAGS="$HOST_CXXFLAGS" + + echo $ac_n "checking whether the host c++ compiler ($HOST_CXX $HOST_CXXFLAGS $HOST_LDFLAGS) works""... $ac_c" 1>&6 +echo "configure:1136: checking whether the host c++ compiler ($HOST_CXX $HOST_CXXFLAGS $HOST_LDFLAGS) works" >&5 + cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_prog_hostcxx_works=1 echo "$ac_t""yes" 1>&6 +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + { echo "configure: error: installation or configuration problem: host compiler $HOST_CXX cannot create executables." 1>&2; exit 1; } +fi +rm -f conftest* + + CC=$_SAVE_CC + CFLAGS=$_SAVE_CFLAGS + LDFLAGS=$_SAVE_LDFLAGS + + case "$build:$target" in + powerpc-apple-darwin8*:i?86-apple-darwin*) + _SAVE_CFLAGS=$CFLAGS + _SAVE_CXXFLAGS=$CXXLAGS + CFLAGS="-isysroot /Developer/SDKs/MacOSX10.4u.sdk $CFLAGS" + CXXFLAGS="-isysroot /Developer/SDKs/MacOSX10.4u.sdk $CXXFLAGS" + ;; + esac + + case "$target" in + *symbian*) + # Check whether --with-symbian-sdk or --without-symbian-sdk was given. +if test "${with_symbian_sdk+set}" = set; then + withval="$with_symbian_sdk" + SYMBIAN_SDK_DIR=$withval +fi + + + OS_EXE_CFLAGS="$OS_EXE_CFLAGS -D__EXE__" + CFLAGS="-MD -nostdinc" + SYMBIAN_SYS_INCLUDE="-I$SYMBIAN_SDK_DIR/Epoc32/include -I$SYMBIAN_SDK_DIR/Epoc32/include/variant -I$SYMBIAN_SDK_DIR/Epoc32/include/stdapis" + + case "$target" in + *-symbianelf) + OS_TARGET=GCCE + CC=arm-none-symbianelf-gcc.exe + CXX=arm-none-symbianelf-g++.exe + LD=arm-none-symbianelf-ld.exe + AR=arm-none-symbianelf-ar.exe + CPP=arm-none-symbianelf-cpp.exe + CFLAGS="$CFLAGS -c -Wall -Wno-unknown-pragmas -fexceptions -march=armv5t -mapcs -pipe -msoft-float" + CXXFLAGS="$CFLAGS -Wno-ctor-dtor-privacy" + GCCE_INCLUDE="-include $SYMBIAN_SDK_DIR/EPOC32/INCLUDE/GCCE/GCCE.h -D__PRODUCT_INCLUDE__=$SYMBIAN_SDK_DIR/Epoc32/include/variant/Symbian_OS_v9.2.hrh" + CFLAGS="$CFLAGS ${GCCE_INCLUDE} -x c" + CXXFLAGS="$CXXFLAGS ${GCCE_INCLUDE} -x c++" + CPPFLAGS="$CPPFLAGS ${SYMBIAN_SYS_INCLUDE}" + ;; + *-symbianwinscw) + OS_TARGET=WINSCW + ;; + esac + ;; + esac + + for ac_prog in $CC "${target_alias}-gcc" "${target}-gcc" +do +# Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1209: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="$ac_prog" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +test -n "$CC" && break +done +test -n "$CC" || CC=":" + + unset ac_cv_prog_CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1243: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="gcc" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1273: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_prog_rejected=no + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + break + fi + done + IFS="$ac_save_ifs" +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# -gt 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + set dummy "$ac_dir/$ac_word" "$@" + shift + ac_cv_prog_CC="$@" + fi +fi +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + if test -z "$CC"; then + case "`uname -s`" in + *win32* | *WIN32*) + # Extract the first word of "cl", so it can be a program name with args. +set dummy cl; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1324: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="cl" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + ;; + esac + fi + test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; } +fi + +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 +echo "configure:1356: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +cat > conftest.$ac_ext << EOF + +#line 1367 "configure" +#include "confdefs.h" + +main(){return(0);} +EOF +if { (eval echo configure:1372: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + ac_cv_prog_cc_works=yes + # If we can't run a trivial program, we are probably using a cross compiler. + if (./conftest; exit) 2>/dev/null; then + ac_cv_prog_cc_cross=no + else + ac_cv_prog_cc_cross=yes + fi +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_cv_prog_cc_works=no +fi +rm -fr conftest* +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +echo "$ac_t""$ac_cv_prog_cc_works" 1>&6 +if test $ac_cv_prog_cc_works = no; then + { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } +fi +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 +echo "configure:1398: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 +cross_compiling=$ac_cv_prog_cc_cross + +echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 +echo "configure:1403: checking whether we are using GNU C" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.c <&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then + ac_cv_prog_gcc=yes +else + ac_cv_prog_gcc=no +fi +fi + +echo "$ac_t""$ac_cv_prog_gcc" 1>&6 + +if test $ac_cv_prog_gcc = yes; then + GCC=yes +else + GCC= +fi + +ac_test_CFLAGS="${CFLAGS+set}" +ac_save_CFLAGS="$CFLAGS" +CFLAGS= +echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 +echo "configure:1431: checking whether ${CC-cc} accepts -g" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + echo 'void f(){}' > conftest.c +if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then + ac_cv_prog_cc_g=yes +else + ac_cv_prog_cc_g=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_prog_cc_g" 1>&6 +if test "$ac_test_CFLAGS" = set; then + CFLAGS="$ac_save_CFLAGS" +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi + + for ac_prog in $CXX "${target_alias}-g++" "${target}-g++" +do +# Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1467: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CXX'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CXX"; then + ac_cv_prog_CXX="$CXX" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CXX="$ac_prog" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CXX="$ac_cv_prog_CXX" +if test -n "$CXX"; then + echo "$ac_t""$CXX" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +test -n "$CXX" && break +done +test -n "$CXX" || CXX=":" + + unset ac_cv_prog_CXX + for ac_prog in $CCC c++ g++ gcc CC cxx cc++ cl +do +# Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1503: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CXX'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CXX"; then + ac_cv_prog_CXX="$CXX" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CXX="$ac_prog" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CXX="$ac_cv_prog_CXX" +if test -n "$CXX"; then + echo "$ac_t""$CXX" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +test -n "$CXX" && break +done +test -n "$CXX" || CXX="gcc" + + +echo $ac_n "checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) works""... $ac_c" 1>&6 +echo "configure:1535: checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) works" >&5 + +ac_ext=C +# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cxx_cross + +cat > conftest.$ac_ext << EOF + +#line 1546 "configure" +#include "confdefs.h" + +int main(){return(0);} +EOF +if { (eval echo configure:1551: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + ac_cv_prog_cxx_works=yes + # If we can't run a trivial program, we are probably using a cross compiler. + if (./conftest; exit) 2>/dev/null; then + ac_cv_prog_cxx_cross=no + else + ac_cv_prog_cxx_cross=yes + fi +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_cv_prog_cxx_works=no +fi +rm -fr conftest* +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +echo "$ac_t""$ac_cv_prog_cxx_works" 1>&6 +if test $ac_cv_prog_cxx_works = no; then + { echo "configure: error: installation or configuration problem: C++ compiler cannot create executables." 1>&2; exit 1; } +fi +echo $ac_n "checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 +echo "configure:1577: checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "$ac_t""$ac_cv_prog_cxx_cross" 1>&6 +cross_compiling=$ac_cv_prog_cxx_cross + +echo $ac_n "checking whether we are using GNU C++""... $ac_c" 1>&6 +echo "configure:1582: checking whether we are using GNU C++" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gxx'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.C <&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then + ac_cv_prog_gxx=yes +else + ac_cv_prog_gxx=no +fi +fi + +echo "$ac_t""$ac_cv_prog_gxx" 1>&6 + +if test $ac_cv_prog_gxx = yes; then + GXX=yes +else + GXX= +fi + +ac_test_CXXFLAGS="${CXXFLAGS+set}" +ac_save_CXXFLAGS="$CXXFLAGS" +CXXFLAGS= +echo $ac_n "checking whether ${CXX-g++} accepts -g""... $ac_c" 1>&6 +echo "configure:1610: checking whether ${CXX-g++} accepts -g" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_cxx_g'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + echo 'void f(){}' > conftest.cc +if test -z "`${CXX-g++} -g -c conftest.cc 2>&1`"; then + ac_cv_prog_cxx_g=yes +else + ac_cv_prog_cxx_g=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_prog_cxx_g" 1>&6 +if test "$ac_test_CXXFLAGS" = set; then + CXXFLAGS="$ac_save_CXXFLAGS" +elif test $ac_cv_prog_cxx_g = yes; then + if test "$GXX" = yes; then + CXXFLAGS="-g -O2" + else + CXXFLAGS="-g" + fi +else + if test "$GXX" = yes; then + CXXFLAGS="-O2" + else + CXXFLAGS= + fi +fi + + + case "$build:$target" in + powerpc-apple-darwin8*:i?86-apple-darwin*) + CFLAGS=$_SAVE_CFLAGS + CXXFLAGS=$_SAVE_CXXFLAGS + ;; + esac + + for ac_prog in $RANLIB "${target_alias}-ranlib" "${target}-ranlib" +do +# Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1654: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_RANLIB="$ac_prog" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +RANLIB="$ac_cv_prog_RANLIB" +if test -n "$RANLIB"; then + echo "$ac_t""$RANLIB" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +test -n "$RANLIB" && break +done +test -n "$RANLIB" || RANLIB=":" + + for ac_prog in $AR "${target_alias}-ar" "${target}-ar" +do +# Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1689: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_AR'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$AR"; then + ac_cv_prog_AR="$AR" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_AR="$ac_prog" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +AR="$ac_cv_prog_AR" +if test -n "$AR"; then + echo "$ac_t""$AR" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +test -n "$AR" && break +done +test -n "$AR" || AR=":" + + for ac_prog in $AS "${target_alias}-as" "${target}-as" +do +# Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1724: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_path_AS'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + case "$AS" in + /*) + ac_cv_path_AS="$AS" # Let the user override the test with a path. + ;; + ?:/*) + ac_cv_path_AS="$AS" # Let the user override the test with a dos path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_path_AS="$ac_dir/$ac_word" + break + fi + done + IFS="$ac_save_ifs" + ;; +esac +fi +AS="$ac_cv_path_AS" +if test -n "$AS"; then + echo "$ac_t""$AS" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +test -n "$AS" && break +done +test -n "$AS" || AS=":" + + if test "$msyshost"; then + case "$AS" in + /*) + tmp_DIRNAME=`dirname "$AS"` + tmp_BASENAME=`basename "$AS"` + tmp_PWD=`cd "$tmp_DIRNAME" && pwd -W` + AS="$tmp_PWD/$tmp_BASENAME" + if test -e "$AS.exe"; then + AS="$AS.exe" + fi + esac + fi + + for ac_prog in $LD "${target_alias}-ld" "${target}-ld" +do +# Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1778: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_LD'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$LD"; then + ac_cv_prog_LD="$LD" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_LD="$ac_prog" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +LD="$ac_cv_prog_LD" +if test -n "$LD"; then + echo "$ac_t""$LD" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +test -n "$LD" && break +done +test -n "$LD" || LD=":" + + for ac_prog in $STRIP "${target_alias}-strip" "${target}-strip" +do +# Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1813: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_STRIP'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_STRIP="$ac_prog" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +STRIP="$ac_cv_prog_STRIP" +if test -n "$STRIP"; then + echo "$ac_t""$STRIP" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +test -n "$STRIP" && break +done +test -n "$STRIP" || STRIP=":" + + for ac_prog in $WINDRES "${target_alias}-windres" "${target}-windres" +do +# Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1848: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_WINDRES'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$WINDRES"; then + ac_cv_prog_WINDRES="$WINDRES" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_WINDRES="$ac_prog" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +WINDRES="$ac_cv_prog_WINDRES" +if test -n "$WINDRES"; then + echo "$ac_t""$WINDRES" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +test -n "$WINDRES" && break +done +test -n "$WINDRES" || WINDRES=":" + + cat >> confdefs.h <<\EOF +#define CROSS_COMPILE 1 +EOF + + + cross_compiling=yes +else + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1888: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="gcc" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1918: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_prog_rejected=no + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + break + fi + done + IFS="$ac_save_ifs" +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# -gt 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + set dummy "$ac_dir/$ac_word" "$@" + shift + ac_cv_prog_CC="$@" + fi +fi +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + if test -z "$CC"; then + case "`uname -s`" in + *win32* | *WIN32*) + # Extract the first word of "cl", so it can be a program name with args. +set dummy cl; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1969: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="cl" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + ;; + esac + fi + test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; } +fi + +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 +echo "configure:2001: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +cat > conftest.$ac_ext << EOF + +#line 2012 "configure" +#include "confdefs.h" + +main(){return(0);} +EOF +if { (eval echo configure:2017: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + ac_cv_prog_cc_works=yes + # If we can't run a trivial program, we are probably using a cross compiler. + if (./conftest; exit) 2>/dev/null; then + ac_cv_prog_cc_cross=no + else + ac_cv_prog_cc_cross=yes + fi +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_cv_prog_cc_works=no +fi +rm -fr conftest* +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +echo "$ac_t""$ac_cv_prog_cc_works" 1>&6 +if test $ac_cv_prog_cc_works = no; then + { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } +fi +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 +echo "configure:2043: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 +cross_compiling=$ac_cv_prog_cc_cross + +echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 +echo "configure:2048: checking whether we are using GNU C" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.c <&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then + ac_cv_prog_gcc=yes +else + ac_cv_prog_gcc=no +fi +fi + +echo "$ac_t""$ac_cv_prog_gcc" 1>&6 + +if test $ac_cv_prog_gcc = yes; then + GCC=yes +else + GCC= +fi + +ac_test_CFLAGS="${CFLAGS+set}" +ac_save_CFLAGS="$CFLAGS" +CFLAGS= +echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 +echo "configure:2076: checking whether ${CC-cc} accepts -g" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + echo 'void f(){}' > conftest.c +if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then + ac_cv_prog_cc_g=yes +else + ac_cv_prog_cc_g=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_prog_cc_g" 1>&6 +if test "$ac_test_CFLAGS" = set; then + CFLAGS="$ac_save_CFLAGS" +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi + + for ac_prog in $CCC c++ g++ gcc CC cxx cc++ cl +do +# Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:2112: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CXX'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CXX"; then + ac_cv_prog_CXX="$CXX" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CXX="$ac_prog" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CXX="$ac_cv_prog_CXX" +if test -n "$CXX"; then + echo "$ac_t""$CXX" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +test -n "$CXX" && break +done +test -n "$CXX" || CXX="gcc" + + +echo $ac_n "checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) works""... $ac_c" 1>&6 +echo "configure:2144: checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) works" >&5 + +ac_ext=C +# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cxx_cross + +cat > conftest.$ac_ext << EOF + +#line 2155 "configure" +#include "confdefs.h" + +int main(){return(0);} +EOF +if { (eval echo configure:2160: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + ac_cv_prog_cxx_works=yes + # If we can't run a trivial program, we are probably using a cross compiler. + if (./conftest; exit) 2>/dev/null; then + ac_cv_prog_cxx_cross=no + else + ac_cv_prog_cxx_cross=yes + fi +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_cv_prog_cxx_works=no +fi +rm -fr conftest* +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +echo "$ac_t""$ac_cv_prog_cxx_works" 1>&6 +if test $ac_cv_prog_cxx_works = no; then + { echo "configure: error: installation or configuration problem: C++ compiler cannot create executables." 1>&2; exit 1; } +fi +echo $ac_n "checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 +echo "configure:2186: checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "$ac_t""$ac_cv_prog_cxx_cross" 1>&6 +cross_compiling=$ac_cv_prog_cxx_cross + +echo $ac_n "checking whether we are using GNU C++""... $ac_c" 1>&6 +echo "configure:2191: checking whether we are using GNU C++" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gxx'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.C <&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then + ac_cv_prog_gxx=yes +else + ac_cv_prog_gxx=no +fi +fi + +echo "$ac_t""$ac_cv_prog_gxx" 1>&6 + +if test $ac_cv_prog_gxx = yes; then + GXX=yes +else + GXX= +fi + +ac_test_CXXFLAGS="${CXXFLAGS+set}" +ac_save_CXXFLAGS="$CXXFLAGS" +CXXFLAGS= +echo $ac_n "checking whether ${CXX-g++} accepts -g""... $ac_c" 1>&6 +echo "configure:2219: checking whether ${CXX-g++} accepts -g" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_cxx_g'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + echo 'void f(){}' > conftest.cc +if test -z "`${CXX-g++} -g -c conftest.cc 2>&1`"; then + ac_cv_prog_cxx_g=yes +else + ac_cv_prog_cxx_g=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_prog_cxx_g" 1>&6 +if test "$ac_test_CXXFLAGS" = set; then + CXXFLAGS="$ac_save_CXXFLAGS" +elif test $ac_cv_prog_cxx_g = yes; then + if test "$GXX" = yes; then + CXXFLAGS="-g -O2" + else + CXXFLAGS="-g" + fi +else + if test "$GXX" = yes; then + CXXFLAGS="-O2" + else + CXXFLAGS= + fi +fi + + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:2253: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_RANLIB="ranlib" + break + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_prog_RANLIB" && ac_cv_prog_RANLIB=":" +fi +fi +RANLIB="$ac_cv_prog_RANLIB" +if test -n "$RANLIB"; then + echo "$ac_t""$RANLIB" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + for ac_prog in $AS as +do +# Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:2285: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_path_AS'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + case "$AS" in + /*) + ac_cv_path_AS="$AS" # Let the user override the test with a path. + ;; + ?:/*) + ac_cv_path_AS="$AS" # Let the user override the test with a dos path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_path_AS="$ac_dir/$ac_word" + break + fi + done + IFS="$ac_save_ifs" + ;; +esac +fi +AS="$ac_cv_path_AS" +if test -n "$AS"; then + echo "$ac_t""$AS" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +test -n "$AS" && break +done +test -n "$AS" || AS="$CC" + + if test "$msyshost"; then + case "$AS" in + /*) + tmp_DIRNAME=`dirname "$AS"` + tmp_BASENAME=`basename "$AS"` + tmp_PWD=`cd "$tmp_DIRNAME" && pwd -W` + AS="$tmp_PWD/$tmp_BASENAME" + if test -e "$AS.exe"; then + AS="$AS.exe" + fi + esac + fi + + for ac_prog in ar +do +# Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:2339: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_AR'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$AR"; then + ac_cv_prog_AR="$AR" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_AR="$ac_prog" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +AR="$ac_cv_prog_AR" +if test -n "$AR"; then + echo "$ac_t""$AR" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +test -n "$AR" && break +done +test -n "$AR" || AR=":" + + for ac_prog in ld +do +# Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:2374: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_LD'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$LD"; then + ac_cv_prog_LD="$LD" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_LD="$ac_prog" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +LD="$ac_cv_prog_LD" +if test -n "$LD"; then + echo "$ac_t""$LD" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +test -n "$LD" && break +done +test -n "$LD" || LD=":" + + for ac_prog in strip +do +# Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:2409: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_STRIP'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_STRIP="$ac_prog" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +STRIP="$ac_cv_prog_STRIP" +if test -n "$STRIP"; then + echo "$ac_t""$STRIP" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +test -n "$STRIP" && break +done +test -n "$STRIP" || STRIP=":" + + for ac_prog in windres +do +# Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:2444: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_WINDRES'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$WINDRES"; then + ac_cv_prog_WINDRES="$WINDRES" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_WINDRES="$ac_prog" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +WINDRES="$ac_cv_prog_WINDRES" +if test -n "$WINDRES"; then + echo "$ac_t""$WINDRES" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +test -n "$WINDRES" && break +done +test -n "$WINDRES" || WINDRES=":" + + if test -z "$HOST_CC"; then + HOST_CC="$CC" + fi + if test -z "$HOST_CFLAGS"; then + HOST_CFLAGS="$CFLAGS" + fi + if test -z "$HOST_CXX"; then + HOST_CXX="$CXX" + fi + if test -z "$HOST_CXXFLAGS"; then + HOST_CXXFLAGS="$CXXFLAGS" + fi + if test -z "$HOST_LDFLAGS"; then + HOST_LDFLAGS="$LDFLAGS" + fi + if test -z "$HOST_RANLIB"; then + HOST_RANLIB="$RANLIB" + fi + if test -z "$HOST_AR"; then + HOST_AR="$AR" + fi +fi + +GNU_AS= +GNU_LD= +GNU_CC= +GNU_CXX= +CC_VERSION='N/A' +CXX_VERSION='N/A' +if test "$GCC" = "yes"; then + GNU_CC=1 + CC_VERSION=`$CC -v 2>&1 | grep 'gcc version'` +fi +if test "$GXX" = "yes"; then + GNU_CXX=1 + CXX_VERSION=`$CXX -v 2>&1 | grep 'gcc version'` +fi +if test "`echo | $AS -v 2>&1 | grep -c GNU`" != "0"; then + GNU_AS=1 +fi +if test "`echo | $LD -v 2>&1 | grep -c GNU`" != "0"; then + GNU_LD=1 +fi +if test "$GNU_CC"; then + if `$CC -print-prog-name=ld` -v 2>&1 | grep -c GNU >/dev/null; then + GCC_USE_GNU_LD=1 + fi +fi + +INTEL_CC= +INTEL_CXX= +if test "$GCC" = yes; then + if test "`$CC -help 2>&1 | grep -c 'Intel(R) C++ Compiler'`" != "0"; then + INTEL_CC=1 + fi +fi + +if test "$GXX" = yes; then + if test "`$CXX -help 2>&1 | grep -c 'Intel(R) C++ Compiler'`" != "0"; then + INTEL_CXX=1 + fi +fi + +case "$target" in +*-wince) + WINVER=500 + ;; +*) + if test -n "$GNU_CC"; then + WINVER=501 + else + WINVER=500 + fi + ;; +esac + +case "$target" in +*-cygwin*|*-mingw*|*-msvc*|*-mks*|*-wince) + if test "$GCC" != "yes"; then + # Check to see if we are really running in a msvc environemnt + _WIN32_MSVC=1 + + # Make sure compilers are valid + CFLAGS="$CFLAGS -TC -nologo" + CXXFLAGS="$CXXFLAGS -TP -nologo" + + ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + + cat > conftest.$ac_ext < +int main() { + printf("Hello World\n"); +; return 0; } +EOF +if { (eval echo configure:2575: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + : +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + { echo "configure: error: \$(CC) test failed. You must have MS VC++ in your path to build." 1>&2; exit 1; } +fi +rm -f conftest* + + ac_ext=C +# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cxx_cross + + cat > conftest.$ac_ext < +int main() { + unsigned *test = new unsigned(42); +; return 0; } +EOF +if { (eval echo configure:2600: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + : +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + { echo "configure: error: \$(CXX) test failed. You must have MS VC++ in your path to build." 1>&2; exit 1; } +fi +rm -f conftest* + ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + + + + _MSVC_VER_FILTER='s|.* ([0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?).*|\1|p' + + + # Determine compiler version + CC_VERSION=`"${CC}" -v 2>&1 | sed -nre "$_MSVC_VER_FILTER"` + _CC_MAJOR_VERSION=`echo ${CC_VERSION} | $AWK -F\. '{ print $1 }'` + _CC_MINOR_VERSION=`echo ${CC_VERSION} | $AWK -F\. '{ print $2 }'` + _CC_RELEASE=`echo ${CC_VERSION} | $AWK -F\. '{ print $3 }'` + _CC_BUILD=`echo ${CC_VERSION} | $AWK -F\. '{ print $4 }'` + _MSC_VER=${_CC_MAJOR_VERSION}${_CC_MINOR_VERSION} + + CXX_VERSION=`"${CXX}" -v 2>&1 | sed -nre "$_MSVC_VER_FILTER"` + _CXX_MAJOR_VERSION=`echo ${CXX_VERSION} | $AWK -F\. '{ print $1 }'` + + if test "$_CC_MAJOR_VERSION" != "$_CXX_MAJOR_VERSION"; then + { echo "configure: error: The major versions of \$CC and \$CXX do not match." 1>&2; exit 1; } + fi + if test "$_CC_MAJOR_VERSION" = "13"; then + _CC_SUITE=7 + elif test "$_CC_MAJOR_VERSION" = "14"; then + _CC_SUITE=8 + CXXFLAGS="$CXXFLAGS -Zc:wchar_t-" + if test $_CC_RELEASE -gt 50727; then + _USE_DYNAMICBASE=1 + elif test $_CC_BUILD -ge 762; then + _USE_DYNAMICBASE=1 + fi + cat >> confdefs.h <<\EOF +#define _CRT_SECURE_NO_DEPRECATE 1 +EOF + + cat >> confdefs.h <<\EOF +#define _CRT_NONSTDC_NO_DEPRECATE 1 +EOF + + elif test "$_CC_MAJOR_VERSION" = "15"; then + _CC_SUITE=9 + CXXFLAGS="$CXXFLAGS -Zc:wchar_t-" + LDFLAGS="$LDFLAGS -MANIFESTUAC:NO" + _USE_DYNAMICBASE=1 + cat >> confdefs.h <<\EOF +#define _CRT_SECURE_NO_WARNINGS 1 +EOF + + cat >> confdefs.h <<\EOF +#define _CRT_NONSTDC_NO_WARNINGS 1 +EOF + + else + { echo "configure: error: This version of the MSVC compiler, $CC_VERSION , is unsupported." 1>&2; exit 1; } + fi + + _MOZ_RTTI_FLAGS_ON='-GR' + _MOZ_RTTI_FLAGS_OFF='-GR-' + _MOZ_EXCEPTIONS_FLAGS_ON='-EHsc' + _MOZ_EXCEPTIONS_FLAGS_OFF='' + + if test -n "$WIN32_REDIST_DIR"; then + WIN32_REDIST_DIR=`cd "$WIN32_REDIST_DIR" && pwd` + fi + + # bug #249782 + # ensure that mt.exe is Microsoft (R) Manifest Tool and not magnetic tape manipulation utility (or something else) + if test "$_CC_SUITE" -ge "8"; then + + _MSMT_VER_FILTER='s|.* \([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*|\1|p' + + + MSMT_TOOL=`mt 2>&1|grep 'Microsoft (R) Manifest Tool'` + if test -n "$MSMT_TOOL"; then + MSMANIFEST_TOOL_VERSION=`echo ${MSMT_TOOL}|sed -ne "$_MSMT_VER_FILTER"` + if test -z "$MSMANIFEST_TOOL_VERSION"; then + echo "configure: warning: Unknown version of the Microsoft (R) Manifest Tool." 1>&2 + fi + MSMANIFEST_TOOL=1 + unset MSMT_TOOL + else + { echo "configure: error: Microsoft (R) Manifest Tool must be in your \$PATH." 1>&2; exit 1; } + fi + fi + + # Check linker version + _LD_FULL_VERSION=`"${LD}" -v 2>&1 | sed -nre "$_MSVC_VER_FILTER"` + _LD_MAJOR_VERSION=`echo ${_LD_FULL_VERSION} | $AWK -F\. '{ print $1 }'` + if test "$_LD_MAJOR_VERSION" != "$_CC_SUITE"; then + { echo "configure: error: The linker major version, $_LD_FULL_VERSION, does not match the compiler suite version, $_CC_SUITE." 1>&2; exit 1; } + fi + INCREMENTAL_LINKER=1 + + unset _MSVC_VER_FILTER + + else + # Check w32api version + _W32API_MAJOR_VERSION=`echo $W32API_VERSION | $AWK -F\. '{ print $1 }'` + _W32API_MINOR_VERSION=`echo $W32API_VERSION | $AWK -F\. '{ print $2 }'` + echo $ac_n "checking for w32api version >= $W32API_VERSION""... $ac_c" 1>&6 +echo "configure:2714: checking for w32api version >= $W32API_VERSION" >&5 + cat > conftest.$ac_ext < +int main() { +#if (__W32API_MAJOR_VERSION < $_W32API_MAJOR_VERSION) || \ + (__W32API_MAJOR_VERSION == $_W32API_MAJOR_VERSION && \ + __W32API_MINOR_VERSION < $_W32API_MINOR_VERSION) + #error "test failed." + #endif + +; return 0; } +EOF +if { (eval echo configure:2728: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + res=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + res=no +fi +rm -f conftest* + echo "$ac_t""$res" 1>&6 + if test "$res" != "yes"; then + { echo "configure: error: w32api version $W32API_VERSION or higher required." 1>&2; exit 1; } + fi + # Check windres version + echo $ac_n "checking for windres version >= $WINDRES_VERSION""... $ac_c" 1>&6 +echo "configure:2744: checking for windres version >= $WINDRES_VERSION" >&5 + _WINDRES_VERSION=`${WINDRES} --version 2>&1 | grep -i windres 2>/dev/null | $AWK '{ print $3 }'` + echo "$ac_t""$_WINDRES_VERSION" 1>&6 + _WINDRES_MAJOR_VERSION=`echo $_WINDRES_VERSION | $AWK -F\. '{ print $1 }'` + _WINDRES_MINOR_VERSION=`echo $_WINDRES_VERSION | $AWK -F\. '{ print $2 }'` + _WINDRES_RELEASE_VERSION=`echo $_WINDRES_VERSION | $AWK -F\. '{ print $3 }'` + WINDRES_MAJOR_VERSION=`echo $WINDRES_VERSION | $AWK -F\. '{ print $1 }'` + WINDRES_MINOR_VERSION=`echo $WINDRES_VERSION | $AWK -F\. '{ print $2 }'` + WINDRES_RELEASE_VERSION=`echo $WINDRES_VERSION | $AWK -F\. '{ print $3 }'` + if test "$_WINDRES_MAJOR_VERSION" -lt "$WINDRES_MAJOR_VERSION" -o \ + "$_WINDRES_MAJOR_VERSION" -eq "$WINDRES_MAJOR_VERSION" -a \ + "$_WINDRES_MINOR_VERSION" -lt "$WINDRES_MINOR_VERSION" -o \ + "$_WINDRES_MAJOR_VERSION" -eq "$WINDRES_MAJOR_VERSION" -a \ + "$_WINDRES_MINOR_VERSION" -eq "$WINDRES_MINOR_VERSION" -a \ + "$_WINDRES_RELEASE_VERSION" -lt "$WINDRES_RELEASE_VERSION" + then + { echo "configure: error: windres version $WINDRES_VERSION or higher is required to build." 1>&2; exit 1; } + fi + fi # !GNU_CC + + cat >> confdefs.h <> confdefs.h <> confdefs.h <&6 +echo "configure:2783: checking whether $CC and cc understand -c and -o together" >&5 +else + echo $ac_n "checking whether cc understands -c and -o together""... $ac_c" 1>&6 +echo "configure:2786: checking whether cc understands -c and -o together" >&5 +fi +set dummy $CC; ac_cc="`echo $2 | + sed -e 's/[^a-zA-Z0-9_]/_/g' -e 's/^[0-9]/_/'`" +if eval "test \"`echo '$''{'ac_cv_prog_cc_${ac_cc}_c_o'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + echo 'foo(){}' > conftest.c +# Make sure it works both with $CC and with simple cc. +# We do the test twice because some compilers refuse to overwrite an +# existing .o file with -o, though they will create one. +ac_try='${CC-cc} -c conftest.c -o conftest.o 1>&5' +if { (eval echo configure:2798: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } && + test -f conftest.o && { (eval echo configure:2799: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; +then + eval ac_cv_prog_cc_${ac_cc}_c_o=yes + if test "x$CC" != xcc; then + # Test first that cc exists at all. + if { ac_try='cc -c conftest.c 1>&5'; { (eval echo configure:2804: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; }; then + ac_try='cc -c conftest.c -o conftest.o 1>&5' + if { (eval echo configure:2806: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } && + test -f conftest.o && { (eval echo configure:2807: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; + then + # cc works too. + : + else + # cc exists but doesn't like -o. + eval ac_cv_prog_cc_${ac_cc}_c_o=no + fi + fi + fi +else + eval ac_cv_prog_cc_${ac_cc}_c_o=no +fi +rm -f conftest* + +fi +if eval "test \"`echo '$ac_cv_prog_cc_'${ac_cc}_c_o`\" = yes"; then + echo "$ac_t""yes" 1>&6 +else + echo "$ac_t""no" 1>&6 + cat >> confdefs.h <<\EOF +#define NO_MINUS_C_MINUS_O 1 +EOF + +fi + + if grep "NO_MINUS_C_MINUS_O 1" ./confdefs.h >/dev/null; then + USING_HCC=1 + _OLDCC=$CC + _OLDCXX=$CXX + CC="${srcdir}/build/hcc '$CC'" + CXX="${srcdir}/build/hcpp '$CXX'" + fi +fi + +echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 +echo "configure:2843: checking how to run the C preprocessor" >&5 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then +if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + # This must be in double quotes, not single quotes, because CPP may get + # substituted into the Makefile and "${CC-cc}" will confuse make. + CPP="${CC-cc} -E" + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. + cat > conftest.$ac_ext < +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:2864: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP="${CC-cc} -E -traditional-cpp" + cat > conftest.$ac_ext < +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:2881: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP="${CC-cc} -nologo -E" + cat > conftest.$ac_ext < +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:2898: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP=/lib/cpp +fi +rm -f conftest* +fi +rm -f conftest* +fi +rm -f conftest* + ac_cv_prog_CPP="$CPP" +fi + CPP="$ac_cv_prog_CPP" +else + ac_cv_prog_CPP="$CPP" +fi +echo "$ac_t""$CPP" 1>&6 + +echo $ac_n "checking how to run the C++ preprocessor""... $ac_c" 1>&6 +echo "configure:2923: checking how to run the C++ preprocessor" >&5 +if test -z "$CXXCPP"; then +if eval "test \"`echo '$''{'ac_cv_prog_CXXCPP'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_ext=C +# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cxx_cross + CXXCPP="${CXX-g++} -E" + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:2941: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CXXCPP=/lib/cpp +fi +rm -f conftest* + ac_cv_prog_CXXCPP="$CXXCPP" +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross +fi +fi +CXXCPP="$ac_cv_prog_CXXCPP" +echo "$ac_t""$CXXCPP" 1>&6 + + +if test -n "$_WIN32_MSVC"; then + SKIP_PATH_CHECKS=1 + SKIP_COMPILER_CHECKS=1 + SKIP_LIBRARY_CHECKS=1 + + # Since we're skipping compiler and library checks, hard-code + # some facts here. + + # Common to all MSVC environments: + # Windows lacks , but has __int8, and so on. + cat >> confdefs.h <<\EOF +#define JS_HAVE___INTN 1 +EOF + + + case "$target" in + *-wince) + cat >> confdefs.h <<\EOF +#define HAVE_SYSTEMTIMETOFILETIME 1 +EOF + + cat >> confdefs.h <<\EOF +#define JS_CRTDEFS_H_HAS_INTPTR_T 1 +EOF + + ;; + *) + cat >> confdefs.h <<\EOF +#define HAVE_SYSTEMTIMETOFILETIME 1 +EOF + + cat >> confdefs.h <<\EOF +#define HAVE_GETSYSTEMTIMEASFILETIME 1 +EOF + + # Windows defines intptr_t and uintptr_t. + # VS2005: http://msdn.microsoft.com/en-us/library/323b6b3k(VS.80).aspx + # VS2008: http://msdn.microsoft.com/en-us/library/323b6b3k.aspx + cat >> confdefs.h <<\EOF +#define JS_STDDEF_H_HAS_INTPTR_T 1 +EOF + + ;; + esac +fi + +fi # COMPILE_ENVIRONMENT + +if test "$cross_compiling" = "yes"; then + CROSS_COMPILE=1 +else + CROSS_COMPILE= +fi + + + + + + + + + + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# ./install, which can be erroneously created by make from ./install.sh. +echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 +echo "configure:3041: checking for a BSD compatible install" >&5 +if test -z "$INSTALL"; then +if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS=":" + for ac_dir in $PATH; do + # Account for people who put trailing slashes in PATH elements. + case "$ac_dir/" in + /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + if test -f $ac_dir/$ac_prog; then + if test $ac_prog = install && + grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + else + ac_cv_path_install="$ac_dir/$ac_prog -c" + break 2 + fi + fi + done + ;; + esac + done + IFS="$ac_save_IFS" + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL="$ac_cv_path_install" + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL="$ac_install_sh" + fi +fi +echo "$ac_t""$INSTALL" 1>&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +echo $ac_n "checking whether ln -s works""... $ac_c" 1>&6 +echo "configure:3094: checking whether ln -s works" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_LN_S'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + rm -f conftestdata +if ln -s X conftestdata 2>/dev/null +then + rm -f conftestdata + ac_cv_prog_LN_S="ln -s" +else + ac_cv_prog_LN_S=ln +fi +fi +LN_S="$ac_cv_prog_LN_S" +if test "$ac_cv_prog_LN_S" = "ln -s"; then + echo "$ac_t""yes" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + for ac_prog in $PERL perl5 perl +do +# Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:3119: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_path_PERL'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + case "$PERL" in + /*) + ac_cv_path_PERL="$PERL" # Let the user override the test with a path. + ;; + ?:/*) + ac_cv_path_PERL="$PERL" # Let the user override the test with a dos path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_path_PERL="$ac_dir/$ac_word" + break + fi + done + IFS="$ac_save_ifs" + ;; +esac +fi +PERL="$ac_cv_path_PERL" +if test -n "$PERL"; then + echo "$ac_t""$PERL" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +test -n "$PERL" && break +done + + if test "$msyshost"; then + case "$PERL" in + /*) + tmp_DIRNAME=`dirname "$PERL"` + tmp_BASENAME=`basename "$PERL"` + tmp_PWD=`cd "$tmp_DIRNAME" && pwd -W` + PERL="$tmp_PWD/$tmp_BASENAME" + if test -e "$PERL.exe"; then + PERL="$PERL.exe" + fi + esac + fi + +if test -z "$PERL" || test "$PERL" = ":"; then + { echo "configure: error: perl not found in \$PATH" 1>&2; exit 1; } +fi + +if test -z "$TINDERBOX_SKIP_PERL_VERSION_CHECK"; then +echo $ac_n "checking for minimum required perl version >= $PERL_VERSION""... $ac_c" 1>&6 +echo "configure:3173: checking for minimum required perl version >= $PERL_VERSION" >&5 +_perl_version=`PERL_VERSION=$PERL_VERSION $PERL -e 'print "$]"; if ($] >= $ENV{PERL_VERSION}) { exit(0); } else { exit(1); }' 2>&5` +_perl_res=$? +echo "$ac_t""$_perl_version" 1>&6 + +if test "$_perl_res" != 0; then + { echo "configure: error: Perl $PERL_VERSION or higher is required." 1>&2; exit 1; } +fi +fi + +echo $ac_n "checking for full perl installation""... $ac_c" 1>&6 +echo "configure:3184: checking for full perl installation" >&5 +_perl_archlib=`$PERL -e 'use Config; if ( -d $Config{archlib} ) { exit(0); } else { exit(1); }' 2>&5` +_perl_res=$? +if test "$_perl_res" != 0; then + echo "$ac_t""no" 1>&6 + { echo "configure: error: Cannot find Config.pm or \$Config{archlib}. A full perl installation is required." 1>&2; exit 1; } +else + echo "$ac_t""yes" 1>&6 +fi + + for ac_prog in $PYTHON python +do +# Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:3199: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_path_PYTHON'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + case "$PYTHON" in + /*) + ac_cv_path_PYTHON="$PYTHON" # Let the user override the test with a path. + ;; + ?:/*) + ac_cv_path_PYTHON="$PYTHON" # Let the user override the test with a dos path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_path_PYTHON="$ac_dir/$ac_word" + break + fi + done + IFS="$ac_save_ifs" + ;; +esac +fi +PYTHON="$ac_cv_path_PYTHON" +if test -n "$PYTHON"; then + echo "$ac_t""$PYTHON" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +test -n "$PYTHON" && break +done + + if test "$msyshost"; then + case "$PYTHON" in + /*) + tmp_DIRNAME=`dirname "$PYTHON"` + tmp_BASENAME=`basename "$PYTHON"` + tmp_PWD=`cd "$tmp_DIRNAME" && pwd -W` + PYTHON="$tmp_PWD/$tmp_BASENAME" + if test -e "$PYTHON.exe"; then + PYTHON="$PYTHON.exe" + fi + esac + fi + +if test -z "$PYTHON"; then + { echo "configure: error: python was not found in \$PATH" 1>&2; exit 1; } +fi + +if test -z "$COMPILE_ENVIRONMENT"; then + NSINSTALL_BIN='$(PYTHON) $(topsrcdir)/config/nsinstall.py' +fi + + + # Extract the first word of "doxygen", so it can be a program name with args. +set dummy doxygen; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:3259: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_path_DOXYGEN'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + case "$DOXYGEN" in + /*) + ac_cv_path_DOXYGEN="$DOXYGEN" # Let the user override the test with a path. + ;; + ?:/*) + ac_cv_path_DOXYGEN="$DOXYGEN" # Let the user override the test with a dos path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_path_DOXYGEN="$ac_dir/$ac_word" + break + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_path_DOXYGEN" && ac_cv_path_DOXYGEN=":" + ;; +esac +fi +DOXYGEN="$ac_cv_path_DOXYGEN" +if test -n "$DOXYGEN"; then + echo "$ac_t""$DOXYGEN" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + if test "$msyshost"; then + case "$DOXYGEN" in + /*) + tmp_DIRNAME=`dirname "$DOXYGEN"` + tmp_BASENAME=`basename "$DOXYGEN"` + tmp_PWD=`cd "$tmp_DIRNAME" && pwd -W` + DOXYGEN="$tmp_PWD/$tmp_BASENAME" + if test -e "$DOXYGEN.exe"; then + DOXYGEN="$DOXYGEN.exe" + fi + esac + fi + + # Extract the first word of "whoami", so it can be a program name with args. +set dummy whoami; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:3308: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_path_WHOAMI'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + case "$WHOAMI" in + /*) + ac_cv_path_WHOAMI="$WHOAMI" # Let the user override the test with a path. + ;; + ?:/*) + ac_cv_path_WHOAMI="$WHOAMI" # Let the user override the test with a dos path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_path_WHOAMI="$ac_dir/$ac_word" + break + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_path_WHOAMI" && ac_cv_path_WHOAMI=":" + ;; +esac +fi +WHOAMI="$ac_cv_path_WHOAMI" +if test -n "$WHOAMI"; then + echo "$ac_t""$WHOAMI" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + if test "$msyshost"; then + case "$WHOAMI" in + /*) + tmp_DIRNAME=`dirname "$WHOAMI"` + tmp_BASENAME=`basename "$WHOAMI"` + tmp_PWD=`cd "$tmp_DIRNAME" && pwd -W` + WHOAMI="$tmp_PWD/$tmp_BASENAME" + if test -e "$WHOAMI.exe"; then + WHOAMI="$WHOAMI.exe" + fi + esac + fi + + # Extract the first word of "autoconf", so it can be a program name with args. +set dummy autoconf; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:3357: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_path_AUTOCONF'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + case "$AUTOCONF" in + /*) + ac_cv_path_AUTOCONF="$AUTOCONF" # Let the user override the test with a path. + ;; + ?:/*) + ac_cv_path_AUTOCONF="$AUTOCONF" # Let the user override the test with a dos path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_path_AUTOCONF="$ac_dir/$ac_word" + break + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_path_AUTOCONF" && ac_cv_path_AUTOCONF=":" + ;; +esac +fi +AUTOCONF="$ac_cv_path_AUTOCONF" +if test -n "$AUTOCONF"; then + echo "$ac_t""$AUTOCONF" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + if test "$msyshost"; then + case "$AUTOCONF" in + /*) + tmp_DIRNAME=`dirname "$AUTOCONF"` + tmp_BASENAME=`basename "$AUTOCONF"` + tmp_PWD=`cd "$tmp_DIRNAME" && pwd -W` + AUTOCONF="$tmp_PWD/$tmp_BASENAME" + if test -e "$AUTOCONF.exe"; then + AUTOCONF="$AUTOCONF.exe" + fi + esac + fi + + # Extract the first word of "makedepend", so it can be a program name with args. +set dummy makedepend; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:3406: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_path_SYSTEM_MAKEDEPEND'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + case "$SYSTEM_MAKEDEPEND" in + /*) + ac_cv_path_SYSTEM_MAKEDEPEND="$SYSTEM_MAKEDEPEND" # Let the user override the test with a path. + ;; + ?:/*) + ac_cv_path_SYSTEM_MAKEDEPEND="$SYSTEM_MAKEDEPEND" # Let the user override the test with a dos path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_path_SYSTEM_MAKEDEPEND="$ac_dir/$ac_word" + break + fi + done + IFS="$ac_save_ifs" + ;; +esac +fi +SYSTEM_MAKEDEPEND="$ac_cv_path_SYSTEM_MAKEDEPEND" +if test -n "$SYSTEM_MAKEDEPEND"; then + echo "$ac_t""$SYSTEM_MAKEDEPEND" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + if test "$msyshost"; then + case "$SYSTEM_MAKEDEPEND" in + /*) + tmp_DIRNAME=`dirname "$SYSTEM_MAKEDEPEND"` + tmp_BASENAME=`basename "$SYSTEM_MAKEDEPEND"` + tmp_PWD=`cd "$tmp_DIRNAME" && pwd -W` + SYSTEM_MAKEDEPEND="$tmp_PWD/$tmp_BASENAME" + if test -e "$SYSTEM_MAKEDEPEND.exe"; then + SYSTEM_MAKEDEPEND="$SYSTEM_MAKEDEPEND.exe" + fi + esac + fi + + # Extract the first word of "xargs", so it can be a program name with args. +set dummy xargs; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:3454: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_path_XARGS'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + case "$XARGS" in + /*) + ac_cv_path_XARGS="$XARGS" # Let the user override the test with a path. + ;; + ?:/*) + ac_cv_path_XARGS="$XARGS" # Let the user override the test with a dos path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_path_XARGS="$ac_dir/$ac_word" + break + fi + done + IFS="$ac_save_ifs" + ;; +esac +fi +XARGS="$ac_cv_path_XARGS" +if test -n "$XARGS"; then + echo "$ac_t""$XARGS" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + if test "$msyshost"; then + case "$XARGS" in + /*) + tmp_DIRNAME=`dirname "$XARGS"` + tmp_BASENAME=`basename "$XARGS"` + tmp_PWD=`cd "$tmp_DIRNAME" && pwd -W` + XARGS="$tmp_PWD/$tmp_BASENAME" + if test -e "$XARGS.exe"; then + XARGS="$XARGS.exe" + fi + esac + fi + +if test -z "$XARGS" || test "$XARGS" = ":"; then + { echo "configure: error: xargs not found in \$PATH ." 1>&2; exit 1; } +fi + +if test "$COMPILE_ENVIRONMENT"; then + + +case "$target_os" in +darwin*) + + GCC_VERSION_FULL=`echo $CXX_VERSION | $PERL -pe 's/^.*gcc version ([^ ]*).*/$1/'` + GCC_VERSION=`echo $GCC_VERSION_FULL | $PERL -pe '(split(/\./))[0]>=4&&s/(^\d*\.\d*).*/$1/;'` + + if test "$GCC_VERSION_FULL" = "4.0.0" ; then + + GCC_BUILD=`echo $CXX_VERSION | $PERL -pe 's/^.*build ([^ )]*).*/$1/'` + + if test "$GCC_BUILD" = "4061" ; then + { echo "configure: error: You are attempting to use Apple gcc 4.0 build 4061. +This compiler was supplied with Xcode 2.0, and contains bugs that prevent it +from building Mozilla. Upgrade to Xcode 2.1 or later." 1>&2; exit 1; } + fi + fi + + for ac_prog in pbbuild xcodebuild pbxbuild +do +# Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:3528: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_path_PBBUILD'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + case "$PBBUILD" in + /*) + ac_cv_path_PBBUILD="$PBBUILD" # Let the user override the test with a path. + ;; + ?:/*) + ac_cv_path_PBBUILD="$PBBUILD" # Let the user override the test with a dos path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_path_PBBUILD="$ac_dir/$ac_word" + break + fi + done + IFS="$ac_save_ifs" + ;; +esac +fi +PBBUILD="$ac_cv_path_PBBUILD" +if test -n "$PBBUILD"; then + echo "$ac_t""$PBBUILD" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +test -n "$PBBUILD" && break +done + + if test "$msyshost"; then + case "$PBBUILD" in + /*) + tmp_DIRNAME=`dirname "$PBBUILD"` + tmp_BASENAME=`basename "$PBBUILD"` + tmp_PWD=`cd "$tmp_DIRNAME" && pwd -W` + PBBUILD="$tmp_PWD/$tmp_BASENAME" + if test -e "$PBBUILD.exe"; then + PBBUILD="$PBBUILD.exe" + fi + esac + fi + + + case "$PBBUILD" in + *xcodebuild*) + + XCODEBUILD_VERSION=`$PBBUILD -version 2>/dev/null | xargs | sed -e 's/.*DevToolsCore-\([0-9]*\).*/\1/'` + + if test -n "$XCODEBUILD_VERSION" && test "$XCODEBUILD_VERSION" -ge 620 ; then + HAS_XCODE_2_1=1; + fi + ;; + esac + + # Extract the first word of "sdp", so it can be a program name with args. +set dummy sdp; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:3591: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_path_SDP'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + case "$SDP" in + /*) + ac_cv_path_SDP="$SDP" # Let the user override the test with a path. + ;; + ?:/*) + ac_cv_path_SDP="$SDP" # Let the user override the test with a dos path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH:/usr/bin:/Developer/Tools" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_path_SDP="$ac_dir/$ac_word" + break + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_path_SDP" && ac_cv_path_SDP=":" + ;; +esac +fi +SDP="$ac_cv_path_SDP" +if test -n "$SDP"; then + echo "$ac_t""$SDP" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + if test "$msyshost"; then + case "$SDP" in + /*) + tmp_DIRNAME=`dirname "$SDP"` + tmp_BASENAME=`basename "$SDP"` + tmp_PWD=`cd "$tmp_DIRNAME" && pwd -W` + SDP="$tmp_PWD/$tmp_BASENAME" + if test -e "$SDP.exe"; then + SDP="$SDP.exe" + fi + esac + fi + + ;; +esac + + + + + + + + +# Check whether --enable-macos-target or --disable-macos-target was given. +if test "${enable_macos_target+set}" = set; then + enableval="$enable_macos_target" + _MACOSX_DEPLOYMENT_TARGET=$enableval +fi + + +case "$target" in +*-darwin*) + if test -n "$_MACOSX_DEPLOYMENT_TARGET" ; then + export MACOSX_DEPLOYMENT_TARGET=$_MACOSX_DEPLOYMENT_TARGET + cat >> confdefs.h <&2; exit 1; } + fi + + GCC_VERSION_MAJOR=`echo $GCC_VERSION_FULL | $PERL -pe 's/(^\d*).*/$1/;'` + if test "$GCC_VERSION_MAJOR" -lt "4" ; then + { echo "configure: error: You need to upgrade the compiler version to 4.x" 1>&2; exit 1; } + else + CFLAGS="$CFLAGS -isysroot ${MACOS_SDK_DIR}" + CXXFLAGS="$CXXFLAGS -isysroot ${MACOS_SDK_DIR}" + + CPP="$CPP -isysroot ${MACOS_SDK_DIR}" + CXXCPP="$CXXCPP -isysroot ${MACOS_SDK_DIR}" + + if test "$GCC_VERSION_FULL" = "4.0.0" ; then + LDFLAGS="$LDFLAGS -Wl,-syslibroot,${MACOS_SDK_DIR}" + fi + fi + + + echo $ac_n "checking for valid compiler/Mac OS X SDK combination""... $ac_c" 1>&6 +echo "configure:3707: checking for valid compiler/Mac OS X SDK combination" >&5 + ac_ext=C +# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cxx_cross + + cat > conftest.$ac_ext < + int main() { return 0; } +int main() { +result=yes +; return 0; } +EOF +if { (eval echo configure:3724: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + result=no +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 +fi +rm -f conftest* + ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + + echo "$ac_t""$result" 1>&6 + + if test "$result" = "no" ; then + { echo "configure: error: The selected compiler and Mac OS X SDK are incompatible." 1>&2; exit 1; } + fi +fi + +fi # COMPILE_ENVIRONMENT + +if test -z "$MAKE"; then + case "$host_os" in + cygwin*|mingw*|mks*|msvc*) + for ac_prog in $MAKE make gmake +do +# Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:3756: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_path_MAKE'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + case "$MAKE" in + /*) + ac_cv_path_MAKE="$MAKE" # Let the user override the test with a path. + ;; + ?:/*) + ac_cv_path_MAKE="$MAKE" # Let the user override the test with a dos path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_path_MAKE="$ac_dir/$ac_word" + break + fi + done + IFS="$ac_save_ifs" + ;; +esac +fi +MAKE="$ac_cv_path_MAKE" +if test -n "$MAKE"; then + echo "$ac_t""$MAKE" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +test -n "$MAKE" && break +done +test -n "$MAKE" || MAKE=":" + + if test "$msyshost"; then + case "$MAKE" in + /*) + tmp_DIRNAME=`dirname "$MAKE"` + tmp_BASENAME=`basename "$MAKE"` + tmp_PWD=`cd "$tmp_DIRNAME" && pwd -W` + MAKE="$tmp_PWD/$tmp_BASENAME" + if test -e "$MAKE.exe"; then + MAKE="$MAKE.exe" + fi + esac + fi + + ;; + *) + for ac_prog in $MAKE gmake make +do +# Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:3812: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_path_MAKE'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + case "$MAKE" in + /*) + ac_cv_path_MAKE="$MAKE" # Let the user override the test with a path. + ;; + ?:/*) + ac_cv_path_MAKE="$MAKE" # Let the user override the test with a dos path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_path_MAKE="$ac_dir/$ac_word" + break + fi + done + IFS="$ac_save_ifs" + ;; +esac +fi +MAKE="$ac_cv_path_MAKE" +if test -n "$MAKE"; then + echo "$ac_t""$MAKE" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +test -n "$MAKE" && break +done +test -n "$MAKE" || MAKE=":" + + if test "$msyshost"; then + case "$MAKE" in + /*) + tmp_DIRNAME=`dirname "$MAKE"` + tmp_BASENAME=`basename "$MAKE"` + tmp_PWD=`cd "$tmp_DIRNAME" && pwd -W` + MAKE="$tmp_PWD/$tmp_BASENAME" + if test -e "$MAKE.exe"; then + MAKE="$MAKE.exe" + fi + esac + fi + + ;; + esac +fi + +if test "$COMPILE_ENVIRONMENT"; then + +# If we find X, set shell vars x_includes and x_libraries to the +# paths, otherwise set no_x=yes. +# Uses ac_ vars as temps to allow command line to override cache and checks. +# --without-x overrides everything else, but does not touch the cache. +echo $ac_n "checking for X""... $ac_c" 1>&6 +echo "configure:3872: checking for X" >&5 + +# Check whether --with-x or --without-x was given. +if test "${with_x+set}" = set; then + withval="$with_x" + : +fi + +# $have_x is `yes', `no', `disabled', or empty when we do not yet know. +if test "x$with_x" = xno; then + # The user explicitly disabled X. + have_x=disabled +else + if test "x$x_includes" != xNONE && test "x$x_libraries" != xNONE; then + # Both variables are already set. + have_x=yes + else +if eval "test \"`echo '$''{'ac_cv_have_x'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + # One or both of the vars are not set, and there is no cached value. +ac_x_includes=NO ac_x_libraries=NO +rm -fr conftestdir +if mkdir conftestdir; then + cd conftestdir + # Make sure to not put "make" in the Imakefile rules, since we grep it out. + cat > Imakefile <<'EOF' +acfindx: + @echo 'ac_im_incroot="${INCROOT}"; ac_im_usrlibdir="${USRLIBDIR}"; ac_im_libdir="${LIBDIR}"' +EOF + if (xmkmf) >/dev/null 2>/dev/null && test -f Makefile; then + # GNU make sometimes prints "make[1]: Entering...", which would confuse us. + eval `${MAKE-make} acfindx 2>/dev/null | grep -v make` + # Open Windows xmkmf reportedly sets LIBDIR instead of USRLIBDIR. + for ac_extension in a so sl; do + if test ! -f $ac_im_usrlibdir/libX11.$ac_extension && + test -f $ac_im_libdir/libX11.$ac_extension; then + ac_im_usrlibdir=$ac_im_libdir; break + fi + done + # Screen out bogus values from the imake configuration. They are + # bogus both because they are the default anyway, and because + # using them would break gcc on systems where it needs fixed includes. + case "$ac_im_incroot" in + /usr/include) ;; + *) test -f "$ac_im_incroot/X11/Xos.h" && ac_x_includes="$ac_im_incroot" ;; + esac + case "$ac_im_usrlibdir" in + /usr/lib | /lib) ;; + *) test -d "$ac_im_usrlibdir" && ac_x_libraries="$ac_im_usrlibdir" ;; + esac + fi + cd .. + rm -fr conftestdir +fi + +if test "$ac_x_includes" = NO; then + # Guess where to find include files, by looking for this one X11 .h file. + test -z "$x_direct_test_include" && x_direct_test_include=X11/Intrinsic.h + + # First, try using that file with no special directory specified. +cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:3939: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + # We can compile using X headers with no special include directory. +ac_x_includes= +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + # Look for the header file in a standard set of common directories. +# Check X11 before X11Rn because it is often a symlink to the current release. + for ac_dir in \ + /usr/X11/include \ + /usr/X11R6/include \ + /usr/X11R5/include \ + /usr/X11R4/include \ + \ + /usr/include/X11 \ + /usr/include/X11R6 \ + /usr/include/X11R5 \ + /usr/include/X11R4 \ + \ + /usr/local/X11/include \ + /usr/local/X11R6/include \ + /usr/local/X11R5/include \ + /usr/local/X11R4/include \ + \ + /usr/local/include/X11 \ + /usr/local/include/X11R6 \ + /usr/local/include/X11R5 \ + /usr/local/include/X11R4 \ + \ + /usr/X386/include \ + /usr/x386/include \ + /usr/XFree86/include/X11 \ + \ + /usr/include \ + /usr/local/include \ + /usr/unsupported/include \ + /usr/athena/include \ + /usr/local/x11r5/include \ + /usr/lpp/Xamples/include \ + \ + /usr/openwin/include \ + /usr/openwin/share/include \ + ; \ + do + if test -r "$ac_dir/$x_direct_test_include"; then + ac_x_includes=$ac_dir + break + fi + done +fi +rm -f conftest* +fi # $ac_x_includes = NO + +if test "$ac_x_libraries" = NO; then + # Check for the libraries. + + test -z "$x_direct_test_library" && x_direct_test_library=Xt + test -z "$x_direct_test_function" && x_direct_test_function=XtMalloc + + # See if we find them without any special options. + # Don't add to $LIBS permanently. + ac_save_LIBS="$LIBS" + LIBS="-l$x_direct_test_library $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + LIBS="$ac_save_LIBS" +# We can link X programs with no special library path. +ac_x_libraries= +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + LIBS="$ac_save_LIBS" +# First see if replacing the include by lib works. +# Check X11 before X11Rn because it is often a symlink to the current release. +for ac_dir in `echo "$ac_x_includes" | sed s/include/lib/` \ + /usr/X11/lib \ + /usr/X11R6/lib \ + /usr/X11R5/lib \ + /usr/X11R4/lib \ + \ + /usr/lib/X11 \ + /usr/lib/X11R6 \ + /usr/lib/X11R5 \ + /usr/lib/X11R4 \ + \ + /usr/local/X11/lib \ + /usr/local/X11R6/lib \ + /usr/local/X11R5/lib \ + /usr/local/X11R4/lib \ + \ + /usr/local/lib/X11 \ + /usr/local/lib/X11R6 \ + /usr/local/lib/X11R5 \ + /usr/local/lib/X11R4 \ + \ + /usr/X386/lib \ + /usr/x386/lib \ + /usr/XFree86/lib/X11 \ + \ + /usr/lib \ + /usr/local/lib \ + /usr/unsupported/lib \ + /usr/athena/lib \ + /usr/local/x11r5/lib \ + /usr/lpp/Xamples/lib \ + /lib/usr/lib/X11 \ + \ + /usr/openwin/lib \ + /usr/openwin/share/lib \ + ; \ +do + for ac_extension in a so sl; do + if test -r $ac_dir/lib${x_direct_test_library}.$ac_extension; then + ac_x_libraries=$ac_dir + break 2 + fi + done +done +fi +rm -f conftest* +fi # $ac_x_libraries = NO + +if test "$ac_x_includes" = NO || test "$ac_x_libraries" = NO; then + # Didn't find X anywhere. Cache the known absence of X. + ac_cv_have_x="have_x=no" +else + # Record where we found X for the cache. + ac_cv_have_x="have_x=yes \ + ac_x_includes=$ac_x_includes ac_x_libraries=$ac_x_libraries" +fi +fi + fi + eval "$ac_cv_have_x" +fi # $with_x != no + +if test "$have_x" != yes; then + echo "$ac_t""$have_x" 1>&6 + no_x=yes +else + # If each of the values was on the command line, it overrides each guess. + test "x$x_includes" = xNONE && x_includes=$ac_x_includes + test "x$x_libraries" = xNONE && x_libraries=$ac_x_libraries + # Update the cache value to reflect the command line values. + ac_cv_have_x="have_x=yes \ + ac_x_includes=$x_includes ac_x_libraries=$x_libraries" + echo "$ac_t""libraries $x_libraries, headers $x_includes" 1>&6 +fi + +if test "$no_x" = yes; then + # Not all programs may use this symbol, but it does not hurt to define it. + cat >> confdefs.h <<\EOF +#define X_DISPLAY_MISSING 1 +EOF + + X_CFLAGS= X_PRE_LIBS= X_LIBS= X_EXTRA_LIBS= +else + if test -n "$x_includes"; then + X_CFLAGS="$X_CFLAGS -I$x_includes" + fi + + # It would also be nice to do this for all -L options, not just this one. + if test -n "$x_libraries"; then + X_LIBS="$X_LIBS -L$x_libraries" + # For Solaris; some versions of Sun CC require a space after -R and + # others require no space. Words are not sufficient . . . . + case "`(uname -sr) 2>/dev/null`" in + "SunOS 5"*) + echo $ac_n "checking whether -R must be followed by a space""... $ac_c" 1>&6 +echo "configure:4121: checking whether -R must be followed by a space" >&5 + ac_xsave_LIBS="$LIBS"; LIBS="$LIBS -R$x_libraries" + cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + ac_R_nospace=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_R_nospace=no +fi +rm -f conftest* + if test $ac_R_nospace = yes; then + echo "$ac_t""no" 1>&6 + X_LIBS="$X_LIBS -R$x_libraries" + else + LIBS="$ac_xsave_LIBS -R $x_libraries" + cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + ac_R_space=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_R_space=no +fi +rm -f conftest* + if test $ac_R_space = yes; then + echo "$ac_t""yes" 1>&6 + X_LIBS="$X_LIBS -R $x_libraries" + else + echo "$ac_t""neither works" 1>&6 + fi + fi + LIBS="$ac_xsave_LIBS" + esac + fi + + # Check for system-dependent libraries X programs must link with. + # Do this before checking for the system-independent R6 libraries + # (-lICE), since we may need -lsocket or whatever for X linking. + + if test "$ISC" = yes; then + X_EXTRA_LIBS="$X_EXTRA_LIBS -lnsl_s -linet" + else + # Martyn.Johnson@cl.cam.ac.uk says this is needed for Ultrix, if the X + # libraries were built with DECnet support. And karl@cs.umb.edu says + # the Alpha needs dnet_stub (dnet does not exist). + echo $ac_n "checking for dnet_ntoa in -ldnet""... $ac_c" 1>&6 +echo "configure:4186: checking for dnet_ntoa in -ldnet" >&5 +ac_lib_var=`echo dnet'_'dnet_ntoa | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-ldnet $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + X_EXTRA_LIBS="$X_EXTRA_LIBS -ldnet" +else + echo "$ac_t""no" 1>&6 +fi + + if test $ac_cv_lib_dnet_dnet_ntoa = no; then + echo $ac_n "checking for dnet_ntoa in -ldnet_stub""... $ac_c" 1>&6 +echo "configure:4227: checking for dnet_ntoa in -ldnet_stub" >&5 +ac_lib_var=`echo dnet_stub'_'dnet_ntoa | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-ldnet_stub $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + X_EXTRA_LIBS="$X_EXTRA_LIBS -ldnet_stub" +else + echo "$ac_t""no" 1>&6 +fi + + fi + + # msh@cis.ufl.edu says -lnsl (and -lsocket) are needed for his 386/AT, + # to get the SysV transport functions. + # chad@anasazi.com says the Pyramis MIS-ES running DC/OSx (SVR4) + # needs -lnsl. + # The nsl library prevents programs from opening the X display + # on Irix 5.2, according to dickey@clark.net. + echo $ac_n "checking for gethostbyname""... $ac_c" 1>&6 +echo "configure:4275: checking for gethostbyname" >&5 +if eval "test \"`echo '$''{'ac_cv_func_gethostbyname'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char gethostbyname(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_gethostbyname) || defined (__stub___gethostbyname) +choke me +#else +gethostbyname(); +#endif + +; return 0; } +EOF +if { (eval echo configure:4303: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_gethostbyname=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_gethostbyname=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'gethostbyname`\" = yes"; then + echo "$ac_t""yes" 1>&6 + : +else + echo "$ac_t""no" 1>&6 +fi + + if test $ac_cv_func_gethostbyname = no; then + echo $ac_n "checking for gethostbyname in -lnsl""... $ac_c" 1>&6 +echo "configure:4324: checking for gethostbyname in -lnsl" >&5 +ac_lib_var=`echo nsl'_'gethostbyname | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lnsl $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + X_EXTRA_LIBS="$X_EXTRA_LIBS -lnsl" +else + echo "$ac_t""no" 1>&6 +fi + + fi + + # lieder@skyler.mavd.honeywell.com says without -lsocket, + # socket/setsockopt and other routines are undefined under SCO ODT + # 2.0. But -lsocket is broken on IRIX 5.2 (and is not necessary + # on later versions), says simon@lia.di.epfl.ch: it contains + # gethostby* variants that don't use the nameserver (or something). + # -lsocket must be given before -lnsl if both are needed. + # We assume that if connect needs -lnsl, so does gethostbyname. + echo $ac_n "checking for connect""... $ac_c" 1>&6 +echo "configure:4373: checking for connect" >&5 +if eval "test \"`echo '$''{'ac_cv_func_connect'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char connect(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_connect) || defined (__stub___connect) +choke me +#else +connect(); +#endif + +; return 0; } +EOF +if { (eval echo configure:4401: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_connect=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_connect=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'connect`\" = yes"; then + echo "$ac_t""yes" 1>&6 + : +else + echo "$ac_t""no" 1>&6 +fi + + if test $ac_cv_func_connect = no; then + echo $ac_n "checking for connect in -lsocket""... $ac_c" 1>&6 +echo "configure:4422: checking for connect in -lsocket" >&5 +ac_lib_var=`echo socket'_'connect | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lsocket $X_EXTRA_LIBS $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + X_EXTRA_LIBS="-lsocket $X_EXTRA_LIBS" +else + echo "$ac_t""no" 1>&6 +fi + + fi + + # gomez@mi.uni-erlangen.de says -lposix is necessary on A/UX. + echo $ac_n "checking for remove""... $ac_c" 1>&6 +echo "configure:4465: checking for remove" >&5 +if eval "test \"`echo '$''{'ac_cv_func_remove'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char remove(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_remove) || defined (__stub___remove) +choke me +#else +remove(); +#endif + +; return 0; } +EOF +if { (eval echo configure:4493: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_remove=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_remove=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'remove`\" = yes"; then + echo "$ac_t""yes" 1>&6 + : +else + echo "$ac_t""no" 1>&6 +fi + + if test $ac_cv_func_remove = no; then + echo $ac_n "checking for remove in -lposix""... $ac_c" 1>&6 +echo "configure:4514: checking for remove in -lposix" >&5 +ac_lib_var=`echo posix'_'remove | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lposix $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + X_EXTRA_LIBS="$X_EXTRA_LIBS -lposix" +else + echo "$ac_t""no" 1>&6 +fi + + fi + + # BSDI BSD/OS 2.1 needs -lipc for XOpenDisplay. + echo $ac_n "checking for shmat""... $ac_c" 1>&6 +echo "configure:4557: checking for shmat" >&5 +if eval "test \"`echo '$''{'ac_cv_func_shmat'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char shmat(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_shmat) || defined (__stub___shmat) +choke me +#else +shmat(); +#endif + +; return 0; } +EOF +if { (eval echo configure:4585: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_shmat=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_shmat=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'shmat`\" = yes"; then + echo "$ac_t""yes" 1>&6 + : +else + echo "$ac_t""no" 1>&6 +fi + + if test $ac_cv_func_shmat = no; then + echo $ac_n "checking for shmat in -lipc""... $ac_c" 1>&6 +echo "configure:4606: checking for shmat in -lipc" >&5 +ac_lib_var=`echo ipc'_'shmat | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lipc $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + X_EXTRA_LIBS="$X_EXTRA_LIBS -lipc" +else + echo "$ac_t""no" 1>&6 +fi + + fi + fi + + # Check for libraries that X11R6 Xt/Xaw programs need. + ac_save_LDFLAGS="$LDFLAGS" + test -n "$x_libraries" && LDFLAGS="$LDFLAGS -L$x_libraries" + # SM needs ICE to (dynamically) link under SunOS 4.x (so we have to + # check for ICE first), but we must link in the order -lSM -lICE or + # we get undefined symbols. So assume we have SM if we have ICE. + # These have to be linked with before -lX11, unlike the other + # libraries we check for below, so use a different variable. + # --interran@uluru.Stanford.EDU, kb@cs.umb.edu. + echo $ac_n "checking for IceConnectionNumber in -lICE""... $ac_c" 1>&6 +echo "configure:4658: checking for IceConnectionNumber in -lICE" >&5 +ac_lib_var=`echo ICE'_'IceConnectionNumber | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lICE $X_EXTRA_LIBS $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + X_PRE_LIBS="$X_PRE_LIBS -lSM -lICE" +else + echo "$ac_t""no" 1>&6 +fi + + LDFLAGS="$ac_save_LDFLAGS" + +fi + + +XCFLAGS="$X_CFLAGS" + +fi # COMPILE_ENVIRONMENT + +AS_BIN=$AS +AR_FLAGS='cr $@' +AR_LIST='$(AR) t' +AR_EXTRACT='$(AR) x' +AR_DELETE='$(AR) d' +AS='$(CC)' +AS_DASH_C_FLAG='-c' +DLL_PREFIX=lib +LIB_PREFIX=lib +DLL_SUFFIX=.so +OBJ_SUFFIX=o +LIB_SUFFIX=a +ASM_SUFFIX=s +IMPORT_LIB_SUFFIX= +TARGET_MD_ARCH=unix +DIRENT_INO=d_ino +CYGWIN_WRAPPER= +WIN_TOP_SRC= +MOZ_USER_DIR=".mozilla" +HOST_AR='$(AR)' +HOST_AR_FLAGS='$(AR_FLAGS)' + +MOZ_JS_LIBS='-L$(libdir) -lmozjs' +MOZ_FIX_LINK_PATHS='-Wl,-rpath-link,$(LIBXUL_DIST)/bin -Wl,-rpath-link,$(PREFIX)/lib' + +MOZ_COMPONENT_NSPR_LIBS='-L$(LIBXUL_DIST)/bin $(NSPR_LIBS)' + +USE_DEPENDENT_LIBS=1 + +_PLATFORM_DEFAULT_TOOLKIT=cairo-gtk2 + +MOZ_ENABLE_POSTSCRIPT=1 + +if test -n "$CROSS_COMPILE"; then + OS_TARGET="${target_os}" + OS_ARCH=`echo $target_os | sed -e 's|/|_|g'` + OS_RELEASE= + case "${target_os}" in + linux*) OS_ARCH=Linux OS_TARGET=Linux ;; + kfreebsd*-gnu) OS_ARCH=GNU_kFreeBSD OS_TARGET=GNU_kFreeBSD ;; + solaris*) OS_ARCH=SunOS OS_RELEASE=5 ;; + mingw*) OS_ARCH=WINNT ;; + wince*) OS_ARCH=WINCE ;; + darwin*) OS_ARCH=Darwin OS_TARGET=Darwin ;; + esac +else + OS_TARGET=`uname -s` + OS_ARCH=`uname -s | sed -e 's|/|_|g'` + OS_RELEASE=`uname -r` +fi + +# Before this used `uname -m` when not cross compiling +# but that breaks when you have a 64 bit kernel with a 32 bit userland. +OS_TEST="${target_cpu}" + +_COMPILER_PREFIX= + +HOST_OS_ARCH=`echo $host_os | sed -e 's|/|_|g'` + +####################################################################### +# Master "Core Components" macros for getting the OS target # +####################################################################### + +# +# If OS_TARGET is not specified, it defaults to $(OS_ARCH), i.e., no +# cross-compilation. +# + +# +# Define and override various archtecture-specific variables, including +# HOST_OS_ARCH +# OS_ARCH +# OS_TEST +# OS_TARGET +# OS_RELEASE +# OS_MINOR_RELEASE +# + +case "$HOST_OS_ARCH" in +cygwin*|mingw*|mks*|msvc*) + HOST_OS_ARCH=WINNT + ;; +linux*) + HOST_OS_ARCH=Linux + ;; +solaris*) + HOST_OS_ARCH=SunOS + SOLARIS_SUNPRO_CC= + SOLARIS_SUNPRO_CXX= + if test -z "$GNU_CC"; then + if test "`$CC -V 2>&1 | egrep -c 'Sun.*C '`" != "0"; then + SOLARIS_SUNPRO_CC=1 + fi + fi + + if test -z "$GNU_CXX"; then + if test "`$CXX -V 2>&1 | egrep -c 'Sun.*C\+\+ '`" != "0"; then + SOLARIS_SUNPRO_CXX=1 + fi + fi + + + ;; +BSD_386) + HOST_OS_ARCH=BSD + ;; +dgux) + HOST_OS_ARCH=DGUX + ;; +IRIX64) + HOST_OS_ARCH=IRIX + ;; +UNIX_SV) + if "`cat /etc/bcheckrc | grep -c NCR 2>/dev/null`" != "0"; then + HOST_OS_ARCH=NCR + else + HOST_OS_ARCH=UNIXWARE + fi + ;; +ncr) + HOST_OS_ARCH=NCR + ;; +UNIX_SYSTEM_V) + HOST_OS_ARCH=NEC + ;; +OSF1) + ;; +*OpenVMS*) + HOST_OS_ARCH=OpenVMS + ;; +OS_2) + HOST_OS_ARCH=OS2 + ;; +QNX) + ;; +SCO_SV) + HOST_OS_ARCH=SCOOS + ;; +SINIX-N | SINIX-Y | SINIX-Z |ReliantUNIX-M) + HOST_OS_ARCH=SINIX + ;; +UnixWare) + HOST_OS_ARCH=UNIXWARE + ;; +esac + +case "$OS_ARCH" in +WINNT) + OS_TEST=`uname -p` + ;; +Windows_NT) +# +# If uname -s returns "Windows_NT", we assume that we are using +# the uname.exe in MKS toolkit. +# +# The -r option of MKS uname only returns the major version number. +# So we need to use its -v option to get the minor version number. +# Moreover, it doesn't have the -p option, so we need to use uname -m. +# + OS_ARCH=WINNT + OS_TARGET=WINNT + OS_MINOR_RELEASE=`uname -v` + if test "$OS_MINOR_RELEASE" = "00"; then + OS_MINOR_RELEASE=0 + fi + OS_RELEASE="${OS_RELEASE}.${OS_MINOR_RELEASE}" + ;; +CYGWIN32_NT|CYGWIN_NT*|MINGW*_NT*) +# +# If uname -s returns "CYGWIN_NT-4.0", we assume that we are using +# the uname.exe in the Cygwin tools. +# Prior to the Beta 20 release, Cygwin was called GNU-Win32. +# If uname -s returns "CYGWIN32/NT", we assume that we are using +# the uname.exe in the GNU-Win32 tools. +# If uname -s returns MINGW32_NT-5.1, we assume that we are using +# the uname.exe in the MSYS tools. +# + OS_RELEASE=`expr $OS_ARCH : '.*NT-\(.*\)'` + OS_ARCH=WINNT + OS_TARGET=WINNT + ;; +AIX) + OS_RELEASE=`uname -v`.`uname -r` + OS_TEST=${target_cpu} + ;; +BSD_386) + OS_ARCH=BSD + ;; +dgux) + OS_ARCH=DGUX + ;; +IRIX64) + OS_ARCH=IRIX + ;; +UNIX_SV) + if "`cat /etc/bcheckrc | grep -c NCR 2>/dev/null`" != "0"; then + OS_ARCH=NCR + else + OS_ARCH=UNIXWARE + OS_RELEASE=`uname -v` + fi + ;; +ncr) + OS_ARCH=NCR + ;; +UNIX_SYSTEM_V) + OS_ARCH=NEC + ;; +OSF1) + case `uname -v` in + 148) + OS_RELEASE=V3.2C + ;; + 564) + OS_RELEASE=V4.0B + ;; + 878) + OS_RELEASE=V4.0D + ;; + esac + ;; +*OpenVMS*) + OS_ARCH=OpenVMS + OS_RELEASE=`uname -v` + OS_TEST=`uname -p` + ;; +OS_2) + OS_ARCH=OS2 + OS_TARGET=OS2 + OS_RELEASE=`uname -v` + ;; +QNX) + if test "$OS_TARGET" != "NTO"; then + + OS_RELEASE=`uname -v | sed 's/^\([0-9]\)\([0-9]*\)$/\1.\2/'` + + fi + OS_TEST=x86 + ;; +SCO_SV) + OS_ARCH=SCOOS + OS_RELEASE=5.0 + ;; +SINIX-N | SINIX-Y | SINIX-Z |ReliantUNIX-M) + OS_ARCH=SINIX + OS_TEST=`uname -p` + ;; +UnixWare) + OS_ARCH=UNIXWARE + OS_RELEASE=`uname -v` + ;; +WINCE) + WINCE=1 + OS_ARCH=WINCE + OS_TARGET=WINCE + ;; +Darwin) + case "${target_cpu}" in + powerpc*) + OS_TEST=ppc + ;; + i*86*) + OS_TEST=i386 + ;; + x86_64) + OS_TEST=x86_64 + ;; + *) + if test -z "$CROSS_COMPILE" ; then + OS_TEST=`uname -p` + fi + ;; + esac + ;; +esac + +if test "$OS_ARCH" = "NCR"; then + + OS_RELEASE=`awk '{print $3}' /etc/.relid | sed 's/^\([0-9]\)\(.\)\(..\)\(.*\)$/\2.\3/'` + +fi + +# Only set CPU_ARCH if we recognize the value of OS_TEST + +case "$OS_TEST" in +*86 | i86pc) + CPU_ARCH=x86 + ;; + +powerpc* | ppc | rs6000) + CPU_ARCH=ppc + ;; + +Alpha | alpha | ALPHA) + CPU_ARCH=Alpha + ;; + +hppa* | parisc) + CPU_ARCH=hppa + ;; + +sun4u | sparc*) + CPU_ARCH=sparc + ;; + +x86_64 | ia64) + CPU_ARCH="$OS_TEST" + ;; + +arm) + if test "$OS_TARGET" = "WINCE"; then + CPU_ARCH="$OS_TEST" + fi + ;; +esac + +if test -z "$OS_TARGET"; then + OS_TARGET=$OS_ARCH +fi +OS_CONFIG="${OS_TARGET}${OS_RELEASE}" + +if test "$GNU_CC"; then + # FIXME: Let us build with strict aliasing. bug 414641. + CFLAGS="$CFLAGS -fno-strict-aliasing" + MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-h,$@ -o $@' + MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-h,$@ -o $@' + DSO_LDOPTS='-shared' + if test "$GCC_USE_GNU_LD"; then + # Don't allow undefined symbols in libraries + DSO_LDOPTS="$DSO_LDOPTS -Wl,-z,defs" + fi + WARNINGS_AS_ERRORS='-Werror' + DSO_CFLAGS='' + DSO_PIC_CFLAGS='-fPIC' + ASFLAGS="$ASFLAGS -fPIC" + _MOZ_RTTI_FLAGS_ON=${_COMPILER_PREFIX}-frtti + _MOZ_RTTI_FLAGS_OFF=${_COMPILER_PREFIX}-fno-rtti + _MOZ_EXCEPTIONS_FLAGS_ON='-fhandle-exceptions' + _MOZ_EXCEPTIONS_FLAGS_OFF='-fno-handle-exceptions' + + # Turn on GNU specific features + # -Wall - turn on all warnings + # -pedantic - make compiler warn about non-ANSI stuff, and + # be a little bit stricter + # Warnings slamm took out for now (these were giving more noise than help): + # -Wbad-function-cast - warns when casting a function to a new return type + # -Wshadow - removed because it generates more noise than help --pete + _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wall -W -Wno-unused -Wpointer-arith" + if test -z "$INTEL_CC"; then + # Don't use -Wcast-align with ICC + case "$CPU_ARCH" in + # And don't use it on hppa, ia64, sparc, since it's noisy there + hppa | ia64 | sparc) + ;; + *) + _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wcast-align" + ;; + esac + fi + + _PEDANTIC=1 + + if test -z "$INTEL_CC"; then + _IGNORE_LONG_LONG_WARNINGS=1 + _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -W" + else + _IGNORE_LONG_LONG_WARNINGS= + fi + + + _DEFINES_CFLAGS='-include $(DEPTH)/js-confdefs.h -DMOZILLA_CLIENT' + _USE_CPP_INCLUDE_FLAG=1 +elif test "$SOLARIS_SUNPRO_CC"; then + MKSHLIB='$(LD) $(DSO_LDOPTS) -h $@ -o $@' + MKCSHLIB='$(LD) $(DSO_LDOPTS) -h $@ -o $@' + + DSO_LDOPTS='-shared' + if test "$GNU_LD"; then + # Don't allow undefined symbols in libraries + DSO_LDOPTS="$DSO_LDOPTS -z defs" + fi + + DSO_CFLAGS='' + if test "$CPU_ARCH" = "sparc"; then + # for Sun Studio on Solaris/SPARC + DSO_PIC_CFLAGS='-xcode=pic32' + else + DSO_PIC_CFLAGS='-KPIC' + fi + _DEFINES_CFLAGS='$(ACDEFINES) -D_JS_CONFDEFS_H_ -DMOZILLA_CLIENT' +else + MKSHLIB='$(LD) $(DSO_LDOPTS) -h $@ -o $@' + MKCSHLIB='$(LD) $(DSO_LDOPTS) -h $@ -o $@' + + DSO_LDOPTS='-shared' + if test "$GNU_LD"; then + # Don't allow undefined symbols in libraries + DSO_LDOPTS="$DSO_LDOPTS -z defs" + fi + + DSO_CFLAGS='' + DSO_PIC_CFLAGS='-KPIC' + _DEFINES_CFLAGS='$(ACDEFINES) -D_JS_CONFDEFS_H_ -DMOZILLA_CLIENT' +fi + +if test "$GNU_CXX"; then + # FIXME: Let us build with strict aliasing. bug 414641. + CXXFLAGS="$CXXFLAGS -fno-strict-aliasing" + # Turn on GNU specific features + _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wall -Wpointer-arith -Woverloaded-virtual -Wsynth -Wno-ctor-dtor-privacy -Wno-non-virtual-dtor" + if test -z "$INTEL_CC"; then + # Don't use -Wcast-align with ICC + case "$CPU_ARCH" in + # And don't use it on hppa, ia64, sparc, since it's noisy there + hppa | ia64 | sparc) + ;; + *) + _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wcast-align" + ;; + esac + fi + + _DEFINES_CXXFLAGS='-DMOZILLA_CLIENT -include $(DEPTH)/js-confdefs.h' + _USE_CPP_INCLUDE_FLAG=1 + + echo $ac_n "checking whether the compiler supports -Wno-invalid-offsetof""... $ac_c" 1>&6 +echo "configure:5132: checking whether the compiler supports -Wno-invalid-offsetof" >&5 +if eval "test \"`echo '$''{'ac_has_wno_invalid_offsetof'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + + ac_ext=C +# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cxx_cross + + _SAVE_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS ${_COMPILER_PREFIX}-Wno-invalid-offsetof" + cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_has_wno_invalid_offsetof="yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_has_wno_invalid_offsetof="no" +fi +rm -f conftest* + CXXFLAGS="$_SAVE_CXXFLAGS" + ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + + +fi + +echo "$ac_t""$ac_has_wno_invalid_offsetof" 1>&6 + if test "$ac_has_wno_invalid_offsetof" = "yes"; then + _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} ${_COMPILER_PREFIX}-Wno-invalid-offsetof" + fi + + echo $ac_n "checking whether the compiler supports -Wno-variadic-macros""... $ac_c" 1>&6 +echo "configure:5182: checking whether the compiler supports -Wno-variadic-macros" >&5 +if eval "test \"`echo '$''{'ac_has_wno_variadic_macros'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + + ac_ext=C +# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cxx_cross + + _SAVE_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS ${_COMPILER_PREFIX}-Wno-variadic-macros" + cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_has_wno_variadic_macros="yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_has_wno_variadic_macros="no" +fi +rm -f conftest* + CXXFLAGS="$_SAVE_CXXFLAGS" + ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + + +fi + +echo "$ac_t""$ac_has_wno_variadic_macros" 1>&6 + if test "$ac_has_wno_variadic_macros" = "yes"; then + _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} ${_COMPILER_PREFIX}-Wno-variadic-macros" + fi +else + _DEFINES_CXXFLAGS='-DMOZILLA_CLIENT -D_JS_CONFDEFS_H_ $(ACDEFINES)' +fi + +MKSHLIB_FORCE_ALL= +MKSHLIB_UNFORCE_ALL= + +if test "$COMPILE_ENVIRONMENT"; then +if test "$GNU_CC"; then + echo $ac_n "checking whether ld has archive extraction flags""... $ac_c" 1>&6 +echo "configure:5240: checking whether ld has archive extraction flags" >&5 + if eval "test \"`echo '$''{'ac_cv_mkshlib_force_and_unforce'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + _SAVE_LDFLAGS=$LDFLAGS; _SAVE_LIBS=$LIBS + ac_cv_mkshlib_force_and_unforce="no" + exec 3<&0 < conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + ac_cv_mkshlib_force_and_unforce=$line; break +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 +fi +rm -f conftest* + done + exec 0<&3 3<&- + LDFLAGS=$_SAVE_LDFLAGS; LIBS=$_SAVE_LIBS + +fi + + if test "$ac_cv_mkshlib_force_and_unforce" = "no"; then + echo "$ac_t""no" 1>&6 + else + echo "$ac_t""yes" 1>&6 + eval $ac_cv_mkshlib_force_and_unforce + MKSHLIB_FORCE_ALL=$force + MKSHLIB_UNFORCE_ALL=$unforce + fi +fi # GNU_CC +fi # COMPILE_ENVIRONMENT + +if test "$COMPILE_ENVIRONMENT"; then +configure_static_assert_macros=' +#define CONFIGURE_STATIC_ASSERT(condition) CONFIGURE_STATIC_ASSERT_IMPL(condition, __LINE__) +#define CONFIGURE_STATIC_ASSERT_IMPL(condition, line) CONFIGURE_STATIC_ASSERT_IMPL2(condition, line) +#define CONFIGURE_STATIC_ASSERT_IMPL2(condition, line) typedef int static_assert_line_##line[(condition) ? 1 : -1] +' + +echo $ac_n "checking that static assertion macros used in autoconf tests work""... $ac_c" 1>&6 +echo "configure:5297: checking that static assertion macros used in autoconf tests work" >&5 +if eval "test \"`echo '$''{'ac_cv_static_assertion_macros_work'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + + ac_cv_static_assertion_macros_work="yes" + cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + : +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_static_assertion_macros_work="no" +fi +rm -f conftest* + cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_static_assertion_macros_work="no" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 +fi +rm -f conftest* + ac_ext=C +# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cxx_cross + + cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + : +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_static_assertion_macros_work="no" +fi +rm -f conftest* + cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_static_assertion_macros_work="no" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 +fi +rm -f conftest* + ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + + +fi + +echo "$ac_t"""$ac_cv_static_assertion_macros_work"" 1>&6 +if test "$ac_cv_static_assertion_macros_work" = "no"; then + { echo "configure: error: Compiler cannot compile macros used in autoconf tests." 1>&2; exit 1; } +fi +fi # COMPILE_ENVIRONMENT + +if test "$COMPILE_ENVIRONMENT"; then + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +echo $ac_n "checking for 64-bit OS""... $ac_c" 1>&6 +echo "configure:5409: checking for 64-bit OS" >&5 +cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + result="yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + result="no" +fi +rm -f conftest* +echo "$ac_t"""$result"" 1>&6 +if test "$result" = "yes"; then + cat >> confdefs.h <<\EOF +#define HAVE_64BIT_OS 1 +EOF + + HAVE_64BIT_OS=1 +fi + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +fi # COMPILE_ENVIRONMENT + +MOZ_OS2_HIGH_MEMORY=1 +# Check whether --enable-os2-high-mem or --disable-os2-high-mem was given. +if test "${enable_os2_high_mem+set}" = set; then + enableval="$enable_os2_high_mem" + if test "$enableval" = "no"; then + MOZ_OS2_HIGH_MEMORY= + elif test "$enableval" = "yes"; then + MOZ_OS2_HIGH_MEMORY=1 + else + { echo "configure: error: Option, os2-high-mem, does not take an argument ($enableval)." 1>&2; exit 1; } + fi +fi + + + +case "$host" in +*-beos*) + HOST_CFLAGS="$HOST_CFLAGS -DXP_BEOS -DBeOS -DBEOS -D_POSIX_SOURCE -DNO_X11" + HOST_NSPR_MDCPUCFG='\"md/_beos.cfg\"' + HOST_OPTIMIZE_FLAGS="${HOST_OPTIMIZE_FLAGS=-O3}" + ;; + +*cygwin*|*mingw*|*mks*|*msvc*|*wince) + if test -n "$_WIN32_MSVC"; then + HOST_AR=lib + HOST_AR_FLAGS='-NOLOGO -OUT:"$@"' + HOST_CFLAGS="$HOST_CFLAGS -TC -nologo -Fd\$(HOST_PDBFILE)" + HOST_RANLIB='echo ranlib' + else + HOST_CFLAGS="$HOST_CFLAGS -mno-cygwin" + fi + HOST_CFLAGS="$HOST_CFLAGS -DXP_WIN32 -DXP_WIN -DWIN32 -D_WIN32 -DNO_X11" + HOST_NSPR_MDCPUCFG='\"md/_winnt.cfg\"' + HOST_OPTIMIZE_FLAGS="${HOST_OPTIMIZE_FLAGS=-O2}" + HOST_BIN_SUFFIX=.exe + case "$host" in + *mingw*) + ;; + *) + CYGWIN_WRAPPER="${srcdir}/build/cygwin-wrapper" + if test "`echo ${srcdir} | grep -c ^/ 2>/dev/null`" = 0; then + _pwd=`pwd` + CYGWIN_WRAPPER="${_pwd}/${srcdir}/build/cygwin-wrapper" + fi + if test "`${PERL} -v | grep -c cygwin 2>/dev/null`" = 0; then + AS_PERL=1 + PERL="${CYGWIN_WRAPPER} $PERL" + fi + + if test "`${PYTHON} -c 'import sys; print sys.platform;'`" != "cygwin"; then + PYTHON="${CYGWIN_WRAPPER} $PYTHON" + fi + ;; + esac + ;; + +*-darwin*) + HOST_CFLAGS="$HOST_CFLAGS -DXP_UNIX -DXP_MACOSX -DNO_X11" + HOST_NSPR_MDCPUCFG='\"md/_darwin.cfg\"' + HOST_OPTIMIZE_FLAGS="${HOST_OPTIMIZE_FLAGS=-O3}" + MOZ_FIX_LINK_PATHS='-Wl,-executable_path,$(LIBXUL_DIST)/bin' + LIBXUL_LIBS='$(XPCOM_FROZEN_LDOPTS) $(LIBXUL_DIST)/bin/XUL -lobjc' + ;; + +*-linux*|*-kfreebsd*-gnu) + HOST_CFLAGS="$HOST_CFLAGS -DXP_UNIX" + HOST_NSPR_MDCPUCFG='\"md/_linux.cfg\"' + HOST_OPTIMIZE_FLAGS="${HOST_OPTIMIZE_FLAGS=-O3}" + ;; + +*os2*) + HOST_CFLAGS="$HOST_CFLAGS -DXP_OS2 -DNO_X11 -Zomf" + HOST_NSPR_MDCPUCFG='\"md/_os2.cfg\"' + HOST_OPTIMIZE_FLAGS="${HOST_OPTIMIZE_FLAGS=-O2}" + HOST_BIN_SUFFIX=.exe + MOZ_FIX_LINK_PATHS= + ;; + +*-osf*) + HOST_CFLAGS="$HOST_CFLAGS -DXP_UNIX" + HOST_NSPR_MDCPUCFG='\"md/_osf1.cfg\"' + HOST_OPTIMIZE_FLAGS="${HOST_OPTIMIZE_FLAGS=-O2}" + ;; + +*) + HOST_CFLAGS="$HOST_CFLAGS -DXP_UNIX" + HOST_OPTIMIZE_FLAGS="${HOST_OPTIMIZE_FLAGS=-O2}" + ;; +esac + + +case "$target" in +*-aix*) + cat >> confdefs.h <<\EOF +#define AIX 1 +EOF + + if test ! "$GNU_CC"; then + if test ! "$HAVE_64BIT_OS"; then + # Compiling with Visual Age C++ object model compat is the + # default. To compile with object model ibm, add + # AIX_OBJMODEL=ibm to .mozconfig. + if test "$AIX_OBJMODEL" = "ibm"; then + CXXFLAGS="$CXXFLAGS -qobjmodel=ibm" + else + AIX_OBJMODEL=compat + fi + else + AIX_OBJMODEL=compat + fi + + DSO_LDOPTS='-qmkshrobj=1' + DSO_CFLAGS='-qflag=w:w' + DSO_PIC_CFLAGS= + LDFLAGS="$LDFLAGS -Wl,-brtl -blibpath:/usr/lib:/lib" + echo "configure: warning: Clearing MOZ_FIX_LINK_PATHS till we can fix bug 332075." 1>&2 + MOZ_FIX_LINK_PATHS= + MKSHLIB='$(CXX) $(DSO_LDOPTS) -o $@' + MKCSHLIB='$(CC) $(DSO_LDOPTS) -o $@' + if test "$COMPILE_ENVIRONMENT"; then + + ac_ext=C +# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cxx_cross + + echo $ac_n "checking for VisualAge C++ compiler version >= 6.0.0.3""... $ac_c" 1>&6 +echo "configure:5575: checking for VisualAge C++ compiler version >= 6.0.0.3" >&5 + cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + _BAD_COMPILER= +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + _BAD_COMPILER=1 +fi +rm -f conftest* + if test -n "$_BAD_COMPILER"; then + echo "$ac_t""no" 1>&6 + { echo "configure: error: VisualAge C++ version 6.0.0.3 or higher is required to build." 1>&2; exit 1; } + else + echo "$ac_t""yes" 1>&6 + fi + ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + + TARGET_COMPILER_ABI="ibmc" + CC_VERSION=`lslpp -Lcq vac.C 2>/dev/null | awk -F: '{ print $3 }'` + CXX_VERSION=`lslpp -Lcq vacpp.cmp.core 2>/dev/null | awk -F: '{ print $3 }'` + fi + fi + case "${target_os}" in + aix4.1*) + DLL_SUFFIX='_shr.a' + ;; + esac + if test "$COMPILE_ENVIRONMENT"; then + for ac_hdr in sys/inttypes.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:5624: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:5634: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done + + fi + cat >> confdefs.h <<\EOF +#define NSCAP_DISABLE_DEBUG_PTR_TYPES 1 +EOF + + ;; + +*-beos*) + no_x=yes + MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_LDOPTS) -Wl,-h,$@ -o $@' + _PLATFORM_DEFAULT_TOOLKIT="cairo-beos" + DSO_LDOPTS='-nostart' + TK_LIBS='-lbe -lroot' + LIBS="$LIBS -lbe" + if test "$COMPILE_ENVIRONMENT"; then + echo $ac_n "checking for main in -lbind""... $ac_c" 1>&6 +echo "configure:5676: checking for main in -lbind" >&5 +ac_lib_var=`echo bind'_'main | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lbind $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + LIBS="$LIBS -lbind" +else + echo "$ac_t""no" 1>&6 +fi + + echo $ac_n "checking for main in -lzeta""... $ac_c" 1>&6 +echo "configure:5712: checking for main in -lzeta" >&5 +ac_lib_var=`echo zeta'_'main | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lzeta $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + LIBS="$LIBS -lzeta" +else + echo "$ac_t""no" 1>&6 +fi + + fi + _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wno-multichar" + _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wno-multichar" + _MOZ_USE_RTTI=1 + USE_DEPENDENT_LIBS= + MOZ_USER_DIR="Mozilla" + ;; + +*-bsdi*) + _PEDANTIC= + _IGNORE_LONG_LONG_WARNINGS= + case $OS_RELEASE in + 4.*|5.*) + STRIP="$STRIP -d" + ;; + *) + DSO_CFLAGS='' + DSO_LDOPTS='-r' + _WARNINGS_CFLAGS="-Wall" + _WARNINGS_CXXFLAGS="-Wall" + # The test above doesn't work properly, at least on 3.1. + MKSHLIB_FORCE_ALL='' + MKSHLIB_UNFORCE_ALL='' + ;; + esac + ;; + +*-darwin*) + MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@' + MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@' + MOZ_OPTIMIZE_FLAGS="-O3" + _PEDANTIC= + CFLAGS="$CFLAGS -fpascal-strings -fno-common" + CXXFLAGS="$CXXFLAGS -fpascal-strings -fno-common" + DLL_SUFFIX=".dylib" + DSO_LDOPTS='' + STRIP="$STRIP -x -S" + _PLATFORM_DEFAULT_TOOLKIT='cairo-cocoa' + MOZ_ENABLE_POSTSCRIPT= + TARGET_NSPR_MDCPUCFG='\"md/_darwin.cfg\"' + LDFLAGS="$LDFLAGS -framework Cocoa" + # The ExceptionHandling framework is needed for Objective-C exception + # logging code in nsObjCExceptions.h. Currently we only use that in debug + # builds. + MOZ_DEBUG_LDFLAGS="$MOZ_DEBUG_LDFLAGS -framework ExceptionHandling" + # set MACOSX to generate lib/mac/MoreFiles/Makefile + MACOSX=1 + + if test "x$enable_dtrace" = "xyes"; then + echo "Skipping -dead_strip because DTrace is enabled. See bug 403132." + else + echo $ac_n "checking for -dead_strip option to ld""... $ac_c" 1>&6 +echo "configure:5799: checking for -dead_strip option to ld" >&5 + _SAVE_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS -Wl,-dead_strip" + cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + _HAVE_DEAD_STRIP=1 +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + _HAVE_DEAD_STRIP= +fi +rm -f conftest* + if test -n "$_HAVE_DEAD_STRIP" ; then + echo "$ac_t""yes" 1>&6 + MOZ_OPTIMIZE_LDFLAGS="-Wl,-dead_strip" + else + echo "$ac_t""no" 1>&6 + fi + + LDFLAGS=$_SAVE_LDFLAGS + fi + ;; + +*-freebsd*) + if test `test -x /usr/bin/objformat && /usr/bin/objformat || echo elf` != "elf"; then + DLL_SUFFIX=".so.1.0" + DSO_LDOPTS="-shared" + fi + if test ! "$GNU_CC"; then + DSO_LDOPTS="-Bshareable $DSO_LDOPTS" + fi +# Can't have force w/o an unforce. +# # Hack for FreeBSD 2.2 +# if test -z "$MKSHLIB_FORCE_ALL"; then +# MKSHLIB_FORCE_ALL='-Wl,-Bforcearchive' +# MKSHLIB_UNFORCE_ALL='' +# fi + ;; + +*-hpux*) + DLL_SUFFIX=".sl" + if test ! "$GNU_CC"; then + DSO_LDOPTS='-b -Wl,+s' + DSO_CFLAGS="" + DSO_PIC_CFLAGS="+Z" + MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_LDOPTS) -L$(LIBXUL_DIST)/bin -o $@' + MKCSHLIB='$(LD) -b +s -L$(LIBXUL_DIST)/bin -o $@' + CXXFLAGS="$CXXFLAGS -Wc,-ansi_for_scope,on" + else + DSO_LDOPTS='-b -E +s' + MKSHLIB='$(LD) $(DSO_LDOPTS) -L$(LIBXUL_DIST)/bin -L$(LIBXUL_DIST)/lib -o $@' + MKCSHLIB='$(LD) $(DSO_LDOPTS) -L$(LIBXUL_DIST)/bin -L$(LIBXUL_DIST)/lib -o $@' + fi + MOZ_POST_PROGRAM_COMMAND='chatr +s enable' + cat >> confdefs.h <<\EOF +#define NSCAP_DISABLE_DEBUG_PTR_TYPES 1 +EOF + + ;; + +*-irix5*) + cat >> confdefs.h <<\EOF +#define IRIX 1 +EOF + + DSO_LDOPTS='-elf -shared' + + if test "$GNU_CC"; then + MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@' + MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@' + MKSHLIB_FORCE_ALL='-Wl,-all' + MKSHLIB_UNFORCE_ALL='-Wl,-none' + CXXFLAGS="$CXXFLAGS -D_LANGUAGE_C_PLUS_PLUS" + else + MKSHLIB='$(LD) $(DSO_LDOPTS) -o $@' + MKCSHLIB='$(LD) $(DSO_LDOPTS) -o $@' + MKSHLIB_FORCE_ALL='-all' + MKSHLIB_UNFORCE_ALL='-none' + fi + ;; + +*-irix6*) + cat >> confdefs.h <<\EOF +#define IRIX 1 +EOF + + USE_N32=1 + TARGET_COMPILER_ABI=n32 + DSO_LDOPTS='-elf -shared' + MKSHLIB='$(CCC) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@' + MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@' + _MOZ_EXCEPTIONS_FLAGS_OFF="-LANG:exceptions=OFF" + _MOZ_EXCEPTIONS_FLAGS_ON="-LANG:exceptions=ON" + if test "$GNU_CC"; then + MKSHLIB_FORCE_ALL='-Wl,-all' + MKSHLIB_UNFORCE_ALL='-Wl,-none' + _WARNINGS_CFLAGS="-Wall" + _WARNINGS_CXXFLAGS="-Wall" + CXXFLAGS="$CXXFLAGS -D_LANGUAGE_C_PLUS_PLUS" + else + MKSHLIB_FORCE_ALL='-all' + MKSHLIB_UNFORCE_ALL='-none' + AR_LIST="$AR t" + AR_EXTRACT="$AR x" + AR_DELETE="$AR d" + AR='$(CXX) -ar' + AR_FLAGS='-o $@' + CFLAGS="$CFLAGS -woff 3262 -G 4" + CXXFLAGS="$CXXFLAGS -woff 3262 -G 4" + if test -n "$USE_N32"; then + ASFLAGS="$ASFLAGS -n32" + CFLAGS="$CFLAGS -n32" + CXXFLAGS="$CXXFLAGS -n32" + LDFLAGS="$LDFLAGS -n32" + fi + cat >> confdefs.h <<\EOF +#define NSCAP_DISABLE_DEBUG_PTR_TYPES 1 +EOF + + echo "configure: warning: Clearing MOZ_FIX_LINK_PATHS for OSF/1 as fix for bug 333545 (till the reference bug 332075 is fixed." 1>&2 + MOZ_FIX_LINK_PATHS= + fi + if test -z "$GNU_CXX"; then + MIPSPRO_CXX=1 + fi + ;; + +*-*linux*) + # Note: both GNU_CXX and INTEL_CXX are set when using Intel's C compiler. + if test "$INTEL_CXX"; then + # -Os has been broken on Intel's C/C++ compilers for quite a + # while; Intel recommends against using it. + MOZ_OPTIMIZE_FLAGS="-O2" + MOZ_DEBUG_FLAGS="-g -fno-inline" + elif test "$GNU_CXX"; then + GCC_VERSION=`$CXX -v 2>&1 | awk '/^gcc version/ { print $3 }'` + case $GCC_VERSION in + 4.1.*|4.2.*) + # -Os is broken on gcc 4.1.x and 4.2.x, we need to tweak it to get good results. + MOZ_OPTIMIZE_SIZE_TWEAK="-finline-limit=50" + esac + MOZ_OPTIMIZE_FLAGS="-Os -freorder-blocks -fno-reorder-functions $MOZ_OPTIMIZE_SIZE_TWEAK" + MOZ_DEBUG_FLAGS="-g -fno-inline" # most people on linux use gcc/gdb, + # and that combo is not yet good at + # debugging inlined functions (even + # when using DWARF2 as the debugging + # format) + fi + + TARGET_NSPR_MDCPUCFG='\"md/_linux.cfg\"' + + case "${target_cpu}" in + alpha*) + CFLAGS="$CFLAGS -mieee" + CXXFLAGS="$CXXFLAGS -mieee" + ;; + mips*) + MOZ_DEBUG_FLAGS="-g" # We want inlining + ;; + esac + ;; + +*-wince*) + TARGET_COMPILER_ABI=msvc + MOZ_TOOLS_DIR=`echo $MOZ_TOOLS` + AR_LIST="$AR -list" + AR_EXTRACT="$AR -extract" + AR_DELETE="$AR d" + AR_FLAGS='-OUT:"$@"' + AS="$AS_BIN" + + DSO_CFLAGS= + DSO_PIC_CFLAGS= + DLL_SUFFIX=.dll + BIN_SUFFIX='.exe' + if test -z "$RC"; then + RC=rc.exe + fi + # certain versions of cygwin's makedepend barf on the + # #include vs -I./dist/include/string issue so don't use it + SYSTEM_MAKEDEPEND= + + HOST_CC=cl + HOST_CXX=cl + HOST_LD=link + HOST_AR='lib -OUT:$@' + HOST_RANLIB='echo ranlib' + HOST_CFLAGS="$HOST_CFLAGS -D_X86_" + + + WARNINGS_AS_ERRORS='-WX' + MOZ_OPTIMIZE_FLAGS='-O1' + AR_FLAGS='-NOLOGO -OUT:"$@"' + ASM_SUFFIX=asm + CFLAGS="$CFLAGS -W3 -Gy -Fd\$(COMPILE_PDBFILE)" + CXXFLAGS="$CXXFLAGS -W3 -Gy -Fd\$(COMPILE_PDBFILE)" + DLL_PREFIX= + DOXYGEN=: + DSO_LDOPTS=-SUBSYSTEM:WINDOWSCE + DYNAMIC_XPCOM_LIBS='$(LIBXUL_DIST)/lib/xpcom.lib $(LIBXUL_DIST)/lib/xpcom_core.lib' + GARBAGE= + IMPORT_LIB_SUFFIX=lib + LIBS="$LIBS" + LIBXUL_LIBS='$(LIBXUL_DIST)/lib/xpcom.lib $(LIBXUL_DIST)/lib/xul.lib' + LIB_PREFIX= + LIB_SUFFIX=lib + MKCSHLIB='$(LD) -NOLOGO -DLL -OUT:$@ $(DSO_LDOPTS)' + MKSHLIB='$(LD) -NOLOGO -DLL -OUT:$@ $(DSO_LDOPTS)' + MKSHLIB_FORCE_ALL= + MKSHLIB_UNFORCE_ALL= + MOZ_COMPONENT_NSPR_LIBS='$(NSPR_LIBS)' + MOZ_COMPONENT_NSPR_LIBS='$(NSPR_LIBS)' + MOZ_DEBUG_FLAGS='-Zi' + MOZ_DEBUG_LDFLAGS='-DEBUG -DEBUGTYPE:CV' + MOZ_FIX_LINK_PATHS= + MOZ_JS_LIBS='$(libdir)/mozjs.lib' + OBJ_SUFFIX=obj + RANLIB='echo not_ranlib' + STRIP='echo not_strip' + TARGET_NSPR_MDCPUCFG='\"md/_wince.cfg\"' + XARGS=xargs + XPCOM_FROZEN_LDOPTS='$(LIBXUL_DIST)/lib/xpcom.lib' + + cat >> confdefs.h <<\EOF +#define WINCE 1 +EOF + + cat >> confdefs.h <<\EOF +#define HAVE_SNPRINTF 1 +EOF + + cat >> confdefs.h <<\EOF +#define _WINDOWS 1 +EOF + + cat >> confdefs.h <<\EOF +#define WIN32 1 +EOF + + cat >> confdefs.h <<\EOF +#define XP_WIN 1 +EOF + + cat >> confdefs.h <<\EOF +#define XP_WIN32 1 +EOF + + cat >> confdefs.h <<\EOF +#define HW_THREADS 1 +EOF + + cat >> confdefs.h <<\EOF +#define STDC_HEADERS 1 +EOF + + cat >> confdefs.h <<\EOF +#define NEW_H +EOF + + cat >> confdefs.h <<\EOF +#define WIN32_LEAN_AND_MEAN 1 +EOF + + + TARGET_MD_ARCH=win32 + _PLATFORM_DEFAULT_TOOLKIT='windows' + BIN_SUFFIX='.exe' + MOZ_ENABLE_POSTSCRIPT= + MOZ_USER_DIR="Mozilla" + + WINCE_WINDOWS_MOBILE=1 + + # Check whether --enable-windows-mobile-components or --disable-windows-mobile-components was given. +if test "${enable_windows_mobile_components+set}" = set; then + enableval="$enable_windows_mobile_components" + if test "$enableval" = "no"; then + WINCE_WINDOWS_MOBILE= + elif test "$enableval" = "yes"; then + WINCE_WINDOWS_MOBILE=1 + else + { echo "configure: error: Option, windows-mobile-components, does not take an argument ($enableval)." 1>&2; exit 1; } + fi +fi + + + if test "$WINCE_WINDOWS_MOBILE"; then + cat >> confdefs.h <<\EOF +#define WINCE_WINDOWS_MOBILE 1 +EOF + + fi +;; + +*-symbian*) + + cat >> confdefs.h <<\EOF +#define XP_UNIX 1 +EOF + + cat >> confdefs.h <<\EOF +#define SYMBIAN 1 +EOF + + cat >> confdefs.h <<\EOF +#define __arm__ 1 +EOF + + cat >> confdefs.h <<\EOF +#define __SYMBIAN32__ 1 +EOF + + cat >> confdefs.h <<\EOF +#define _UNICODE 1 +EOF + + cat >> confdefs.h <<\EOF +#define NDEBUG 1 +EOF + + cat >> confdefs.h <<\EOF +#define __SUPPORT_CPP_EXCEPTIONS__ 1 +EOF + + cat >> confdefs.h <<\EOF +#define MOZ_STDERR_TO_STDOUT 1 +EOF + + cat >> confdefs.h <<\EOF +#define HAVE_FCNTL_FILE_LOCKING 1 +EOF + + cat >> confdefs.h <<\EOF +#define HAVE_SOCKLEN_T 1 +EOF + + cat >> confdefs.h <<\EOF +#define __GCCE__ 1 +EOF + + + CPU_ARCH=ARM + OS_RELEASE=9.2 + OS_ARCH=SYMBIAN + USE_PTHREADS=1 + LIB_SUFFIX=lib + DLL_SUFFIX=dll + MKSHLIB= + DSO_LDOPTS= + DSO_CFLAGS= + VISIBILITY_FLAGS= + TARGET_NSPR_MDCPUCFG='\"md/_symbian.cfg\"' + RANLIB='echo no ranlib ' +;; + +*-mingw*|*-cygwin*|*-msvc*|*-mks*) + DSO_CFLAGS= + DSO_PIC_CFLAGS= + DLL_SUFFIX=.dll + RC=rc.exe + # certain versions of cygwin's makedepend barf on the + # #include vs -I./dist/include/string issue so don't use it + SYSTEM_MAKEDEPEND= + if test -n "$GNU_CC"; then + CC="$CC -mno-cygwin" + CXX="$CXX -mno-cygwin" + CPP="$CPP -mno-cygwin" + CFLAGS="$CFLAGS -mms-bitfields" + CXXFLAGS="$CXXFLAGS -mms-bitfields" + DSO_LDOPTS='-shared' + MKSHLIB='$(CXX) $(DSO_LDOPTS) -o $@' + MKCSHLIB='$(CC) $(DSO_LDOPTS) -o $@' + RC='$(WINDRES)' + # Use temp file for windres (bug 213281) + RCFLAGS='-O coff --use-temp-file' + # mingw doesn't require kernel32, user32, and advapi32 explicitly + LIBS="$LIBS -lgdi32 -lwinmm -lwsock32" + MOZ_JS_LIBS='-L$(libdir) -lmozjs' + MOZ_FIX_LINK_PATHS= + DYNAMIC_XPCOM_LIBS='-L$(LIBXUL_DIST)/lib -lxpcom -lxpcom_core' + XPCOM_FROZEN_LDOPTS='-L$(LIBXUL_DIST)/lib -lxpcom' + DLL_PREFIX= + IMPORT_LIB_SUFFIX=dll.a + else + TARGET_COMPILER_ABI=msvc + HOST_CC='$(CC)' + HOST_CXX='$(CXX)' + HOST_LD='$(LD)' + AR='lib -NOLOGO -OUT:"$@"' + AR_FLAGS= + RANLIB='echo not_ranlib' + STRIP='echo not_strip' + XARGS=xargs + DOXYGEN=: + GARBAGE='$(OBJDIR)/vc20.pdb $(OBJDIR)/vc40.pdb' + OBJ_SUFFIX=obj + LIB_SUFFIX=lib + DLL_PREFIX= + LIB_PREFIX= + IMPORT_LIB_SUFFIX=lib + MKSHLIB='$(LD) -NOLOGO -DLL -OUT:$@ -PDB:$(LINK_PDBFILE) $(DSO_LDOPTS)' + MKCSHLIB='$(LD) -NOLOGO -DLL -OUT:$@ -PDB:$(LINK_PDBFILE) $(DSO_LDOPTS)' + MKSHLIB_FORCE_ALL= + MKSHLIB_UNFORCE_ALL= + DSO_LDOPTS=-SUBSYSTEM:WINDOWS + _USE_CPP_INCLUDE_FLAG=1 + _DEFINES_CFLAGS='-FI $(DEPTH)/js-confdefs.h -DMOZILLA_CLIENT' + _DEFINES_CXXFLAGS='-FI $(DEPTH)/js-confdefs.h -DMOZILLA_CLIENT' + CFLAGS="$CFLAGS -W3 -Gy -Fd\$(COMPILE_PDBFILE)" + CXXFLAGS="$CXXFLAGS -W3 -Gy -Fd\$(COMPILE_PDBFILE)" + LIBS="$LIBS kernel32.lib user32.lib gdi32.lib winmm.lib wsock32.lib advapi32.lib" + MOZ_DEBUG_FLAGS='-Zi' + MOZ_DEBUG_LDFLAGS='-DEBUG -DEBUGTYPE:CV' + WARNINGS_AS_ERRORS='-WX' + MOZ_OPTIMIZE_FLAGS='-O1' + MOZ_JS_LIBS='$(libdir)/mozjs.lib' + MOZ_FIX_LINK_PATHS= + DYNAMIC_XPCOM_LIBS='$(LIBXUL_DIST)/lib/xpcom.lib $(LIBXUL_DIST)/lib/xpcom_core.lib' + XPCOM_FROZEN_LDOPTS='$(LIBXUL_DIST)/lib/xpcom.lib' + LIBXUL_LIBS='$(LIBXUL_DIST)/lib/xpcom.lib $(LIBXUL_DIST)/lib/xul.lib' + MOZ_COMPONENT_NSPR_LIBS='$(NSPR_LIBS)' + if test $_MSC_VER -ge 1400; then + LDFLAGS="$LDFLAGS -NXCOMPAT" + PROFILE_GEN_CFLAGS="-GL" + PROFILE_GEN_LDFLAGS="-LTCG:PGINSTRUMENT" + PROFILE_USE_CFLAGS="-GL -wd4624 -wd4952" + PROFILE_USE_LDFLAGS="-LTCG:PGUPDATE" + if test -n "$_USE_DYNAMICBASE"; then + LDFLAGS="$LDFLAGS -DYNAMICBASE" + fi + fi + fi + cat >> confdefs.h <<\EOF +#define HAVE_SNPRINTF 1 +EOF + + cat >> confdefs.h <<\EOF +#define _WINDOWS 1 +EOF + + cat >> confdefs.h <<\EOF +#define WIN32 1 +EOF + + cat >> confdefs.h <<\EOF +#define XP_WIN 1 +EOF + + cat >> confdefs.h <<\EOF +#define XP_WIN32 1 +EOF + + cat >> confdefs.h <<\EOF +#define HW_THREADS 1 +EOF + + cat >> confdefs.h <<\EOF +#define STDC_HEADERS 1 +EOF + + cat >> confdefs.h <<\EOF +#define NEW_H +EOF + + cat >> confdefs.h <<\EOF +#define WIN32_LEAN_AND_MEAN 1 +EOF + + TARGET_MD_ARCH=win32 + _PLATFORM_DEFAULT_TOOLKIT='cairo-windows' + BIN_SUFFIX='.exe' + MOZ_ENABLE_POSTSCRIPT= + MOZ_USER_DIR="Mozilla" + + TARGET_NSPR_MDCPUCFG='\"md/_win95.cfg\"' + + no_x=yes + cat >> confdefs.h <<\EOF +#define NO_X11 1 +EOF + + + case "$host" in + *-mingw*) + CYGPATH_W=echo + CYGPATH_S=cat + MOZ_BUILD_ROOT=`cd $MOZ_BUILD_ROOT && pwd -W` + ;; + *-cygwin*|*-msvc*|*-mks*) + CYGPATH_W="cygpath -a -w" + CYGPATH_S="sed -e s|\\\\|/|g" + MOZ_BUILD_ROOT=`$CYGPATH_W $MOZ_BUILD_ROOT | $CYGPATH_S` + ;; + esac + case "$host" in + *-mingw*|*-cygwin*|*-msvc*|*-mks*) + + if test -z "$MOZ_TOOLS"; then + { echo "configure: error: MOZ_TOOLS is not set" 1>&2; exit 1; } + fi + + MOZ_TOOLS_DIR=`cd $MOZ_TOOLS && pwd` + if test "$?" != "0" || test -z "$MOZ_TOOLS_DIR"; then + { echo "configure: error: cd \$MOZ_TOOLS failed. MOZ_TOOLS ==? $MOZ_TOOLS" 1>&2; exit 1; } + fi + if test `echo ${PATH}: | grep -ic "$MOZ_TOOLS_DIR/bin:"` = 0; then + { echo "configure: error: \$MOZ_TOOLS\\bin must be in your path." 1>&2; exit 1; } + fi + MOZ_TOOLS_DIR=`$CYGPATH_W $MOZ_TOOLS_DIR | $CYGPATH_S` + ;; + esac + + + case "$host_os" in + cygwin*|msvc*|mks*) + echo "configure: warning: Using a cygwin build environment is unsupported. Configure cannot check for the presence of necessary headers. Please upgrade to MozillaBuild; see http://developer.mozilla.org/en/docs/Windows_Build_Prerequisites" 1>&2 + ;; + + *) + for ac_hdr in oleacc.idl +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:6330: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:6340: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done + + + + ac_ext=C +# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cxx_cross + + for ac_hdr in atlbase.h wpcapi.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:6379: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:6389: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done + + ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + + ;; + esac + + case "$target" in + i*86-*) + if test "$HAVE_64BIT_OS"; then + { echo "configure: error: You are targeting i386 but using the 64-bit compiler." 1>&2; exit 1; } + fi + + if test $_MSC_VER -ge 1400; then + LDFLAGS="$LDFLAGS -SAFESEH" + fi + for ac_hdr in mmintrin.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:6438: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:6448: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done + + cat >> confdefs.h <<\EOF +#define _X86_ 1 +EOF + + ;; + alpha-*) + cat >> confdefs.h <<\EOF +#define _ALPHA_ 1 +EOF + + ;; + mips-*) + cat >> confdefs.h <<\EOF +#define _MIPS_ 1 +EOF + + ;; + x86_64-*) + cat >> confdefs.h <<\EOF +#define _AMD64_ 1 +EOF + + ;; + *) + cat >> confdefs.h <<\EOF +#define _CPU_ARCH_NOT_DEFINED 1 +EOF + + ;; + esac + + if test "$HAVE_64BIT_OS"; then + cat >> confdefs.h <<\EOF +#define _WIN64 1 +EOF + + fi + ;; + +*-netbsd*) + DSO_CFLAGS='' + CFLAGS="$CFLAGS -Dunix" + CXXFLAGS="$CXXFLAGS -Dunix" + if $CC -E - -dM /dev/null; then + DLL_SUFFIX=".so" + DSO_PIC_CFLAGS='-fPIC -DPIC' + DSO_LDOPTS='-shared' + BIN_FLAGS='-Wl,--export-dynamic' + else + DSO_PIC_CFLAGS='-fPIC -DPIC' + DLL_SUFFIX=".so.1.0" + DSO_LDOPTS='-shared' + fi + # This will fail on a.out systems prior to 1.5.1_ALPHA. + MKSHLIB_FORCE_ALL='-Wl,--whole-archive' + MKSHLIB_UNFORCE_ALL='-Wl,--no-whole-archive' + if test "$LIBRUNPATH"; then + DSO_LDOPTS="-Wl,-R$LIBRUNPATH $DSO_LDOPTS" + fi + MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-soname,lib$(LIBRARY_NAME)$(DLL_SUFFIX) -o $@' + MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-soname,lib$(LIBRARY_NAME)$(DLL_SUFFIX) -o $@' + ;; + +*-nto*) + cat >> confdefs.h <<\EOF +#define NTO 1 +EOF + + cat >> confdefs.h <<\EOF +#define _QNX_SOURCE 1 +EOF + + cat >> confdefs.h <<\EOF +#define _i386 1 +EOF + + OS_TARGET=NTO + WARNINGS_AS_ERRORS='' + MOZ_OPTIMIZE_FLAGS="-O" + MOZ_DEBUG_FLAGS="-gstabs" + USE_PTHREADS=1 + _PEDANTIC= + LIBS="$LIBS -lsocket -lstdc++" + _DEFINES_CFLAGS='-Wp,-include -Wp,$(DEPTH)/js-confdefs.h -DMOZILLA_CLIENT -D_POSIX_C_SOURCE=199506' + _DEFINES_CXXFLAGS='-DMOZILLA_CLIENT -Wp,-include -Wp,$(DEPTH)/js-confdefs.h -D_POSIX_C_SOURCE=199506' + if test "$with_x" != "yes" + then + _PLATFORM_DEFAULT_TOOLKIT="photon" + TK_CFLAGS='-I/usr/include/photon' + TK_LIBS='-lph' + fi + case "${target_cpu}" in + ppc*) + cat >> confdefs.h <<\EOF +#define HAVE_VA_LIST_AS_ARRAY 1 +EOF + + ;; + esac + ;; + +*-openbsd*) + DLL_SUFFIX=".so.1.0" + DSO_CFLAGS='' + DSO_PIC_CFLAGS='-fPIC' + DSO_LDOPTS='-shared -fPIC' + if test "$LIBRUNPATH"; then + DSO_LDOPTS="-R$LIBRUNPATH $DSO_LDOPTS" + fi + ;; + +*-openvms*) + cat >> confdefs.h <<\EOF +#define NO_PW_GECOS 1 +EOF + + cat >> confdefs.h <<\EOF +#define NO_UDSOCK 1 +EOF + + cat >> confdefs.h <<\EOF +#define POLL_WITH_XCONNECTIONNUMBER 1 +EOF + + USE_PTHREADS=1 + MKSHLIB_FORCE_ALL='-all' + MKSHLIB_UNFORCE_ALL='-none' + AS='as' + AS_DASH_C_FLAG='-Wc/names=as_is' + AR_FLAGS='c $@' + DSO_LDOPTS='-shared -auto_symvec' + DSO_PIC_CFLAGS= + MOZ_DEBUG_LDFLAGS='-g' + COMPAQ_CXX=1 + CC_VERSION=`$CC -V 2>&1 | awk '/ C / { print $3 }'` + CXX_VERSION=`$CXX -V 2>&1 | awk '/ C\+\+ / { print $3 }'` + ;; + + +*-os2*) + MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@' + MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@' + cat >> confdefs.h <<\EOF +#define OS2 1 +EOF + + cat >> confdefs.h <<\EOF +#define XP_OS2 1 +EOF + + cat >> confdefs.h <<\EOF +#define OS2EMX_PLAIN_CHAR 1 +EOF + + cat >> confdefs.h <<\EOF +#define TCPV40HDRS 1 +EOF + + DLL_PREFIX= + LIB_PREFIX= + LIB_SUFFIX=lib + BIN_SUFFIX=".exe" + DLL_SUFFIX=".dll" + IMPORT_LIB_SUFFIX=lib + DSO_PIC_CFLAGS= + AR=emxomfar + AR_FLAGS='r $@' + CFLAGS="$CFLAGS -Zomf" + CXXFLAGS="$CXXFLAGS -Zomf" + DSO_LDOPTS='-Zdll' + BIN_FLAGS='-Zlinker /ST:0x100000' + IMPLIB='emximp -o' + FILTER='emxexp -o' + LDFLAGS='-Zmap' + WARNINGS_AS_ERRORS='-Werror' + MOZ_DEBUG_FLAGS="-g -fno-inline" + MOZ_OPTIMIZE_FLAGS="-O2" + MOZ_OPTIMIZE_LDFLAGS="-s -Zlinker /EXEPACK:2 -Zlinker /PACKCODE -Zlinker /PACKDATA" + DYNAMIC_XPCOM_LIBS='-L$(LIBXUL_DIST)/lib $(LIBXUL_DIST)/lib/xpcom.lib $(LIBXUL_DIST)/lib/xpcomcor.lib' + LIBXUL_LIBS='-L$(LIBXUL_DIST)/lib $(LIBXUL_DIST)/lib/xpcom.lib $(LIBXUL_DIST)/lib/xul.lib' + TARGET_MD_ARCH=os2 + _PLATFORM_DEFAULT_TOOLKIT="cairo-os2" + MOZ_ENABLE_POSTSCRIPT= + RC=rc.exe + RCFLAGS='-n' + MOZ_USER_DIR="Mozilla" + + if test "$MOZTOOLS"; then + MOZ_TOOLS_DIR=`echo $MOZTOOLS | sed -e 's|\\\\|/|g'` + else + { echo "configure: error: MOZTOOLS is not set" 1>&2; exit 1; } + fi + if test -n "$MOZ_OS2_HIGH_MEMORY"; then + DSO_LDOPTS="$DSO_LDOPTS -Zhigh-mem" + LDFLAGS="$LDFLAGS -Zhigh-mem" + MOZ_OPTIMIZE_LDFLAGS="$MOZ_OPTIMIZE_LDFLAGS -Zhigh-mem" + cat >> confdefs.h <<\EOF +#define MOZ_OS2_HIGH_MEMORY 1 +EOF + + fi + + # GCC for OS/2 currently predefines these, but we don't want them + _DEFINES_CFLAGS="$_DEFINES_CFLAGS -Uunix -U__unix -U__unix__" + _DEFINES_CXXFLAGS="$_DEFINES_CXXFLAGS -Uunix -U__unix -U__unix__" + + echo $ac_n "checking for __declspec(dllexport)""... $ac_c" 1>&6 +echo "configure:6681: checking for __declspec(dllexport)" >&5 +if eval "test \"`echo '$''{'ac_os2_declspec'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_os2_declspec="yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_os2_declspec="no" +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_os2_declspec" 1>&6 + if test "$ac_os2_declspec" = "yes"; then + FILTER='true' + MOZ_OS2_USE_DECLSPEC='1' + fi + ;; + +alpha*-*-osf*) + if test "$GNU_CC"; then + MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-soname,$@ -o $@' + MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-soname,$@ -o $@' + + else + MOZ_DEBUG_FLAGS='-g' + ASFLAGS='-I$(topsrcdir)/xpcom/reflect/xptcall/public -g' + CFLAGS="$CFLAGS -ieee" + CXXFLAGS="$CXXFLAGS "'-noexceptions -ieee -ptr $(DIST)/cxx_repository' + DSO_LDOPTS='-shared -msym -expect_unresolved \* -update_registry $(DIST)/so_locations' + DSO_CFLAGS= + DSO_PIC_CFLAGS= + MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -soname $@ -o $@' + MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -soname $@ -o $@' + MKSHLIB_FORCE_ALL='-all' + MKSHLIB_UNFORCE_ALL='-none' + cat >> confdefs.h <<\EOF +#define NSCAP_DISABLE_TEST_DONTQUERY_CASES 1 +EOF + + cat >> confdefs.h <<\EOF +#define NSCAP_DISABLE_DEBUG_PTR_TYPES 1 +EOF + + fi + if test -z "$GNU_CXX"; then + COMPAQ_CXX=1 + fi + cat >> confdefs.h <<\EOF +#define NEED_USLEEP_PROTOTYPE 1 +EOF + + ;; + +*-qnx*) + DIRENT_INO=d_stat.st_ino + cat >> confdefs.h <<\EOF +#define NSCAP_DISABLE_TEST_DONTQUERY_CASES 1 +EOF + + cat >> confdefs.h <<\EOF +#define NSCAP_DISABLE_DEBUG_PTR_TYPES 1 +EOF + + cat >> confdefs.h <<\EOF +#define STDC_HEADERS 1 +EOF + + if test "$no_x" = "yes"; then + _PLATFORM_DEFAULT_TOOLKIT='photon' + TK_CFLAGS='-I/usr/nto/include/photon' + TK_LIBS='-lphoton -lphrender' + fi + ;; + +*-sco*) + cat >> confdefs.h <<\EOF +#define NSCAP_DISABLE_TEST_DONTQUERY_CASES 1 +EOF + + cat >> confdefs.h <<\EOF +#define NSCAP_DISABLE_DEBUG_PTR_TYPES 1 +EOF + + CXXFLAGS="$CXXFLAGS -I/usr/include/CC" + if test ! "$GNU_CC"; then + DSO_LDOPTS='-G' + fi + ;; + +*-solaris*) + cat >> confdefs.h <<\EOF +#define SOLARIS 1 +EOF + + TARGET_NSPR_MDCPUCFG='\"md/_solaris.cfg\"' + if test -z "$CROSS_COMPILE" && pkginfo -q SUNWpr && pkginfo -q SUNWprd; then + NO_NSPR_CONFIG_SYSTEM_LDFLAGS="-L/usr/lib/mps -R/usr/lib/mps -lnspr4" + NO_NSPR_CONFIG_SYSTEM_CFLAGS="-I/usr/include/mps" + NO_NSPR_CONFIG_SYSTEM_VERSION="`pkgparam SUNWpr SUNW_PRODVERS | sed -e 's/^[1-9][0-9]*\.[0-9][0-9]*$/&.0/'`" + fi + SYSTEM_MAKEDEPEND= + # $ORIGIN/.. is for shared libraries under components/ to locate shared + # libraries one level up (e.g. libnspr4.so) + LDFLAGS="$LDFLAGS -z ignore -R '\$\$ORIGIN:\$\$ORIGIN/..'" + if test "$SOLARIS_SUNPRO_CC"; then + LIBS="-lCrun -lCstd $LIBS" + NS_USE_NATIVE=1 + MOZ_FIX_LINK_PATHS= + cat >> confdefs.h <<\EOF +#define NSCAP_DISABLE_DEBUG_PTR_TYPES 1 +EOF + + CFLAGS="$CFLAGS -xlibmieee -xstrconst -xbuiltin=%all" + CXXFLAGS="$CXXFLAGS -xlibmieee -xbuiltin=%all -features=tmplife -norunpath" + LDFLAGS="-xildoff -z lazyload -z combreloc $LDFLAGS" + if test -z "$CROSS_COMPILE" && test -f /usr/lib/ld/map.noexstk; then + _SAVE_LDFLAGS=$LDFLAGS + LDFLAGS="-M /usr/lib/ld/map.noexstk $LDFLAGS" + cat > conftest.$ac_ext < +int main() { +printf("Hello World\n"); +; return 0; } +EOF +if { (eval echo configure:6820: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + : +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + LDFLAGS=$_SAVE_LDFLAGS +fi +rm -f conftest* + fi + WARNINGS_AS_ERRORS='-Werror' + MOZ_OPTIMIZE_FLAGS="-xO4" + MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_FLAGS) $(DSO_LDOPTS) -h $@ -o $@' + MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_FLAGS) -G -z muldefs -h $@ -o $@' + MKSHLIB_FORCE_ALL='-z allextract' + MKSHLIB_UNFORCE_ALL='-z defaultextract' + DSO_LDOPTS='-G -z muldefs' + AR_LIST="$AR t" + AR_EXTRACT="$AR x" + AR_DELETE="$AR d" + AR='$(CXX) -xar' + AR_FLAGS='-o $@' + AS='/usr/ccs/bin/as' + ASFLAGS="$ASFLAGS -K PIC -L -P -D_ASM -D__STDC__=0" + AS_DASH_C_FLAG='' + TARGET_COMPILER_ABI="sunc" + CC_VERSION=`$CC -V 2>&1 | grep '^cc:' 2>/dev/null | $AWK -F\: '{ print $2 }'` + CXX_VERSION=`$CXX -V 2>&1 | grep '^CC:' 2>/dev/null | $AWK -F\: '{ print $2 }'` + echo $ac_n "checking for Sun C++ compiler version >= 5.9""... $ac_c" 1>&6 +echo "configure:6849: checking for Sun C++ compiler version >= 5.9" >&5 + + ac_ext=C +# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cxx_cross + + cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + _BAD_COMPILER= +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + _BAD_COMPILER=1 +fi +rm -f conftest* + if test -n "$_BAD_COMPILER"; then + _res="no" + { echo "configure: error: Sun C++ 5.9 (Sun Studio 12) or higher is required to build. Your compiler version is $CXX_VERSION ." 1>&2; exit 1; } + else + _res="yes" + fi + echo "$ac_t""$_res" 1>&6 + ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + + else + ASFLAGS="$ASFLAGS -fPIC" + DSO_LDOPTS='-G' + _WARNINGS_CFLAGS='' + _WARNINGS_CXXFLAGS='' + if test "$OS_RELEASE" = "5.3"; then + cat >> confdefs.h <<\EOF +#define MUST_UNDEF_HAVE_BOOLEAN_AFTER_INCLUDES 1 +EOF + + fi + fi + if test "$OS_RELEASE" = "5.5.1"; then + cat >> confdefs.h <<\EOF +#define NEED_USLEEP_PROTOTYPE 1 +EOF + + fi + ;; + +*-sunos*) + DSO_LDOPTS='-Bdynamic' + MKSHLIB='-$(LD) $(DSO_LDOPTS) -o $@' + MKCSHLIB='-$(LD) $(DSO_LDOPTS) -o $@' + cat >> confdefs.h <<\EOF +#define SUNOS4 1 +EOF + + cat >> confdefs.h <<\EOF +#define SPRINTF_RETURNS_STRING 1 +EOF + + case "$(target_os)" in + sunos4.1*) + DLL_SUFFIX='.so.1.0' + ;; + esac + ;; + +*-sysv4.2uw7*) + NSPR_LIBS="-lnspr$NSPR_VERSION -lplc$NSPR_VERSION -lplds$NSPR_VERSION -L/usr/ccs/lib -lcrt" + ;; + +*-os2*) + HOST_NSPR_MDCPUCFG='\"md/_os2.cfg\"' + ;; + +esac + +cat >> confdefs.h <> confdefs.h <<\EOF +#define MOZ_OJI_REQUIRE_THREAD_SAFE_ON_STARTUP 1 +EOF + + ;; +esac + +case "$target" in + *-linux*|*-kfreebsd*-gnu) + MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS='-Wl,--version-script -Wl,$(BUILD_TOOLS)/gnu-ld-scripts/components-version-script' + ;; + *-solaris*) + if test -z "$GNU_CC"; then + MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS='-M $(BUILD_TOOLS)/gnu-ld-scripts/components-mapfile' + else + if test -z "$GCC_USE_GNU_LD"; then + MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS='-Wl,-M -Wl,$(BUILD_TOOLS)/gnu-ld-scripts/components-mapfile' + else + MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS='-Wl,--version-script -Wl,$(BUILD_TOOLS)/gnu-ld-scripts/components-version-script' + fi + fi + ;; + *-nto*) + MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS='-Wl,--version-script,$(BUILD_TOOLS)/gnu-ld-scripts/components-version-script' + ;; + *-darwin*) + MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS='-Wl,-exported_symbols_list -Wl,$(BUILD_TOOLS)/gnu-ld-scripts/components-export-list' + ;; + *-cygwin*|*-mingw*|*-mks*|*-msvc|*-wince) + if test -n "$GNU_CC"; then + MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS='-Wl,--version-script,$(BUILD_TOOLS)/gnu-ld-scripts/components-version-script' + fi + ;; +esac + +if test -z "$COMPILE_ENVIRONMENT"; then + SKIP_COMPILER_CHECKS=1 + SKIP_LIBRARY_CHECKS=1 +fi + + +case "$target" in +i?86-*) + ENABLE_JIT=1 + NANOJIT_ARCH=i386 + ;; +x86_64*-*) + ENABLE_JIT=1 + NANOJIT_ARCH=X64 + ;; +arm*-*) + ENABLE_JIT=1 + NANOJIT_ARCH=ARM + ;; +sparc*-*) + ENABLE_JIT=1 + NANOJIT_ARCH=Sparc + ;; +esac + +# Check whether --enable-jit or --disable-jit was given. +if test "${enable_jit+set}" = set; then + enableval="$enable_jit" + if test "$enableval" = "no"; then + ENABLE_JIT= + elif test "$enableval" = "yes"; then + : + else + { echo "configure: error: Option, jit, does not take an argument ($enableval)." 1>&2; exit 1; } + fi +fi + + +if test "$ENABLE_JIT"; then + +cat >> confdefs.h <<\EOF +#define FEATURE_NANOJIT 1 +EOF + +cat >> confdefs.h <<\EOF +#define JS_TRACER 1 +EOF + + +case "$target" in +i?86-*) + cat >> confdefs.h <<\EOF +#define AVMPLUS_IA32 1 +EOF + + ;; +x86_64*-*) + cat >> confdefs.h <<\EOF +#define AVMPLUS_AMD64 1 +EOF + + cat >> confdefs.h <<\EOF +#define AVMPLUS_64BIT 1 +EOF + + ;; +arm*-*) + cat >> confdefs.h <<\EOF +#define AVMPLUS_ARM 1 +EOF + + ;; +sparc-*) + cat >> confdefs.h <<\EOF +#define AVMPLUS_SPARC 1 +EOF + + ;; +esac + +case "$target_os" in +linux*) + cat >> confdefs.h <<\EOF +#define AVMPLUS_UNIX 1 +EOF + + cat >> confdefs.h <<\EOF +#define AVMPLUS_LINUX 1 +EOF + + ;; +darwin*) + cat >> confdefs.h <<\EOF +#define AVMPLUS_UNIX 1 +EOF + + ;; +solaris*) + cat >> confdefs.h <<\EOF +#define AVMPLUS_UNIX 1 +EOF + + ;; +freebsd*|kfreebsd*) + cat >> confdefs.h <<\EOF +#define AVMPLUS_UNIX 1 +EOF + + ;; +*cygwin*|*mingw*|*mks*|*msvc*|*wince) + cat >> confdefs.h <<\EOF +#define AVMPLUS_WIN32 1 +EOF + + ;; +*os2*) + cat >> confdefs.h <<\EOF +#define AVMPLUS_OS2 1 +EOF + + ;; +*) + { echo "configure: error: Unrecognized nanojit platform. Use --disable-jit to build without JIT support." 1>&2; exit 1; } +esac + +fi # ENABLE_JIT + + + + +if test -z "$SKIP_COMPILER_CHECKS"; then +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6 +echo "configure:7144: checking for ANSI C header files" >&5 +if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include +#include +#include +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:7157: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + ac_cv_header_stdc=yes +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. +cat > conftest.$ac_ext < +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "memchr" >/dev/null 2>&1; then + : +else + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. +cat > conftest.$ac_ext < +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "free" >/dev/null 2>&1; then + : +else + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. +if test "$cross_compiling" = yes; then + : +else + cat > conftest.$ac_ext < +#define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int main () { int i; for (i = 0; i < 256; i++) +if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); +exit (0); } + +EOF +if { (eval echo configure:7224: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + : +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_header_stdc=no +fi +rm -fr conftest* +fi + +fi +fi + +echo "$ac_t""$ac_cv_header_stdc" 1>&6 +if test $ac_cv_header_stdc = yes; then + cat >> confdefs.h <<\EOF +#define STDC_HEADERS 1 +EOF + +fi + +echo $ac_n "checking for working const""... $ac_c" 1>&6 +echo "configure:7248: checking for working const" >&5 +if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <j = 5; +} +{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; +} + +; return 0; } +EOF +if { (eval echo configure:7302: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_c_const=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_c_const=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_c_const" 1>&6 +if test $ac_cv_c_const = no; then + cat >> confdefs.h <<\EOF +#define const +EOF + +fi + +echo $ac_n "checking for mode_t""... $ac_c" 1>&6 +echo "configure:7323: checking for mode_t" >&5 +if eval "test \"`echo '$''{'ac_cv_type_mode_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#if STDC_HEADERS +#include +#include +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "(^|[^a-zA-Z_0-9])mode_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_mode_t=yes +else + rm -rf conftest* + ac_cv_type_mode_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_mode_t" 1>&6 +if test $ac_cv_type_mode_t = no; then + cat >> confdefs.h <<\EOF +#define mode_t int +EOF + +fi + +echo $ac_n "checking for off_t""... $ac_c" 1>&6 +echo "configure:7356: checking for off_t" >&5 +if eval "test \"`echo '$''{'ac_cv_type_off_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#if STDC_HEADERS +#include +#include +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "(^|[^a-zA-Z_0-9])off_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_off_t=yes +else + rm -rf conftest* + ac_cv_type_off_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_off_t" 1>&6 +if test $ac_cv_type_off_t = no; then + cat >> confdefs.h <<\EOF +#define off_t long +EOF + +fi + +echo $ac_n "checking for pid_t""... $ac_c" 1>&6 +echo "configure:7389: checking for pid_t" >&5 +if eval "test \"`echo '$''{'ac_cv_type_pid_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#if STDC_HEADERS +#include +#include +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "(^|[^a-zA-Z_0-9])pid_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_pid_t=yes +else + rm -rf conftest* + ac_cv_type_pid_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_pid_t" 1>&6 +if test $ac_cv_type_pid_t = no; then + cat >> confdefs.h <<\EOF +#define pid_t int +EOF + +fi + +echo $ac_n "checking for size_t""... $ac_c" 1>&6 +echo "configure:7422: checking for size_t" >&5 +if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#if STDC_HEADERS +#include +#include +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "(^|[^a-zA-Z_0-9])size_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_size_t=yes +else + rm -rf conftest* + ac_cv_type_size_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_size_t" 1>&6 +if test $ac_cv_type_size_t = no; then + cat >> confdefs.h <<\EOF +#define size_t unsigned +EOF + +fi + +echo $ac_n "checking for st_blksize in struct stat""... $ac_c" 1>&6 +echo "configure:7455: checking for st_blksize in struct stat" >&5 +if eval "test \"`echo '$''{'ac_cv_struct_st_blksize'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include +int main() { +struct stat s; s.st_blksize; +; return 0; } +EOF +if { (eval echo configure:7468: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_struct_st_blksize=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_struct_st_blksize=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_struct_st_blksize" 1>&6 +if test $ac_cv_struct_st_blksize = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_ST_BLKSIZE 1 +EOF + +fi + +echo $ac_n "checking for siginfo_t""... $ac_c" 1>&6 +echo "configure:7489: checking for siginfo_t" >&5 +if eval "test \"`echo '$''{'ac_cv_siginfo_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +int main() { +siginfo_t* info; +; return 0; } +EOF +if { (eval echo configure:7502: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_siginfo_t=true +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_siginfo_t=false +fi +rm -f conftest* +fi + +if test "$ac_cv_siginfo_t" = true ; then + cat >> confdefs.h <<\EOF +#define HAVE_SIGINFO_T 1 +EOF + + echo "$ac_t""yes" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + +ac_safe=`echo "stdint.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for stdint.h""... $ac_c" 1>&6 +echo "configure:7527: checking for stdint.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:7537: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + : +else + echo "$ac_t""no" 1>&6 +fi + +if test "$ac_cv_header_stdint_h" = yes; then + cat >> confdefs.h <<\EOF +#define JS_HAVE_STDINT_H 1 +EOF + +else + +echo $ac_n "checking for a 1-byte type""... $ac_c" 1>&6 +echo "configure:7566: checking for a 1-byte type" >&5 +if eval "test \"`echo '$''{'moz_cv_n_byte_type_JS_INT8_TYPE'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + moz_cv_n_byte_type_JS_INT8_TYPE= + for type in char; do + cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + moz_cv_n_byte_type_JS_INT8_TYPE=$type; break +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 +fi +rm -f conftest* + done + if test ! "$moz_cv_n_byte_type_JS_INT8_TYPE"; then + { echo "configure: error: Couldn't find a 1-byte type" 1>&2; exit 1; } + fi + +fi + +echo "$ac_t""$moz_cv_n_byte_type_JS_INT8_TYPE" 1>&6 +cat >> confdefs.h <&6 +echo "configure:7607: checking for a 2-byte type" >&5 +if eval "test \"`echo '$''{'moz_cv_n_byte_type_JS_INT16_TYPE'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + moz_cv_n_byte_type_JS_INT16_TYPE= + for type in short int long; do + cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + moz_cv_n_byte_type_JS_INT16_TYPE=$type; break +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 +fi +rm -f conftest* + done + if test ! "$moz_cv_n_byte_type_JS_INT16_TYPE"; then + { echo "configure: error: Couldn't find a 2-byte type" 1>&2; exit 1; } + fi + +fi + +echo "$ac_t""$moz_cv_n_byte_type_JS_INT16_TYPE" 1>&6 +cat >> confdefs.h <&6 +echo "configure:7648: checking for a 4-byte type" >&5 +if eval "test \"`echo '$''{'moz_cv_n_byte_type_JS_INT32_TYPE'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + moz_cv_n_byte_type_JS_INT32_TYPE= + for type in int long 'long long' short; do + cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + moz_cv_n_byte_type_JS_INT32_TYPE=$type; break +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 +fi +rm -f conftest* + done + if test ! "$moz_cv_n_byte_type_JS_INT32_TYPE"; then + { echo "configure: error: Couldn't find a 4-byte type" 1>&2; exit 1; } + fi + +fi + +echo "$ac_t""$moz_cv_n_byte_type_JS_INT32_TYPE" 1>&6 +cat >> confdefs.h <&6 +echo "configure:7689: checking for a 8-byte type" >&5 +if eval "test \"`echo '$''{'moz_cv_n_byte_type_JS_INT64_TYPE'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + moz_cv_n_byte_type_JS_INT64_TYPE= + for type in int long 'long long'; do + cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + moz_cv_n_byte_type_JS_INT64_TYPE=$type; break +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 +fi +rm -f conftest* + done + if test ! "$moz_cv_n_byte_type_JS_INT64_TYPE"; then + { echo "configure: error: Couldn't find a 8-byte type" 1>&2; exit 1; } + fi + +fi + +echo "$ac_t""$moz_cv_n_byte_type_JS_INT64_TYPE" 1>&6 +cat >> confdefs.h <&6 +echo "configure:7730: checking for a sizeof (void *)-byte type" >&5 +if eval "test \"`echo '$''{'moz_cv_n_byte_type_JS_INTPTR_TYPE'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + moz_cv_n_byte_type_JS_INTPTR_TYPE= + for type in int long 'long long' short; do + cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + moz_cv_n_byte_type_JS_INTPTR_TYPE=$type; break +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 +fi +rm -f conftest* + done + if test ! "$moz_cv_n_byte_type_JS_INTPTR_TYPE"; then + { echo "configure: error: Couldn't find a sizeof (void *)-byte type" 1>&2; exit 1; } + fi + +fi + +echo "$ac_t""$moz_cv_n_byte_type_JS_INTPTR_TYPE" 1>&6 +cat >> confdefs.h <&6 +echo "configure:7773: checking for the size of void*" >&5 +if eval "test \"`echo '$''{'moz_cv_size_of_JS_BYTES_PER_WORD'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + moz_cv_size_of_JS_BYTES_PER_WORD= + for size in 4 8; do + cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + moz_cv_size_of_JS_BYTES_PER_WORD=$size; break +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 +fi +rm -f conftest* + done + if test ! "$moz_cv_size_of_JS_BYTES_PER_WORD"; then + { echo "configure: error: No size found for void*" 1>&2; exit 1; } + fi + +fi + +echo "$ac_t""$moz_cv_size_of_JS_BYTES_PER_WORD" 1>&6 +cat >> confdefs.h <> confdefs.h <<\EOF +#define JS_BITS_PER_WORD_LOG2 5 +EOF + +elif test "$moz_cv_size_of_JS_BYTES_PER_WORD" -eq "8"; then + cat >> confdefs.h <<\EOF +#define JS_BITS_PER_WORD_LOG2 6 +EOF + +else + { echo "configure: error: Unexpected JS_BYTES_PER_WORD" 1>&2; exit 1; } +fi + + +echo $ac_n "checking for the alignment of void*""... $ac_c" 1>&6 +echo "configure:7828: checking for the alignment of void*" >&5 +if eval "test \"`echo '$''{'moz_cv_align_of_JS_ALIGN_OF_POINTER'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + moz_cv_align_of_JS_ALIGN_OF_POINTER= + for align in 2 4 8 16; do + cat > conftest.$ac_ext < + struct aligner { char c; void* a; }; + +int main() { + + int a[offsetof(struct aligner, a) == $align ? 1 : -1]; + return; + +; return 0; } +EOF +if { (eval echo configure:7849: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + moz_cv_align_of_JS_ALIGN_OF_POINTER=$align; break +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 +fi +rm -f conftest* + done + if test ! "$moz_cv_align_of_JS_ALIGN_OF_POINTER"; then + { echo "configure: error: No alignment found for void*" 1>&2; exit 1; } + fi + +fi + +echo "$ac_t""$moz_cv_align_of_JS_ALIGN_OF_POINTER" 1>&6 +cat >> confdefs.h <&6 +echo "configure:7872: checking for the size of double" >&5 +if eval "test \"`echo '$''{'moz_cv_size_of_JS_BYTES_PER_DOUBLE'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + moz_cv_size_of_JS_BYTES_PER_DOUBLE= + for size in 6 8 10 12 14; do + cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + moz_cv_size_of_JS_BYTES_PER_DOUBLE=$size; break +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 +fi +rm -f conftest* + done + if test ! "$moz_cv_size_of_JS_BYTES_PER_DOUBLE"; then + { echo "configure: error: No size found for double" 1>&2; exit 1; } + fi + +fi + +echo "$ac_t""$moz_cv_size_of_JS_BYTES_PER_DOUBLE" 1>&6 +cat >> confdefs.h <&6 +echo "configure:7913: checking for int16_t" >&5 +if eval "test \"`echo '$''{'ac_cv_int16_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < + #include +int main() { +int16_t foo = 0; +; return 0; } +EOF +if { (eval echo configure:7926: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_int16_t=true +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_int16_t=false +fi +rm -f conftest* +fi + +if test "$ac_cv_int16_t" = true ; then + cat >> confdefs.h <<\EOF +#define HAVE_INT16_T 1 +EOF + + echo "$ac_t""yes" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi +echo $ac_n "checking for int32_t""... $ac_c" 1>&6 +echo "configure:7948: checking for int32_t" >&5 +if eval "test \"`echo '$''{'ac_cv_int32_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < + #include +int main() { +int32_t foo = 0; +; return 0; } +EOF +if { (eval echo configure:7961: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_int32_t=true +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_int32_t=false +fi +rm -f conftest* +fi + +if test "$ac_cv_int32_t" = true ; then + cat >> confdefs.h <<\EOF +#define HAVE_INT32_T 1 +EOF + + echo "$ac_t""yes" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi +echo $ac_n "checking for int64_t""... $ac_c" 1>&6 +echo "configure:7983: checking for int64_t" >&5 +if eval "test \"`echo '$''{'ac_cv_int64_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < + #include +int main() { +int64_t foo = 0; +; return 0; } +EOF +if { (eval echo configure:7996: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_int64_t=true +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_int64_t=false +fi +rm -f conftest* +fi + +if test "$ac_cv_int64_t" = true ; then + cat >> confdefs.h <<\EOF +#define HAVE_INT64_T 1 +EOF + + echo "$ac_t""yes" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi +echo $ac_n "checking for int64""... $ac_c" 1>&6 +echo "configure:8018: checking for int64" >&5 +if eval "test \"`echo '$''{'ac_cv_int64'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < + #include +int main() { +int64 foo = 0; +; return 0; } +EOF +if { (eval echo configure:8031: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_int64=true +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_int64=false +fi +rm -f conftest* +fi + +if test "$ac_cv_int64" = true ; then + cat >> confdefs.h <<\EOF +#define HAVE_INT64 1 +EOF + + echo "$ac_t""yes" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi +echo $ac_n "checking for uint""... $ac_c" 1>&6 +echo "configure:8053: checking for uint" >&5 +if eval "test \"`echo '$''{'ac_cv_uint'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < + #include +int main() { +uint foo = 0; +; return 0; } +EOF +if { (eval echo configure:8066: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_uint=true +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_uint=false +fi +rm -f conftest* +fi + +if test "$ac_cv_uint" = true ; then + cat >> confdefs.h <<\EOF +#define HAVE_UINT 1 +EOF + + echo "$ac_t""yes" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi +echo $ac_n "checking for uint_t""... $ac_c" 1>&6 +echo "configure:8088: checking for uint_t" >&5 +if eval "test \"`echo '$''{'ac_cv_uint_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < + #include +int main() { +uint_t foo = 0; +; return 0; } +EOF +if { (eval echo configure:8101: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_uint_t=true +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_uint_t=false +fi +rm -f conftest* +fi + +if test "$ac_cv_uint_t" = true ; then + cat >> confdefs.h <<\EOF +#define HAVE_UINT_T 1 +EOF + + echo "$ac_t""yes" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi +echo $ac_n "checking for uint16_t""... $ac_c" 1>&6 +echo "configure:8123: checking for uint16_t" >&5 +if eval "test \"`echo '$''{'ac_cv_uint16_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < + #include +int main() { +uint16_t foo = 0; +; return 0; } +EOF +if { (eval echo configure:8136: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_uint16_t=true +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_uint16_t=false +fi +rm -f conftest* +fi + +if test "$ac_cv_uint16_t" = true ; then + cat >> confdefs.h <<\EOF +#define HAVE_UINT16_T 1 +EOF + + echo "$ac_t""yes" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +ac_ext=C +# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cxx_cross + + +echo $ac_n "checking for uname.domainname""... $ac_c" 1>&6 +echo "configure:8167: checking for uname.domainname" >&5 +if eval "test \"`echo '$''{'ac_cv_have_uname_domainname_field'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +int main() { + struct utsname *res; char *domain; + (void)uname(res); if (res != 0) { domain = res->domainname; } +; return 0; } +EOF +if { (eval echo configure:8180: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_have_uname_domainname_field=true +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_have_uname_domainname_field=false +fi +rm -f conftest* +fi + + +if test "$ac_cv_have_uname_domainname_field" = "true"; then + cat >> confdefs.h <<\EOF +#define HAVE_UNAME_DOMAINNAME_FIELD 1 +EOF + + echo "$ac_t""yes" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +echo $ac_n "checking for uname.__domainname""... $ac_c" 1>&6 +echo "configure:8204: checking for uname.__domainname" >&5 +if eval "test \"`echo '$''{'ac_cv_have_uname_us_domainname_field'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +int main() { + struct utsname *res; char *domain; + (void)uname(res); if (res != 0) { domain = res->__domainname; } +; return 0; } +EOF +if { (eval echo configure:8217: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_have_uname_us_domainname_field=true +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_have_uname_us_domainname_field=false +fi +rm -f conftest* +fi + + +if test "$ac_cv_have_uname_us_domainname_field" = "true"; then + cat >> confdefs.h <<\EOF +#define HAVE_UNAME_US_DOMAINNAME_FIELD 1 +EOF + + echo "$ac_t""yes" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + + +if test "$GNU_CC"; then + echo $ac_n "checking for visibility(hidden) attribute""... $ac_c" 1>&6 +echo "configure:8250: checking for visibility(hidden) attribute" >&5 +if eval "test \"`echo '$''{'ac_cv_visibility_hidden'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.c </dev/null 2>&1; then + if egrep '\.(hidden|private_extern).*foo' conftest.s >/dev/null; then + ac_cv_visibility_hidden=yes + fi + fi + rm -f conftest.cs + +fi + +echo "$ac_t""$ac_cv_visibility_hidden" 1>&6 + if test "$ac_cv_visibility_hidden" = "yes"; then + cat >> confdefs.h <<\EOF +#define HAVE_VISIBILITY_HIDDEN_ATTRIBUTE 1 +EOF + + + echo $ac_n "checking for visibility(default) attribute""... $ac_c" 1>&6 +echo "configure:8275: checking for visibility(default) attribute" >&5 +if eval "test \"`echo '$''{'ac_cv_visibility_default'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.c </dev/null 2>&1; then + if ! egrep '\.(hidden|private_extern).*foo' conftest.s >/dev/null; then + ac_cv_visibility_default=yes + fi + fi + rm -f conftest.cs + +fi + +echo "$ac_t""$ac_cv_visibility_default" 1>&6 + if test "$ac_cv_visibility_default" = "yes"; then + cat >> confdefs.h <<\EOF +#define HAVE_VISIBILITY_ATTRIBUTE 1 +EOF + + + echo $ac_n "checking for visibility pragma support""... $ac_c" 1>&6 +echo "configure:8300: checking for visibility pragma support" >&5 +if eval "test \"`echo '$''{'ac_cv_visibility_pragma'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.c </dev/null 2>&1; then + if egrep '\.(hidden|private_extern).*foo_hidden' conftest.s >/dev/null; then + if ! egrep '\.(hidden|private_extern).*foo_default' conftest.s > /dev/null; then + ac_cv_visibility_pragma=yes + fi + fi + fi + rm -f conftest.cs + +fi + +echo "$ac_t""$ac_cv_visibility_pragma" 1>&6 + if test "$ac_cv_visibility_pragma" = "yes"; then + echo $ac_n "checking For gcc visibility bug with class-level attributes (GCC bug 26905)""... $ac_c" 1>&6 +echo "configure:8325: checking For gcc visibility bug with class-level attributes (GCC bug 26905)" >&5 +if eval "test \"`echo '$''{'ac_cv_have_visibility_class_bug'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.c < /dev/null 2>&1 ; then + ac_cv_have_visibility_class_bug=yes + else + if test `egrep -c '@PLT|\\$stub' conftest.S` = 0; then + ac_cv_have_visibility_class_bug=yes + fi + fi + rm -rf conftest.{c,S} + +fi + +echo "$ac_t""$ac_cv_have_visibility_class_bug" 1>&6 + + echo $ac_n "checking For x86_64 gcc visibility bug with builtins (GCC bug 20297)""... $ac_c" 1>&6 +echo "configure:8353: checking For x86_64 gcc visibility bug with builtins (GCC bug 20297)" >&5 +if eval "test \"`echo '$''{'ac_cv_have_visibility_builtin_bug'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.c < +#pragma GCC visibility pop + +__attribute__ ((visibility ("default"))) void Func() { + char c[100]; + memset(c, 0, sizeof(c)); +} +EOF + ac_cv_have_visibility_builtin_bug=no + if ! ${CC-cc} ${CFLAGS} ${DSO_PIC_CFLAGS} ${DSO_LDOPTS} -O2 -S -o conftest.S conftest.c > /dev/null 2>&1 ; then + ac_cv_have_visibility_builtin_bug=yes + else + if test `grep -c "@PLT" conftest.S` = 0; then + ac_cv_visibility_builtin_bug=yes + fi + fi + rm -f conftest.{c,S} + +fi + +echo "$ac_t""$ac_cv_have_visibility_builtin_bug" 1>&6 + if test "$ac_cv_have_visibility_builtin_bug" = "no" -a \ + "$ac_cv_have_visibility_class_bug" = "no"; then + VISIBILITY_FLAGS='-I$(DIST)/system_wrappers_js -include $(topsrcdir)/config/gcc_hidden.h' + WRAP_SYSTEM_INCLUDES=1 + else + VISIBILITY_FLAGS='-fvisibility=hidden' + fi # have visibility pragma bug + fi # have visibility pragma + fi # have visibility(default) attribute + fi # have visibility(hidden) attribute +fi # GNU_CC + +# visibility hidden flag for Sun Studio on Solaris +if test "$SOLARIS_SUNPRO_CC"; then +VISIBILITY_FLAGS='-xldscope=hidden' +fi # Sun Studio on Solaris + + + + +ac_header_dirent=no +for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr that defines DIR""... $ac_c" 1>&6 +echo "configure:8406: checking for $ac_hdr that defines DIR" >&5 +if eval "test \"`echo '$''{'ac_cv_header_dirent_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include <$ac_hdr> +int main() { +DIR *dirp = 0; +; return 0; } +EOF +if { (eval echo configure:8419: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + eval "ac_cv_header_dirent_$ac_safe=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_dirent_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_dirent_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done +# Two versions of opendir et al. are in -ldir and -lx on SCO Xenix. +if test $ac_header_dirent = dirent.h; then +echo $ac_n "checking for opendir in -ldir""... $ac_c" 1>&6 +echo "configure:8444: checking for opendir in -ldir" >&5 +ac_lib_var=`echo dir'_'opendir | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-ldir $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + LIBS="$LIBS -ldir" +else + echo "$ac_t""no" 1>&6 +fi + +else +echo $ac_n "checking for opendir in -lx""... $ac_c" 1>&6 +echo "configure:8485: checking for opendir in -lx" >&5 +ac_lib_var=`echo x'_'opendir | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lx $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + LIBS="$LIBS -lx" +else + echo "$ac_t""no" 1>&6 +fi + +fi + +case "$target_os" in +freebsd*) +# for stuff like -lXshm + CPPFLAGS="${CPPFLAGS} ${X_CFLAGS}" + ;; +esac +for ac_hdr in sys/byteorder.h compat.h getopt.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:8536: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:8546: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done + +for ac_hdr in sys/bitypes.h memory.h unistd.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:8576: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:8586: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done + +for ac_hdr in gnu/libc-version.h nl_types.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:8616: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:8626: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done + +for ac_hdr in malloc.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:8656: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:8666: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done + +for ac_hdr in X11/XKBlib.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:8696: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:8706: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done + + +for ac_hdr in sys/statvfs.h sys/statfs.h sys/vfs.h sys/mount.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:8737: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:8747: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done + + +for ac_hdr in mmintrin.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:8778: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:8788: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done + + +ac_ext=C +# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cxx_cross + +NEW_H=new.h +ac_safe=`echo "new" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for new""... $ac_c" 1>&6 +echo "configure:8825: checking for new" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:8835: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + NEW_H=new +else + echo "$ac_t""no" 1>&6 +fi + +cat >> confdefs.h < +EOF + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + + +# Check whether --enable-dtrace or --disable-dtrace was given. +if test "${enable_dtrace+set}" = set; then + enableval="$enable_dtrace" + enable_dtrace="yes" +fi + +if test "x$enable_dtrace" = "xyes"; then + ac_safe=`echo "sys/sdt.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for sys/sdt.h""... $ac_c" 1>&6 +echo "configure:8877: checking for sys/sdt.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:8887: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + HAVE_DTRACE=1 +else + echo "$ac_t""no" 1>&6 +fi + + if test -n "$HAVE_DTRACE"; then + cat >> confdefs.h <<\EOF +#define INCLUDE_MOZILLA_DTRACE 1 +EOF + + else + { echo "configure: error: dtrace enabled but sys/sdt.h not found" 1>&2; exit 1; }; + fi +fi + + +case $target in +*-aix4.3*|*-aix5*) + ;; +*) + for ac_hdr in sys/cdefs.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:8927: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:8937: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done + + ;; +esac + +case $target in +*-hpux11.*) + ;; +*) + echo $ac_n "checking for gethostbyname_r in -lc_r""... $ac_c" 1>&6 +echo "configure:8971: checking for gethostbyname_r in -lc_r" >&5 +ac_lib_var=`echo c_r'_'gethostbyname_r | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lc_r $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo c_r | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +fi + + ;; +esac + +case $target in +*-darwin*) + ;; +*-beos*) + ;; +*-os2*) + ;; +*) + echo $ac_n "checking for atan in -lm""... $ac_c" 1>&6 +echo "configure:9029: checking for atan in -lm" >&5 +ac_lib_var=`echo m'_'atan | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lm $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo m | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +fi + + echo $ac_n "checking for dlopen in -ldl""... $ac_c" 1>&6 +echo "configure:9076: checking for dlopen in -ldl" >&5 +ac_lib_var=`echo dl'_'dlopen | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-ldl $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_safe=`echo "dlfcn.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for dlfcn.h""... $ac_c" 1>&6 +echo "configure:9112: checking for dlfcn.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:9122: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + LIBS="-ldl $LIBS" + cat >> confdefs.h <<\EOF +#define HAVE_LIBDL 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi + +else + echo "$ac_t""no" 1>&6 +fi + + ;; +esac + +_SAVE_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS -D_GNU_SOURCE" +for ac_func in dladdr +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:9159: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:9187: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <&6 +fi +done + +CFLAGS="$_SAVE_CFLAGS" + +if test ! "$GNU_CXX"; then + + case $target in + *-aix*) + echo $ac_n "checking for demangle in -lC_r""... $ac_c" 1>&6 +echo "configure:9218: checking for demangle in -lC_r" >&5 +ac_lib_var=`echo C_r'_'demangle | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lC_r $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo C_r | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +fi + + ;; + *) + echo $ac_n "checking for demangle in -lC""... $ac_c" 1>&6 +echo "configure:9267: checking for demangle in -lC" >&5 +ac_lib_var=`echo C'_'demangle | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lC $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo C | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +fi + + ;; + esac +fi + +case $target in +*-os2*) + ;; +*) + echo $ac_n "checking for socket in -lsocket""... $ac_c" 1>&6 +echo "configure:9322: checking for socket in -lsocket" >&5 +ac_lib_var=`echo socket'_'socket | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lsocket $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo socket | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +fi + +esac + +case "$target_os" in +darwin*) + USE_PTHREADS=1 + ;; +*) + +echo $ac_n "checking for pthread_create in -lpthreads""... $ac_c" 1>&6 +echo "configure:9377: checking for pthread_create in -lpthreads" >&5 +echo " + #include + #include + void *foo(void *v) { int a = 1; } + int main() { + pthread_t t; + if (!pthread_create(&t, 0, &foo, 0)) { + pthread_join(t, 0); + } + exit(0); + }" > dummy.c ; + echo "${CC-cc} -o dummy${ac_exeext} dummy.c $CFLAGS $CPPFLAGS -lpthreads $LDFLAGS $LIBS" 1>&5; + ${CC-cc} -o dummy${ac_exeext} dummy.c $CFLAGS $CPPFLAGS -lpthreads $LDFLAGS $LIBS 2>&5; + _res=$? ; + rm -f dummy.c dummy${ac_exeext} ; + if test "$_res" = "0"; then + echo "$ac_t""yes" 1>&6 + USE_PTHREADS=1 _PTHREAD_LDFLAGS="-lpthreads" + else + echo "$ac_t""no" 1>&6 + +echo $ac_n "checking for pthread_create in -lpthread""... $ac_c" 1>&6 +echo "configure:9400: checking for pthread_create in -lpthread" >&5 +echo " + #include + #include + void *foo(void *v) { int a = 1; } + int main() { + pthread_t t; + if (!pthread_create(&t, 0, &foo, 0)) { + pthread_join(t, 0); + } + exit(0); + }" > dummy.c ; + echo "${CC-cc} -o dummy${ac_exeext} dummy.c $CFLAGS $CPPFLAGS -lpthread $LDFLAGS $LIBS" 1>&5; + ${CC-cc} -o dummy${ac_exeext} dummy.c $CFLAGS $CPPFLAGS -lpthread $LDFLAGS $LIBS 2>&5; + _res=$? ; + rm -f dummy.c dummy${ac_exeext} ; + if test "$_res" = "0"; then + echo "$ac_t""yes" 1>&6 + USE_PTHREADS=1 _PTHREAD_LDFLAGS="-lpthread" + else + echo "$ac_t""no" 1>&6 + +echo $ac_n "checking for pthread_create in -lc_r""... $ac_c" 1>&6 +echo "configure:9423: checking for pthread_create in -lc_r" >&5 +echo " + #include + #include + void *foo(void *v) { int a = 1; } + int main() { + pthread_t t; + if (!pthread_create(&t, 0, &foo, 0)) { + pthread_join(t, 0); + } + exit(0); + }" > dummy.c ; + echo "${CC-cc} -o dummy${ac_exeext} dummy.c $CFLAGS $CPPFLAGS -lc_r $LDFLAGS $LIBS" 1>&5; + ${CC-cc} -o dummy${ac_exeext} dummy.c $CFLAGS $CPPFLAGS -lc_r $LDFLAGS $LIBS 2>&5; + _res=$? ; + rm -f dummy.c dummy${ac_exeext} ; + if test "$_res" = "0"; then + echo "$ac_t""yes" 1>&6 + USE_PTHREADS=1 _PTHREAD_LDFLAGS="-lc_r" + else + echo "$ac_t""no" 1>&6 + +echo $ac_n "checking for pthread_create in -lc""... $ac_c" 1>&6 +echo "configure:9446: checking for pthread_create in -lc" >&5 +echo " + #include + #include + void *foo(void *v) { int a = 1; } + int main() { + pthread_t t; + if (!pthread_create(&t, 0, &foo, 0)) { + pthread_join(t, 0); + } + exit(0); + }" > dummy.c ; + echo "${CC-cc} -o dummy${ac_exeext} dummy.c $CFLAGS $CPPFLAGS -lc $LDFLAGS $LIBS" 1>&5; + ${CC-cc} -o dummy${ac_exeext} dummy.c $CFLAGS $CPPFLAGS -lc $LDFLAGS $LIBS 2>&5; + _res=$? ; + rm -f dummy.c dummy${ac_exeext} ; + if test "$_res" = "0"; then + echo "$ac_t""yes" 1>&6 + USE_PTHREADS=1 + + else + echo "$ac_t""no" 1>&6 + + fi + + + fi + + + fi + + + fi + + ;; +esac + +# Check whether --with-pthreads or --without-pthreads was given. +if test "${with_pthreads+set}" = set; then + withval="$with_pthreads" + if test "$withval" = "yes"; then + if test "$USE_PTHREADS"x = x; then + { echo "configure: error: --with-pthreads specified for a system without pthread support " 1>&2; exit 1; }; +fi + elif test "$withval" = "no"; then + USE_PTHREADS= + _PTHREAD_LDFLAGS= + + else + { echo "configure: error: Option, pthreads, does not take an argument ($withval)." 1>&2; exit 1; } + fi +fi + + +if test "$USE_PTHREADS"x != x +then + rm -f conftest* + ac_cv_have_dash_pthread=no + echo $ac_n "checking whether ${CC-cc} accepts -pthread""... $ac_c" 1>&6 +echo "configure:9505: checking whether ${CC-cc} accepts -pthread" >&5 + echo 'int main() { return 0; }' | cat > conftest.c + ${CC-cc} -pthread -o conftest conftest.c > conftest.out 2>&1 + if test $? -eq 0; then + if test -z "`egrep -i '(unrecognize|unknown)' conftest.out | grep pthread`" && test -z "`egrep -i '(error|incorrect)' conftest.out`" ; then + ac_cv_have_dash_pthread=yes + case "$target_os" in + freebsd*) +# Freebsd doesn't use -pthread for compiles, it uses them for linking + ;; + *) + CFLAGS="$CFLAGS -pthread" + CXXFLAGS="$CXXFLAGS -pthread" + ;; + esac + fi + fi + rm -f conftest* + echo "$ac_t""$ac_cv_have_dash_pthread" 1>&6 + + ac_cv_have_dash_pthreads=no + if test "$ac_cv_have_dash_pthread" = "no"; then + echo $ac_n "checking whether ${CC-cc} accepts -pthreads""... $ac_c" 1>&6 +echo "configure:9528: checking whether ${CC-cc} accepts -pthreads" >&5 + echo 'int main() { return 0; }' | cat > conftest.c + ${CC-cc} -pthreads -o conftest conftest.c > conftest.out 2>&1 + if test $? -eq 0; then + if test -z "`egrep -i '(unrecognize|unknown)' conftest.out | grep pthreads`" && test -z "`egrep -i '(error|incorrect)' conftest.out`" ; then + ac_cv_have_dash_pthreads=yes + CFLAGS="$CFLAGS -pthreads" + CXXFLAGS="$CXXFLAGS -pthreads" + fi + fi + rm -f conftest* + echo "$ac_t""$ac_cv_have_dash_pthreads" 1>&6 + fi + + case "$target" in + *-*-freebsd*) + cat >> confdefs.h <<\EOF +#define _REENTRANT 1 +EOF + + cat >> confdefs.h <<\EOF +#define _THREAD_SAFE 1 +EOF + + if test "$ac_cv_have_dash_pthread" = "yes"; then + _PTHREAD_LDFLAGS="-pthread" + else + _PTHREAD_LDFLAGS="-lc_r" + fi + ;; + + *-*-openbsd*|*-*-bsdi*) + cat >> confdefs.h <<\EOF +#define _REENTRANT 1 +EOF + + cat >> confdefs.h <<\EOF +#define _THREAD_SAFE 1 +EOF + + if test "$ac_cv_have_dash_pthread" = "yes"; then + _PTHREAD_LDFLAGS="-pthread" + fi + ;; + + *-*-linux*|*-*-kfreebsd*-gnu) + cat >> confdefs.h <<\EOF +#define _REENTRANT 1 +EOF + + ;; + + *-*-nto*) + cat >> confdefs.h <<\EOF +#define _REENTRANT 1 +EOF + + ;; + + *-aix4.3*|*-aix5*) + cat >> confdefs.h <<\EOF +#define _REENTRANT 1 +EOF + + ;; + + *-hpux11.*) + cat >> confdefs.h <<\EOF +#define _REENTRANT 1 +EOF + + ;; + + alpha*-*-osf*) + cat >> confdefs.h <<\EOF +#define _REENTRANT 1 +EOF + + ;; + + *-*-solaris*) + cat >> confdefs.h <<\EOF +#define _REENTRANT 1 +EOF + + if test "$SOLARIS_SUNPRO_CC"; then + CFLAGS="$CFLAGS -mt" + CXXFLAGS="$CXXFLAGS -mt" + fi + ;; + esac + LDFLAGS="${_PTHREAD_LDFLAGS} ${LDFLAGS}" +fi + +echo $ac_n "checking whether mmap() sees write()s""... $ac_c" 1>&6 +echo "configure:9623: checking whether mmap() sees write()s" >&5 + + +mmap_test_prog=' + #include + #include + #include + #include + #include + #include + + char fname[] = "conftest.file"; + char zbuff[1024]; /* Fractional page is probably worst case */ + + int main() { + char *map; + int fd; + int i; + unlink(fname); + fd = open(fname, O_RDWR | O_CREAT, 0660); + if(fd<0) return 1; + unlink(fname); + write(fd, zbuff, sizeof(zbuff)); + lseek(fd, 0, SEEK_SET); + map = (char*)mmap(0, sizeof(zbuff), PROT_READ, MAP_SHARED, fd, 0); + if(map==(char*)-1) return 2; + for(i=0; fname[i]; i++) { + int rc = write(fd, &fname[i], 1); + if(map[i]!=fname[i]) return 4; + } + return 0; + } +' + + +if test "$cross_compiling" = yes; then + result="yes" +else + cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + result="yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + result="no" +fi +rm -fr conftest* +fi + + +echo "$ac_t"""$result"" 1>&6 + +if test "$result" = "no"; then + cat >> confdefs.h <<\EOF +#define MMAP_MISSES_WRITES 1 +EOF + +fi + + +if test $ac_cv_prog_gcc = yes; then + echo $ac_n "checking whether ${CC-cc} needs -traditional""... $ac_c" 1>&6 +echo "configure:9691: checking whether ${CC-cc} needs -traditional" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gcc_traditional'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_pattern="Autoconf.*'x'" + cat > conftest.$ac_ext < +Autoconf TIOCGETP +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "$ac_pattern" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_prog_gcc_traditional=yes +else + rm -rf conftest* + ac_cv_prog_gcc_traditional=no +fi +rm -f conftest* + + + if test $ac_cv_prog_gcc_traditional = no; then + cat > conftest.$ac_ext < +Autoconf TCGETA +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "$ac_pattern" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_prog_gcc_traditional=yes +fi +rm -f conftest* + + fi +fi + +echo "$ac_t""$ac_cv_prog_gcc_traditional" 1>&6 + if test $ac_cv_prog_gcc_traditional = yes; then + CC="$CC -traditional" + fi +fi + +echo $ac_n "checking for 8-bit clean memcmp""... $ac_c" 1>&6 +echo "configure:9737: checking for 8-bit clean memcmp" >&5 +if eval "test \"`echo '$''{'ac_cv_func_memcmp_clean'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_memcmp_clean=no +else + cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + ac_cv_func_memcmp_clean=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_func_memcmp_clean=no +fi +rm -fr conftest* +fi + +fi + +echo "$ac_t""$ac_cv_func_memcmp_clean" 1>&6 +test $ac_cv_func_memcmp_clean = no && LIBOBJS="$LIBOBJS memcmp.${ac_objext}" + + +for ac_func in fchmod flockfile getc_unlocked _getc_nolock getpagesize \ + lchown localtime_r lstat64 memmove random rint sbrk snprintf \ + stat64 statvfs statvfs64 strerror strtok_r truncate64 +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:9778: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:9806: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <&6 +fi +done + + +cat > conftest.$ac_ext < +int main() { +SYSTEMTIME st;FILETIME ft;SystemTimeToFileTime(&st,&ft); +; return 0; } +EOF +if { (eval echo configure:9839: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + ac_cv_have_systemtimetofiletime="yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_have_systemtimetofiletime="no" +fi +rm -f conftest* +if test "$ac_cv_have_systemtimetofiletime" = "yes"; then + cat >> confdefs.h <<\EOF +#define HAVE_SYSTEMTIMETOFILETIME 1 +EOF + +fi +cat > conftest.$ac_ext < +int main() { +FILETIME ft;GetSystemTimeAsFileTime(&ft); +; return 0; } +EOF +if { (eval echo configure:9863: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + ac_cv_have_getsystemtimeasfiletime="yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_have_getsystemtimeasfiletime="no" +fi +rm -f conftest* +if test "$ac_cv_have_getsystemtimeasfiletime" = "yes"; then + cat >> confdefs.h <<\EOF +#define HAVE_GETSYSTEMTIMEASFILETIME 1 +EOF + +fi + +if test -z "$MACOS_DEPLOYMENT_TARGET" || test "$MACOS_DEPLOYMENT_TARGET" -ge "100300"; then + +ac_ext=C +# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cxx_cross + +echo $ac_n "checking for wcrtomb""... $ac_c" 1>&6 +echo "configure:9890: checking for wcrtomb" >&5 +if eval "test \"`echo '$''{'ac_cv_have_wcrtomb'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +int main() { +mbstate_t ps={0};wcrtomb(0,'f',&ps); +; return 0; } +EOF +if { (eval echo configure:9902: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + ac_cv_have_wcrtomb="yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_have_wcrtomb="no" +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_have_wcrtomb" 1>&6 +if test "$ac_cv_have_wcrtomb" = "yes"; then + cat >> confdefs.h <<\EOF +#define HAVE_WCRTOMB 1 +EOF + +fi +echo $ac_n "checking for mbrtowc""... $ac_c" 1>&6 +echo "configure:9922: checking for mbrtowc" >&5 +if eval "test \"`echo '$''{'ac_cv_have_mbrtowc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +int main() { +mbstate_t ps={0};mbrtowc(0,0,0,&ps); +; return 0; } +EOF +if { (eval echo configure:9934: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + ac_cv_have_mbrtowc="yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_have_mbrtowc="no" +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_have_mbrtowc" 1>&6 +if test "$ac_cv_have_mbrtowc" = "yes"; then + cat >> confdefs.h <<\EOF +#define HAVE_MBRTOWC 1 +EOF + +fi +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +fi + +echo $ac_n "checking for res_ninit()""... $ac_c" 1>&6 +echo "configure:9963: checking for res_ninit()" >&5 +if eval "test \"`echo '$''{'ac_cv_func_res_ninit'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < + +int main() { +int foo = res_ninit(&_res); +; return 0; } +EOF +if { (eval echo configure:9980: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + ac_cv_func_res_ninit=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_func_res_ninit=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_func_res_ninit" 1>&6 + +if test "$ac_cv_func_res_ninit" = "yes"; then + cat >> confdefs.h <<\EOF +#define HAVE_RES_NINIT 1 +EOF + +fi + +ac_ext=C +# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cxx_cross + +echo $ac_n "checking for gnu_get_libc_version()""... $ac_c" 1>&6 +echo "configure:10010: checking for gnu_get_libc_version()" >&5 +if eval "test \"`echo '$''{'ac_cv_func_gnu_get_libc_version'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < + #endif + +int main() { +const char *glibc_version = gnu_get_libc_version(); +; return 0; } +EOF +if { (eval echo configure:10026: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + ac_cv_func_gnu_get_libc_version=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_func_gnu_get_libc_version=no + +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_func_gnu_get_libc_version" 1>&6 + +if test "$ac_cv_func_gnu_get_libc_version" = "yes"; then + cat >> confdefs.h <<\EOF +#define HAVE_GNU_GET_LIBC_VERSION 1 +EOF + +fi + +case $target_os in + os2*|msvc*|mks*|cygwin*|mingw*|darwin*|wince*|beos*) + ;; + *) + +echo $ac_n "checking for iconv in -lc""... $ac_c" 1>&6 +echo "configure:10055: checking for iconv in -lc" >&5 +ac_lib_var=`echo c'_'iconv | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lc $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + _ICONV_LIBS="$_ICONV_LIBS" +else + echo "$ac_t""no" 1>&6 +echo $ac_n "checking for iconv in -liconv""... $ac_c" 1>&6 +echo "configure:10096: checking for iconv in -liconv" >&5 +ac_lib_var=`echo iconv'_'iconv | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-liconv $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + _ICONV_LIBS="$_ICONV_LIBS -liconv" +else + echo "$ac_t""no" 1>&6 +echo $ac_n "checking for libiconv in -liconv""... $ac_c" 1>&6 +echo "configure:10137: checking for libiconv in -liconv" >&5 +ac_lib_var=`echo iconv'_'libiconv | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-liconv $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + _ICONV_LIBS="$_ICONV_LIBS -liconv" +else + echo "$ac_t""no" 1>&6 +fi + +fi + +fi + +_SAVE_LIBS=$LIBS +LIBS="$LIBS $_ICONV_LIBS" +echo $ac_n "checking for iconv()""... $ac_c" 1>&6 +echo "configure:10186: checking for iconv()" >&5 +if eval "test \"`echo '$''{'ac_cv_func_iconv'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < + #include + +int main() { + + iconv_t h = iconv_open("", ""); + iconv(h, NULL, NULL, NULL, NULL); + iconv_close(h); + +; return 0; } +EOF +if { (eval echo configure:10205: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + ac_cv_func_iconv=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_func_iconv=no + +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_func_iconv" 1>&6 +if test "$ac_cv_func_iconv" = "yes"; then + cat >> confdefs.h <<\EOF +#define HAVE_ICONV 1 +EOF + + DYNAMIC_XPCOM_LIBS="$DYNAMIC_XPCOM_LIBS $_ICONV_LIBS" + LIBXUL_LIBS="$LIBXUL_LIBS $_ICONV_LIBS" + LIBICONV="$_ICONV_LIBS" + echo $ac_n "checking for iconv() with const input""... $ac_c" 1>&6 +echo "configure:10229: checking for iconv() with const input" >&5 +if eval "test \"`echo '$''{'ac_cv_func_const_iconv'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < + #include + +int main() { + + const char *input = "testing"; + iconv_t h = iconv_open("", ""); + iconv(h, &input, NULL, NULL, NULL); + iconv_close(h); + +; return 0; } +EOF +if { (eval echo configure:10249: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_func_const_iconv=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_func_const_iconv=no + +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_func_const_iconv" 1>&6 + if test "$ac_cv_func_const_iconv" = "yes"; then + cat >> confdefs.h <<\EOF +#define HAVE_ICONV_WITH_CONST_INPUT 1 +EOF + + fi +fi +LIBS=$_SAVE_LIBS + + ;; +esac + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + + +echo $ac_n "checking for an implementation of va_copy()""... $ac_c" 1>&6 +echo "configure:10285: checking for an implementation of va_copy()" >&5 +if eval "test \"`echo '$''{'ac_cv_va_copy'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + if test "$cross_compiling" = yes; then + ac_cv_va_copy=no + +else + cat > conftest.$ac_ext < + void f (int i, ...) { + va_list args1, args2; + va_start (args1, i); + va_copy (args2, args1); + if (va_arg (args2, int) != 42 || va_arg (args1, int) != 42) + exit (1); + va_end (args1); va_end (args2); + } + int main() { f (0, 42); return 0; } +EOF +if { (eval echo configure:10309: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + ac_cv_va_copy=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_va_copy=no +fi +rm -fr conftest* +fi + + +fi + +echo "$ac_t""$ac_cv_va_copy" 1>&6 +echo $ac_n "checking for an implementation of __va_copy()""... $ac_c" 1>&6 +echo "configure:10326: checking for an implementation of __va_copy()" >&5 +if eval "test \"`echo '$''{'ac_cv___va_copy'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + if test "$cross_compiling" = yes; then + ac_cv___va_copy=no + +else + cat > conftest.$ac_ext < + void f (int i, ...) { + va_list args1, args2; + va_start (args1, i); + __va_copy (args2, args1); + if (va_arg (args2, int) != 42 || va_arg (args1, int) != 42) + exit (1); + va_end (args1); va_end (args2); + } + int main() { f (0, 42); return 0; } +EOF +if { (eval echo configure:10350: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + ac_cv___va_copy=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv___va_copy=no +fi +rm -fr conftest* +fi + + +fi + +echo "$ac_t""$ac_cv___va_copy" 1>&6 +echo $ac_n "checking whether va_lists can be copied by value""... $ac_c" 1>&6 +echo "configure:10367: checking whether va_lists can be copied by value" >&5 +if eval "test \"`echo '$''{'ac_cv_va_val_copy'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + if test "$cross_compiling" = yes; then + ac_cv_va_val_copy=yes + +else + cat > conftest.$ac_ext < + void f (int i, ...) { + va_list args1, args2; + va_start (args1, i); + args2 = args1; + if (va_arg (args2, int) != 42 || va_arg (args1, int) != 42) + exit (1); + va_end (args1); va_end (args2); + } + int main() { f (0, 42); return 0; } +EOF +if { (eval echo configure:10391: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + ac_cv_va_val_copy=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_va_val_copy=no +fi +rm -fr conftest* +fi + + +fi + +if test "x$ac_cv_va_copy" = "xyes"; then + cat >> confdefs.h <<\EOF +#define VA_COPY va_copy +EOF + + cat >> confdefs.h <<\EOF +#define HAVE_VA_COPY 1 +EOF + +elif test "x$ac_cv___va_copy" = "xyes"; then + cat >> confdefs.h <<\EOF +#define VA_COPY __va_copy +EOF + + cat >> confdefs.h <<\EOF +#define HAVE_VA_COPY 1 +EOF + +fi + +if test "x$ac_cv_va_val_copy" = "xno"; then + cat >> confdefs.h <<\EOF +#define HAVE_VA_LIST_AS_ARRAY 1 +EOF + +fi +echo "$ac_t""$ac_cv_va_val_copy" 1>&6 + +case "$target" in + *-linux*) + _curdir=`pwd` + export _curdir + rm -rf conftest* _conftest + mkdir _conftest + cat >> conftest.C <<\EOF +#include +#include +#include +#ifdef _dl_loaded +void __dump_link_map(void) { + struct link_map *map = _dl_loaded; + while (NULL != map) {printf("0x%08x %s\n", map->l_addr, map->l_name); map = map->l_next;} +} +int main() { + dlopen("./conftest1.so",RTLD_LAZY); + dlopen("./../_conftest/conftest1.so",RTLD_LAZY); + dlopen("CURDIR/_conftest/conftest1.so",RTLD_LAZY); + dlopen("CURDIR/_conftest/../_conftest/conftest1.so",RTLD_LAZY); + __dump_link_map(); +} +#else +/* _dl_loaded isn't defined, so this should be either a libc5 (glibc1) system, or a glibc2 system that doesn't have the multiple load bug (i.e., RH6.0).*/ +int main() { printf("./conftest1.so\n"); } +#endif +EOF + + $PERL -p -i -e "s/CURDIR/\$ENV{_curdir}/g;" conftest.C + + cat >> conftest1.C <<\EOF +#include +void foo(void) {printf("foo in dll called\n");} +EOF + ${CXX-g++} -fPIC -c -g conftest1.C + ${CXX-g++} -shared -Wl,-h -Wl,conftest1.so -o conftest1.so conftest1.o + ${CXX-g++} -g conftest.C -o conftest -ldl + cp -f conftest1.so conftest _conftest + cd _conftest + if test `./conftest | grep conftest1.so | wc -l` -gt 1 + then + echo + echo "*** Your libc has a bug that can result in loading the same dynamic" + echo "*** library multiple times. This bug is known to be fixed in glibc-2.0.7-32" + echo "*** or later. However, if you choose not to upgrade, the only effect" + echo "*** will be excessive memory usage at runtime." + echo + fi + cd ${_curdir} + rm -rf conftest* _conftest + ;; +esac + +if test "$GNU_CXX"; then + + echo $ac_n "checking for C++ exceptions flag""... $ac_c" 1>&6 +echo "configure:10490: checking for C++ exceptions flag" >&5 + + if eval "test \"`echo '$''{'ac_cv_cxx_exceptions_flags'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + echo "int main() { return 0; }" | cat > conftest.C + + ${CXX-g++} ${CXXFLAGS} -c -fno-handle-exceptions conftest.C > conftest.out 2>&1 + + if egrep "warning.*renamed" conftest.out >/dev/null; then + ac_cv_cxx_exceptions_flags=${_COMPILER_PREFIX}-fno-exceptions + else + ac_cv_cxx_exceptions_flags=${_COMPILER_PREFIX}-fno-handle-exceptions + fi + + rm -f conftest* +fi + + + echo "$ac_t""$ac_cv_cxx_exceptions_flags" 1>&6 + _MOZ_EXCEPTIONS_FLAGS_OFF=$ac_cv_cxx_exceptions_flags + _MOZ_EXCEPTIONS_FLAGS_ON=`echo $ac_cv_cxx_exceptions_flags | sed 's|no-||'` +fi + +ac_ext=C +# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cxx_cross + + +HAVE_GCC3_ABI= +if test "$GNU_CC"; then + echo $ac_n "checking for gcc 3.0 ABI""... $ac_c" 1>&6 +echo "configure:10525: checking for gcc 3.0 ABI" >&5 +if eval "test \"`echo '$''{'ac_cv_gcc_three_abi'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <= 100 /* G++ V3 ABI */ + return 0; +#else +#error Not gcc3. +#endif + +; return 0; } +EOF +if { (eval echo configure:10543: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_gcc_three_abi="yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_gcc_three_abi="no" +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_gcc_three_abi" 1>&6 + if test "$ac_cv_gcc_three_abi" = "yes"; then + TARGET_COMPILER_ABI="${TARGET_COMPILER_ABI-gcc3}" + HAVE_GCC3_ABI=1 + else + TARGET_COMPILER_ABI="${TARGET_COMPILER_ABI-gcc2}" + fi +fi + + + +echo $ac_n "checking for C++ \"explicit\" keyword""... $ac_c" 1>&6 +echo "configure:10567: checking for C++ \"explicit\" keyword" >&5 +if eval "test \"`echo '$''{'ac_cv_cpp_explicit'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_cpp_explicit=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_cpp_explicit=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_cpp_explicit" 1>&6 +if test "$ac_cv_cpp_explicit" = yes ; then + cat >> confdefs.h <<\EOF +#define HAVE_CPP_EXPLICIT 1 +EOF + +fi + +echo $ac_n "checking for C++ \"typename\" keyword""... $ac_c" 1>&6 +echo "configure:10603: checking for C++ \"typename\" keyword" >&5 +if eval "test \"`echo '$''{'ac_cv_cpp_typename'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < class tplt { + public: + typedef typename T::num_type t_num_type; + t_num_type foo(typename T::num_type num) { + return num; + } + }; +int main() { +tplt A; + A.foo(0); +; return 0; } +EOF +if { (eval echo configure:10627: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_cpp_typename=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_cpp_typename=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_cpp_typename" 1>&6 +if test "$ac_cv_cpp_typename" = yes ; then + cat >> confdefs.h <<\EOF +#define HAVE_CPP_TYPENAME 1 +EOF + +fi + +echo $ac_n "checking for modern C++ template specialization syntax support""... $ac_c" 1>&6 +echo "configure:10648: checking for modern C++ template specialization syntax support" >&5 +if eval "test \"`echo '$''{'ac_cv_cpp_modern_specialize_template_syntax'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < struct X { int a; }; + class Y {}; + template <> struct X { double a; }; +int main() { +X int_x; + X y_x; +; return 0; } +EOF +if { (eval echo configure:10663: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_cpp_modern_specialize_template_syntax=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_cpp_modern_specialize_template_syntax=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_cpp_modern_specialize_template_syntax" 1>&6 +if test "$ac_cv_cpp_modern_specialize_template_syntax" = yes ; then + cat >> confdefs.h <<\EOF +#define HAVE_CPP_MODERN_SPECIALIZE_TEMPLATE_SYNTAX 1 +EOF + +fi + + +echo $ac_n "checking whether partial template specialization works""... $ac_c" 1>&6 +echo "configure:10685: checking whether partial template specialization works" >&5 +if eval "test \"`echo '$''{'ac_cv_cpp_partial_specialization'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < class Foo {}; + template class Foo {}; +int main() { +return 0; +; return 0; } +EOF +if { (eval echo configure:10698: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_cpp_partial_specialization=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_cpp_partial_specialization=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_cpp_partial_specialization" 1>&6 +if test "$ac_cv_cpp_partial_specialization" = yes ; then + cat >> confdefs.h <<\EOF +#define HAVE_CPP_PARTIAL_SPECIALIZATION 1 +EOF + +fi + +echo $ac_n "checking whether operators must be re-defined for templates derived from templates""... $ac_c" 1>&6 +echo "configure:10719: checking whether operators must be re-defined for templates derived from templates" >&5 +if eval "test \"`echo '$''{'ac_cv_need_derived_template_operators'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < class Base { }; + template + Base operator+(const Base& lhs, const Base& rhs) { return lhs; } + template class Derived : public Base { }; +int main() { +Derived a, b; + Base c = a + b; + return 0; +; return 0; } +EOF +if { (eval echo configure:10736: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_need_derived_template_operators=no +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_need_derived_template_operators=yes +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_need_derived_template_operators" 1>&6 +if test "$ac_cv_need_derived_template_operators" = yes ; then + cat >> confdefs.h <<\EOF +#define NEED_CPP_DERIVED_TEMPLATE_OPERATORS 1 +EOF + +fi + + +echo $ac_n "checking whether we need to cast a derived template to pass as its base class""... $ac_c" 1>&6 +echo "configure:10758: checking whether we need to cast a derived template to pass as its base class" >&5 +if eval "test \"`echo '$''{'ac_cv_need_cpp_template_cast_to_base'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < class Base { }; + template class Derived : public Base { }; + template int foo(const Base&) { return 0; } +int main() { +Derived bar; return foo(bar); +; return 0; } +EOF +if { (eval echo configure:10772: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_need_cpp_template_cast_to_base=no +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_need_cpp_template_cast_to_base=yes +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_need_cpp_template_cast_to_base" 1>&6 +if test "$ac_cv_need_cpp_template_cast_to_base" = yes ; then + cat >> confdefs.h <<\EOF +#define NEED_CPP_TEMPLATE_CAST_TO_BASE 1 +EOF + +fi + +echo $ac_n "checking whether the compiler can resolve const ambiguities for templates""... $ac_c" 1>&6 +echo "configure:10793: checking whether the compiler can resolve const ambiguities for templates" >&5 +if eval "test \"`echo '$''{'ac_cv_can_resolve_const_ambiguity'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < class ptrClass { + public: T* ptr; + }; + + template T* a(ptrClass *arg) { + return arg->ptr; + } + + template + const T* a(const ptrClass *arg) { + return arg->ptr; + } + +int main() { + ptrClass i; + a(&i); +; return 0; } +EOF +if { (eval echo configure:10819: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_can_resolve_const_ambiguity=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_can_resolve_const_ambiguity=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_can_resolve_const_ambiguity" 1>&6 +if test "$ac_cv_can_resolve_const_ambiguity" = no ; then + cat >> confdefs.h <<\EOF +#define CANT_RESOLVE_CPP_CONST_AMBIGUITY 1 +EOF + +fi + + +echo $ac_n "checking whether the C++ \"using\" keyword can change access""... $ac_c" 1>&6 +echo "configure:10841: checking whether the C++ \"using\" keyword can change access" >&5 +if eval "test \"`echo '$''{'ac_cv_cpp_access_changing_using2'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_cpp_access_changing_using2=no +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_cpp_access_changing_using2=yes +fi +rm -f conftest* +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_cpp_access_changing_using2=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_cpp_access_changing_using2" 1>&6 +if test "$ac_cv_cpp_access_changing_using2" = yes ; then + cat >> confdefs.h <<\EOF +#define HAVE_CPP_ACCESS_CHANGING_USING 1 +EOF + +fi + +echo $ac_n "checking whether the C++ \"using\" keyword resolves ambiguity""... $ac_c" 1>&6 +echo "configure:10893: checking whether the C++ \"using\" keyword resolves ambiguity" >&5 +if eval "test \"`echo '$''{'ac_cv_cpp_ambiguity_resolving_using'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_cpp_ambiguity_resolving_using=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_cpp_ambiguity_resolving_using=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_cpp_ambiguity_resolving_using" 1>&6 +if test "$ac_cv_cpp_ambiguity_resolving_using" = yes ; then + cat >> confdefs.h <<\EOF +#define HAVE_CPP_AMBIGUITY_RESOLVING_USING 1 +EOF + +fi + +echo $ac_n "checking for \"std::\" namespace""... $ac_c" 1>&6 +echo "configure:10935: checking for \"std::\" namespace" >&5 +if eval "test \"`echo '$''{'ac_cv_cpp_namespace_std'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +int main() { +return std::min(0, 1); +; return 0; } +EOF +if { (eval echo configure:10947: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_cpp_namespace_std=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_cpp_namespace_std=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_cpp_namespace_std" 1>&6 +if test "$ac_cv_cpp_namespace_std" = yes ; then + cat >> confdefs.h <<\EOF +#define HAVE_CPP_NAMESPACE_STD 1 +EOF + +fi + +echo $ac_n "checking whether standard template operator!=() is ambiguous""... $ac_c" 1>&6 +echo "configure:10968: checking whether standard template operator!=() is ambiguous" >&5 +if eval "test \"`echo '$''{'ac_cv_cpp_unambiguous_std_notequal'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < + struct T1 {}; + int operator==(const T1&, const T1&) { return 0; } + int operator!=(const T1&, const T1&) { return 0; } +int main() { +T1 a,b; return a != b; +; return 0; } +EOF +if { (eval echo configure:10983: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_cpp_unambiguous_std_notequal=unambiguous +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_cpp_unambiguous_std_notequal=ambiguous +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_cpp_unambiguous_std_notequal" 1>&6 +if test "$ac_cv_cpp_unambiguous_std_notequal" = unambiguous ; then + cat >> confdefs.h <<\EOF +#define HAVE_CPP_UNAMBIGUOUS_STD_NOTEQUAL 1 +EOF + +fi + + +echo $ac_n "checking for C++ reinterpret_cast""... $ac_c" 1>&6 +echo "configure:11005: checking for C++ reinterpret_cast" >&5 +if eval "test \"`echo '$''{'ac_cv_cpp_reinterpret_cast'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <(z); +; return 0; } +EOF +if { (eval echo configure:11018: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_cpp_reinterpret_cast=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_cpp_reinterpret_cast=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_cpp_reinterpret_cast" 1>&6 +if test "$ac_cv_cpp_reinterpret_cast" = yes ; then + cat >> confdefs.h <<\EOF +#define HAVE_CPP_NEW_CASTS 1 +EOF + +fi + +echo $ac_n "checking for C++ dynamic_cast to void*""... $ac_c" 1>&6 +echo "configure:11039: checking for C++ dynamic_cast to void*" >&5 +if eval "test \"`echo '$''{'ac_cv_cpp_dynamic_cast_void_ptr'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + ac_cv_cpp_dynamic_cast_void_ptr=no +else + cat > conftest.$ac_ext <(subx))) || + (((void*)&mdo != (void*)suby) && + ((void*)&mdo == dynamic_cast(suby)))); + } +EOF +if { (eval echo configure:11066: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + ac_cv_cpp_dynamic_cast_void_ptr=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_cpp_dynamic_cast_void_ptr=no +fi +rm -fr conftest* +fi + +fi + +echo "$ac_t""$ac_cv_cpp_dynamic_cast_void_ptr" 1>&6 +if test "$ac_cv_cpp_dynamic_cast_void_ptr" = yes ; then + cat >> confdefs.h <<\EOF +#define HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR 1 +EOF + +fi + + +echo $ac_n "checking whether C++ requires implementation of unused virtual methods""... $ac_c" 1>&6 +echo "configure:11090: checking whether C++ requires implementation of unused virtual methods" >&5 +if eval "test \"`echo '$''{'ac_cv_cpp_unused_required'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + ac_cv_cpp_unused_required=no +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_cpp_unused_required=yes +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_cpp_unused_required" 1>&6 +if test "$ac_cv_cpp_unused_required" = yes ; then + cat >> confdefs.h <<\EOF +#define NEED_CPP_UNUSED_IMPLEMENTATIONS 1 +EOF + +fi + + + +echo $ac_n "checking for trouble comparing to zero near std::operator!=()""... $ac_c" 1>&6 +echo "configure:11125: checking for trouble comparing to zero near std::operator!=()" >&5 +if eval "test \"`echo '$''{'ac_cv_trouble_comparing_to_zero'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < + template class Foo {}; + class T2; + template int operator==(const T2*, const T&) { return 0; } + template int operator!=(const T2*, const T&) { return 0; } +int main() { +Foo f; return (0 != f); +; return 0; } +EOF +if { (eval echo configure:11141: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_trouble_comparing_to_zero=no +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_trouble_comparing_to_zero=yes +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_trouble_comparing_to_zero" 1>&6 +if test "$ac_cv_trouble_comparing_to_zero" = yes ; then + cat >> confdefs.h <<\EOF +#define HAVE_CPP_TROUBLE_COMPARING_TO_ZERO 1 +EOF + +fi + + + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + + + +echo $ac_n "checking for LC_MESSAGES""... $ac_c" 1>&6 +echo "configure:11173: checking for LC_MESSAGES" >&5 +if eval "test \"`echo '$''{'ac_cv_i18n_lc_messages'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +int main() { +int category = LC_MESSAGES; +; return 0; } +EOF +if { (eval echo configure:11185: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_i18n_lc_messages=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_i18n_lc_messages=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_i18n_lc_messages" 1>&6 +if test "$ac_cv_i18n_lc_messages" = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_I18N_LC_MESSAGES 1 +EOF + +fi + +fi # SKIP_COMPILER_CHECKS + +TARGET_XPCOM_ABI= +if test -n "${CPU_ARCH}" -a -n "${TARGET_COMPILER_ABI}"; then + TARGET_XPCOM_ABI="${CPU_ARCH}-${TARGET_COMPILER_ABI}" +fi + + + +# External Packages + +# Check whether --with-system-nspr or --without-system-nspr was given. +if test "${with_system_nspr+set}" = set; then + withval="$with_system_nspr" + if test "$withval" = "yes"; then + _USE_SYSTEM_NSPR=1 + elif test "$withval" = "no"; then + : + else + { echo "configure: error: Option, system-nspr, does not take an argument ($withval)." 1>&2; exit 1; } + fi +fi + + +# Check whether --with-nspr-cflags or --without-nspr-cflags was given. +if test "${with_nspr_cflags+set}" = set; then + withval="$with_nspr_cflags" + NSPR_CFLAGS=$withval +fi + +# Check whether --with-nspr-libs or --without-nspr-libs was given. +if test "${with_nspr_libs+set}" = set; then + withval="$with_nspr_libs" + NSPR_LIBS=$withval +fi + + + + +if test "$_USE_SYSTEM_NSPR" && (test "$NSPR_CFLAGS" || test "$NSPR_LIBS"); then + { echo "configure: error: --with-system-nspr and --with-nspr-libs/cflags are mutually exclusive. +See 'configure --help'." 1>&2; exit 1; } +fi + +if test -n "$_USE_SYSTEM_NSPR"; then + MOZ_NATIVE_NSPR= + +# Check whether --with-nspr-prefix or --without-nspr-prefix was given. +if test "${with_nspr_prefix+set}" = set; then + withval="$with_nspr_prefix" + nspr_config_prefix="$withval" +else + nspr_config_prefix="" +fi + + +# Check whether --with-nspr-exec-prefix or --without-nspr-exec-prefix was given. +if test "${with_nspr_exec_prefix+set}" = set; then + withval="$with_nspr_exec_prefix" + nspr_config_exec_prefix="$withval" +else + nspr_config_exec_prefix="" +fi + + + if test -n "$nspr_config_exec_prefix"; then + nspr_config_args="$nspr_config_args --exec-prefix=$nspr_config_exec_prefix" + if test -z "$NSPR_CONFIG"; then + NSPR_CONFIG=$nspr_config_exec_prefix/bin/nspr-config + fi + fi + if test -n "$nspr_config_prefix"; then + nspr_config_args="$nspr_config_args --prefix=$nspr_config_prefix" + if test -z "$NSPR_CONFIG"; then + NSPR_CONFIG=$nspr_config_prefix/bin/nspr-config + fi + fi + + unset ac_cv_path_NSPR_CONFIG + # Extract the first word of "nspr-config", so it can be a program name with args. +set dummy nspr-config; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:11287: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_path_NSPR_CONFIG'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + case "$NSPR_CONFIG" in + /*) + ac_cv_path_NSPR_CONFIG="$NSPR_CONFIG" # Let the user override the test with a path. + ;; + ?:/*) + ac_cv_path_NSPR_CONFIG="$NSPR_CONFIG" # Let the user override the test with a dos path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_path_NSPR_CONFIG="$ac_dir/$ac_word" + break + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_path_NSPR_CONFIG" && ac_cv_path_NSPR_CONFIG="no" + ;; +esac +fi +NSPR_CONFIG="$ac_cv_path_NSPR_CONFIG" +if test -n "$NSPR_CONFIG"; then + echo "$ac_t""$NSPR_CONFIG" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + min_nspr_version=4.7.0 + echo $ac_n "checking for NSPR - version >= $min_nspr_version""... $ac_c" 1>&6 +echo "configure:11322: checking for NSPR - version >= $min_nspr_version" >&5 + + no_nspr="" + if test "$NSPR_CONFIG" != "no"; then + NSPR_CFLAGS=`$NSPR_CONFIG $nspr_config_args --cflags` + NSPR_LIBS=`$NSPR_CONFIG $nspr_config_args --libs` + NSPR_VERSION_STRING=`$NSPR_CONFIG $nspr_config_args --version` + elif test -n "${NO_NSPR_CONFIG_SYSTEM_VERSION}"; then + NSPR_CFLAGS="${NO_NSPR_CONFIG_SYSTEM_CFLAGS}" + NSPR_LIBS="${NO_NSPR_CONFIG_SYSTEM_LDFLAGS}" + NSPR_VERSION_STRING="$NO_NSPR_CONFIG_SYSTEM_VERSION" + else + no_nspr="yes" + fi + + if test -z "$no_nspr"; then + nspr_config_major_version=`echo $NSPR_VERSION_STRING | \ + sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\1/'` + nspr_config_minor_version=`echo $NSPR_VERSION_STRING | \ + sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\2/'` + nspr_config_micro_version=`echo $NSPR_VERSION_STRING | \ + sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\3/'` + min_nspr_major_version=`echo $min_nspr_version | \ + sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\1/'` + min_nspr_minor_version=`echo $min_nspr_version | \ + sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\2/'` + min_nspr_micro_version=`echo $min_nspr_version | \ + sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\3/'` + if test "$nspr_config_major_version" -ne "$min_nspr_major_version"; then + no_nspr="yes" + elif test "$nspr_config_major_version" -eq "$min_nspr_major_version" && + test "$nspr_config_minor_version" -lt "$min_nspr_minor_version"; then + no_nspr="yes" + elif test "$nspr_config_major_version" -eq "$min_nspr_major_version" && + test "$nspr_config_minor_version" -eq "$min_nspr_minor_version" && + test "$nspr_config_micro_version" -lt "$min_nspr_micro_version"; then + no_nspr="yes" + fi + fi + + if test -z "$no_nspr"; then + echo "$ac_t""yes" 1>&6 + MOZ_NATIVE_NSPR=1 + else + echo "$ac_t""no" 1>&6 + fi + + + + + + + if test -z "$MOZ_NATIVE_NSPR"; then + { echo "configure: error: --with-system-nspr given, but configure could not find a suitable NSPR. +Pass --with-nspr-exec-prefix, --with-nspr-prefix, or --with-nspr-cflags/libs. +See 'configure --help'." 1>&2; exit 1; } + fi +fi + +if test -n "$MOZ_NATIVE_NSPR"; then + _SAVE_CFLAGS=$CFLAGS + CFLAGS="$CFLAGS $NSPR_CFLAGS" + cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + MOZ_NATIVE_NSPR=1 +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + { echo "configure: error: system NSPR does not support PR_STATIC_ASSERT" 1>&2; exit 1; } +fi +rm -f conftest* + CFLAGS=$_SAVE_CFLAGS +fi + +# Check whether --with-arm-kuser or --without-arm-kuser was given. +if test "${with_arm_kuser+set}" = set; then + withval="$with_arm_kuser" + if test "$withval" = "yes"; then + USE_ARM_KUSER=1 + elif test "$withval" = "no"; then + USE_ARM_KUSER= + else + { echo "configure: error: Option, arm-kuser, does not take an argument ($withval)." 1>&2; exit 1; } + fi +fi + +if test -n "$USE_ARM_KUSER"; then + cat >> confdefs.h <<\EOF +#define USE_ARM_KUSER 1 +EOF + +fi + + +# Application + +BUILD_STATIC_LIBS= +ENABLE_TESTS=1 +MOZ_DBGRINFO_MODULES= + +# Components and Features + +# Check whether --enable-ui-locale or --disable-ui-locale was given. +if test "${enable_ui_locale+set}" = set; then + enableval="$enable_ui_locale" + MOZ_UI_LOCALE=$enableval +fi + + + +# Check whether --enable-tests or --disable-tests was given. +if test "${enable_tests+set}" = set; then + enableval="$enable_tests" + if test "$enableval" = "no"; then + ENABLE_TESTS= + elif test "$enableval" = "yes"; then + ENABLE_TESTS=1 + else + { echo "configure: error: Option, tests, does not take an argument ($enableval)." 1>&2; exit 1; } + fi +fi + + +# Individual module options + +# Debugging and Optimizations + +if test -z "$MOZ_DEBUG_FLAGS" +then + case "$target" in + *-irix*) + if test "$GNU_CC"; then + GCC_VERSION=`$CC -v 2>&1 | awk '/version/ { print $3 }'` + case "$GCC_VERSION" in + 2.95.*) + MOZ_DEBUG_FLAGS="" + ;; + *) + MOZ_DEBUG_FLAGS="-g" + ;; + esac + else + MOZ_DEBUG_FLAGS="-g" + fi + ;; + *) + MOZ_DEBUG_FLAGS="-g" + ;; + esac +fi + +# Check whether --enable-debug or --disable-debug was given. +if test "${enable_debug+set}" = set; then + enableval="$enable_debug" + if test "$enableval" != "no"; then + MOZ_DEBUG=1 + if test -n "$enableval" && test "$enableval" != "yes"; then + MOZ_DEBUG_FLAGS=`echo $enableval | sed -e 's|\\\ | |g'` + fi + else + MOZ_DEBUG= + fi +else + MOZ_DEBUG= +fi + + +MOZ_DEBUG_ENABLE_DEFS="-DDEBUG -D_DEBUG" + case "${target_os}" in + beos*) + MOZ_DEBUG_ENABLE_DEFS="$MOZ_DEBUG_ENABLE_DEFS -DDEBUG_${USER}" + ;; + msvc*|mks*|cygwin*|mingw*|os2*|wince*) + MOZ_DEBUG_ENABLE_DEFS="$MOZ_DEBUG_ENABLE_DEFS -DDEBUG_`echo ${USERNAME} | sed -e 's| |_|g'`" + ;; + *) + MOZ_DEBUG_ENABLE_DEFS="$MOZ_DEBUG_ENABLE_DEFS -DDEBUG_`$WHOAMI`" + ;; + esac +MOZ_DEBUG_ENABLE_DEFS="$MOZ_DEBUG_ENABLE_DEFS -DTRACING" + +MOZ_DEBUG_DISABLE_DEFS="-DNDEBUG -DTRIMMED" + +if test -n "$MOZ_DEBUG"; then + echo $ac_n "checking for valid debug flags""... $ac_c" 1>&6 +echo "configure:11518: checking for valid debug flags" >&5 + _SAVE_CFLAGS=$CFLAGS + CFLAGS="$CFLAGS $MOZ_DEBUG_FLAGS" + cat > conftest.$ac_ext < +int main() { +printf("Hello World\n"); +; return 0; } +EOF +if { (eval echo configure:11529: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + _results=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + _results=no +fi +rm -f conftest* + echo "$ac_t""$_results" 1>&6 + if test "$_results" = "no"; then + { echo "configure: error: These compiler flags are invalid: $MOZ_DEBUG_FLAGS" 1>&2; exit 1; } + fi + CFLAGS=$_SAVE_CFLAGS +fi + +if test -z "$MOZ_OPTIMIZE_FLAGS"; then + MOZ_OPTIMIZE_FLAGS="-O" +fi + +# Check whether --enable-optimize or --disable-optimize was given. +if test "${enable_optimize+set}" = set; then + enableval="$enable_optimize" + if test "$enableval" != "no"; then + MOZ_OPTIMIZE=1 + if test -n "$enableval" && test "$enableval" != "yes"; then + MOZ_OPTIMIZE_FLAGS=`echo "$enableval" | sed -e 's|\\\ | |g'` + MOZ_OPTIMIZE=2 + fi +else + MOZ_OPTIMIZE= +fi +else + MOZ_OPTIMIZE=1 +fi + + +if test "$COMPILE_ENVIRONMENT"; then +if test -n "$MOZ_OPTIMIZE"; then + echo $ac_n "checking for valid optimization flags""... $ac_c" 1>&6 +echo "configure:11570: checking for valid optimization flags" >&5 + _SAVE_CFLAGS=$CFLAGS + CFLAGS="$CFLAGS $MOZ_OPTIMIZE_FLAGS" + cat > conftest.$ac_ext < +int main() { +printf("Hello World\n"); +; return 0; } +EOF +if { (eval echo configure:11581: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + _results=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + _results=no +fi +rm -f conftest* + echo "$ac_t""$_results" 1>&6 + if test "$_results" = "no"; then + { echo "configure: error: These compiler flags are invalid: $MOZ_OPTIMIZE_FLAGS" 1>&2; exit 1; } + fi + CFLAGS=$_SAVE_CFLAGS +fi +fi # COMPILE_ENVIRONMENT + + + + + + +# Check whether --enable-debug-modules or --disable-debug-modules was given. +if test "${enable_debug_modules+set}" = set; then + enableval="$enable_debug_modules" + MOZ_DEBUG_MODULES=`echo $enableval| sed 's/,/ /g'` +fi + + +# Check whether --enable-debugger-info-modules or --disable-debugger-info-modules was given. +if test "${enable_debugger_info_modules+set}" = set; then + enableval="$enable_debugger_info_modules" + for i in `echo $enableval | sed 's/,/ /g'`; do + if test "$i" = "no"; then + i="^ALL_MODULES" + fi + if test "$i" = "yes"; then + i="ALL_MODULES" + fi + MOZ_DBGRINFO_MODULES="$i $MOZ_DBGRINFO_MODULES"; + done +fi + + +# Check whether --enable-narcissus or --disable-narcissus was given. +if test "${enable_narcissus+set}" = set; then + enableval="$enable_narcissus" + if test "$enableval" = "yes"; then + NARCISSUS=1 + elif test "$enableval" = "no"; then + NARCISSUS= + else + { echo "configure: error: Option, narcissus, does not take an argument ($enableval)." 1>&2; exit 1; } + fi +fi + +if test -n "$NARCISSUS"; then + cat >> confdefs.h <<\EOF +#define NARCISSUS 1 +EOF + +fi + +NS_TRACE_MALLOC=${MOZ_TRACE_MALLOC} +# Check whether --enable-trace-malloc or --disable-trace-malloc was given. +if test "${enable_trace_malloc+set}" = set; then + enableval="$enable_trace_malloc" + if test "$enableval" = "yes"; then + NS_TRACE_MALLOC=1 + elif test "$enableval" = "no"; then + NS_TRACE_MALLOC= + else + { echo "configure: error: Option, trace-malloc, does not take an argument ($enableval)." 1>&2; exit 1; } + fi +fi + +if test "$NS_TRACE_MALLOC"; then + # Please, Mr. Linker Man, don't take away our symbol names + MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS= + cat >> confdefs.h <<\EOF +#define NS_TRACE_MALLOC 1 +EOF + +fi + + +# Check whether --enable-jemalloc or --disable-jemalloc was given. +if test "${enable_jemalloc+set}" = set; then + enableval="$enable_jemalloc" + if test "$enableval" = "yes"; then + MOZ_MEMORY=1 + elif test "$enableval" = "no"; then + MOZ_MEMORY= + else + { echo "configure: error: Option, jemalloc, does not take an argument ($enableval)." 1>&2; exit 1; } + fi +fi + + +if test "$NS_TRACE_MALLOC"; then + MOZ_MEMORY= +fi + +if test "$MOZ_MEMORY"; then + + if test "$OS_ARCH" = "WINNT"; then + if test -z "$HAVE_64BIT_OS"; then + cat >> confdefs.h <> confdefs.h <&6 +echo "configure:11701: checking size of int *" >&5 +if eval "test \"`echo '$''{'ac_cv_sizeof_int_p'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + ac_cv_sizeof_int_p=4 +else + cat > conftest.$ac_ext < +main() +{ + FILE *f=fopen("conftestval", "w"); + if (!f) exit(1); + fprintf(f, "%d\n", sizeof(int *)); + exit(0); +} +EOF +if { (eval echo configure:11720: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + ac_cv_sizeof_int_p=`cat conftestval` +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_sizeof_int_p=0 +fi +rm -fr conftest* +fi + +fi +echo "$ac_t""$ac_cv_sizeof_int_p" 1>&6 +cat >> confdefs.h <> confdefs.h <> confdefs.h <&2; exit 1; } + ;; + esac + fi + + cat >> confdefs.h <<\EOF +#define MOZ_MEMORY 1 +EOF + + if test "x$MOZ_DEBUG" = "x1"; then + cat >> confdefs.h <<\EOF +#define MOZ_MEMORY_DEBUG 1 +EOF + + fi + case "${target_os}" in + darwin*) + cat >> confdefs.h <<\EOF +#define MOZ_MEMORY_DARWIN 1 +EOF + + ;; + *freebsd*) + cat >> confdefs.h <<\EOF +#define MOZ_MEMORY_BSD 1 +EOF + + ;; + *linux*) + cat >> confdefs.h <<\EOF +#define MOZ_MEMORY_LINUX 1 +EOF + + ;; + netbsd*) + cat >> confdefs.h <<\EOF +#define MOZ_MEMORY_BSD 1 +EOF + + ;; + solaris*) + cat >> confdefs.h <<\EOF +#define MOZ_MEMORY_SOLARIS 1 +EOF + + ;; + msvc*|mks*|cygwin*|mingw*) + cat >> confdefs.h <<\EOF +#define MOZ_MEMORY_WINDOWS 1 +EOF + + # the interesting bits will get passed down in MOZ_MEMORY_LDFLAGS + ;; + *wince) + cat >> confdefs.h <<\EOF +#define MOZ_MEMORY_WINCE 1 +EOF + + cat >> confdefs.h <<\EOF +#define MOZ_MEMORY_WINDOWS 1 +EOF + + ;; + *) + { echo "configure: error: --enable-jemalloc not supported on ${target}" 1>&2; exit 1; } + ;; + esac +fi + + + +# Check whether --enable-wrap-malloc or --disable-wrap-malloc was given. +if test "${enable_wrap_malloc+set}" = set; then + enableval="$enable_wrap_malloc" + if test "$enableval" = "yes"; then + _WRAP_MALLOC=1 + elif test "$enableval" = "no"; then + _WRAP_MALLOC= + else + { echo "configure: error: Option, wrap-malloc, does not take an argument ($enableval)." 1>&2; exit 1; } + fi +fi + + +if test -n "$_WRAP_MALLOC"; then + if test "$GNU_CC"; then + WRAP_MALLOC_CFLAGS="${LDFLAGS} -Wl,--wrap -Wl,malloc -Wl,--wrap -Wl,free -Wl,--wrap -Wl,realloc -Wl,--wrap -Wl,__builtin_new -Wl,--wrap -Wl,__builtin_vec_new -Wl,--wrap -Wl,__builtin_delete -Wl,--wrap -Wl,__builtin_vec_delete -Wl,--wrap -Wl,PR_Free -Wl,--wrap -Wl,PR_Malloc -Wl,--wrap -Wl,PR_Calloc -Wl,--wrap -Wl,PR_Realloc" + MKSHLIB='$(CXX) $(DSO_LDOPTS) $(WRAP_MALLOC_CFLAGS) -o $@' + fi +fi + +# Check whether --with-wrap-malloc or --without-wrap-malloc was given. +if test "${with_wrap_malloc+set}" = set; then + withval="$with_wrap_malloc" + WRAP_MALLOC_LIB=$withval +fi + + +# Check whether --enable-tracevis or --disable-tracevis was given. +if test "${enable_tracevis+set}" = set; then + enableval="$enable_tracevis" + if test "$enableval" = "yes"; then + MOZ_TRACEVIS=1 + elif test "$enableval" = "no"; then + MOZ_TRACEVIS= + else + { echo "configure: error: Option, tracevis, does not take an argument ($enableval)." 1>&2; exit 1; } + fi +fi + +if test -n "$MOZ_TRACEVIS"; then + cat >> confdefs.h <<\EOF +#define MOZ_TRACEVIS 1 +EOF + + if test -z "$ENABLE_JIT"; then + { echo "configure: error: --enable-tracevis is incompatible with --disable-jit" 1>&2; exit 1; } + fi +fi + + +# Check whether --enable-valgrind or --disable-valgrind was given. +if test "${enable_valgrind+set}" = set; then + enableval="$enable_valgrind" + if test "$enableval" = "yes"; then + MOZ_VALGRIND=1 + elif test "$enableval" = "no"; then + MOZ_VALGRIND= + else + { echo "configure: error: Option, valgrind, does not take an argument ($enableval)." 1>&2; exit 1; } + fi +fi + +if test -n "$MOZ_VALGRIND"; then + ac_safe=`echo "valgrind/valgrind.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for valgrind/valgrind.h""... $ac_c" 1>&6 +echo "configure:11889: checking for valgrind/valgrind.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:11899: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + : +else + echo "$ac_t""no" 1>&6 +{ echo "configure: error: --enable-valgrind specified but Valgrind is not installed" 1>&2; exit 1; } +fi + + cat >> confdefs.h <<\EOF +#define MOZ_VALGRIND 1 +EOF + +fi + +# Check whether --enable-jprof or --disable-jprof was given. +if test "${enable_jprof+set}" = set; then + enableval="$enable_jprof" + if test "$enableval" = "yes"; then + MOZ_JPROF=1 + elif test "$enableval" = "no"; then + MOZ_JPROF= + else + { echo "configure: error: Option, jprof, does not take an argument ($enableval)." 1>&2; exit 1; } + fi +fi + +if test -n "$MOZ_JPROF"; then + cat >> confdefs.h <<\EOF +#define MOZ_JPROF 1 +EOF + +fi + +# Check whether --enable-shark or --disable-shark was given. +if test "${enable_shark+set}" = set; then + enableval="$enable_shark" + if test "$enableval" = "yes"; then + MOZ_SHARK=1 + elif test "$enableval" = "no"; then + MOZ_SHARK= + else + { echo "configure: error: Option, shark, does not take an argument ($enableval)." 1>&2; exit 1; } + fi +fi + +if test -n "$MOZ_SHARK"; then + cat >> confdefs.h <<\EOF +#define MOZ_SHARK 1 +EOF + +fi + +# Check whether --enable-callgrind or --disable-callgrind was given. +if test "${enable_callgrind+set}" = set; then + enableval="$enable_callgrind" + if test "$enableval" = "yes"; then + MOZ_CALLGRIND=1 + elif test "$enableval" = "no"; then + MOZ_CALLGRIND= + else + { echo "configure: error: Option, callgrind, does not take an argument ($enableval)." 1>&2; exit 1; } + fi +fi + +if test -n "$MOZ_CALLGRIND"; then + cat >> confdefs.h <<\EOF +#define MOZ_CALLGRIND 1 +EOF + +fi + +# Check whether --enable-vtune or --disable-vtune was given. +if test "${enable_vtune+set}" = set; then + enableval="$enable_vtune" + if test "$enableval" = "yes"; then + MOZ_VTUNE=1 + elif test "$enableval" = "no"; then + MOZ_VTUNE= + else + { echo "configure: error: Option, vtune, does not take an argument ($enableval)." 1>&2; exit 1; } + fi +fi + +if test -n "$MOZ_VTUNE"; then + cat >> confdefs.h <<\EOF +#define MOZ_VTUNE 1 +EOF + +fi + +# Check whether --enable-gczeal or --disable-gczeal was given. +if test "${enable_gczeal+set}" = set; then + enableval="$enable_gczeal" + if test "$enableval" = "yes"; then + JS_GC_ZEAL=1 + elif test "$enableval" = "no"; then + JS_GC_ZEAL= + else + { echo "configure: error: Option, gczeal, does not take an argument ($enableval)." 1>&2; exit 1; } + fi +fi + +if test -n "$JS_GC_ZEAL"; then + cat >> confdefs.h <<\EOF +#define JS_GC_ZEAL 1 +EOF + +fi + + +# Check whether --with-static-checking or --without-static-checking was given. +if test "${with_static_checking+set}" = set; then + withval="$with_static_checking" + DEHYDRA_PATH=$withval +else + DEHYDRA_PATH= +fi + + +if test -n "$DEHYDRA_PATH"; then + if test ! -f "$DEHYDRA_PATH"; then + { echo "configure: error: The dehydra plugin is not at the specified path." 1>&2; exit 1; } + fi + cat >> confdefs.h <<\EOF +#define NS_STATIC_CHECKING 1 +EOF + +fi + + +# Check whether --enable-strip or --disable-strip was given. +if test "${enable_strip+set}" = set; then + enableval="$enable_strip" + if test "$enableval" = "yes"; then + ENABLE_STRIP=1 + elif test "$enableval" = "no"; then + ENABLE_STRIP= + else + { echo "configure: error: Option, strip, does not take an argument ($enableval)." 1>&2; exit 1; } + fi +fi + + +# Check whether --enable-install-strip or --disable-install-strip was given. +if test "${enable_install_strip+set}" = set; then + enableval="$enable_install_strip" + if test "$enableval" = "yes"; then + PKG_SKIP_STRIP= + elif test "$enableval" = "no"; then + PKG_SKIP_STRIP=1 + else + { echo "configure: error: Option, install-strip, does not take an argument ($enableval)." 1>&2; exit 1; } + fi +fi + + +# Profiling and Instrumenting + +# Check whether --enable-timeline or --disable-timeline was given. +if test "${enable_timeline+set}" = set; then + enableval="$enable_timeline" + if test "$enableval" = "yes"; then + MOZ_TIMELINE=1 + elif test "$enableval" = "no"; then + MOZ_TIMELINE= + else + { echo "configure: error: Option, timeline, does not take an argument ($enableval)." 1>&2; exit 1; } + fi +fi + +if test -n "$MOZ_TIMELINE"; then + cat >> confdefs.h <<\EOF +#define MOZ_TIMELINE 1 +EOF + +fi + +# Check whether --enable-insure or --disable-insure was given. +if test "${enable_insure+set}" = set; then + enableval="$enable_insure" + if test "$enableval" = "yes"; then + _ENABLE_INSURE=1 + elif test "$enableval" = "no"; then + _ENABLE_INSURE= + else + { echo "configure: error: Option, insure, does not take an argument ($enableval)." 1>&2; exit 1; } + fi +fi + +if test -n "$_ENABLE_INSURE"; then + MOZ_INSURE="insure" + MOZ_INSURIFYING=1 + MOZ_INSURE_DIRS="." + MOZ_INSURE_EXCLUDE_DIRS="config" +fi + +# Check whether --with-insure-dirs or --without-insure-dirs was given. +if test "${with_insure_dirs+set}" = set; then + withval="$with_insure_dirs" + MOZ_INSURE_DIRS=$withval +fi + + +# Check whether --with-insure-exclude-dirs or --without-insure-exclude-dirs was given. +if test "${with_insure_exclude_dirs+set}" = set; then + withval="$with_insure_exclude_dirs" + MOZ_INSURE_EXCLUDE_DIRS="config $withval" +fi + + +# Check whether --enable-quantify or --disable-quantify was given. +if test "${enable_quantify+set}" = set; then + enableval="$enable_quantify" + if test "$enableval" = "yes"; then + MOZ_QUANTIFY=1 + elif test "$enableval" = "no"; then + MOZ_QUANTIFY= + else + { echo "configure: error: Option, quantify, does not take an argument ($enableval)." 1>&2; exit 1; } + fi +fi + + +if test -z "$SKIP_LIBRARY_CHECKS"; then + + ac_ext=C +# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cxx_cross + + for ac_func in __cxa_demangle +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:12148: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:12179: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <&6 +HAVE_DEMANGLE= +fi +done + + ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +fi + +# Demangle only for debug or trace-malloc builds +MOZ_DEMANGLE_SYMBOLS= +if test "$HAVE_DEMANGLE" -a "$HAVE_GCC3_ABI" && test "$MOZ_DEBUG" -o "$NS_TRACE_MALLOC"; then + MOZ_DEMANGLE_SYMBOLS=1 + cat >> confdefs.h <<\EOF +#define MOZ_DEMANGLE_SYMBOLS 1 +EOF + +fi + + +if test "$HAVE_GCC3_ABI" && test -z "$SKIP_LIBRARY_CHECKS"; then + ac_safe=`echo "unwind.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for unwind.h""... $ac_c" 1>&6 +echo "configure:12227: checking for unwind.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:12237: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + for ac_func in _Unwind_Backtrace +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:12256: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:12284: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <&6 +fi +done + +else + echo "$ac_t""no" 1>&6 +fi + +fi + +# Misc. Options + +# Check whether --enable-xterm-updates or --disable-xterm-updates was given. +if test "${enable_xterm_updates+set}" = set; then + enableval="$enable_xterm_updates" + if test "$enableval" = "yes"; then + MOZ_UPDATE_XTERM=1 + elif test "$enableval" = "no"; then + MOZ_UPDATE_XTERM= + else + { echo "configure: error: Option, xterm-updates, does not take an argument ($enableval)." 1>&2; exit 1; } + fi +fi + + +if test -z "$SKIP_COMPILER_CHECKS"; then +# Compiler Options + +echo $ac_n "checking for gcc -pipe support""... $ac_c" 1>&6 +echo "configure:12333: checking for gcc -pipe support" >&5 +if test -n "$GNU_CC" && test -n "$GNU_CXX" && test -n "$GNU_AS"; then + echo '#include ' > dummy-hello.c + echo 'int main() { printf("Hello World\n"); exit(0); }' >> dummy-hello.c + ${CC} -S dummy-hello.c -o dummy-hello.s 2>&5 + cat dummy-hello.s 2> /dev/null | ${AS_BIN} -o dummy-hello.S - 2>&5 + if test $? = 0; then + _res_as_stdin="yes" + else + _res_as_stdin="no" + fi + if test "$_res_as_stdin" = "yes"; then + _SAVE_CFLAGS=$CFLAGS + CFLAGS="$CFLAGS -pipe" + cat > conftest.$ac_ext < +int main() { +printf("Hello World\n"); +; return 0; } +EOF +if { (eval echo configure:12355: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + _res_gcc_pipe="yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + _res_gcc_pipe="no" +fi +rm -f conftest* + CFLAGS=$_SAVE_CFLAGS + fi + if test "$_res_as_stdin" = "yes" && test "$_res_gcc_pipe" = "yes"; then + _res="yes"; + CFLAGS="$CFLAGS -pipe" + CXXFLAGS="$CXXFLAGS -pipe" + else + _res="no" + fi + rm -f dummy-hello.c dummy-hello.s dummy-hello.S dummy-hello a.out + echo "$ac_t""$_res" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +# Check whether --enable-long-long-warning or --disable-long-long-warning was given. +if test "${enable_long_long_warning+set}" = set; then + enableval="$enable_long_long_warning" + if test "$enableval" = "yes"; then + _IGNORE_LONG_LONG_WARNINGS= + elif test "$enableval" = "no"; then + _IGNORE_LONG_LONG_WARNINGS=1 + else + { echo "configure: error: Option, long-long-warning, does not take an argument ($enableval)." 1>&2; exit 1; } + fi +fi + + +if test "$_IGNORE_LONG_LONG_WARNINGS"; then + _SAVE_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS ${_COMPILER_PREFIX}-Wno-long-long" + echo $ac_n "checking whether compiler supports -Wno-long-long""... $ac_c" 1>&6 +echo "configure:12397: checking whether compiler supports -Wno-long-long" >&5 + cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} ${_COMPILER_PREFIX}-Wno-long-long" + _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} ${_COMPILER_PREFIX}-Wno-long-long" + result="yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + result="no" +fi +rm -f conftest* + echo "$ac_t""$result" 1>&6 + CFLAGS="$_SAVE_CFLAGS" +fi + + +# Check whether --enable-profile-guided-optimization or --disable-profile-guided-optimization was given. +if test "${enable_profile_guided_optimization+set}" = set; then + enableval="$enable_profile_guided_optimization" + if test "$enableval" = "no"; then + MOZ_PROFILE_GUIDED_OPTIMIZE_DISABLE=1 + elif test "$enableval" = "yes"; then + MOZ_PROFILE_GUIDED_OPTIMIZE_DISABLE= + else + { echo "configure: error: Option, profile-guided-optimization, does not take an argument ($enableval)." 1>&2; exit 1; } + fi +fi + + + + +_SAVE_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS -fprofile-generate" + +echo $ac_n "checking whether C compiler supports -fprofile-generate""... $ac_c" 1>&6 +echo "configure:12442: checking whether C compiler supports -fprofile-generate" >&5 +cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + PROFILE_GEN_CFLAGS="-fprofile-generate" + result="yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + result="no" +fi +rm -f conftest* +echo "$ac_t""$result" 1>&6 + +if test $result = "yes"; then + PROFILE_GEN_LDFLAGS="-fprofile-generate" + PROFILE_USE_CFLAGS="-fprofile-use" + PROFILE_USE_LDFLAGS="-fprofile-use" +else + CFLAGS="$_SAVE_CFLAGS -fprofile-arcs" + echo $ac_n "checking whether C compiler supports -fprofile-arcs""... $ac_c" 1>&6 +echo "configure:12471: checking whether C compiler supports -fprofile-arcs" >&5 + cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + PROFILE_GEN_CFLAGS="-fprofile-arcs" + result="yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + result="no" +fi +rm -f conftest* + echo "$ac_t""$result" 1>&6 + if test $result = "yes"; then + PROFILE_USE_CFLAGS="-fbranch-probabilities" + fi + # don't really care, this is an old GCC + PROFILE_GEN_LDFLAGS= + PROFILE_USE_LDFLAGS= +fi + +CFLAGS="$_SAVE_CFLAGS" + +if test -n "$INTEL_CC"; then + PROFILE_GEN_CFLAGS="-prof-gen -prof-dir ." + PROFILE_GEN_LDFLAGS= + PROFILE_USE_CFLAGS="-prof-use -prof-dir ." + PROFILE_USE_LDFLAGS= +fi + +if test "$SOLARIS_SUNPRO_CC"; then + PROFILE_GEN_CFLAGS="-xprofile=collect:$_objdir/$enable_application" + PROFILE_GEN_LDFLAGS="-xprofile=collect:$_objdir/$enable_application" + if test "$CPU_ARCH" != "sparc"; then + PROFILE_USE_CFLAGS="-xprofile=use:$_objdir/$enable_application" + PROFILE_USE_LDFLAGS="-xprofile=use:$_objdir/$enable_application" + else + PROFILE_USE_CFLAGS="-xlinkopt=2 -xprofile=use:$_objdir/$enable_application" + PROFILE_USE_LDFLAGS="-xlinkopt=2 -xprofile=use:$_objdir/$enable_application" + fi +fi + + + + + + +ac_ext=C +# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cxx_cross + + +# Check whether --enable-pedantic or --disable-pedantic was given. +if test "${enable_pedantic+set}" = set; then + enableval="$enable_pedantic" + if test "$enableval" = "no"; then + _PEDANTIC= + elif test "$enableval" = "yes"; then + : + else + { echo "configure: error: Option, pedantic, does not take an argument ($enableval)." 1>&2; exit 1; } + fi +fi + +if test "$_PEDANTIC"; then + _SAVE_CXXFLAGS=$CXXFLAGS + CXXFLAGS="$CXXFLAGS ${_WARNINGS_CXXFLAGS} ${_COMPILER_PREFIX}-pedantic" + echo $ac_n "checking whether C++ compiler has -pedantic long long bug""... $ac_c" 1>&6 +echo "configure:12550: checking whether C++ compiler has -pedantic long long bug" >&5 + cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + result="no" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + result="yes" +fi +rm -f conftest* + echo "$ac_t""$result" 1>&6 + CXXFLAGS="$_SAVE_CXXFLAGS" + + case "$result" in + no) + _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} ${_COMPILER_PREFIX}-pedantic" + _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} ${_COMPILER_PREFIX}-pedantic" + ;; + yes) + { echo "configure: error: Your compiler appears to have a known bug where long long is miscompiled when using -pedantic. Reconfigure using --disable-pedantic. " 1>&2; exit 1; } + ;; + esac +fi + +echo $ac_n "checking for correct temporary object destruction order""... $ac_c" 1>&6 +echo "configure:12584: checking for correct temporary object destruction order" >&5 +if test "$cross_compiling" = yes; then + result="maybe" +else + cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + result="yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + result="no" +fi +rm -fr conftest* +fi + +echo "$ac_t""$result" 1>&6 + +if test "$result" = "no"; then + { echo "configure: error: Your compiler does not follow the C++ specification for temporary object destruction order." 1>&2; exit 1; } +fi + +_SAVE_CXXFLAGS=$CXXFLAGS +CXXFLAGS="$CXXFLAGS ${_WARNINGS_CXXFLAGS}" +echo $ac_n "checking for correct overload resolution with const and templates""... $ac_c" 1>&6 +echo "configure:12633: checking for correct overload resolution with const and templates" >&5 +if eval "test \"`echo '$''{'ac_nscap_nonconst_opeq_bug'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < + class Pointer + { + public: + T* myPtr; + }; + + template + int operator==(const Pointer& rhs, U* lhs) + { + return rhs.myPtr == lhs; + } + + template + int operator==(const Pointer& rhs, const U* lhs) + { + return rhs.myPtr == lhs; + } + +int main() { + + Pointer foo; + const int* bar; + return foo == bar; + +; return 0; } +EOF +if { (eval echo configure:12668: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_nscap_nonconst_opeq_bug="no" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_nscap_nonconst_opeq_bug="yes" +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_nscap_nonconst_opeq_bug" 1>&6 +CXXFLAGS="$_SAVE_CXXFLAGS" + +if test "$ac_nscap_nonconst_opeq_bug" = "yes" ; then + cat >> confdefs.h <<\EOF +#define NSCAP_DONT_PROVIDE_NONCONST_OPEQ 1 +EOF + +fi + +echo $ac_n "checking for tm_zone tm_gmtoff in struct tm""... $ac_c" 1>&6 +echo "configure:12691: checking for tm_zone tm_gmtoff in struct tm" >&5 +if eval "test \"`echo '$''{'ac_cv_struct_tm_zone_tm_gmtoff'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +int main() { +struct tm tm; tm.tm_zone = 0; tm.tm_gmtoff = 1; +; return 0; } +EOF +if { (eval echo configure:12703: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_struct_tm_zone_tm_gmtoff="yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_struct_tm_zone_tm_gmtoff="no" +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_struct_tm_zone_tm_gmtoff" 1>&6 +if test "$ac_cv_struct_tm_zone_tm_gmtoff" = "yes" ; then + cat >> confdefs.h <<\EOF +#define HAVE_TM_ZONE_TM_GMTOFF 1 +EOF + +fi + +fi # SKIP_COMPILER_CHECKS + +# Check whether --enable-cpp-rtti or --disable-cpp-rtti was given. +if test "${enable_cpp_rtti+set}" = set; then + enableval="$enable_cpp_rtti" + if test "$enableval" = "yes"; then + _MOZ_USE_RTTI=1 + elif test "$enableval" = "no"; then + _MOZ_USE_RTTI= + else + { echo "configure: error: Option, cpp-rtti, does not take an argument ($enableval)." 1>&2; exit 1; } + fi +fi + + +if test "$_MOZ_USE_RTTI"; then + _MOZ_RTTI_FLAGS=$_MOZ_RTTI_FLAGS_ON +else + _MOZ_RTTI_FLAGS=$_MOZ_RTTI_FLAGS_OFF +fi + + + +# Check whether --enable-cpp-exceptions or --disable-cpp-exceptions was given. +if test "${enable_cpp_exceptions+set}" = set; then + enableval="$enable_cpp_exceptions" + if test "$enableval" = "yes"; then + _MOZ_CPP_EXCEPTIONS=1 + elif test "$enableval" = "no"; then + _MOZ_CPP_EXCEPTIONS= + else + { echo "configure: error: Option, cpp-exceptions, does not take an argument ($enableval)." 1>&2; exit 1; } + fi +fi + + +if test "$_MOZ_CPP_EXCEPTIONS"; then + _MOZ_EXCEPTIONS_FLAGS=$_MOZ_EXCEPTIONS_FLAGS_ON +else + _MOZ_EXCEPTIONS_FLAGS=$_MOZ_EXCEPTIONS_FLAGS_OFF +fi + + + +# Irix & OSF native compilers do not like exception declarations +# when exceptions are disabled +if test -n "$MIPSPRO_CXX" -o -n "$COMPAQ_CXX" -o -n "$VACPP"; then + cat >> confdefs.h <<\EOF +#define CPP_THROW_NEW +EOF + +else + cat >> confdefs.h <<\EOF +#define CPP_THROW_NEW throw() +EOF + +fi +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + + +# Build dependencies + +MOZ_AUTO_DEPS=1 +# Check whether --enable-auto-deps or --disable-auto-deps was given. +if test "${enable_auto_deps+set}" = set; then + enableval="$enable_auto_deps" + if test "$enableval" = "no"; then + MOZ_AUTO_DEPS= + elif test "$enableval" = "yes"; then + MOZ_AUTO_DEPS=1 + else + { echo "configure: error: Option, auto-deps, does not take an argument ($enableval)." 1>&2; exit 1; } + fi +fi + + +if test -n "$MOZ_AUTO_DEPS"; then +_cpp_md_flag= +# Check whether --enable-md or --disable-md was given. +if test "${enable_md+set}" = set; then + enableval="$enable_md" + if test "$enableval" = "no"; then + _cpp_md_flag= + elif test "$enableval" = "yes"; then + _cpp_md_flag=1 + else + { echo "configure: error: Option, md, does not take an argument ($enableval)." 1>&2; exit 1; } + fi +else + if test "$GNU_CC" -a "$GNU_CXX" -a "$OS_ARCH" != "WINNT" -a "$OS_ARCH" != "WINCE"; then + _cpp_md_flag=1 + fi + if test "$SOLARIS_SUNPRO_CC"; then + _cpp_md_flag=1 + fi +fi + +if test "$_cpp_md_flag"; then + COMPILER_DEPEND=1 + if test "$OS_ARCH" = "OpenVMS"; then + _DEPEND_CFLAGS='$(subst =, ,$(filter-out %/.pp,-MM=-MD=-MF=$(MDDEPDIR)/$(basename $(@F)).pp))' + else + _DEPEND_CFLAGS='$(filter-out %/.pp,-Wp,-MD,$(MDDEPDIR)/$(basename $(@F)).pp)' + fi + if test "$SOLARIS_SUNPRO_CC"; then + _DEPEND_CFLAGS= + fi +else + COMPILER_DEPEND= + if test -z "$_WIN32_MSVC"; then + _USE_CPP_INCLUDE_FLAG= + _DEFINES_CFLAGS='$(ACDEFINES) -D_JS_CONFDEFS_H_ -DMOZILLA_CLIENT' + _DEFINES_CXXFLAGS='$(ACDEFINES) -D_JS_CONFDEFS_H_ -DMOZILLA_CLIENT' + fi +fi +fi # MOZ_AUTO_DEPS +MDDEPDIR='.deps' + + + + + +# Static build options + +# Check whether --enable-static or --disable-static was given. +if test "${enable_static+set}" = set; then + enableval="$enable_static" + if test "$enableval" = "yes"; then + BUILD_STATIC_LIBS=1 + elif test "$enableval" = "no"; then + BUILD_STATIC_LIBS= + else + { echo "configure: error: Option, static, does not take an argument ($enableval)." 1>&2; exit 1; } + fi +fi + + +# Check whether --enable-readline or --disable-readline was given. +if test "${enable_readline+set}" = set; then + enableval="$enable_readline" + if test "$enableval" = "yes"; then + JS_WANT_READLINE=1 + elif test "$enableval" = "no"; then + JS_WANT_READLINE= + else + { echo "configure: error: Option, readline, does not take an argument ($enableval)." 1>&2; exit 1; } + fi +fi + + +JS_NATIVE_EDITLINE= +EDITLINE_LIBS= +JS_DISABLE_SHELL= + +case "$target" in +*-wince*|*-mingw*|*-cygwin*|*-msvc*|*-mks*) + NO_EDITLINE=1 + ;; +*-symbian*) + NO_EDITLINE=1 + JS_DISABLE_SHELL=1 + ;; +*) + ;; +esac + +if test -z "$SKIP_LIBRARY_CHECKS" -a -z "$NO_EDITLINE"; then + if test -n "$JS_WANT_READLINE"; then + echo $ac_n "checking for readline in -lreadline""... $ac_c" 1>&6 +echo "configure:12897: checking for readline in -lreadline" >&5 +ac_lib_var=`echo readline'_'readline | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lreadline $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + EDITLINE_LIBS="-lreadline" +else + echo "$ac_t""no" 1>&6 +{ echo "configure: error: No system readline library found." 1>&2; exit 1; } +fi + + else + JS_NATIVE_EDITLINE=1 + EDITLINE_LIBS='$(DEPTH)/editline/$(LIB_PREFIX)editline.$(LIB_SUFFIX)' + fi + + cat >> confdefs.h <<\EOF +#define EDITLINE 1 +EOF + +fi + + + + +# Standalone module options (Not for building Mozilla) + +if test "$MOZ_DEBUG" || test "$NS_TRACE_MALLOC"; then + MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS= +fi + +# Check whether --with-sync-build-files or --without-sync-build-files was given. +if test "${with_sync_build_files+set}" = set; then + withval="$with_sync_build_files" + MOZ_SYNC_BUILD_FILES=$withval +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +CFLAGS=`echo \ + $_WARNINGS_CFLAGS \ + $CFLAGS` + +CXXFLAGS=`echo \ + $_MOZ_RTTI_FLAGS \ + $_MOZ_EXCEPTIONS_FLAGS \ + $_WARNINGS_CXXFLAGS \ + $CXXFLAGS` + +COMPILE_CFLAGS=`echo \ + $_DEFINES_CFLAGS \ + $_DEPEND_CFLAGS \ + $COMPILE_CFLAGS` + +COMPILE_CXXFLAGS=`echo \ + $_DEFINES_CXXFLAGS \ + $_DEPEND_CFLAGS \ + $COMPILE_CXXFLAGS` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +cat >> confdefs.h < conftest.$ac_ext < + #include + #include + #include + +int main() { + + Display *dpy = 0; + if ((dpy = XOpenDisplay(NULL)) == NULL) { + fprintf(stderr, ": can't open %s\n", XDisplayName(NULL)); + exit(1); + } + +; return 0; } +EOF +if { (eval echo configure:13163: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + : +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + { echo "configure: error: Could not compile basic X program." 1>&2; exit 1; } +fi +rm -f conftest* + CFLAGS="$_SAVE_CFLAGS" + + if test ! -z "$MISSING_X"; then + { echo "configure: error: Could not find the following X libraries: $MISSING_X " 1>&2; exit 1; }; + fi + +fi # MOZ_X11 +fi # COMPILE_ENVIRONMENT + + +if test "$OS_ARCH" = "BeOS"; then + cat >> confdefs.h <<\EOF +#define XP_BEOS 1 +EOF + + MOZ_MOVEMAIL=1 +elif test "$OS_ARCH" = "Darwin"; then + cat >> confdefs.h <<\EOF +#define XP_UNIX 1 +EOF + + cat >> confdefs.h <<\EOF +#define UNIX_ASYNC_DNS 1 +EOF + + MOZ_MOVEMAIL=1 +elif test "$OS_ARCH" = "OpenVMS"; then + cat >> confdefs.h <<\EOF +#define XP_UNIX 1 +EOF + +elif test "$OS_ARCH" != "WINNT" -a "$OS_ARCH" != "OS2" -a "$OS_ARCH" != "WINCE"; then + cat >> confdefs.h <<\EOF +#define XP_UNIX 1 +EOF + + cat >> confdefs.h <<\EOF +#define UNIX_ASYNC_DNS 1 +EOF + + MOZ_MOVEMAIL=1 +fi + + +# Check whether --enable-threadsafe or --disable-threadsafe was given. +if test "${enable_threadsafe+set}" = set; then + enableval="$enable_threadsafe" + cat >> confdefs.h <<\EOF +#define JS_THREADSAFE 1 +EOF + +fi + + +if test "$MOZ_DEBUG"; then + cat >> confdefs.h <<\EOF +#define MOZ_REFLOW_PERF 1 +EOF + + cat >> confdefs.h <<\EOF +#define MOZ_REFLOW_PERF_DSP 1 +EOF + +fi + +if test "$ACCESSIBILITY" -a "$MOZ_ENABLE_GTK2" ; then + cat >> confdefs.h <<\EOF +#define MOZ_ACCESSIBILITY_ATK 1 +EOF + + ATK_FULL_VERSION=`$PKG_CONFIG --modversion atk` + ATK_MAJOR_VERSION=`echo ${ATK_FULL_VERSION} | $AWK -F\. '{ print $1 }'` + ATK_MINOR_VERSION=`echo ${ATK_FULL_VERSION} | $AWK -F\. '{ print $2 }'` + ATK_REV_VERSION=`echo ${ATK_FULL_VERSION} | $AWK -F\. '{ print $3 }'` + cat >> confdefs.h <> confdefs.h <> confdefs.h <&6 +echo "configure:13264: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:13292: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <&6 +fi +done + + +case "$host_os" in +mingw*) + WIN_TOP_SRC=`cd $srcdir; pwd -W` + ;; +cygwin*|msvc*|mks*) + HOST_CC="\$(CYGWIN_WRAPPER) $HOST_CC" + HOST_CXX="\$(CYGWIN_WRAPPER) $HOST_CXX" + CC="\$(CYGWIN_WRAPPER) $CC" + CXX="\$(CYGWIN_WRAPPER) $CXX" + CPP="\$(CYGWIN_WRAPPER) $CPP" + LD="\$(CYGWIN_WRAPPER) $LD" + AS="\$(CYGWIN_WRAPPER) $AS" + RC="\$(CYGWIN_WRAPPER) $RC" + CYGDRIVE_MOUNT=`mount -p | awk '{ if (/^\//) { print $1; exit } }'` + WIN_TOP_SRC=`cygpath -a -w $srcdir | sed -e 's|\\\\|/|g'` + ;; +esac + + + + + + + + + +# Save the defines header file before autoconf removes it. +# (Do not add AC_DEFINE calls after this line.) + _CONFIG_TMP=confdefs-tmp.h + _CONFIG_DEFS_H=js-confdefs.h + + cat > $_CONFIG_TMP <<\EOF +/* List of defines generated by configure. Included with preprocessor flag, + * -include, to avoid long list of -D defines on the compile command-line. + * Do not edit. + */ + +#ifndef _JS_CONFDEFS_H_ +#define _JS_CONFDEFS_H_ +EOF + +_EGREP_PATTERN='^#define (' +if test -n "$_NON_GLOBAL_ACDEFINES"; then + for f in $_NON_GLOBAL_ACDEFINES; do + _EGREP_PATTERN="${_EGREP_PATTERN}$f|" + done +fi +_EGREP_PATTERN="${_EGREP_PATTERN}dummy_never_defined)" + + sort confdefs.h | egrep -v "$_EGREP_PATTERN" >> $_CONFIG_TMP + + cat >> $_CONFIG_TMP <<\EOF + +#endif /* _JS_CONFDEFS_H_ */ + +EOF + + # Only write js-confdefs.h when something changes (or it doesn't exist) + if cmp -s $_CONFIG_TMP $_CONFIG_DEFS_H; then + rm $_CONFIG_TMP + else + echo "$ac_t"""creating $_CONFIG_DEFS_H"" 1>&6 + mv -f $_CONFIG_TMP $_CONFIG_DEFS_H + + echo ==== $_CONFIG_DEFS_H ================================= + cat $_CONFIG_DEFS_H + fi + +rm -f confdefs.h.save +mv confdefs.h confdefs.h.save +egrep -v "$_EGREP_PATTERN" confdefs.h.save > confdefs.h +# Transform confdefs.h into DEFS. +# Protect against shell expansion while executing Makefile rules. +# Protect against Makefile macro expansion. +cat > conftest.defs <<\EOF +s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%-D\1=\2%g +s%[ `~#$^&*(){}\\|;'"<>?]%\\&%g +s%\[%\\&%g +s%\]%\\&%g +s%\$%$$%g +EOF +DEFS=`sed -f conftest.defs confdefs.h | tr '\012' ' '` +rm -f conftest.defs + +MOZ_DEFINES=$DEFS + +rm -f confdefs.h +mv confdefs.h.save confdefs.h + +MAKEFILES=" + Makefile + config/Makefile + config/autoconf.mk + config/mkdepend/Makefile +" + +if test -n "$JS_NATIVE_EDITLINE"; then + MAKEFILES="$MAKEFILES +editline/Makefile +" +fi + +if test -z "${AS_PERL}"; then +echo $MAKEFILES | ${PERL} $srcdir/build/autoconf/acoutput-fast.pl > conftest.sh +else +echo $MAKEFILES | ${PERL} $srcdir/build/autoconf/acoutput-fast.pl -nowrap --cygwin-srcdir=$srcdir > conftest.sh +fi +. ./conftest.sh +rm conftest.sh + +echo $MAKEFILES > unallmakefiles + +mv -f config/autoconf.mk config/autoconf.mk.orig 2> /dev/null + +trap '' 1 2 15 +cat > confcache <<\EOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs. It is not useful on other systems. +# If it contains results you don't want to keep, you may remove or edit it. +# +# By default, configure uses ./config.cache as the cache file, +# creating it if it does not exist already. You can give configure +# the --cache-file=FILE option to use a different cache file; that is +# what configure does when it calls configure scripts in +# subdirectories, so they share the cache. +# Giving --cache-file=/dev/null disables caching, for debugging configure. +# config.status only pays attention to the cache file if you give it the +# --recheck option to rerun configure. +# +EOF +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +(set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote substitution + # turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + -e "s/'/'\\\\''/g" \ + -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p' + ;; + esac >> confcache +if cmp -s $cache_file confcache; then + : +else + if test -w $cache_file; then + echo "updating cache $cache_file" + cat confcache > $cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# Any assignment to VPATH causes Sun make to only execute +# the first set of double-colon rules, so remove it if not needed. +# If there is a colon in the path, we need to keep it. +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d' +fi + +trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15 + +DEFS=-DHAVE_CONFIG_H + +# Without the "./", some shells look in PATH for config.status. +: ${CONFIG_STATUS=./config.status} + +echo creating $CONFIG_STATUS +rm -f $CONFIG_STATUS +cat > $CONFIG_STATUS </dev/null | sed 1q`: +# +# $0 $ac_configure_args +# +# Compiler output produced by configure, useful for debugging +# configure, is in ./config.log if it exists. + +ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]" +for ac_option +do + case "\$ac_option" in + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion" + exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;; + -version | --version | --versio | --versi | --vers | --ver | --ve | --v) + echo "$CONFIG_STATUS generated by autoconf version 2.13" + exit 0 ;; + -help | --help | --hel | --he | --h) + echo "\$ac_cs_usage"; exit 0 ;; + *) echo "\$ac_cs_usage"; exit 1 ;; + esac +done + +ac_given_srcdir=$srcdir +ac_given_INSTALL="$INSTALL" + +trap 'rm -fr `echo "$MAKEFILES js-config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 +EOF +cat >> $CONFIG_STATUS < conftest.subs <<\\CEOF +$ac_vpsub +$extrasub +s%@SHELL@%$SHELL%g +s%@CFLAGS@%$CFLAGS%g +s%@CPPFLAGS@%$CPPFLAGS%g +s%@CXXFLAGS@%$CXXFLAGS%g +s%@FFLAGS@%$FFLAGS%g +s%@DEFS@%$DEFS%g +s%@LDFLAGS@%$LDFLAGS%g +s%@LIBS@%$LIBS%g +s%@exec_prefix@%$exec_prefix%g +s%@prefix@%$prefix%g +s%@program_transform_name@%$program_transform_name%g +s%@bindir@%$bindir%g +s%@sbindir@%$sbindir%g +s%@libexecdir@%$libexecdir%g +s%@datadir@%$datadir%g +s%@sysconfdir@%$sysconfdir%g +s%@sharedstatedir@%$sharedstatedir%g +s%@localstatedir@%$localstatedir%g +s%@libdir@%$libdir%g +s%@includedir@%$includedir%g +s%@oldincludedir@%$oldincludedir%g +s%@infodir@%$infodir%g +s%@mandir@%$mandir%g +s%@host@%$host%g +s%@host_alias@%$host_alias%g +s%@host_cpu@%$host_cpu%g +s%@host_vendor@%$host_vendor%g +s%@host_os@%$host_os%g +s%@target@%$target%g +s%@target_alias@%$target_alias%g +s%@target_cpu@%$target_cpu%g +s%@target_vendor@%$target_vendor%g +s%@target_os@%$target_os%g +s%@build@%$build%g +s%@build_alias@%$build_alias%g +s%@build_cpu@%$build_cpu%g +s%@build_vendor@%$build_vendor%g +s%@build_os@%$build_os%g +s%@AWK@%$AWK%g +s%@TOP_DIST@%$TOP_DIST%g +s%@HOST_CC@%$HOST_CC%g +s%@HOST_CXX@%$HOST_CXX%g +s%@HOST_RANLIB@%$HOST_RANLIB%g +s%@HOST_AR@%$HOST_AR%g +s%@CC@%$CC%g +s%@CXX@%$CXX%g +s%@RANLIB@%$RANLIB%g +s%@AR@%$AR%g +s%@AS@%$AS%g +s%@LD@%$LD%g +s%@STRIP@%$STRIP%g +s%@WINDRES@%$WINDRES%g +s%@CPP@%$CPP%g +s%@CXXCPP@%$CXXCPP%g +s%@_MSC_VER@%$_MSC_VER%g +s%@GNU_AS@%$GNU_AS%g +s%@GNU_LD@%$GNU_LD%g +s%@GNU_CC@%$GNU_CC%g +s%@GNU_CXX@%$GNU_CXX%g +s%@INTEL_CC@%$INTEL_CC%g +s%@INTEL_CXX@%$INTEL_CXX%g +s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g +s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g +s%@INSTALL_DATA@%$INSTALL_DATA%g +s%@LN_S@%$LN_S%g +s%@PERL@%$PERL%g +s%@PYTHON@%$PYTHON%g +s%@NSINSTALL_BIN@%$NSINSTALL_BIN%g +s%@DOXYGEN@%$DOXYGEN%g +s%@WHOAMI@%$WHOAMI%g +s%@AUTOCONF@%$AUTOCONF%g +s%@SYSTEM_MAKEDEPEND@%$SYSTEM_MAKEDEPEND%g +s%@XARGS@%$XARGS%g +s%@PBBUILD@%$PBBUILD%g +s%@SDP@%$SDP%g +s%@GCC_VERSION@%$GCC_VERSION%g +s%@XCODEBUILD_VERSION@%$XCODEBUILD_VERSION%g +s%@HAS_XCODE_2_1@%$HAS_XCODE_2_1%g +s%@UNIVERSAL_BINARY@%$UNIVERSAL_BINARY%g +s%@MACOSX_DEPLOYMENT_TARGET@%$MACOSX_DEPLOYMENT_TARGET%g +s%@MACOS_SDK_DIR@%$MACOS_SDK_DIR%g +s%@NEXT_ROOT@%$NEXT_ROOT%g +s%@MAKE@%$MAKE%g +s%@X_CFLAGS@%$X_CFLAGS%g +s%@X_PRE_LIBS@%$X_PRE_LIBS%g +s%@X_LIBS@%$X_LIBS%g +s%@X_EXTRA_LIBS@%$X_EXTRA_LIBS%g +s%@SOLARIS_SUNPRO_CC@%$SOLARIS_SUNPRO_CC%g +s%@SOLARIS_SUNPRO_CXX@%$SOLARIS_SUNPRO_CXX%g +s%@HAVE_64BIT_OS@%$HAVE_64BIT_OS%g +s%@MOZ_OS2_HIGH_MEMORY@%$MOZ_OS2_HIGH_MEMORY%g +s%@AIX_OBJMODEL@%$AIX_OBJMODEL%g +s%@NO_LD_ARCHIVE_FLAGS@%$NO_LD_ARCHIVE_FLAGS%g +s%@ENABLE_JIT@%$ENABLE_JIT%g +s%@NANOJIT_ARCH@%$NANOJIT_ARCH%g +s%@WRAP_SYSTEM_INCLUDES@%$WRAP_SYSTEM_INCLUDES%g +s%@VISIBILITY_FLAGS@%$VISIBILITY_FLAGS%g +s%@HAVE_DTRACE@%$HAVE_DTRACE%g +s%@LIBOBJS@%$LIBOBJS%g +s%@HAVE_GCC3_ABI@%$HAVE_GCC3_ABI%g +s%@NSPR_CFLAGS@%$NSPR_CFLAGS%g +s%@NSPR_LIBS@%$NSPR_LIBS%g +s%@NSPR_CONFIG@%$NSPR_CONFIG%g +s%@MOZ_UI_LOCALE@%$MOZ_UI_LOCALE%g +s%@MOZ_OPTIMIZE@%$MOZ_OPTIMIZE%g +s%@MOZ_OPTIMIZE_FLAGS@%$MOZ_OPTIMIZE_FLAGS%g +s%@MOZ_OPTIMIZE_LDFLAGS@%$MOZ_OPTIMIZE_LDFLAGS%g +s%@MOZ_OPTIMIZE_SIZE_TWEAK@%$MOZ_OPTIMIZE_SIZE_TWEAK%g +s%@NS_TRACE_MALLOC@%$NS_TRACE_MALLOC%g +s%@MOZ_MEMORY@%$MOZ_MEMORY%g +s%@MOZ_MEMORY_LDFLAGS@%$MOZ_MEMORY_LDFLAGS%g +s%@DEHYDRA_PATH@%$DEHYDRA_PATH%g +s%@MOZ_DEMANGLE_SYMBOLS@%$MOZ_DEMANGLE_SYMBOLS%g +s%@MOZ_PROFILE_GUIDED_OPTIMIZE_DISABLE@%$MOZ_PROFILE_GUIDED_OPTIMIZE_DISABLE%g +s%@PROFILE_GEN_CFLAGS@%$PROFILE_GEN_CFLAGS%g +s%@PROFILE_GEN_LDFLAGS@%$PROFILE_GEN_LDFLAGS%g +s%@PROFILE_USE_CFLAGS@%$PROFILE_USE_CFLAGS%g +s%@PROFILE_USE_LDFLAGS@%$PROFILE_USE_LDFLAGS%g +s%@_MOZ_RTTI_FLAGS_ON@%$_MOZ_RTTI_FLAGS_ON%g +s%@_MOZ_EXCEPTIONS_FLAGS_ON@%$_MOZ_EXCEPTIONS_FLAGS_ON%g +s%@MOZ_AUTO_DEPS@%$MOZ_AUTO_DEPS%g +s%@COMPILER_DEPEND@%$COMPILER_DEPEND%g +s%@MDDEPDIR@%$MDDEPDIR%g +s%@JS_NATIVE_EDITLINE@%$JS_NATIVE_EDITLINE%g +s%@JS_DISABLE_SHELL@%$JS_DISABLE_SHELL%g +s%@EDITLINE_LIBS@%$EDITLINE_LIBS%g +s%@MOZ_SYNC_BUILD_FILES@%$MOZ_SYNC_BUILD_FILES%g +s%@AR_FLAGS@%$AR_FLAGS%g +s%@AR_LIST@%$AR_LIST%g +s%@AR_EXTRACT@%$AR_EXTRACT%g +s%@AR_DELETE@%$AR_DELETE%g +s%@ASFLAGS@%$ASFLAGS%g +s%@AS_DASH_C_FLAG@%$AS_DASH_C_FLAG%g +s%@RC@%$RC%g +s%@RCFLAGS@%$RCFLAGS%g +s%@IMPLIB@%$IMPLIB%g +s%@FILTER@%$FILTER%g +s%@BIN_FLAGS@%$BIN_FLAGS%g +s%@NS_USE_NATIVE@%$NS_USE_NATIVE%g +s%@MOZ_JS_LIBS@%$MOZ_JS_LIBS%g +s%@MOZ_PSM@%$MOZ_PSM%g +s%@MOZ_DEBUG@%$MOZ_DEBUG%g +s%@MOZ_DEBUG_MODULES@%$MOZ_DEBUG_MODULES%g +s%@MOZ_DEBUG_ENABLE_DEFS@%$MOZ_DEBUG_ENABLE_DEFS%g +s%@MOZ_DEBUG_DISABLE_DEFS@%$MOZ_DEBUG_DISABLE_DEFS%g +s%@MOZ_DEBUG_FLAGS@%$MOZ_DEBUG_FLAGS%g +s%@MOZ_DEBUG_LDFLAGS@%$MOZ_DEBUG_LDFLAGS%g +s%@WARNINGS_AS_ERRORS@%$WARNINGS_AS_ERRORS%g +s%@MOZ_DBGRINFO_MODULES@%$MOZ_DBGRINFO_MODULES%g +s%@MOZ_LEAKY@%$MOZ_LEAKY%g +s%@MOZ_JPROF@%$MOZ_JPROF%g +s%@MOZ_SHARK@%$MOZ_SHARK%g +s%@MOZ_CALLGRIND@%$MOZ_CALLGRIND%g +s%@MOZ_VTUNE@%$MOZ_VTUNE%g +s%@MOZ_XPCTOOLS@%$MOZ_XPCTOOLS%g +s%@MOZ_JSLOADER@%$MOZ_JSLOADER%g +s%@MOZ_INSURE@%$MOZ_INSURE%g +s%@MOZ_INSURE_DIRS@%$MOZ_INSURE_DIRS%g +s%@MOZ_INSURE_EXCLUDE_DIRS@%$MOZ_INSURE_EXCLUDE_DIRS%g +s%@MOZ_QUANTIFY@%$MOZ_QUANTIFY%g +s%@MOZ_INSURIFYING@%$MOZ_INSURIFYING%g +s%@LIBICONV@%$LIBICONV%g +s%@BUILD_STATIC_LIBS@%$BUILD_STATIC_LIBS%g +s%@ENABLE_TESTS@%$ENABLE_TESTS%g +s%@ENABLE_STRIP@%$ENABLE_STRIP%g +s%@PKG_SKIP_STRIP@%$PKG_SKIP_STRIP%g +s%@INCREMENTAL_LINKER@%$INCREMENTAL_LINKER%g +s%@MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS@%$MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS%g +s%@MOZ_COMPONENT_NSPR_LIBS@%$MOZ_COMPONENT_NSPR_LIBS%g +s%@MOZ_FIX_LINK_PATHS@%$MOZ_FIX_LINK_PATHS%g +s%@USE_DEPENDENT_LIBS@%$USE_DEPENDENT_LIBS%g +s%@MOZ_BUILD_ROOT@%$MOZ_BUILD_ROOT%g +s%@MOZ_OS2_TOOLS@%$MOZ_OS2_TOOLS%g +s%@MOZ_OS2_USE_DECLSPEC@%$MOZ_OS2_USE_DECLSPEC%g +s%@MOZ_POST_DSO_LIB_COMMAND@%$MOZ_POST_DSO_LIB_COMMAND%g +s%@MOZ_POST_PROGRAM_COMMAND@%$MOZ_POST_PROGRAM_COMMAND%g +s%@MOZ_TIMELINE@%$MOZ_TIMELINE%g +s%@WINCE@%$WINCE%g +s%@WINCE_WINDOWS_MOBILE@%$WINCE_WINDOWS_MOBILE%g +s%@MOZ_APP_NAME@%$MOZ_APP_NAME%g +s%@MOZ_APP_DISPLAYNAME@%$MOZ_APP_DISPLAYNAME%g +s%@MOZ_APP_VERSION@%$MOZ_APP_VERSION%g +s%@MOZ_PKG_SPECIAL@%$MOZ_PKG_SPECIAL%g +s%@MOZILLA_OFFICIAL@%$MOZILLA_OFFICIAL%g +s%@MOZ_DEBUG_SYMBOLS@%$MOZ_DEBUG_SYMBOLS%g +s%@MOZ_MAPINFO@%$MOZ_MAPINFO%g +s%@MOZ_BROWSE_INFO@%$MOZ_BROWSE_INFO%g +s%@MOZ_TOOLS_DIR@%$MOZ_TOOLS_DIR%g +s%@CYGWIN_WRAPPER@%$CYGWIN_WRAPPER%g +s%@AS_PERL@%$AS_PERL%g +s%@WIN32_REDIST_DIR@%$WIN32_REDIST_DIR%g +s%@MOZ_NATIVE_NSPR@%$MOZ_NATIVE_NSPR%g +s%@COMPILE_CFLAGS@%$COMPILE_CFLAGS%g +s%@COMPILE_CXXFLAGS@%$COMPILE_CXXFLAGS%g +s%@CROSS_COMPILE@%$CROSS_COMPILE%g +s%@HOST_CFLAGS@%$HOST_CFLAGS%g +s%@HOST_CXXFLAGS@%$HOST_CXXFLAGS%g +s%@HOST_OPTIMIZE_FLAGS@%$HOST_OPTIMIZE_FLAGS%g +s%@HOST_AR_FLAGS@%$HOST_AR_FLAGS%g +s%@HOST_LD@%$HOST_LD%g +s%@HOST_NSPR_MDCPUCFG@%$HOST_NSPR_MDCPUCFG%g +s%@HOST_BIN_SUFFIX@%$HOST_BIN_SUFFIX%g +s%@HOST_OS_ARCH@%$HOST_OS_ARCH%g +s%@TARGET_CPU@%$TARGET_CPU%g +s%@TARGET_VENDOR@%$TARGET_VENDOR%g +s%@TARGET_OS@%$TARGET_OS%g +s%@TARGET_NSPR_MDCPUCFG@%$TARGET_NSPR_MDCPUCFG%g +s%@TARGET_MD_ARCH@%$TARGET_MD_ARCH%g +s%@TARGET_XPCOM_ABI@%$TARGET_XPCOM_ABI%g +s%@OS_TARGET@%$OS_TARGET%g +s%@OS_ARCH@%$OS_ARCH%g +s%@OS_RELEASE@%$OS_RELEASE%g +s%@OS_TEST@%$OS_TEST%g +s%@WRAP_MALLOC_CFLAGS@%$WRAP_MALLOC_CFLAGS%g +s%@WRAP_MALLOC_LIB@%$WRAP_MALLOC_LIB%g +s%@MKSHLIB@%$MKSHLIB%g +s%@MKCSHLIB@%$MKCSHLIB%g +s%@MKSHLIB_FORCE_ALL@%$MKSHLIB_FORCE_ALL%g +s%@MKSHLIB_UNFORCE_ALL@%$MKSHLIB_UNFORCE_ALL%g +s%@DSO_CFLAGS@%$DSO_CFLAGS%g +s%@DSO_PIC_CFLAGS@%$DSO_PIC_CFLAGS%g +s%@DSO_LDOPTS@%$DSO_LDOPTS%g +s%@LIB_PREFIX@%$LIB_PREFIX%g +s%@DLL_PREFIX@%$DLL_PREFIX%g +s%@DLL_SUFFIX@%$DLL_SUFFIX%g +s%@LIB_SUFFIX@%$LIB_SUFFIX%g +s%@OBJ_SUFFIX@%$OBJ_SUFFIX%g +s%@BIN_SUFFIX@%$BIN_SUFFIX%g +s%@ASM_SUFFIX@%$ASM_SUFFIX%g +s%@IMPORT_LIB_SUFFIX@%$IMPORT_LIB_SUFFIX%g +s%@USE_N32@%$USE_N32%g +s%@CC_VERSION@%$CC_VERSION%g +s%@CXX_VERSION@%$CXX_VERSION%g +s%@MSMANIFEST_TOOL@%$MSMANIFEST_TOOL%g +s%@MOZ_MOVEMAIL@%$MOZ_MOVEMAIL%g +s%@CYGDRIVE_MOUNT@%$CYGDRIVE_MOUNT%g +s%@WIN_TOP_SRC@%$WIN_TOP_SRC%g +s%@MOZILLA_VERSION@%$MOZILLA_VERSION%g +s%@ac_configure_args@%$ac_configure_args%g +s%@MOZ_DEFINES@%$MOZ_DEFINES%g + +CEOF +EOF + +cat >> $CONFIG_STATUS <<\EOF + +# Split the substitutions into bite-sized pieces for seds with +# small command number limits, like on Digital OSF/1 and HP-UX. +ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script. +ac_file=1 # Number of current file. +ac_beg=1 # First line for current file. +ac_end=$ac_max_sed_cmds # Line after last line for current file. +ac_more_lines=: +ac_sed_cmds="" +while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file + else + sed "${ac_end}q" conftest.subs > conftest.s$ac_file + fi + if test ! -s conftest.s$ac_file; then + ac_more_lines=false + rm -f conftest.s$ac_file + else + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f conftest.s$ac_file" + else + ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file" + fi + ac_file=`expr $ac_file + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_cmds` + fi +done +if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat +fi +EOF + +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF +for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories. + + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`" + # A "../" for each directory in $ac_dir_suffix. + ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'` + else + ac_dir_suffix= ac_dots= + fi + + case "$ac_given_srcdir" in + .) srcdir=. + if test -z "$ac_dots"; then top_srcdir=. + else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;; + /* | ?:/*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;; + *) # Relative path. + srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix" + top_srcdir="$ac_dots$ac_given_srcdir" ;; + esac + + case "$ac_given_INSTALL" in + [/$]*) INSTALL="$ac_given_INSTALL" ;; + *) INSTALL="$ac_dots$ac_given_INSTALL" ;; + esac + + echo creating "$ac_file" + rm -f "$ac_file" + configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure." + case "$ac_file" in + *Makefile*) ac_comsub="1i\\ +# $configure_input" ;; + *) ac_comsub= ;; + esac + + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" ` + sed -e "$ac_comsub +s%@configure_input@%$configure_input%g +s%@srcdir@%$srcdir%g +s%@top_srcdir@%$top_srcdir%g +s%@INSTALL@%$INSTALL%g +" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file +fi; done +rm -f conftest.s* + + + + +# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where +# NAME is the cpp macro being defined and VALUE is the value it is being given. +# +# ac_d sets the value in "#define NAME VALUE" lines. +ac_dA='s%^\([ ]*\)#\([ ]*define[ ][ ]*\)' +ac_dB='\([ ][ ]*\)[^ ]*%\1#\2' +ac_dC='\3' +ac_dD='%g' +# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE". +ac_uA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_uB='\([ ]\)%\1#\2define\3' +ac_uC=' ' +ac_uD='\4%g' +# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE". +ac_eA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_eB='$%\1#\2define\3' +ac_eC=' ' +ac_eD='%g' + +if test "${CONFIG_HEADERS+set}" != set; then +EOF +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF +fi +for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + echo creating $ac_file + + rm -f conftest.frag conftest.in conftest.out + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" ` + cat $ac_file_inputs > conftest.in + +EOF + +# Transform confdefs.h into a sed script conftest.vals that substitutes +# the proper values into config.h.in to produce config.h. And first: +# Protect against being on the right side of a sed subst in config.status. +# Protect against being in an unquoted here document in config.status. +rm -f conftest.vals +cat > conftest.hdr <<\EOF +s/[\\&%]/\\&/g +s%[\\$`]%\\&%g +s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp +s%ac_d%ac_u%gp +s%ac_u%ac_e%gp +EOF +sed -n -f conftest.hdr confdefs.h > conftest.vals +rm -f conftest.hdr + +# This sed command replaces #undef with comments. This is necessary, for +# example, in the case of _POSIX_SOURCE, which is predefined and required +# on some systems where configure will not decide to define it. +cat >> conftest.vals <<\EOF +s%^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */% +EOF + +# Break up conftest.vals because some shells have a limit on +# the size of here documents, and old seds have small limits too. + +rm -f conftest.tail +while : +do + ac_lines=`grep -c . conftest.vals` + # grep -c gives empty output for an empty file on some AIX systems. + if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi + # Write a limited-size here document to conftest.frag. + echo ' cat > conftest.frag <> $CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS + echo 'CEOF + sed -f conftest.frag conftest.in > conftest.out + rm -f conftest.in + mv conftest.out conftest.in +' >> $CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail + rm -f conftest.vals + mv conftest.tail conftest.vals +done +rm -f conftest.vals + +cat >> $CONFIG_STATUS <<\EOF + rm -f conftest.frag conftest.h + echo "/* $ac_file. Generated automatically by configure. */" > conftest.h + cat conftest.in >> conftest.h + rm -f conftest.in + if cmp -s $ac_file conftest.h 2>/dev/null; then + echo "$ac_file is unchanged" + rm -f conftest.h + else + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + fi + rm -f $ac_file + mv conftest.h $ac_file + fi +fi; done + + +EOF +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF + +exit 0 +EOF +chmod +x $CONFIG_STATUS +rm -fr confdefs* $ac_clean_files +test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1 + + +if cmp -s config/autoconf.mk config/autoconf.mk.orig; then + echo "config/autoconf.mk is unchanged" + mv -f config/autoconf.mk.orig config/autoconf.mk 2> /dev/null +else + rm -f config/autoconf.mk.orig 2> /dev/null +fi + +# Produce the js-config script at configure time; see the comments for +# 'js-config' in Makefile.in. +echo "$ac_t""invoking make to create js-config script" 1>&6 +$MAKE js-config diff --git a/ape-server/deps/js/src/configure.in b/ape-server/deps/js/src/configure.in new file mode 100755 index 0000000..2541b3f --- /dev/null +++ b/ape-server/deps/js/src/configure.in @@ -0,0 +1,5223 @@ +dnl -*- Mode: Autoconf; tab-width: 4; indent-tabs-mode: nil; -*- +dnl vi: set tabstop=4 shiftwidth=4 expandtab: +dnl ***** BEGIN LICENSE BLOCK ***** +dnl Version: MPL 1.1/GPL 2.0/LGPL 2.1 +dnl +dnl The contents of this file are subject to the Mozilla Public License Version +dnl 1.1 (the "License"); you may not use this file except in compliance with +dnl the License. You may obtain a copy of the License at +dnl http://www.mozilla.org/MPL/ +dnl +dnl Software distributed under the License is distributed on an "AS IS" basis, +dnl WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +dnl for the specific language governing rights and limitations under the +dnl License. +dnl +dnl The Original Code is this file as it was released upon August 6, 1998. +dnl +dnl The Initial Developer of the Original Code is +dnl Christopher Seawood. +dnl Portions created by the Initial Developer are Copyright (C) 1998-2001 +dnl the Initial Developer. All Rights Reserved. +dnl +dnl Contributor(s): +dnl Jamie Zawinski +dnl gettimeofday args check +dnl Christopher Blizzard +dnl gnomefe update & enable-pthreads +dnl Ramiro Estrugo +dnl X11 makedepend support +dnl Insure support. +dnl Henry Sobotka +dnl OS/2 support +dnl Dan Mosedale +dnl LDAP support +dnl Seth Spitzer +dnl xpctools support +dnl Benjamin Smedberg +dnl Howard Chu +dnl MSYS support +dnl Mark Mentovai : +dnl Mac OS X 10.4 support +dnl Giorgio Maone +dnl MSVC l10n compatible version check +dnl Ben Turner +dnl +dnl Alternatively, the contents of this file may be used under the terms of +dnl either the GNU General Public License Version 2 or later (the "GPL"), or +dnl the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +dnl in which case the provisions of the GPL or the LGPL are applicable instead +dnl of those above. If you wish to allow use of your version of this file only +dnl under the terms of either the GPL or the LGPL, and not to allow others to +dnl use your version of this file under the terms of the MPL, indicate your +dnl decision by deleting the provisions above and replace them with the notice +dnl and other provisions required by the GPL or the LGPL. If you do not delete +dnl the provisions above, a recipient may use your version of this file under +dnl the terms of any one of the MPL, the GPL or the LGPL. +dnl +dnl ***** END LICENSE BLOCK ***** + +dnl Process this file with autoconf to produce a configure script. +dnl ======================================================== + +AC_PREREQ(2.13) +AC_INIT(jsapi.h) +AC_CONFIG_AUX_DIR(${srcdir}/build/autoconf) +AC_CONFIG_HEADER(js-config.h) +AC_CANONICAL_SYSTEM +TARGET_CPU="${target_cpu}" +TARGET_VENDOR="${target_vendor}" +TARGET_OS="${target_os}" + +dnl ======================================================== +dnl = +dnl = Don't change the following two lines. Doing so breaks: +dnl = +dnl = CFLAGS="-foo" ./configure +dnl = +dnl ======================================================== +CFLAGS="${CFLAGS=}" +CPPFLAGS="${CPPFLAGS=}" +CXXFLAGS="${CXXFLAGS=}" +LDFLAGS="${LDFLAGS=}" +HOST_CFLAGS="${HOST_CFLAGS=}" +HOST_CXXFLAGS="${HOST_CXXFLAGS=}" +HOST_LDFLAGS="${HOST_LDFLAGS=}" + +dnl ======================================================== +dnl = Preserve certain environment flags passed to configure +dnl = We want sub projects to receive the same flags +dnl = untainted by this configure script +dnl ======================================================== +_SUBDIR_CC="$CC" +_SUBDIR_CXX="$CXX" +_SUBDIR_CFLAGS="$CFLAGS" +_SUBDIR_CPPFLAGS="$CPPFLAGS" +_SUBDIR_CXXFLAGS="$CXXFLAGS" +_SUBDIR_LDFLAGS="$LDFLAGS" +_SUBDIR_HOST_CC="$HOST_CC" +_SUBDIR_HOST_CFLAGS="$HOST_CFLAGS" +_SUBDIR_HOST_CXXFLAGS="$HOST_CXXFLAGS" +_SUBDIR_HOST_LDFLAGS="$HOST_LDFLAGS" +_SUBDIR_CONFIG_ARGS="$ac_configure_args" + +dnl Set the version number of the libs included with mozilla +dnl ======================================================== +NSPR_VERSION=4 + +dnl Set the minimum version of toolkit libs used by mozilla +dnl ======================================================== +PERL_VERSION=5.006 +WINDRES_VERSION=2.14.90 +W32API_VERSION=3.8 + +MSMANIFEST_TOOL= + +dnl Set various checks +dnl ======================================================== +MISSING_X= +AC_PROG_AWK + +dnl Initialize the Pthread test variables early so they can be +dnl overridden by each platform. +dnl ======================================================== +USE_PTHREADS= +_PTHREAD_LDFLAGS="" + +dnl Do not allow a separate objdir build if a srcdir build exists. +dnl ============================================================== +_topsrcdir=`cd \`dirname $0\`; pwd` +_objdir=`pwd` + +if test "$_topsrcdir" != "$_objdir" +then + # Check for a couple representative files in the source tree + _conflict_files= + for file in $_topsrcdir/Makefile $_topsrcdir/config/autoconf.mk; do + if test -f $file; then + _conflict_files="$_conflict_files $file" + fi + done + if test "$_conflict_files"; then + echo "***" + echo "* Your source tree contains these files:" + for file in $_conflict_files; do + echo "* $file" + done + cat 1>&2 <<-EOF + * This indicates that you previously built in the source tree. + * A source tree build can confuse the separate objdir build. + * + * To clean up the source tree: + * 1. cd $_topsrcdir + * 2. gmake distclean + *** + EOF + exit 1 + break + fi +fi +MOZ_BUILD_ROOT=`pwd` + +dnl Choose where to put the 'dist' directory. +dnl ============================================================== + +MOZ_ARG_WITH_STRING(dist-dir, +[ --with-dist-dir=DIR Use DIR as 'dist' staging area. DIR may be + relative to the top of SpiderMonkey build tree, + or absolute.], + TOP_DIST=$withval, + TOP_DIST=dist) +AC_SUBST(TOP_DIST) + +dnl Default to MSVC for win32 and gcc-4.2 for darwin +dnl ============================================================== +if test -z "$CROSS_COMPILE"; then +case "$target" in +*-cygwin*|*-mingw*|*-msvc*|*-mks*) + if test -z "$CC"; then CC=cl; fi + if test -z "$CXX"; then CXX=cl; fi + if test -z "$CPP"; then CPP="cl -E -nologo"; fi + if test -z "$CXXCPP"; then CXXCPP="cl -TP -E -nologo"; ac_cv_prog_CXXCPP="$CXXCPP"; fi + if test -z "$LD"; then LD=link; fi + if test -z "$AS"; then + case "${target_cpu}" in + i*86) + AS=ml; + ;; + x86_64) + AS=ml64; + ;; + esac + fi + if test -z "$MIDL"; then MIDL=midl; fi + ;; +*-darwin*) + if test -z "$CC"; then CC=gcc-4.2; fi + if test -z "$CXX"; then CXX=g++-4.2; fi + ;; +esac +fi + +COMPILE_ENVIRONMENT=1 +MOZ_ARG_ENABLE_BOOL(compile-environment, +[ --disable-compile-environment + Disable compiler/library checks.], + COMPILE_ENVIRONMENT=1, + COMPILE_ENVIRONMENT= ) + +dnl ======================================================== +dnl Checks for compilers. +dnl ======================================================== + +if test "$COMPILE_ENVIRONMENT"; then + +if test "$target" != "$host"; then + echo "cross compiling from $host to $target" + + _SAVE_CC="$CC" + _SAVE_CFLAGS="$CFLAGS" + _SAVE_LDFLAGS="$LDFLAGS" + + AC_MSG_CHECKING([for host c compiler]) + AC_CHECK_PROGS(HOST_CC, $HOST_CC gcc cc /usr/ucb/cc cl icc, "") + if test -z "$HOST_CC"; then + AC_MSG_ERROR([no acceptable c compiler found in \$PATH]) + fi + AC_MSG_RESULT([$HOST_CC]) + AC_MSG_CHECKING([for host c++ compiler]) + AC_CHECK_PROGS(HOST_CXX, $HOST_CXX $CCC c++ g++ gcc CC cxx cc++ cl icc, "") + if test -z "$HOST_CXX"; then + AC_MSG_ERROR([no acceptable c++ compiler found in \$PATH]) + fi + AC_MSG_RESULT([$HOST_CXX]) + + if test -z "$HOST_CFLAGS"; then + HOST_CFLAGS="$CFLAGS" + fi + if test -z "$HOST_CXXFLAGS"; then + HOST_CXXFLAGS="$CXXFLAGS" + fi + if test -z "$HOST_LDFLAGS"; then + HOST_LDFLAGS="$LDFLAGS" + fi + AC_CHECK_PROGS(HOST_RANLIB, $HOST_RANLIB ranlib, ranlib, :) + AC_CHECK_PROGS(HOST_AR, $HOST_AR ar, ar, :) + CC="$HOST_CC" + CFLAGS="$HOST_CFLAGS" + LDFLAGS="$HOST_LDFLAGS" + + AC_MSG_CHECKING([whether the host c compiler ($HOST_CC $HOST_CFLAGS $HOST_LDFLAGS) works]) + AC_TRY_COMPILE([], [return(0);], + [ac_cv_prog_hostcc_works=1 AC_MSG_RESULT([yes])], + AC_MSG_ERROR([installation or configuration problem: host compiler $HOST_CC cannot create executables.]) ) + + CC="$HOST_CXX" + CFLAGS="$HOST_CXXFLAGS" + + AC_MSG_CHECKING([whether the host c++ compiler ($HOST_CXX $HOST_CXXFLAGS $HOST_LDFLAGS) works]) + AC_TRY_COMPILE([], [return(0);], + [ac_cv_prog_hostcxx_works=1 AC_MSG_RESULT([yes])], + AC_MSG_ERROR([installation or configuration problem: host compiler $HOST_CXX cannot create executables.]) ) + + CC=$_SAVE_CC + CFLAGS=$_SAVE_CFLAGS + LDFLAGS=$_SAVE_LDFLAGS + + case "$build:$target" in + powerpc-apple-darwin8*:i?86-apple-darwin*) + dnl The Darwin cross compiler doesn't necessarily point itself at a + dnl root that has libraries for the proper architecture, it defaults + dnl to the system root. The libraries in the system root on current + dnl versions of PPC OS X 10.4 aren't fat, so these target compiler + dnl checks will fail. Fake a working SDK in that case. + _SAVE_CFLAGS=$CFLAGS + _SAVE_CXXFLAGS=$CXXLAGS + CFLAGS="-isysroot /Developer/SDKs/MacOSX10.4u.sdk $CFLAGS" + CXXFLAGS="-isysroot /Developer/SDKs/MacOSX10.4u.sdk $CXXFLAGS" + ;; + esac + + case "$target" in + *symbian*) + AC_ARG_WITH(symbian-sdk, + [ --with-symbian-sdk=SYMBIAN_SDK_DIR + The path to the Symbian SDK], + SYMBIAN_SDK_DIR=$withval) + + OS_EXE_CFLAGS="$OS_EXE_CFLAGS -D__EXE__" + CFLAGS="-MD -nostdinc" + SYMBIAN_SYS_INCLUDE="-I$SYMBIAN_SDK_DIR/Epoc32/include -I$SYMBIAN_SDK_DIR/Epoc32/include/variant -I$SYMBIAN_SDK_DIR/Epoc32/include/stdapis" + + case "$target" in + *-symbianelf) + OS_TARGET=GCCE + CC=arm-none-symbianelf-gcc.exe + CXX=arm-none-symbianelf-g++.exe + LD=arm-none-symbianelf-ld.exe + AR=arm-none-symbianelf-ar.exe + CPP=arm-none-symbianelf-cpp.exe + CFLAGS="$CFLAGS -c -Wall -Wno-unknown-pragmas -fexceptions -march=armv5t -mapcs -pipe -msoft-float" + CXXFLAGS="$CFLAGS -Wno-ctor-dtor-privacy" + GCCE_INCLUDE="-include $SYMBIAN_SDK_DIR/EPOC32/INCLUDE/GCCE/GCCE.h -D__PRODUCT_INCLUDE__=$SYMBIAN_SDK_DIR/Epoc32/include/variant/Symbian_OS_v9.2.hrh" + CFLAGS="$CFLAGS ${GCCE_INCLUDE} -x c" + CXXFLAGS="$CXXFLAGS ${GCCE_INCLUDE} -x c++" + CPPFLAGS="$CPPFLAGS ${SYMBIAN_SYS_INCLUDE}" + ;; + *-symbianwinscw) + dnl TODO: add emulator build code + OS_TARGET=WINSCW + ;; + esac + ;; + esac + + AC_CHECK_PROGS(CC, $CC "${target_alias}-gcc" "${target}-gcc", :) + unset ac_cv_prog_CC + AC_PROG_CC + AC_CHECK_PROGS(CXX, $CXX "${target_alias}-g++" "${target}-g++", :) + unset ac_cv_prog_CXX + AC_PROG_CXX + + case "$build:$target" in + powerpc-apple-darwin8*:i?86-apple-darwin*) + dnl Revert the changes made above. From this point on, the target + dnl compiler will never be used without applying the SDK to CFLAGS + dnl (see --with-macos-sdk below). + CFLAGS=$_SAVE_CFLAGS + CXXFLAGS=$_SAVE_CXXFLAGS + ;; + esac + + AC_CHECK_PROGS(RANLIB, $RANLIB "${target_alias}-ranlib" "${target}-ranlib", :) + AC_CHECK_PROGS(AR, $AR "${target_alias}-ar" "${target}-ar", :) + MOZ_PATH_PROGS(AS, $AS "${target_alias}-as" "${target}-as", :) + AC_CHECK_PROGS(LD, $LD "${target_alias}-ld" "${target}-ld", :) + AC_CHECK_PROGS(STRIP, $STRIP "${target_alias}-strip" "${target}-strip", :) + AC_CHECK_PROGS(WINDRES, $WINDRES "${target_alias}-windres" "${target}-windres", :) + AC_DEFINE(CROSS_COMPILE) + + dnl If we cross compile for ppc on Mac OS X x86, cross_compiling will + dnl have erroneously been set to "no", because the x86 build host is + dnl able to run ppc code in a translated environment, making a cross + dnl compiler appear native. So we override that here. + cross_compiling=yes +else + AC_PROG_CC + AC_PROG_CXX + AC_PROG_RANLIB + MOZ_PATH_PROGS(AS, $AS as, $CC) + AC_CHECK_PROGS(AR, ar, :) + AC_CHECK_PROGS(LD, ld, :) + AC_CHECK_PROGS(STRIP, strip, :) + AC_CHECK_PROGS(WINDRES, windres, :) + if test -z "$HOST_CC"; then + HOST_CC="$CC" + fi + if test -z "$HOST_CFLAGS"; then + HOST_CFLAGS="$CFLAGS" + fi + if test -z "$HOST_CXX"; then + HOST_CXX="$CXX" + fi + if test -z "$HOST_CXXFLAGS"; then + HOST_CXXFLAGS="$CXXFLAGS" + fi + if test -z "$HOST_LDFLAGS"; then + HOST_LDFLAGS="$LDFLAGS" + fi + if test -z "$HOST_RANLIB"; then + HOST_RANLIB="$RANLIB" + fi + if test -z "$HOST_AR"; then + HOST_AR="$AR" + fi +fi + +GNU_AS= +GNU_LD= +GNU_CC= +GNU_CXX= +CC_VERSION='N/A' +CXX_VERSION='N/A' +if test "$GCC" = "yes"; then + GNU_CC=1 + CC_VERSION=`$CC -v 2>&1 | grep 'gcc version'` +fi +if test "$GXX" = "yes"; then + GNU_CXX=1 + CXX_VERSION=`$CXX -v 2>&1 | grep 'gcc version'` +fi +if test "`echo | $AS -v 2>&1 | grep -c GNU`" != "0"; then + GNU_AS=1 +fi +if test "`echo | $LD -v 2>&1 | grep -c GNU`" != "0"; then + GNU_LD=1 +fi +if test "$GNU_CC"; then + if `$CC -print-prog-name=ld` -v 2>&1 | grep -c GNU >/dev/null; then + GCC_USE_GNU_LD=1 + fi +fi + +INTEL_CC= +INTEL_CXX= +if test "$GCC" = yes; then + if test "`$CC -help 2>&1 | grep -c 'Intel(R) C++ Compiler'`" != "0"; then + INTEL_CC=1 + fi +fi + +if test "$GXX" = yes; then + if test "`$CXX -help 2>&1 | grep -c 'Intel(R) C++ Compiler'`" != "0"; then + INTEL_CXX=1 + fi +fi + +dnl Special win32 checks +dnl ======================================================== +case "$target" in +*-wince) + WINVER=500 + ;; +*) + if test -n "$GNU_CC"; then + WINVER=501 + else + WINVER=500 + fi + ;; +esac + +case "$target" in +*-cygwin*|*-mingw*|*-msvc*|*-mks*|*-wince) + if test "$GCC" != "yes"; then + # Check to see if we are really running in a msvc environemnt + _WIN32_MSVC=1 + + # Make sure compilers are valid + CFLAGS="$CFLAGS -TC -nologo" + CXXFLAGS="$CXXFLAGS -TP -nologo" + AC_LANG_SAVE + AC_LANG_C + AC_TRY_COMPILE([#include ], + [ printf("Hello World\n"); ],, + AC_MSG_ERROR([\$(CC) test failed. You must have MS VC++ in your path to build.]) ) + + AC_LANG_CPLUSPLUS + AC_TRY_COMPILE([#include ], + [ unsigned *test = new unsigned(42); ],, + AC_MSG_ERROR([\$(CXX) test failed. You must have MS VC++ in your path to build.]) ) + AC_LANG_RESTORE + + changequote(,) + _MSVC_VER_FILTER='s|.* ([0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?).*|\1|p' + changequote([,]) + + # Determine compiler version + CC_VERSION=`"${CC}" -v 2>&1 | sed -nre "$_MSVC_VER_FILTER"` + _CC_MAJOR_VERSION=`echo ${CC_VERSION} | $AWK -F\. '{ print $1 }'` + _CC_MINOR_VERSION=`echo ${CC_VERSION} | $AWK -F\. '{ print $2 }'` + _CC_RELEASE=`echo ${CC_VERSION} | $AWK -F\. '{ print $3 }'` + _CC_BUILD=`echo ${CC_VERSION} | $AWK -F\. '{ print $4 }'` + _MSC_VER=${_CC_MAJOR_VERSION}${_CC_MINOR_VERSION} + + CXX_VERSION=`"${CXX}" -v 2>&1 | sed -nre "$_MSVC_VER_FILTER"` + _CXX_MAJOR_VERSION=`echo ${CXX_VERSION} | $AWK -F\. '{ print $1 }'` + + if test "$_CC_MAJOR_VERSION" != "$_CXX_MAJOR_VERSION"; then + AC_MSG_ERROR([The major versions of \$CC and \$CXX do not match.]) + fi + if test "$_CC_MAJOR_VERSION" = "13"; then + _CC_SUITE=7 + elif test "$_CC_MAJOR_VERSION" = "14"; then + _CC_SUITE=8 + CXXFLAGS="$CXXFLAGS -Zc:wchar_t-" + dnl -DYNAMICBASE is only supported on VC8SP1 or newer, + dnl so be very specific here! + dnl VC8 is 14.00.50727.42, VC8SP1 is 14.00.50727.762 + if test $_CC_RELEASE -gt 50727; then + _USE_DYNAMICBASE=1 + elif test $_CC_BUILD -ge 762; then + _USE_DYNAMICBASE=1 + fi + AC_DEFINE(_CRT_SECURE_NO_DEPRECATE) + AC_DEFINE(_CRT_NONSTDC_NO_DEPRECATE) + elif test "$_CC_MAJOR_VERSION" = "15"; then + _CC_SUITE=9 + CXXFLAGS="$CXXFLAGS -Zc:wchar_t-" + LDFLAGS="$LDFLAGS -MANIFESTUAC:NO" + _USE_DYNAMICBASE=1 + AC_DEFINE(_CRT_SECURE_NO_WARNINGS) + AC_DEFINE(_CRT_NONSTDC_NO_WARNINGS) + else + AC_MSG_ERROR([This version of the MSVC compiler, $CC_VERSION , is unsupported.]) + fi + + _MOZ_RTTI_FLAGS_ON='-GR' + _MOZ_RTTI_FLAGS_OFF='-GR-' + _MOZ_EXCEPTIONS_FLAGS_ON='-EHsc' + _MOZ_EXCEPTIONS_FLAGS_OFF='' + + if test -n "$WIN32_REDIST_DIR"; then + WIN32_REDIST_DIR=`cd "$WIN32_REDIST_DIR" && pwd` + fi + + # bug #249782 + # ensure that mt.exe is Microsoft (R) Manifest Tool and not magnetic tape manipulation utility (or something else) + if test "$_CC_SUITE" -ge "8"; then + changequote(,) + _MSMT_VER_FILTER='s|.* \([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*|\1|p' + changequote([,]) + + MSMT_TOOL=`mt 2>&1|grep 'Microsoft (R) Manifest Tool'` + if test -n "$MSMT_TOOL"; then + MSMANIFEST_TOOL_VERSION=`echo ${MSMT_TOOL}|sed -ne "$_MSMT_VER_FILTER"` + if test -z "$MSMANIFEST_TOOL_VERSION"; then + AC_MSG_WARN([Unknown version of the Microsoft (R) Manifest Tool.]) + fi + MSMANIFEST_TOOL=1 + unset MSMT_TOOL + else + AC_MSG_ERROR([Microsoft (R) Manifest Tool must be in your \$PATH.]) + fi + fi + + # Check linker version + _LD_FULL_VERSION=`"${LD}" -v 2>&1 | sed -nre "$_MSVC_VER_FILTER"` + _LD_MAJOR_VERSION=`echo ${_LD_FULL_VERSION} | $AWK -F\. '{ print $1 }'` + if test "$_LD_MAJOR_VERSION" != "$_CC_SUITE"; then + AC_MSG_ERROR([The linker major version, $_LD_FULL_VERSION, does not match the compiler suite version, $_CC_SUITE.]) + fi + INCREMENTAL_LINKER=1 + + unset _MSVC_VER_FILTER + + else + # Check w32api version + _W32API_MAJOR_VERSION=`echo $W32API_VERSION | $AWK -F\. '{ print $1 }'` + _W32API_MINOR_VERSION=`echo $W32API_VERSION | $AWK -F\. '{ print $2 }'` + AC_MSG_CHECKING([for w32api version >= $W32API_VERSION]) + AC_TRY_COMPILE([#include ], + #if (__W32API_MAJOR_VERSION < $_W32API_MAJOR_VERSION) || \ + (__W32API_MAJOR_VERSION == $_W32API_MAJOR_VERSION && \ + __W32API_MINOR_VERSION < $_W32API_MINOR_VERSION) + #error "test failed." + #endif + , [ res=yes ], [ res=no ]) + AC_MSG_RESULT([$res]) + if test "$res" != "yes"; then + AC_MSG_ERROR([w32api version $W32API_VERSION or higher required.]) + fi + # Check windres version + AC_MSG_CHECKING([for windres version >= $WINDRES_VERSION]) + _WINDRES_VERSION=`${WINDRES} --version 2>&1 | grep -i windres 2>/dev/null | $AWK '{ print $3 }'` + AC_MSG_RESULT([$_WINDRES_VERSION]) + _WINDRES_MAJOR_VERSION=`echo $_WINDRES_VERSION | $AWK -F\. '{ print $1 }'` + _WINDRES_MINOR_VERSION=`echo $_WINDRES_VERSION | $AWK -F\. '{ print $2 }'` + _WINDRES_RELEASE_VERSION=`echo $_WINDRES_VERSION | $AWK -F\. '{ print $3 }'` + WINDRES_MAJOR_VERSION=`echo $WINDRES_VERSION | $AWK -F\. '{ print $1 }'` + WINDRES_MINOR_VERSION=`echo $WINDRES_VERSION | $AWK -F\. '{ print $2 }'` + WINDRES_RELEASE_VERSION=`echo $WINDRES_VERSION | $AWK -F\. '{ print $3 }'` + if test "$_WINDRES_MAJOR_VERSION" -lt "$WINDRES_MAJOR_VERSION" -o \ + "$_WINDRES_MAJOR_VERSION" -eq "$WINDRES_MAJOR_VERSION" -a \ + "$_WINDRES_MINOR_VERSION" -lt "$WINDRES_MINOR_VERSION" -o \ + "$_WINDRES_MAJOR_VERSION" -eq "$WINDRES_MAJOR_VERSION" -a \ + "$_WINDRES_MINOR_VERSION" -eq "$WINDRES_MINOR_VERSION" -a \ + "$_WINDRES_RELEASE_VERSION" -lt "$WINDRES_RELEASE_VERSION" + then + AC_MSG_ERROR([windres version $WINDRES_VERSION or higher is required to build.]) + fi + fi # !GNU_CC + + AC_DEFINE_UNQUOTED(WINVER,0x$WINVER) + AC_DEFINE_UNQUOTED(_WIN32_WINNT,0x$WINVER) + # Require OS features provided by IE 5.0 + AC_DEFINE_UNQUOTED(_WIN32_IE,0x0500) + ;; +esac + +dnl Test breaks icc on OS/2 && MSVC +if test "$CC" != "icc" -a -z "$_WIN32_MSVC"; then + AC_PROG_CC_C_O + if grep "NO_MINUS_C_MINUS_O 1" ./confdefs.h >/dev/null; then + USING_HCC=1 + _OLDCC=$CC + _OLDCXX=$CXX + CC="${srcdir}/build/hcc '$CC'" + CXX="${srcdir}/build/hcpp '$CXX'" + fi +fi + +AC_PROG_CPP +AC_PROG_CXXCPP + +if test -n "$_WIN32_MSVC"; then + SKIP_PATH_CHECKS=1 + SKIP_COMPILER_CHECKS=1 + SKIP_LIBRARY_CHECKS=1 + + # Since we're skipping compiler and library checks, hard-code + # some facts here. + + # Common to all MSVC environments: + # Windows lacks , but has __int8, and so on. + AC_DEFINE(JS_HAVE___INTN) + + case "$target" in + *-wince) + AC_DEFINE(HAVE_SYSTEMTIMETOFILETIME) + AC_DEFINE(JS_CRTDEFS_H_HAS_INTPTR_T) + ;; + *) + AC_DEFINE(HAVE_SYSTEMTIMETOFILETIME) + AC_DEFINE(HAVE_GETSYSTEMTIMEASFILETIME) + # Windows defines intptr_t and uintptr_t. + # VS2005: http://msdn.microsoft.com/en-us/library/323b6b3k(VS.80).aspx + # VS2008: http://msdn.microsoft.com/en-us/library/323b6b3k.aspx + AC_DEFINE(JS_STDDEF_H_HAS_INTPTR_T) + ;; + esac +fi + +fi # COMPILE_ENVIRONMENT + +if test "$cross_compiling" = "yes"; then + CROSS_COMPILE=1 +else + CROSS_COMPILE= +fi + +AC_SUBST(_MSC_VER) + +AC_SUBST(GNU_AS) +AC_SUBST(GNU_LD) +AC_SUBST(GNU_CC) +AC_SUBST(GNU_CXX) +AC_SUBST(INTEL_CC) +AC_SUBST(INTEL_CXX) + +dnl ======================================================== +dnl Checks for programs. +dnl ======================================================== +AC_PROG_INSTALL +AC_PROG_LN_S +MOZ_PATH_PROGS(PERL, $PERL perl5 perl ) +if test -z "$PERL" || test "$PERL" = ":"; then + AC_MSG_ERROR([perl not found in \$PATH]) +fi + +if test -z "$TINDERBOX_SKIP_PERL_VERSION_CHECK"; then +AC_MSG_CHECKING([for minimum required perl version >= $PERL_VERSION]) +_perl_version=`PERL_VERSION=$PERL_VERSION $PERL -e 'print "$]"; if ($] >= $ENV{PERL_VERSION}) { exit(0); } else { exit(1); }' 2>&5` +_perl_res=$? +AC_MSG_RESULT([$_perl_version]) + +if test "$_perl_res" != 0; then + AC_MSG_ERROR([Perl $PERL_VERSION or higher is required.]) +fi +fi + +AC_MSG_CHECKING([for full perl installation]) +_perl_archlib=`$PERL -e 'use Config; if ( -d $Config{archlib} ) { exit(0); } else { exit(1); }' 2>&5` +_perl_res=$? +if test "$_perl_res" != 0; then + AC_MSG_RESULT([no]) + AC_MSG_ERROR([Cannot find Config.pm or \$Config{archlib}. A full perl installation is required.]) +else + AC_MSG_RESULT([yes]) +fi + +MOZ_PATH_PROGS(PYTHON, $PYTHON python) +if test -z "$PYTHON"; then + AC_MSG_ERROR([python was not found in \$PATH]) +fi + +if test -z "$COMPILE_ENVIRONMENT"; then + NSINSTALL_BIN='$(PYTHON) $(topsrcdir)/config/nsinstall.py' +fi +AC_SUBST(NSINSTALL_BIN) + +MOZ_PATH_PROG(DOXYGEN, doxygen, :) +MOZ_PATH_PROG(WHOAMI, whoami, :) +MOZ_PATH_PROG(AUTOCONF, autoconf, :) +MOZ_PATH_PROG(SYSTEM_MAKEDEPEND, makedepend) +MOZ_PATH_PROG(XARGS, xargs) +if test -z "$XARGS" || test "$XARGS" = ":"; then + AC_MSG_ERROR([xargs not found in \$PATH .]) +fi + +if test "$COMPILE_ENVIRONMENT"; then + +dnl ======================================================== +dnl = Mac OS X toolchain support +dnl ======================================================== + +case "$target_os" in +darwin*) + dnl Current known valid versions for GCC_VERSION are 2.95.2 3.1 3.3 4.0. + dnl 4.0 identifies itself as 4.0.x, so strip the decidecimal for + dnl the environment and includedir purposes (when using an SDK, below), + dnl but remember the full version number for the libdir (SDK). + changequote(,) + GCC_VERSION_FULL=`echo $CXX_VERSION | $PERL -pe 's/^.*gcc version ([^ ]*).*/$1/'` + GCC_VERSION=`echo $GCC_VERSION_FULL | $PERL -pe '(split(/\./))[0]>=4&&s/(^\d*\.\d*).*/$1/;'` + changequote([,]) + if test "$GCC_VERSION_FULL" = "4.0.0" ; then + dnl Bug 280479, but this keeps popping up in bug 292530 too because + dnl 4.0.0/4061 is the default compiler in Tiger. + changequote(,) + GCC_BUILD=`echo $CXX_VERSION | $PERL -pe 's/^.*build ([^ )]*).*/$1/'` + changequote([,]) + if test "$GCC_BUILD" = "4061" ; then + AC_MSG_ERROR([You are attempting to use Apple gcc 4.0 build 4061. +This compiler was supplied with Xcode 2.0, and contains bugs that prevent it +from building Mozilla. Upgrade to Xcode 2.1 or later.]) + fi + fi + + dnl xcodebuild needs GCC_VERSION defined in the environment, since it + dnl doesn't respect the CC/CXX setting. With GCC_VERSION set, it will use + dnl /usr/bin/g(cc|++)-$GCC_VERSION. + MOZ_PATH_PROGS(PBBUILD, pbbuild xcodebuild pbxbuild) + + case "$PBBUILD" in + *xcodebuild*) + changequote(,) + XCODEBUILD_VERSION=`$PBBUILD -version 2>/dev/null | xargs | sed -e 's/.*DevToolsCore-\([0-9]*\).*/\1/'` + changequote([,]) + if test -n "$XCODEBUILD_VERSION" && test "$XCODEBUILD_VERSION" -ge 620 ; then + HAS_XCODE_2_1=1; + fi + ;; + esac + + dnl sdp was formerly in /Developer/Tools. As of Mac OS X 10.4 (Darwin 8), + dnl it has moved into /usr/bin. + MOZ_PATH_PROG(SDP, sdp, :, [$PATH:/usr/bin:/Developer/Tools]) + ;; +esac + +AC_SUBST(GCC_VERSION) +AC_SUBST(XCODEBUILD_VERSION) +AC_SUBST(HAS_XCODE_2_1) + +dnl The universal machinery sets UNIVERSAL_BINARY to inform packager.mk +dnl that a universal binary is being produced. +AC_SUBST(UNIVERSAL_BINARY) + +dnl ======================================================== +dnl Check for MacOS deployment target version +dnl ======================================================== + +MOZ_ARG_ENABLE_STRING(macos-target, + [ --enable-macos-target=VER (default=10.4) + Set the minimum MacOS version needed at runtime], + [_MACOSX_DEPLOYMENT_TARGET=$enableval]) + +case "$target" in +*-darwin*) + if test -n "$_MACOSX_DEPLOYMENT_TARGET" ; then + dnl Use the specified value + export MACOSX_DEPLOYMENT_TARGET=$_MACOSX_DEPLOYMENT_TARGET + AC_DEFINE_UNQUOTED(__ENVIRONMENT_MAC_OS_X_VERION_MIN_REQUIRED__,$_MACOSX_DEPLOYMENT_TARGET) + elif test -z "$MACOSX_DEPLOYMENT_TARGET" ; then + dnl No value specified on the command line or in the environment, + dnl use the lesser of the application's minimum or the architecture's + dnl minimum. + export MACOSX_DEPLOYMENT_TARGET=10.4 + fi + ;; +esac + +AC_SUBST(MACOSX_DEPLOYMENT_TARGET) + +dnl ======================================================== +dnl = Mac OS X SDK support +dnl ======================================================== +MACOS_SDK_DIR= +NEXT_ROOT= +MOZ_ARG_WITH_STRING(macos-sdk, +[ --with-macos-sdk=dir Location of platform SDK to use (Mac OS X only)], + MACOS_SDK_DIR=$withval) + +dnl MACOS_SDK_DIR will be set to the SDK location whenever one is in use. +dnl NEXT_ROOT will be set and exported only if it's needed. +AC_SUBST(MACOS_SDK_DIR) +AC_SUBST(NEXT_ROOT) + +if test "$MACOS_SDK_DIR"; then + dnl Sync this section with the ones in NSPR and NSS. + dnl Changes to the cross environment here need to be accounted for in + dnl the libIDL checks (below) and xpidl build. + + if test ! -d "$MACOS_SDK_DIR"; then + AC_MSG_ERROR([SDK not found. When using --with-macos-sdk, you must +specify a valid SDK. SDKs are installed when the optional cross-development +tools are selected during the Xcode/Developer Tools installation.]) + fi + + GCC_VERSION_MAJOR=`echo $GCC_VERSION_FULL | $PERL -pe 's/(^\d*).*/$1/;'` + if test "$GCC_VERSION_MAJOR" -lt "4" ; then + AC_MSG_ERROR([You need to upgrade the compiler version to 4.x]) + else + CFLAGS="$CFLAGS -isysroot ${MACOS_SDK_DIR}" + CXXFLAGS="$CXXFLAGS -isysroot ${MACOS_SDK_DIR}" + + dnl CPP/CXXCPP needs to be set for AC_CHECK_HEADER. + CPP="$CPP -isysroot ${MACOS_SDK_DIR}" + CXXCPP="$CXXCPP -isysroot ${MACOS_SDK_DIR}" + + if test "$GCC_VERSION_FULL" = "4.0.0" ; then + dnl If gcc >= 4.0, we're guaranteed to be on Tiger, which has an ld + dnl that supports -syslibroot. Don't set NEXT_ROOT because it will + dnl be ignored and cause warnings when -syslibroot is specified. + dnl gcc 4.0.1 will pass -syslibroot to ld automatically based on + dnl the -isysroot it receives, so this is only needed with 4.0.0. + LDFLAGS="$LDFLAGS -Wl,-syslibroot,${MACOS_SDK_DIR}" + fi + fi + + AC_LANG_SAVE + AC_MSG_CHECKING([for valid compiler/Mac OS X SDK combination]) + AC_LANG_CPLUSPLUS + AC_TRY_COMPILE([#include + int main() { return 0; }], + result=yes, + result=no) + AC_LANG_RESTORE + AC_MSG_RESULT($result) + + if test "$result" = "no" ; then + AC_MSG_ERROR([The selected compiler and Mac OS X SDK are incompatible.]) + fi +fi + +fi # COMPILE_ENVIRONMENT + +if test -z "$MAKE"; then + case "$host_os" in + cygwin*|mingw*|mks*|msvc*) + MOZ_PATH_PROGS(MAKE, $MAKE make gmake, :) + ;; + *) + MOZ_PATH_PROGS(MAKE, $MAKE gmake make, :) + ;; + esac +fi + +if test "$COMPILE_ENVIRONMENT"; then + +AC_PATH_XTRA + +XCFLAGS="$X_CFLAGS" + +fi # COMPILE_ENVIRONMENT + +dnl ======================================================== +dnl set the defaults first +dnl ======================================================== +AS_BIN=$AS +AR_FLAGS='cr $@' +AR_LIST='$(AR) t' +AR_EXTRACT='$(AR) x' +AR_DELETE='$(AR) d' +AS='$(CC)' +AS_DASH_C_FLAG='-c' +DLL_PREFIX=lib +LIB_PREFIX=lib +DLL_SUFFIX=.so +OBJ_SUFFIX=o +LIB_SUFFIX=a +ASM_SUFFIX=s +IMPORT_LIB_SUFFIX= +TARGET_MD_ARCH=unix +DIRENT_INO=d_ino +CYGWIN_WRAPPER= +WIN_TOP_SRC= +MOZ_USER_DIR=".mozilla" +HOST_AR='$(AR)' +HOST_AR_FLAGS='$(AR_FLAGS)' + +MOZ_JS_LIBS='-L$(libdir) -lmozjs' +MOZ_FIX_LINK_PATHS='-Wl,-rpath-link,$(LIBXUL_DIST)/bin -Wl,-rpath-link,$(PREFIX)/lib' + +MOZ_COMPONENT_NSPR_LIBS='-L$(LIBXUL_DIST)/bin $(NSPR_LIBS)' + +USE_DEPENDENT_LIBS=1 + +_PLATFORM_DEFAULT_TOOLKIT=cairo-gtk2 + +MOZ_ENABLE_POSTSCRIPT=1 + +if test -n "$CROSS_COMPILE"; then + OS_TARGET="${target_os}" + OS_ARCH=`echo $target_os | sed -e 's|/|_|g'` + OS_RELEASE= + case "${target_os}" in + linux*) OS_ARCH=Linux OS_TARGET=Linux ;; + kfreebsd*-gnu) OS_ARCH=GNU_kFreeBSD OS_TARGET=GNU_kFreeBSD ;; + solaris*) OS_ARCH=SunOS OS_RELEASE=5 ;; + mingw*) OS_ARCH=WINNT ;; + wince*) OS_ARCH=WINCE ;; + darwin*) OS_ARCH=Darwin OS_TARGET=Darwin ;; + esac +else + OS_TARGET=`uname -s` + OS_ARCH=`uname -s | sed -e 's|/|_|g'` + OS_RELEASE=`uname -r` +fi + +# Before this used `uname -m` when not cross compiling +# but that breaks when you have a 64 bit kernel with a 32 bit userland. +OS_TEST="${target_cpu}" + +_COMPILER_PREFIX= + +HOST_OS_ARCH=`echo $host_os | sed -e 's|/|_|g'` + +####################################################################### +# Master "Core Components" macros for getting the OS target # +####################################################################### + +# +# If OS_TARGET is not specified, it defaults to $(OS_ARCH), i.e., no +# cross-compilation. +# + +# +# Define and override various archtecture-specific variables, including +# HOST_OS_ARCH +# OS_ARCH +# OS_TEST +# OS_TARGET +# OS_RELEASE +# OS_MINOR_RELEASE +# + +case "$HOST_OS_ARCH" in +cygwin*|mingw*|mks*|msvc*) + HOST_OS_ARCH=WINNT + ;; +linux*) + HOST_OS_ARCH=Linux + ;; +solaris*) + HOST_OS_ARCH=SunOS + SOLARIS_SUNPRO_CC= + SOLARIS_SUNPRO_CXX= + if test -z "$GNU_CC"; then + if test "`$CC -V 2>&1 | egrep -c 'Sun.*C '`" != "0"; then + SOLARIS_SUNPRO_CC=1 + fi + fi + + if test -z "$GNU_CXX"; then + if test "`$CXX -V 2>&1 | egrep -c 'Sun.*C\+\+ '`" != "0"; then + SOLARIS_SUNPRO_CXX=1 + fi + fi + AC_SUBST(SOLARIS_SUNPRO_CC) + AC_SUBST(SOLARIS_SUNPRO_CXX) + ;; +BSD_386) + HOST_OS_ARCH=BSD + ;; +dgux) + HOST_OS_ARCH=DGUX + ;; +IRIX64) + HOST_OS_ARCH=IRIX + ;; +UNIX_SV) + if "`cat /etc/bcheckrc | grep -c NCR 2>/dev/null`" != "0"; then + HOST_OS_ARCH=NCR + else + HOST_OS_ARCH=UNIXWARE + fi + ;; +ncr) + HOST_OS_ARCH=NCR + ;; +UNIX_SYSTEM_V) + HOST_OS_ARCH=NEC + ;; +OSF1) + ;; +*OpenVMS*) + HOST_OS_ARCH=OpenVMS + ;; +OS_2) + HOST_OS_ARCH=OS2 + ;; +QNX) + ;; +SCO_SV) + HOST_OS_ARCH=SCOOS + ;; +SINIX-N | SINIX-Y | SINIX-Z |ReliantUNIX-M) + HOST_OS_ARCH=SINIX + ;; +UnixWare) + HOST_OS_ARCH=UNIXWARE + ;; +esac + +case "$OS_ARCH" in +WINNT) + OS_TEST=`uname -p` + ;; +Windows_NT) +# +# If uname -s returns "Windows_NT", we assume that we are using +# the uname.exe in MKS toolkit. +# +# The -r option of MKS uname only returns the major version number. +# So we need to use its -v option to get the minor version number. +# Moreover, it doesn't have the -p option, so we need to use uname -m. +# + OS_ARCH=WINNT + OS_TARGET=WINNT + OS_MINOR_RELEASE=`uname -v` + if test "$OS_MINOR_RELEASE" = "00"; then + OS_MINOR_RELEASE=0 + fi + OS_RELEASE="${OS_RELEASE}.${OS_MINOR_RELEASE}" + ;; +CYGWIN32_NT|CYGWIN_NT*|MINGW*_NT*) +# +# If uname -s returns "CYGWIN_NT-4.0", we assume that we are using +# the uname.exe in the Cygwin tools. +# Prior to the Beta 20 release, Cygwin was called GNU-Win32. +# If uname -s returns "CYGWIN32/NT", we assume that we are using +# the uname.exe in the GNU-Win32 tools. +# If uname -s returns MINGW32_NT-5.1, we assume that we are using +# the uname.exe in the MSYS tools. +# + OS_RELEASE=`expr $OS_ARCH : '.*NT-\(.*\)'` + OS_ARCH=WINNT + OS_TARGET=WINNT + ;; +AIX) + OS_RELEASE=`uname -v`.`uname -r` + OS_TEST=${target_cpu} + ;; +BSD_386) + OS_ARCH=BSD + ;; +dgux) + OS_ARCH=DGUX + ;; +IRIX64) + OS_ARCH=IRIX + ;; +UNIX_SV) + if "`cat /etc/bcheckrc | grep -c NCR 2>/dev/null`" != "0"; then + OS_ARCH=NCR + else + OS_ARCH=UNIXWARE + OS_RELEASE=`uname -v` + fi + ;; +ncr) + OS_ARCH=NCR + ;; +UNIX_SYSTEM_V) + OS_ARCH=NEC + ;; +OSF1) + case `uname -v` in + 148) + OS_RELEASE=V3.2C + ;; + 564) + OS_RELEASE=V4.0B + ;; + 878) + OS_RELEASE=V4.0D + ;; + esac + ;; +*OpenVMS*) + OS_ARCH=OpenVMS + OS_RELEASE=`uname -v` + OS_TEST=`uname -p` + ;; +OS_2) + OS_ARCH=OS2 + OS_TARGET=OS2 + OS_RELEASE=`uname -v` + ;; +QNX) + if test "$OS_TARGET" != "NTO"; then + changequote(,) + OS_RELEASE=`uname -v | sed 's/^\([0-9]\)\([0-9]*\)$/\1.\2/'` + changequote([,]) + fi + OS_TEST=x86 + ;; +SCO_SV) + OS_ARCH=SCOOS + OS_RELEASE=5.0 + ;; +SINIX-N | SINIX-Y | SINIX-Z |ReliantUNIX-M) + OS_ARCH=SINIX + OS_TEST=`uname -p` + ;; +UnixWare) + OS_ARCH=UNIXWARE + OS_RELEASE=`uname -v` + ;; +WINCE) + WINCE=1 + OS_ARCH=WINCE + OS_TARGET=WINCE + ;; +Darwin) + case "${target_cpu}" in + powerpc*) + OS_TEST=ppc + ;; + i*86*) + OS_TEST=i386 + ;; + x86_64) + OS_TEST=x86_64 + ;; + *) + if test -z "$CROSS_COMPILE" ; then + OS_TEST=`uname -p` + fi + ;; + esac + ;; +esac + +if test "$OS_ARCH" = "NCR"; then + changequote(,) + OS_RELEASE=`awk '{print $3}' /etc/.relid | sed 's/^\([0-9]\)\(.\)\(..\)\(.*\)$/\2.\3/'` + changequote([,]) +fi + +# Only set CPU_ARCH if we recognize the value of OS_TEST + +case "$OS_TEST" in +*86 | i86pc) + CPU_ARCH=x86 + ;; + +powerpc* | ppc | rs6000) + CPU_ARCH=ppc + ;; + +Alpha | alpha | ALPHA) + CPU_ARCH=Alpha + ;; + +hppa* | parisc) + CPU_ARCH=hppa + ;; + +sun4u | sparc*) + CPU_ARCH=sparc + ;; + +x86_64 | ia64) + CPU_ARCH="$OS_TEST" + ;; + +arm) + if test "$OS_TARGET" = "WINCE"; then + CPU_ARCH="$OS_TEST" + fi + ;; +esac + +if test -z "$OS_TARGET"; then + OS_TARGET=$OS_ARCH +fi +OS_CONFIG="${OS_TARGET}${OS_RELEASE}" + +dnl ======================================================== +dnl GNU specific defaults +dnl ======================================================== +if test "$GNU_CC"; then + # FIXME: Let us build with strict aliasing. bug 414641. + CFLAGS="$CFLAGS -fno-strict-aliasing" + MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-h,$@ -o $@' + MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-h,$@ -o $@' + DSO_LDOPTS='-shared' + if test "$GCC_USE_GNU_LD"; then + # Don't allow undefined symbols in libraries + DSO_LDOPTS="$DSO_LDOPTS -Wl,-z,defs" + fi + WARNINGS_AS_ERRORS='-Werror' + DSO_CFLAGS='' + DSO_PIC_CFLAGS='-fPIC' + ASFLAGS="$ASFLAGS -fPIC" + _MOZ_RTTI_FLAGS_ON=${_COMPILER_PREFIX}-frtti + _MOZ_RTTI_FLAGS_OFF=${_COMPILER_PREFIX}-fno-rtti + _MOZ_EXCEPTIONS_FLAGS_ON='-fhandle-exceptions' + _MOZ_EXCEPTIONS_FLAGS_OFF='-fno-handle-exceptions' + + # Turn on GNU specific features + # -Wall - turn on all warnings + # -pedantic - make compiler warn about non-ANSI stuff, and + # be a little bit stricter + # Warnings slamm took out for now (these were giving more noise than help): + # -Wbad-function-cast - warns when casting a function to a new return type + # -Wshadow - removed because it generates more noise than help --pete + _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wall -W -Wno-unused -Wpointer-arith" + if test -z "$INTEL_CC"; then + # Don't use -Wcast-align with ICC + case "$CPU_ARCH" in + # And don't use it on hppa, ia64, sparc, since it's noisy there + hppa | ia64 | sparc) + ;; + *) + _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wcast-align" + ;; + esac + fi + + dnl Turn pedantic on but disable the warnings for long long + _PEDANTIC=1 + + if test -z "$INTEL_CC"; then + _IGNORE_LONG_LONG_WARNINGS=1 + _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -W" + else + _IGNORE_LONG_LONG_WARNINGS= + fi + + + _DEFINES_CFLAGS='-include $(DEPTH)/js-confdefs.h -DMOZILLA_CLIENT' + _USE_CPP_INCLUDE_FLAG=1 +elif test "$SOLARIS_SUNPRO_CC"; then + MKSHLIB='$(LD) $(DSO_LDOPTS) -h $@ -o $@' + MKCSHLIB='$(LD) $(DSO_LDOPTS) -h $@ -o $@' + + DSO_LDOPTS='-shared' + if test "$GNU_LD"; then + # Don't allow undefined symbols in libraries + DSO_LDOPTS="$DSO_LDOPTS -z defs" + fi + + DSO_CFLAGS='' + if test "$CPU_ARCH" = "sparc"; then + # for Sun Studio on Solaris/SPARC + DSO_PIC_CFLAGS='-xcode=pic32' + else + DSO_PIC_CFLAGS='-KPIC' + fi + _DEFINES_CFLAGS='$(ACDEFINES) -D_JS_CONFDEFS_H_ -DMOZILLA_CLIENT' +else + MKSHLIB='$(LD) $(DSO_LDOPTS) -h $@ -o $@' + MKCSHLIB='$(LD) $(DSO_LDOPTS) -h $@ -o $@' + + DSO_LDOPTS='-shared' + if test "$GNU_LD"; then + # Don't allow undefined symbols in libraries + DSO_LDOPTS="$DSO_LDOPTS -z defs" + fi + + DSO_CFLAGS='' + DSO_PIC_CFLAGS='-KPIC' + _DEFINES_CFLAGS='$(ACDEFINES) -D_JS_CONFDEFS_H_ -DMOZILLA_CLIENT' +fi + +if test "$GNU_CXX"; then + # FIXME: Let us build with strict aliasing. bug 414641. + CXXFLAGS="$CXXFLAGS -fno-strict-aliasing" + # Turn on GNU specific features + _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wall -Wpointer-arith -Woverloaded-virtual -Wsynth -Wno-ctor-dtor-privacy -Wno-non-virtual-dtor" + if test -z "$INTEL_CC"; then + # Don't use -Wcast-align with ICC + case "$CPU_ARCH" in + # And don't use it on hppa, ia64, sparc, since it's noisy there + hppa | ia64 | sparc) + ;; + *) + _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wcast-align" + ;; + esac + fi + + _DEFINES_CXXFLAGS='-DMOZILLA_CLIENT -include $(DEPTH)/js-confdefs.h' + _USE_CPP_INCLUDE_FLAG=1 + + AC_CACHE_CHECK(whether the compiler supports -Wno-invalid-offsetof, + ac_has_wno_invalid_offsetof, + [ + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + _SAVE_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS ${_COMPILER_PREFIX}-Wno-invalid-offsetof" + AC_TRY_COMPILE([], + [return(0);], + ac_has_wno_invalid_offsetof="yes", + ac_has_wno_invalid_offsetof="no") + CXXFLAGS="$_SAVE_CXXFLAGS" + AC_LANG_RESTORE + ]) + if test "$ac_has_wno_invalid_offsetof" = "yes"; then + _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} ${_COMPILER_PREFIX}-Wno-invalid-offsetof" + fi + + AC_CACHE_CHECK(whether the compiler supports -Wno-variadic-macros, + ac_has_wno_variadic_macros, + [ + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + _SAVE_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS ${_COMPILER_PREFIX}-Wno-variadic-macros" + AC_TRY_COMPILE([], + [return(0);], + ac_has_wno_variadic_macros="yes", + ac_has_wno_variadic_macros="no") + CXXFLAGS="$_SAVE_CXXFLAGS" + AC_LANG_RESTORE + ]) + if test "$ac_has_wno_variadic_macros" = "yes"; then + _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} ${_COMPILER_PREFIX}-Wno-variadic-macros" + fi +else + _DEFINES_CXXFLAGS='-DMOZILLA_CLIENT -D_JS_CONFDEFS_H_ $(ACDEFINES)' +fi + +dnl gcc can come with its own linker so it is better to use the pass-thru calls +dnl MKSHLIB_FORCE_ALL is used to force the linker to include all object +dnl files present in an archive. MKSHLIB_UNFORCE_ALL reverts the linker to +dnl normal behavior. +dnl ======================================================== +MKSHLIB_FORCE_ALL= +MKSHLIB_UNFORCE_ALL= + +if test "$COMPILE_ENVIRONMENT"; then +if test "$GNU_CC"; then + AC_MSG_CHECKING(whether ld has archive extraction flags) + AC_CACHE_VAL(ac_cv_mkshlib_force_and_unforce, + [_SAVE_LDFLAGS=$LDFLAGS; _SAVE_LIBS=$LIBS + ac_cv_mkshlib_force_and_unforce="no" + exec 3<&0 </dev/null`" = 0; then + _pwd=`pwd` + CYGWIN_WRAPPER="${_pwd}/${srcdir}/build/cygwin-wrapper" + fi + if test "`${PERL} -v | grep -c cygwin 2>/dev/null`" = 0; then + AS_PERL=1 + PERL="${CYGWIN_WRAPPER} $PERL" + fi + + if test "`${PYTHON} -c 'import sys; print sys.platform;'`" != "cygwin"; then + PYTHON="${CYGWIN_WRAPPER} $PYTHON" + fi + ;; + esac + ;; + +*-darwin*) + HOST_CFLAGS="$HOST_CFLAGS -DXP_UNIX -DXP_MACOSX -DNO_X11" + HOST_NSPR_MDCPUCFG='\"md/_darwin.cfg\"' + HOST_OPTIMIZE_FLAGS="${HOST_OPTIMIZE_FLAGS=-O3}" + MOZ_FIX_LINK_PATHS='-Wl,-executable_path,$(LIBXUL_DIST)/bin' + LIBXUL_LIBS='$(XPCOM_FROZEN_LDOPTS) $(LIBXUL_DIST)/bin/XUL -lobjc' + ;; + +*-linux*|*-kfreebsd*-gnu) + HOST_CFLAGS="$HOST_CFLAGS -DXP_UNIX" + HOST_NSPR_MDCPUCFG='\"md/_linux.cfg\"' + HOST_OPTIMIZE_FLAGS="${HOST_OPTIMIZE_FLAGS=-O3}" + ;; + +*os2*) + HOST_CFLAGS="$HOST_CFLAGS -DXP_OS2 -DNO_X11 -Zomf" + HOST_NSPR_MDCPUCFG='\"md/_os2.cfg\"' + HOST_OPTIMIZE_FLAGS="${HOST_OPTIMIZE_FLAGS=-O2}" + HOST_BIN_SUFFIX=.exe + MOZ_FIX_LINK_PATHS= + ;; + +*-osf*) + HOST_CFLAGS="$HOST_CFLAGS -DXP_UNIX" + HOST_NSPR_MDCPUCFG='\"md/_osf1.cfg\"' + HOST_OPTIMIZE_FLAGS="${HOST_OPTIMIZE_FLAGS=-O2}" + ;; + +*) + HOST_CFLAGS="$HOST_CFLAGS -DXP_UNIX" + HOST_OPTIMIZE_FLAGS="${HOST_OPTIMIZE_FLAGS=-O2}" + ;; +esac + +dnl ======================================================== +dnl System overrides of the defaults for target +dnl ======================================================== + +case "$target" in +*-aix*) + AC_DEFINE(AIX) + if test ! "$GNU_CC"; then + if test ! "$HAVE_64BIT_OS"; then + # Compiling with Visual Age C++ object model compat is the + # default. To compile with object model ibm, add + # AIX_OBJMODEL=ibm to .mozconfig. + if test "$AIX_OBJMODEL" = "ibm"; then + CXXFLAGS="$CXXFLAGS -qobjmodel=ibm" + else + AIX_OBJMODEL=compat + fi + else + AIX_OBJMODEL=compat + fi + AC_SUBST(AIX_OBJMODEL) + DSO_LDOPTS='-qmkshrobj=1' + DSO_CFLAGS='-qflag=w:w' + DSO_PIC_CFLAGS= + LDFLAGS="$LDFLAGS -Wl,-brtl -blibpath:/usr/lib:/lib" + AC_MSG_WARN([Clearing MOZ_FIX_LINK_PATHS till we can fix bug 332075.]) + MOZ_FIX_LINK_PATHS= + MKSHLIB='$(CXX) $(DSO_LDOPTS) -o $@' + MKCSHLIB='$(CC) $(DSO_LDOPTS) -o $@' + if test "$COMPILE_ENVIRONMENT"; then + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_MSG_CHECKING([for VisualAge C++ compiler version >= 6.0.0.3]) + AC_TRY_COMPILE([], + [#if (__IBMCPP__ < 600) + #error "Bad compiler" + #endif], + _BAD_COMPILER=,_BAD_COMPILER=1) + if test -n "$_BAD_COMPILER"; then + AC_MSG_RESULT([no]) + AC_MSG_ERROR([VisualAge C++ version 6.0.0.3 or higher is required to build.]) + else + AC_MSG_RESULT([yes]) + fi + AC_LANG_RESTORE + TARGET_COMPILER_ABI="ibmc" + CC_VERSION=`lslpp -Lcq vac.C 2>/dev/null | awk -F: '{ print $3 }'` + CXX_VERSION=`lslpp -Lcq vacpp.cmp.core 2>/dev/null | awk -F: '{ print $3 }'` + fi + fi + case "${target_os}" in + aix4.1*) + DLL_SUFFIX='_shr.a' + ;; + esac + if test "$COMPILE_ENVIRONMENT"; then + AC_CHECK_HEADERS(sys/inttypes.h) + fi + AC_DEFINE(NSCAP_DISABLE_DEBUG_PTR_TYPES) + ;; + +*-beos*) + no_x=yes + MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_LDOPTS) -Wl,-h,$@ -o $@' + _PLATFORM_DEFAULT_TOOLKIT="cairo-beos" + DSO_LDOPTS='-nostart' + TK_LIBS='-lbe -lroot' + LIBS="$LIBS -lbe" + if test "$COMPILE_ENVIRONMENT"; then + AC_CHECK_LIB(bind,main,LIBS="$LIBS -lbind") + AC_CHECK_LIB(zeta,main,LIBS="$LIBS -lzeta") + fi + _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wno-multichar" + _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wno-multichar" + _MOZ_USE_RTTI=1 + USE_DEPENDENT_LIBS= + MOZ_USER_DIR="Mozilla" + ;; + +*-bsdi*) + dnl -pedantic doesn't play well with BSDI's _very_ modified gcc (shlicc2) + _PEDANTIC= + _IGNORE_LONG_LONG_WARNINGS= + case $OS_RELEASE in + 4.*|5.*) + STRIP="$STRIP -d" + ;; + *) + DSO_CFLAGS='' + DSO_LDOPTS='-r' + _WARNINGS_CFLAGS="-Wall" + _WARNINGS_CXXFLAGS="-Wall" + # The test above doesn't work properly, at least on 3.1. + MKSHLIB_FORCE_ALL='' + MKSHLIB_UNFORCE_ALL='' + ;; + esac + ;; + +*-darwin*) + MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@' + MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@' + MOZ_OPTIMIZE_FLAGS="-O3" + _PEDANTIC= + CFLAGS="$CFLAGS -fpascal-strings -fno-common" + CXXFLAGS="$CXXFLAGS -fpascal-strings -fno-common" + DLL_SUFFIX=".dylib" + DSO_LDOPTS='' + STRIP="$STRIP -x -S" + _PLATFORM_DEFAULT_TOOLKIT='cairo-cocoa' + MOZ_ENABLE_POSTSCRIPT= + TARGET_NSPR_MDCPUCFG='\"md/_darwin.cfg\"' + LDFLAGS="$LDFLAGS -framework Cocoa" + # The ExceptionHandling framework is needed for Objective-C exception + # logging code in nsObjCExceptions.h. Currently we only use that in debug + # builds. + MOZ_DEBUG_LDFLAGS="$MOZ_DEBUG_LDFLAGS -framework ExceptionHandling" + # set MACOSX to generate lib/mac/MoreFiles/Makefile + MACOSX=1 + + dnl DTrace and -dead_strip don't interact well. See bug 403132. + dnl =================================================================== + if test "x$enable_dtrace" = "xyes"; then + echo "Skipping -dead_strip because DTrace is enabled. See bug 403132." + else + dnl check for the presence of the -dead_strip linker flag + AC_MSG_CHECKING([for -dead_strip option to ld]) + _SAVE_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS -Wl,-dead_strip" + AC_TRY_LINK(,[return 0;],_HAVE_DEAD_STRIP=1,_HAVE_DEAD_STRIP=) + if test -n "$_HAVE_DEAD_STRIP" ; then + AC_MSG_RESULT([yes]) + MOZ_OPTIMIZE_LDFLAGS="-Wl,-dead_strip" + else + AC_MSG_RESULT([no]) + fi + + LDFLAGS=$_SAVE_LDFLAGS + fi + ;; + +*-freebsd*) + if test `test -x /usr/bin/objformat && /usr/bin/objformat || echo elf` != "elf"; then + DLL_SUFFIX=".so.1.0" + DSO_LDOPTS="-shared" + fi + if test ! "$GNU_CC"; then + DSO_LDOPTS="-Bshareable $DSO_LDOPTS" + fi +# Can't have force w/o an unforce. +# # Hack for FreeBSD 2.2 +# if test -z "$MKSHLIB_FORCE_ALL"; then +# MKSHLIB_FORCE_ALL='-Wl,-Bforcearchive' +# MKSHLIB_UNFORCE_ALL='' +# fi + ;; + +*-hpux*) + DLL_SUFFIX=".sl" + if test ! "$GNU_CC"; then + DSO_LDOPTS='-b -Wl,+s' + DSO_CFLAGS="" + DSO_PIC_CFLAGS="+Z" + MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_LDOPTS) -L$(LIBXUL_DIST)/bin -o $@' + MKCSHLIB='$(LD) -b +s -L$(LIBXUL_DIST)/bin -o $@' + CXXFLAGS="$CXXFLAGS -Wc,-ansi_for_scope,on" + else + DSO_LDOPTS='-b -E +s' + MKSHLIB='$(LD) $(DSO_LDOPTS) -L$(LIBXUL_DIST)/bin -L$(LIBXUL_DIST)/lib -o $@' + MKCSHLIB='$(LD) $(DSO_LDOPTS) -L$(LIBXUL_DIST)/bin -L$(LIBXUL_DIST)/lib -o $@' + fi + MOZ_POST_PROGRAM_COMMAND='chatr +s enable' + AC_DEFINE(NSCAP_DISABLE_DEBUG_PTR_TYPES) + ;; + +*-irix5*) + AC_DEFINE(IRIX) + DSO_LDOPTS='-elf -shared' + + if test "$GNU_CC"; then + MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@' + MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@' + MKSHLIB_FORCE_ALL='-Wl,-all' + MKSHLIB_UNFORCE_ALL='-Wl,-none' + CXXFLAGS="$CXXFLAGS -D_LANGUAGE_C_PLUS_PLUS" + else + MKSHLIB='$(LD) $(DSO_LDOPTS) -o $@' + MKCSHLIB='$(LD) $(DSO_LDOPTS) -o $@' + MKSHLIB_FORCE_ALL='-all' + MKSHLIB_UNFORCE_ALL='-none' + fi + ;; + +*-irix6*) + AC_DEFINE(IRIX) + dnl the irix specific xptcinvoke code is written against the n32 ABI so we *must* + dnl compile and link using -n32 + USE_N32=1 + TARGET_COMPILER_ABI=n32 + DSO_LDOPTS='-elf -shared' + MKSHLIB='$(CCC) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@' + MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@' + _MOZ_EXCEPTIONS_FLAGS_OFF="-LANG:exceptions=OFF" + _MOZ_EXCEPTIONS_FLAGS_ON="-LANG:exceptions=ON" + if test "$GNU_CC"; then + MKSHLIB_FORCE_ALL='-Wl,-all' + MKSHLIB_UNFORCE_ALL='-Wl,-none' + _WARNINGS_CFLAGS="-Wall" + _WARNINGS_CXXFLAGS="-Wall" + CXXFLAGS="$CXXFLAGS -D_LANGUAGE_C_PLUS_PLUS" + else + MKSHLIB_FORCE_ALL='-all' + MKSHLIB_UNFORCE_ALL='-none' + AR_LIST="$AR t" + AR_EXTRACT="$AR x" + AR_DELETE="$AR d" + AR='$(CXX) -ar' + AR_FLAGS='-o $@' + CFLAGS="$CFLAGS -woff 3262 -G 4" + CXXFLAGS="$CXXFLAGS -woff 3262 -G 4" + if test -n "$USE_N32"; then + ASFLAGS="$ASFLAGS -n32" + CFLAGS="$CFLAGS -n32" + CXXFLAGS="$CXXFLAGS -n32" + LDFLAGS="$LDFLAGS -n32" + fi + AC_DEFINE(NSCAP_DISABLE_DEBUG_PTR_TYPES) + AC_MSG_WARN([Clearing MOZ_FIX_LINK_PATHS for OSF/1 as fix for bug 333545 (till the reference bug 332075 is fixed.]) + MOZ_FIX_LINK_PATHS= + fi + if test -z "$GNU_CXX"; then + MIPSPRO_CXX=1 + fi + ;; + +*-*linux*) + # Note: both GNU_CXX and INTEL_CXX are set when using Intel's C compiler. + if test "$INTEL_CXX"; then + # -Os has been broken on Intel's C/C++ compilers for quite a + # while; Intel recommends against using it. + MOZ_OPTIMIZE_FLAGS="-O2" + MOZ_DEBUG_FLAGS="-g -fno-inline" + elif test "$GNU_CXX"; then + GCC_VERSION=`$CXX -v 2>&1 | awk '/^gcc version/ { print $3 }'` + case $GCC_VERSION in + 4.1.*|4.2.*) + # -Os is broken on gcc 4.1.x and 4.2.x, we need to tweak it to get good results. + MOZ_OPTIMIZE_SIZE_TWEAK="-finline-limit=50" + esac + MOZ_OPTIMIZE_FLAGS="-Os -freorder-blocks -fno-reorder-functions $MOZ_OPTIMIZE_SIZE_TWEAK" + MOZ_DEBUG_FLAGS="-g -fno-inline" # most people on linux use gcc/gdb, + # and that combo is not yet good at + # debugging inlined functions (even + # when using DWARF2 as the debugging + # format) + fi + + TARGET_NSPR_MDCPUCFG='\"md/_linux.cfg\"' + + case "${target_cpu}" in + alpha*) + CFLAGS="$CFLAGS -mieee" + CXXFLAGS="$CXXFLAGS -mieee" + ;; + mips*) + MOZ_DEBUG_FLAGS="-g" # We want inlining + ;; + esac + ;; + +*-wince*) + TARGET_COMPILER_ABI=msvc + MOZ_TOOLS_DIR=`echo $MOZ_TOOLS` + AR_LIST="$AR -list" + AR_EXTRACT="$AR -extract" + AR_DELETE="$AR d" + AR_FLAGS='-OUT:"$@"' + AS="$AS_BIN" + + DSO_CFLAGS= + DSO_PIC_CFLAGS= + DLL_SUFFIX=.dll + BIN_SUFFIX='.exe' + if test -z "$RC"; then + RC=rc.exe + fi + # certain versions of cygwin's makedepend barf on the + # #include vs -I./dist/include/string issue so don't use it + SYSTEM_MAKEDEPEND= + + HOST_CC=cl + HOST_CXX=cl + HOST_LD=link + HOST_AR='lib -OUT:$@' + HOST_RANLIB='echo ranlib' + HOST_CFLAGS="$HOST_CFLAGS -D_X86_" + + + WARNINGS_AS_ERRORS='-WX' + MOZ_OPTIMIZE_FLAGS='-O1' + AR_FLAGS='-NOLOGO -OUT:"$@"' + ASM_SUFFIX=asm + CFLAGS="$CFLAGS -W3 -Gy -Fd\$(COMPILE_PDBFILE)" + CXXFLAGS="$CXXFLAGS -W3 -Gy -Fd\$(COMPILE_PDBFILE)" + DLL_PREFIX= + DOXYGEN=: + DSO_LDOPTS=-SUBSYSTEM:WINDOWSCE + DYNAMIC_XPCOM_LIBS='$(LIBXUL_DIST)/lib/xpcom.lib $(LIBXUL_DIST)/lib/xpcom_core.lib' + GARBAGE= + IMPORT_LIB_SUFFIX=lib + LIBS="$LIBS" + LIBXUL_LIBS='$(LIBXUL_DIST)/lib/xpcom.lib $(LIBXUL_DIST)/lib/xul.lib' + LIB_PREFIX= + LIB_SUFFIX=lib + MKCSHLIB='$(LD) -NOLOGO -DLL -OUT:$@ $(DSO_LDOPTS)' + MKSHLIB='$(LD) -NOLOGO -DLL -OUT:$@ $(DSO_LDOPTS)' + MKSHLIB_FORCE_ALL= + MKSHLIB_UNFORCE_ALL= + MOZ_COMPONENT_NSPR_LIBS='$(NSPR_LIBS)' + MOZ_COMPONENT_NSPR_LIBS='$(NSPR_LIBS)' + MOZ_DEBUG_FLAGS='-Zi' + MOZ_DEBUG_LDFLAGS='-DEBUG -DEBUGTYPE:CV' + MOZ_FIX_LINK_PATHS= + MOZ_JS_LIBS='$(libdir)/mozjs.lib' + OBJ_SUFFIX=obj + RANLIB='echo not_ranlib' + STRIP='echo not_strip' + TARGET_NSPR_MDCPUCFG='\"md/_wince.cfg\"' + XARGS=xargs + XPCOM_FROZEN_LDOPTS='$(LIBXUL_DIST)/lib/xpcom.lib' + + AC_DEFINE(WINCE) + AC_DEFINE(HAVE_SNPRINTF) + AC_DEFINE(_WINDOWS) + AC_DEFINE(WIN32) + AC_DEFINE(XP_WIN) + AC_DEFINE(XP_WIN32) + AC_DEFINE(HW_THREADS) + AC_DEFINE(STDC_HEADERS) + AC_DEFINE(NEW_H, ) + AC_DEFINE(WIN32_LEAN_AND_MEAN) + + TARGET_MD_ARCH=win32 + _PLATFORM_DEFAULT_TOOLKIT='windows' + BIN_SUFFIX='.exe' + MOZ_ENABLE_POSTSCRIPT= + MOZ_USER_DIR="Mozilla" + + dnl Default to Windows Mobile components enabled + WINCE_WINDOWS_MOBILE=1 + + MOZ_ARG_DISABLE_BOOL(windows-mobile-components, + [ --disable-windows-mobile-components + Disable Windows Mobile specific components from CE build], + WINCE_WINDOWS_MOBILE=, + WINCE_WINDOWS_MOBILE=1) + + if test "$WINCE_WINDOWS_MOBILE"; then + AC_DEFINE(WINCE_WINDOWS_MOBILE) + fi +;; + +*-symbian*) + + AC_DEFINE(XP_UNIX) + AC_DEFINE(SYMBIAN) + AC_DEFINE(__arm__) + AC_DEFINE(__SYMBIAN32__) + AC_DEFINE(_UNICODE) + AC_DEFINE(NDEBUG) + AC_DEFINE(__SUPPORT_CPP_EXCEPTIONS__) + AC_DEFINE(MOZ_STDERR_TO_STDOUT) + AC_DEFINE(HAVE_FCNTL_FILE_LOCKING) + AC_DEFINE(HAVE_SOCKLEN_T) + AC_DEFINE(__GCCE__) + + CPU_ARCH=ARM + OS_RELEASE=9.2 + OS_ARCH=SYMBIAN + USE_PTHREADS=1 + LIB_SUFFIX=lib + DLL_SUFFIX=dll + MKSHLIB= + DSO_LDOPTS= + DSO_CFLAGS= + VISIBILITY_FLAGS= + TARGET_NSPR_MDCPUCFG='\"md/_symbian.cfg\"' + RANLIB='echo no ranlib ' +;; + +*-mingw*|*-cygwin*|*-msvc*|*-mks*) + DSO_CFLAGS= + DSO_PIC_CFLAGS= + DLL_SUFFIX=.dll + RC=rc.exe + # certain versions of cygwin's makedepend barf on the + # #include vs -I./dist/include/string issue so don't use it + SYSTEM_MAKEDEPEND= + if test -n "$GNU_CC"; then + CC="$CC -mno-cygwin" + CXX="$CXX -mno-cygwin" + CPP="$CPP -mno-cygwin" + CFLAGS="$CFLAGS -mms-bitfields" + CXXFLAGS="$CXXFLAGS -mms-bitfields" + DSO_LDOPTS='-shared' + MKSHLIB='$(CXX) $(DSO_LDOPTS) -o $@' + MKCSHLIB='$(CC) $(DSO_LDOPTS) -o $@' + RC='$(WINDRES)' + # Use temp file for windres (bug 213281) + RCFLAGS='-O coff --use-temp-file' + # mingw doesn't require kernel32, user32, and advapi32 explicitly + LIBS="$LIBS -lgdi32 -lwinmm -lwsock32" + MOZ_JS_LIBS='-L$(libdir) -lmozjs' + MOZ_FIX_LINK_PATHS= + DYNAMIC_XPCOM_LIBS='-L$(LIBXUL_DIST)/lib -lxpcom -lxpcom_core' + XPCOM_FROZEN_LDOPTS='-L$(LIBXUL_DIST)/lib -lxpcom' + DLL_PREFIX= + IMPORT_LIB_SUFFIX=dll.a + else + TARGET_COMPILER_ABI=msvc + HOST_CC='$(CC)' + HOST_CXX='$(CXX)' + HOST_LD='$(LD)' + AR='lib -NOLOGO -OUT:"$@"' + AR_FLAGS= + RANLIB='echo not_ranlib' + STRIP='echo not_strip' + XARGS=xargs + DOXYGEN=: + GARBAGE='$(OBJDIR)/vc20.pdb $(OBJDIR)/vc40.pdb' + OBJ_SUFFIX=obj + LIB_SUFFIX=lib + DLL_PREFIX= + LIB_PREFIX= + IMPORT_LIB_SUFFIX=lib + MKSHLIB='$(LD) -NOLOGO -DLL -OUT:$@ -PDB:$(LINK_PDBFILE) $(DSO_LDOPTS)' + MKCSHLIB='$(LD) -NOLOGO -DLL -OUT:$@ -PDB:$(LINK_PDBFILE) $(DSO_LDOPTS)' + MKSHLIB_FORCE_ALL= + MKSHLIB_UNFORCE_ALL= + DSO_LDOPTS=-SUBSYSTEM:WINDOWS + _USE_CPP_INCLUDE_FLAG=1 + _DEFINES_CFLAGS='-FI $(DEPTH)/js-confdefs.h -DMOZILLA_CLIENT' + _DEFINES_CXXFLAGS='-FI $(DEPTH)/js-confdefs.h -DMOZILLA_CLIENT' + CFLAGS="$CFLAGS -W3 -Gy -Fd\$(COMPILE_PDBFILE)" + CXXFLAGS="$CXXFLAGS -W3 -Gy -Fd\$(COMPILE_PDBFILE)" + LIBS="$LIBS kernel32.lib user32.lib gdi32.lib winmm.lib wsock32.lib advapi32.lib" + MOZ_DEBUG_FLAGS='-Zi' + MOZ_DEBUG_LDFLAGS='-DEBUG -DEBUGTYPE:CV' + WARNINGS_AS_ERRORS='-WX' + MOZ_OPTIMIZE_FLAGS='-O1' + MOZ_JS_LIBS='$(libdir)/mozjs.lib' + MOZ_FIX_LINK_PATHS= + DYNAMIC_XPCOM_LIBS='$(LIBXUL_DIST)/lib/xpcom.lib $(LIBXUL_DIST)/lib/xpcom_core.lib' + XPCOM_FROZEN_LDOPTS='$(LIBXUL_DIST)/lib/xpcom.lib' + LIBXUL_LIBS='$(LIBXUL_DIST)/lib/xpcom.lib $(LIBXUL_DIST)/lib/xul.lib' + MOZ_COMPONENT_NSPR_LIBS='$(NSPR_LIBS)' + if test $_MSC_VER -ge 1400; then + LDFLAGS="$LDFLAGS -NXCOMPAT" + dnl For profile-guided optimization + PROFILE_GEN_CFLAGS="-GL" + PROFILE_GEN_LDFLAGS="-LTCG:PGINSTRUMENT" + dnl XXX: PGO builds can fail with warnings treated as errors, + dnl specifically "no profile data available" appears to be + dnl treated as an error sometimes. This might be a consequence + dnl of using WARNINGS_AS_ERRORS in some modules, combined + dnl with the linker doing most of the work in the whole-program + dnl optimization/PGO case. I think it's probably a compiler bug, + dnl but we work around it here. + PROFILE_USE_CFLAGS="-GL -wd4624 -wd4952" + dnl XXX: should be -LTCG:PGOPTIMIZE, but that fails on libxul. + dnl Probably also a compiler bug, but what can you do? + PROFILE_USE_LDFLAGS="-LTCG:PGUPDATE" + if test -n "$_USE_DYNAMICBASE"; then + LDFLAGS="$LDFLAGS -DYNAMICBASE" + fi + fi + fi + AC_DEFINE(HAVE_SNPRINTF) + AC_DEFINE(_WINDOWS) + AC_DEFINE(WIN32) + AC_DEFINE(XP_WIN) + AC_DEFINE(XP_WIN32) + AC_DEFINE(HW_THREADS) + AC_DEFINE(STDC_HEADERS) + AC_DEFINE(NEW_H, ) + AC_DEFINE(WIN32_LEAN_AND_MEAN) + TARGET_MD_ARCH=win32 + _PLATFORM_DEFAULT_TOOLKIT='cairo-windows' + BIN_SUFFIX='.exe' + MOZ_ENABLE_POSTSCRIPT= + MOZ_USER_DIR="Mozilla" + + dnl Hardcode to win95 for now - cls + TARGET_NSPR_MDCPUCFG='\"md/_win95.cfg\"' + + dnl set NO_X11 defines here as the general check is skipped on win32 + no_x=yes + AC_DEFINE(NO_X11) + + dnl MinGW/MSYS doesn't provide or need cygpath + case "$host" in + *-mingw*) + CYGPATH_W=echo + CYGPATH_S=cat + MOZ_BUILD_ROOT=`cd $MOZ_BUILD_ROOT && pwd -W` + ;; + *-cygwin*|*-msvc*|*-mks*) + CYGPATH_W="cygpath -a -w" + CYGPATH_S="sed -e s|\\\\|/|g" + MOZ_BUILD_ROOT=`$CYGPATH_W $MOZ_BUILD_ROOT | $CYGPATH_S` + ;; + esac + case "$host" in + *-mingw*|*-cygwin*|*-msvc*|*-mks*) + + if test -z "$MOZ_TOOLS"; then + AC_MSG_ERROR([MOZ_TOOLS is not set]) + fi + + MOZ_TOOLS_DIR=`cd $MOZ_TOOLS && pwd` + if test "$?" != "0" || test -z "$MOZ_TOOLS_DIR"; then + AC_MSG_ERROR([cd \$MOZ_TOOLS failed. MOZ_TOOLS ==? $MOZ_TOOLS]) + fi + if test `echo ${PATH}: | grep -ic "$MOZ_TOOLS_DIR/bin:"` = 0; then + AC_MSG_ERROR([\$MOZ_TOOLS\\bin must be in your path.]) + fi + MOZ_TOOLS_DIR=`$CYGPATH_W $MOZ_TOOLS_DIR | $CYGPATH_S` + ;; + esac + + + case "$host_os" in + cygwin*|msvc*|mks*) + AC_MSG_WARN([Using a cygwin build environment is unsupported. Configure cannot check for the presence of necessary headers. Please upgrade to MozillaBuild; see http://developer.mozilla.org/en/docs/Windows_Build_Prerequisites]) + ;; + + *) + AC_CHECK_HEADERS(oleacc.idl) + + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_CHECK_HEADERS(atlbase.h wpcapi.h) + AC_LANG_RESTORE + ;; + esac + + case "$target" in + i*86-*) + if test "$HAVE_64BIT_OS"; then + AC_MSG_ERROR([You are targeting i386 but using the 64-bit compiler.]) + fi + + if test $_MSC_VER -ge 1400; then + LDFLAGS="$LDFLAGS -SAFESEH" + fi + AC_CHECK_HEADERS(mmintrin.h) + AC_DEFINE(_X86_) + ;; + alpha-*) + AC_DEFINE(_ALPHA_) + ;; + mips-*) + AC_DEFINE(_MIPS_) + ;; + x86_64-*) + AC_DEFINE(_AMD64_) + ;; + *) + AC_DEFINE(_CPU_ARCH_NOT_DEFINED) + ;; + esac + + if test "$HAVE_64BIT_OS"; then + AC_DEFINE(_WIN64) + fi + ;; + +*-netbsd*) + DSO_CFLAGS='' + CFLAGS="$CFLAGS -Dunix" + CXXFLAGS="$CXXFLAGS -Dunix" + if $CC -E - -dM /dev/null; then + DLL_SUFFIX=".so" + DSO_PIC_CFLAGS='-fPIC -DPIC' + DSO_LDOPTS='-shared' + BIN_FLAGS='-Wl,--export-dynamic' + else + DSO_PIC_CFLAGS='-fPIC -DPIC' + DLL_SUFFIX=".so.1.0" + DSO_LDOPTS='-shared' + fi + # This will fail on a.out systems prior to 1.5.1_ALPHA. + MKSHLIB_FORCE_ALL='-Wl,--whole-archive' + MKSHLIB_UNFORCE_ALL='-Wl,--no-whole-archive' + if test "$LIBRUNPATH"; then + DSO_LDOPTS="-Wl,-R$LIBRUNPATH $DSO_LDOPTS" + fi + MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-soname,lib$(LIBRARY_NAME)$(DLL_SUFFIX) -o $@' + MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-soname,lib$(LIBRARY_NAME)$(DLL_SUFFIX) -o $@' + ;; + +*-nto*) + AC_DEFINE(NTO) + AC_DEFINE(_QNX_SOURCE) + AC_DEFINE(_i386) + OS_TARGET=NTO + WARNINGS_AS_ERRORS='' + MOZ_OPTIMIZE_FLAGS="-O" + MOZ_DEBUG_FLAGS="-gstabs" + USE_PTHREADS=1 + _PEDANTIC= + LIBS="$LIBS -lsocket -lstdc++" + _DEFINES_CFLAGS='-Wp,-include -Wp,$(DEPTH)/js-confdefs.h -DMOZILLA_CLIENT -D_POSIX_C_SOURCE=199506' + _DEFINES_CXXFLAGS='-DMOZILLA_CLIENT -Wp,-include -Wp,$(DEPTH)/js-confdefs.h -D_POSIX_C_SOURCE=199506' + if test "$with_x" != "yes" + then + _PLATFORM_DEFAULT_TOOLKIT="photon" + TK_CFLAGS='-I/usr/include/photon' + TK_LIBS='-lph' + fi + case "${target_cpu}" in + ppc*) + AC_DEFINE(HAVE_VA_LIST_AS_ARRAY) + ;; + esac + ;; + +*-openbsd*) + DLL_SUFFIX=".so.1.0" + DSO_CFLAGS='' + DSO_PIC_CFLAGS='-fPIC' + DSO_LDOPTS='-shared -fPIC' + if test "$LIBRUNPATH"; then + DSO_LDOPTS="-R$LIBRUNPATH $DSO_LDOPTS" + fi + ;; + +*-openvms*) + AC_DEFINE(NO_PW_GECOS) + AC_DEFINE(NO_UDSOCK) + AC_DEFINE(POLL_WITH_XCONNECTIONNUMBER) + USE_PTHREADS=1 + MKSHLIB_FORCE_ALL='-all' + MKSHLIB_UNFORCE_ALL='-none' + AS='as' + AS_DASH_C_FLAG='-Wc/names=as_is' + AR_FLAGS='c $@' + DSO_LDOPTS='-shared -auto_symvec' + DSO_PIC_CFLAGS= + MOZ_DEBUG_LDFLAGS='-g' + COMPAQ_CXX=1 + CC_VERSION=`$CC -V 2>&1 | awk '/ C / { print $3 }'` + CXX_VERSION=`$CXX -V 2>&1 | awk '/ C\+\+ / { print $3 }'` + ;; + + +*-os2*) + MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@' + MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@' + AC_DEFINE(OS2) + AC_DEFINE(XP_OS2) + AC_DEFINE(OS2EMX_PLAIN_CHAR) + AC_DEFINE(TCPV40HDRS) + DLL_PREFIX= + LIB_PREFIX= + LIB_SUFFIX=lib + BIN_SUFFIX=".exe" + DLL_SUFFIX=".dll" + IMPORT_LIB_SUFFIX=lib + DSO_PIC_CFLAGS= + AR=emxomfar + AR_FLAGS='r $@' + CFLAGS="$CFLAGS -Zomf" + CXXFLAGS="$CXXFLAGS -Zomf" + DSO_LDOPTS='-Zdll' + BIN_FLAGS='-Zlinker /ST:0x100000' + IMPLIB='emximp -o' + FILTER='emxexp -o' + LDFLAGS='-Zmap' + WARNINGS_AS_ERRORS='-Werror' + MOZ_DEBUG_FLAGS="-g -fno-inline" + MOZ_OPTIMIZE_FLAGS="-O2" + MOZ_OPTIMIZE_LDFLAGS="-s -Zlinker /EXEPACK:2 -Zlinker /PACKCODE -Zlinker /PACKDATA" + DYNAMIC_XPCOM_LIBS='-L$(LIBXUL_DIST)/lib $(LIBXUL_DIST)/lib/xpcom.lib $(LIBXUL_DIST)/lib/xpcomcor.lib' + LIBXUL_LIBS='-L$(LIBXUL_DIST)/lib $(LIBXUL_DIST)/lib/xpcom.lib $(LIBXUL_DIST)/lib/xul.lib' + TARGET_MD_ARCH=os2 + _PLATFORM_DEFAULT_TOOLKIT="cairo-os2" + MOZ_ENABLE_POSTSCRIPT= + RC=rc.exe + RCFLAGS='-n' + MOZ_USER_DIR="Mozilla" + + if test "$MOZTOOLS"; then + MOZ_TOOLS_DIR=`echo $MOZTOOLS | sed -e 's|\\\\|/|g'` + else + AC_MSG_ERROR([MOZTOOLS is not set]) + fi + if test -n "$MOZ_OS2_HIGH_MEMORY"; then + DSO_LDOPTS="$DSO_LDOPTS -Zhigh-mem" + LDFLAGS="$LDFLAGS -Zhigh-mem" + MOZ_OPTIMIZE_LDFLAGS="$MOZ_OPTIMIZE_LDFLAGS -Zhigh-mem" + AC_DEFINE(MOZ_OS2_HIGH_MEMORY) + fi + + # GCC for OS/2 currently predefines these, but we don't want them + _DEFINES_CFLAGS="$_DEFINES_CFLAGS -Uunix -U__unix -U__unix__" + _DEFINES_CXXFLAGS="$_DEFINES_CXXFLAGS -Uunix -U__unix -U__unix__" + + AC_CACHE_CHECK(for __declspec(dllexport), + ac_os2_declspec, + [AC_TRY_COMPILE([__declspec(dllexport) void ac_os2_declspec(void) {}], + [return 0;], + ac_os2_declspec="yes", + ac_os2_declspec="no")]) + if test "$ac_os2_declspec" = "yes"; then + FILTER='true' + MOZ_OS2_USE_DECLSPEC='1' + fi + ;; + +alpha*-*-osf*) + if test "$GNU_CC"; then + MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-soname,$@ -o $@' + MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-soname,$@ -o $@' + + else + MOZ_DEBUG_FLAGS='-g' + ASFLAGS='-I$(topsrcdir)/xpcom/reflect/xptcall/public -g' + CFLAGS="$CFLAGS -ieee" + CXXFLAGS="$CXXFLAGS "'-noexceptions -ieee -ptr $(DIST)/cxx_repository' + DSO_LDOPTS='-shared -msym -expect_unresolved \* -update_registry $(DIST)/so_locations' + DSO_CFLAGS= + DSO_PIC_CFLAGS= + MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -soname $@ -o $@' + MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -soname $@ -o $@' + MKSHLIB_FORCE_ALL='-all' + MKSHLIB_UNFORCE_ALL='-none' + dnl Might fix the libxpcom.so breakage on this platform as well.... + AC_DEFINE(NSCAP_DISABLE_TEST_DONTQUERY_CASES) + AC_DEFINE(NSCAP_DISABLE_DEBUG_PTR_TYPES) + fi + if test -z "$GNU_CXX"; then + COMPAQ_CXX=1 + fi + AC_DEFINE(NEED_USLEEP_PROTOTYPE) + ;; + +*-qnx*) + DIRENT_INO=d_stat.st_ino + dnl Solves the problems the QNX compiler has with nsCOMPtr.h. + AC_DEFINE(NSCAP_DISABLE_TEST_DONTQUERY_CASES) + AC_DEFINE(NSCAP_DISABLE_DEBUG_PTR_TYPES) + dnl Explicit set STDC_HEADERS to workaround QNX 6.0's failing of std test + AC_DEFINE(STDC_HEADERS) + if test "$no_x" = "yes"; then + _PLATFORM_DEFAULT_TOOLKIT='photon' + TK_CFLAGS='-I/usr/nto/include/photon' + TK_LIBS='-lphoton -lphrender' + fi + ;; + +*-sco*) + AC_DEFINE(NSCAP_DISABLE_TEST_DONTQUERY_CASES) + AC_DEFINE(NSCAP_DISABLE_DEBUG_PTR_TYPES) + CXXFLAGS="$CXXFLAGS -I/usr/include/CC" + if test ! "$GNU_CC"; then + DSO_LDOPTS='-G' + fi + ;; + +dnl the qsort routine under solaris is faulty +*-solaris*) + AC_DEFINE(SOLARIS) + TARGET_NSPR_MDCPUCFG='\"md/_solaris.cfg\"' + if test -z "$CROSS_COMPILE" && pkginfo -q SUNWpr && pkginfo -q SUNWprd; then + NO_NSPR_CONFIG_SYSTEM_LDFLAGS="-L/usr/lib/mps -R/usr/lib/mps -lnspr4" + NO_NSPR_CONFIG_SYSTEM_CFLAGS="-I/usr/include/mps" + NO_NSPR_CONFIG_SYSTEM_VERSION=["`pkgparam SUNWpr SUNW_PRODVERS | sed -e 's/^[1-9][0-9]*\.[0-9][0-9]*$/&.0/'`"] + fi + SYSTEM_MAKEDEPEND= + # $ORIGIN/.. is for shared libraries under components/ to locate shared + # libraries one level up (e.g. libnspr4.so) + LDFLAGS="$LDFLAGS -z ignore -R '\$\$ORIGIN:\$\$ORIGIN/..'" + if test "$SOLARIS_SUNPRO_CC"; then + LIBS="-lCrun -lCstd $LIBS" + NS_USE_NATIVE=1 + MOZ_FIX_LINK_PATHS= + AC_DEFINE(NSCAP_DISABLE_DEBUG_PTR_TYPES) + CFLAGS="$CFLAGS -xlibmieee -xstrconst -xbuiltin=%all" + CXXFLAGS="$CXXFLAGS -xlibmieee -xbuiltin=%all -features=tmplife -norunpath" + LDFLAGS="-xildoff -z lazyload -z combreloc $LDFLAGS" + if test -z "$CROSS_COMPILE" && test -f /usr/lib/ld/map.noexstk; then + _SAVE_LDFLAGS=$LDFLAGS + LDFLAGS="-M /usr/lib/ld/map.noexstk $LDFLAGS" + AC_TRY_LINK([#include ], + [printf("Hello World\n");], + , + [LDFLAGS=$_SAVE_LDFLAGS]) + fi + WARNINGS_AS_ERRORS='-Werror' + MOZ_OPTIMIZE_FLAGS="-xO4" + MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_FLAGS) $(DSO_LDOPTS) -h $@ -o $@' + MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_FLAGS) -G -z muldefs -h $@ -o $@' + MKSHLIB_FORCE_ALL='-z allextract' + MKSHLIB_UNFORCE_ALL='-z defaultextract' + DSO_LDOPTS='-G -z muldefs' + AR_LIST="$AR t" + AR_EXTRACT="$AR x" + AR_DELETE="$AR d" + AR='$(CXX) -xar' + AR_FLAGS='-o $@' + AS='/usr/ccs/bin/as' + ASFLAGS="$ASFLAGS -K PIC -L -P -D_ASM -D__STDC__=0" + AS_DASH_C_FLAG='' + TARGET_COMPILER_ABI="sunc" + CC_VERSION=`$CC -V 2>&1 | grep '^cc:' 2>/dev/null | $AWK -F\: '{ print $2 }'` + CXX_VERSION=`$CXX -V 2>&1 | grep '^CC:' 2>/dev/null | $AWK -F\: '{ print $2 }'` + AC_MSG_CHECKING([for Sun C++ compiler version >= 5.9]) + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_TRY_COMPILE([], + [#if (__SUNPRO_CC < 0x590) + #error "Denied" + #endif], + _BAD_COMPILER=,_BAD_COMPILER=1) + if test -n "$_BAD_COMPILER"; then + _res="no" + AC_MSG_ERROR([Sun C++ 5.9 (Sun Studio 12) or higher is required to build. Your compiler version is $CXX_VERSION .]) + else + _res="yes" + fi + AC_MSG_RESULT([$_res]) + AC_LANG_RESTORE + else + ASFLAGS="$ASFLAGS -fPIC" + DSO_LDOPTS='-G' + _WARNINGS_CFLAGS='' + _WARNINGS_CXXFLAGS='' + if test "$OS_RELEASE" = "5.3"; then + AC_DEFINE(MUST_UNDEF_HAVE_BOOLEAN_AFTER_INCLUDES) + fi + fi + if test "$OS_RELEASE" = "5.5.1"; then + AC_DEFINE(NEED_USLEEP_PROTOTYPE) + fi + ;; + +*-sunos*) + DSO_LDOPTS='-Bdynamic' + MKSHLIB='-$(LD) $(DSO_LDOPTS) -o $@' + MKCSHLIB='-$(LD) $(DSO_LDOPTS) -o $@' + AC_DEFINE(SUNOS4) + AC_DEFINE(SPRINTF_RETURNS_STRING) + case "$(target_os)" in + sunos4.1*) + DLL_SUFFIX='.so.1.0' + ;; + esac + ;; + +*-sysv4.2uw7*) + NSPR_LIBS="-lnspr$NSPR_VERSION -lplc$NSPR_VERSION -lplds$NSPR_VERSION -L/usr/ccs/lib -lcrt" + ;; + +*-os2*) + HOST_NSPR_MDCPUCFG='\"md/_os2.cfg\"' + ;; + +esac + +dnl Only one oddball right now (QNX), but this gives us flexibility +dnl if any other platforms need to override this in the future. +AC_DEFINE_UNQUOTED(D_INO,$DIRENT_INO) + +dnl ======================================================== +dnl Any platform that doesn't have MKSHLIB_FORCE_ALL defined +dnl by now will not have any way to link most binaries (tests +dnl as well as viewer, apprunner, etc.), because some symbols +dnl will be left out of the "composite" .so's by ld as unneeded. +dnl So, by defining NO_LD_ARCHIVE_FLAGS for these platforms, +dnl they can link in the static libs that provide the missing +dnl symbols. +dnl ======================================================== +NO_LD_ARCHIVE_FLAGS= +if test -z "$MKSHLIB_FORCE_ALL" || test -z "$MKSHLIB_UNFORCE_ALL"; then + NO_LD_ARCHIVE_FLAGS=1 +fi +case "$target" in +*-os2*) + NO_LD_ARCHIVE_FLAGS= + ;; +*-aix4.3*|*-aix5*) + NO_LD_ARCHIVE_FLAGS= + ;; +*-openvms*) + NO_LD_ARCHIVE_FLAGS= + ;; +*-msvc*|*-mks*|*-mingw*|*-cygwin*|*-wince) + if test -z "$GNU_CC"; then + NO_LD_ARCHIVE_FLAGS= + fi + ;; +esac +AC_SUBST(NO_LD_ARCHIVE_FLAGS) + +dnl +dnl Indicate that platform requires special thread safe +dnl locking when starting up the OJI JVM +dnl (see mozilla/modules/oji/src/nsJVMManager.cpp) +dnl ======================================================== +case "$target" in + *-hpux*) + AC_DEFINE(MOZ_OJI_REQUIRE_THREAD_SAFE_ON_STARTUP) + ;; +esac + +dnl ======================================================== +dnl = Flags to strip unused symbols from .so components +dnl ======================================================== +case "$target" in + *-linux*|*-kfreebsd*-gnu) + MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS='-Wl,--version-script -Wl,$(BUILD_TOOLS)/gnu-ld-scripts/components-version-script' + ;; + *-solaris*) + if test -z "$GNU_CC"; then + MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS='-M $(BUILD_TOOLS)/gnu-ld-scripts/components-mapfile' + else + if test -z "$GCC_USE_GNU_LD"; then + MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS='-Wl,-M -Wl,$(BUILD_TOOLS)/gnu-ld-scripts/components-mapfile' + else + MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS='-Wl,--version-script -Wl,$(BUILD_TOOLS)/gnu-ld-scripts/components-version-script' + fi + fi + ;; + *-nto*) + MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS='-Wl,--version-script,$(BUILD_TOOLS)/gnu-ld-scripts/components-version-script' + ;; + *-darwin*) + MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS='-Wl,-exported_symbols_list -Wl,$(BUILD_TOOLS)/gnu-ld-scripts/components-export-list' + ;; + *-cygwin*|*-mingw*|*-mks*|*-msvc|*-wince) + if test -n "$GNU_CC"; then + MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS='-Wl,--version-script,$(BUILD_TOOLS)/gnu-ld-scripts/components-version-script' + fi + ;; +esac + +if test -z "$COMPILE_ENVIRONMENT"; then + SKIP_COMPILER_CHECKS=1 + SKIP_LIBRARY_CHECKS=1 +fi + +dnl Configure JIT support + +case "$target" in +i?86-*) + ENABLE_JIT=1 + NANOJIT_ARCH=i386 + ;; +x86_64*-*) + ENABLE_JIT=1 + NANOJIT_ARCH=X64 + ;; +arm*-*) + ENABLE_JIT=1 + NANOJIT_ARCH=ARM + ;; +sparc*-*) + ENABLE_JIT=1 + NANOJIT_ARCH=Sparc + ;; +esac + +MOZ_ARG_DISABLE_BOOL(jit, +[ --disable-jit Disable JIT support], + ENABLE_JIT=) + +if test "$ENABLE_JIT"; then + +AC_DEFINE(FEATURE_NANOJIT) +AC_DEFINE(JS_TRACER) + +case "$target" in +i?86-*) + AC_DEFINE(AVMPLUS_IA32) + ;; +x86_64*-*) + AC_DEFINE(AVMPLUS_AMD64) + AC_DEFINE(AVMPLUS_64BIT) + ;; +arm*-*) + AC_DEFINE(AVMPLUS_ARM) + ;; +sparc-*) + AC_DEFINE(AVMPLUS_SPARC) + ;; +esac + +case "$target_os" in +linux*) + AC_DEFINE(AVMPLUS_UNIX) + AC_DEFINE(AVMPLUS_LINUX) + ;; +darwin*) + AC_DEFINE(AVMPLUS_UNIX) + ;; +solaris*) + AC_DEFINE(AVMPLUS_UNIX) + ;; +freebsd*|kfreebsd*) + AC_DEFINE(AVMPLUS_UNIX) + ;; +*cygwin*|*mingw*|*mks*|*msvc*|*wince) + AC_DEFINE(AVMPLUS_WIN32) + ;; +*os2*) + AC_DEFINE(AVMPLUS_OS2) + ;; +*) + AC_MSG_ERROR([Unrecognized nanojit platform. Use --disable-jit to build without JIT support.]) +esac + +fi # ENABLE_JIT + +AC_SUBST(ENABLE_JIT) +AC_SUBST(NANOJIT_ARCH) + +if test -z "$SKIP_COMPILER_CHECKS"; then +dnl Checks for typedefs, structures, and compiler characteristics. +dnl ======================================================== +AC_LANG_C +AC_HEADER_STDC +AC_C_CONST +AC_TYPE_MODE_T +AC_TYPE_OFF_T +AC_TYPE_PID_T +AC_TYPE_SIZE_T +AC_STRUCT_ST_BLKSIZE +AC_MSG_CHECKING(for siginfo_t) +AC_CACHE_VAL(ac_cv_siginfo_t, + [AC_TRY_COMPILE([#define _POSIX_C_SOURCE 199506L + #include ], + [siginfo_t* info;], + [ac_cv_siginfo_t=true], + [ac_cv_siginfo_t=false])]) +if test "$ac_cv_siginfo_t" = true ; then + AC_DEFINE(HAVE_SIGINFO_T) + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(no) +fi + +dnl Find exact-width integer types, or figure them out +dnl ourselves. +dnl ======================================================== +dnl Once this is working, we can delete the code for int16_t, +dnl etc. below. + +AC_CHECK_HEADER(stdint.h) +if test "$ac_cv_header_stdint_h" = yes; then + AC_DEFINE(JS_HAVE_STDINT_H) +else + dnl We'll figure them out for ourselves. List more likely types + dnl earlier. If we ever really encounter a size for which none of + dnl the listed types are appropriate, we'll get a configure-time + dnl error; just add the right answer. + MOZ_N_BYTE_TYPE(JS_INT8_TYPE, 1, [char]) + MOZ_N_BYTE_TYPE(JS_INT16_TYPE, 2, [short int long]) + MOZ_N_BYTE_TYPE(JS_INT32_TYPE, 4, [int long 'long long' short]) + MOZ_N_BYTE_TYPE(JS_INT64_TYPE, 8, [int long 'long long']) + MOZ_N_BYTE_TYPE(JS_INTPTR_TYPE, sizeof (void *), + [int long 'long long' short]) +fi + +MOZ_SIZE_OF_TYPE(JS_BYTES_PER_WORD, void*, 4 8) +if test "$moz_cv_size_of_JS_BYTES_PER_WORD" -eq "4"; then + AC_DEFINE(JS_BITS_PER_WORD_LOG2, 5) +elif test "$moz_cv_size_of_JS_BYTES_PER_WORD" -eq "8"; then + AC_DEFINE(JS_BITS_PER_WORD_LOG2, 6) +else + AC_MSG_ERROR([Unexpected JS_BYTES_PER_WORD]) +fi + +MOZ_ALIGN_OF_TYPE(JS_ALIGN_OF_POINTER, void*, 2 4 8 16) +MOZ_SIZE_OF_TYPE(JS_BYTES_PER_DOUBLE, double, 6 8 10 12 14) + +dnl Check for int16_t, int32_t, int64_t, int64, uint, uint_t, and uint16_t. +dnl ======================================================== +AC_MSG_CHECKING(for int16_t) +AC_CACHE_VAL(ac_cv_int16_t, + [AC_TRY_COMPILE([#include + #include ], + [int16_t foo = 0;], + [ac_cv_int16_t=true], + [ac_cv_int16_t=false])]) +if test "$ac_cv_int16_t" = true ; then + AC_DEFINE(HAVE_INT16_T) + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(no) +fi +AC_MSG_CHECKING(for int32_t) +AC_CACHE_VAL(ac_cv_int32_t, + [AC_TRY_COMPILE([#include + #include ], + [int32_t foo = 0;], + [ac_cv_int32_t=true], + [ac_cv_int32_t=false])]) +if test "$ac_cv_int32_t" = true ; then + AC_DEFINE(HAVE_INT32_T) + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(no) +fi +AC_MSG_CHECKING(for int64_t) +AC_CACHE_VAL(ac_cv_int64_t, + [AC_TRY_COMPILE([#include + #include ], + [int64_t foo = 0;], + [ac_cv_int64_t=true], + [ac_cv_int64_t=false])]) +if test "$ac_cv_int64_t" = true ; then + AC_DEFINE(HAVE_INT64_T) + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(no) +fi +AC_MSG_CHECKING(for int64) +AC_CACHE_VAL(ac_cv_int64, + [AC_TRY_COMPILE([#include + #include ], + [int64 foo = 0;], + [ac_cv_int64=true], + [ac_cv_int64=false])]) +if test "$ac_cv_int64" = true ; then + AC_DEFINE(HAVE_INT64) + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(no) +fi +AC_MSG_CHECKING(for uint) +AC_CACHE_VAL(ac_cv_uint, + [AC_TRY_COMPILE([#include + #include ], + [uint foo = 0;], + [ac_cv_uint=true], + [ac_cv_uint=false])]) +if test "$ac_cv_uint" = true ; then + AC_DEFINE(HAVE_UINT) + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(no) +fi +AC_MSG_CHECKING(for uint_t) +AC_CACHE_VAL(ac_cv_uint_t, + [AC_TRY_COMPILE([#include + #include ], + [uint_t foo = 0;], + [ac_cv_uint_t=true], + [ac_cv_uint_t=false])]) +if test "$ac_cv_uint_t" = true ; then + AC_DEFINE(HAVE_UINT_T) + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(no) +fi +AC_MSG_CHECKING(for uint16_t) +AC_CACHE_VAL(ac_cv_uint16_t, + [AC_TRY_COMPILE([#include + #include ], + [uint16_t foo = 0;], + [ac_cv_uint16_t=true], + [ac_cv_uint16_t=false])]) +if test "$ac_cv_uint16_t" = true ; then + AC_DEFINE(HAVE_UINT16_T) + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(no) +fi + +dnl On the gcc trunk (as of 2001-02-09) _GNU_SOURCE, and thus __USE_GNU, +dnl are defined when compiling C++ but not C. Since the result of this +dnl test is used only in C++, do it in C++. +AC_LANG_CPLUSPLUS + +AC_MSG_CHECKING(for uname.domainname) +AC_CACHE_VAL(ac_cv_have_uname_domainname_field, + [AC_TRY_COMPILE([#include ], + [ struct utsname *res; char *domain; + (void)uname(res); if (res != 0) { domain = res->domainname; } ], + [ac_cv_have_uname_domainname_field=true], + [ac_cv_have_uname_domainname_field=false])]) + +if test "$ac_cv_have_uname_domainname_field" = "true"; then + AC_DEFINE(HAVE_UNAME_DOMAINNAME_FIELD) + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(no) +fi + +AC_MSG_CHECKING(for uname.__domainname) +AC_CACHE_VAL(ac_cv_have_uname_us_domainname_field, + [AC_TRY_COMPILE([#include ], + [ struct utsname *res; char *domain; + (void)uname(res); if (res != 0) { domain = res->__domainname; } ], + [ac_cv_have_uname_us_domainname_field=true], + [ac_cv_have_uname_us_domainname_field=false])]) + +if test "$ac_cv_have_uname_us_domainname_field" = "true"; then + AC_DEFINE(HAVE_UNAME_US_DOMAINNAME_FIELD) + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(no) +fi + +AC_LANG_C + +dnl Check for .hidden assembler directive and visibility attribute. +dnl Borrowed from glibc configure.in +dnl =============================================================== +if test "$GNU_CC"; then + AC_CACHE_CHECK(for visibility(hidden) attribute, + ac_cv_visibility_hidden, + [cat > conftest.c </dev/null 2>&1; then + if egrep '\.(hidden|private_extern).*foo' conftest.s >/dev/null; then + ac_cv_visibility_hidden=yes + fi + fi + rm -f conftest.[cs] + ]) + if test "$ac_cv_visibility_hidden" = "yes"; then + AC_DEFINE(HAVE_VISIBILITY_HIDDEN_ATTRIBUTE) + + AC_CACHE_CHECK(for visibility(default) attribute, + ac_cv_visibility_default, + [cat > conftest.c </dev/null 2>&1; then + if ! egrep '\.(hidden|private_extern).*foo' conftest.s >/dev/null; then + ac_cv_visibility_default=yes + fi + fi + rm -f conftest.[cs] + ]) + if test "$ac_cv_visibility_default" = "yes"; then + AC_DEFINE(HAVE_VISIBILITY_ATTRIBUTE) + + AC_CACHE_CHECK(for visibility pragma support, + ac_cv_visibility_pragma, + [cat > conftest.c </dev/null 2>&1; then + if egrep '\.(hidden|private_extern).*foo_hidden' conftest.s >/dev/null; then + if ! egrep '\.(hidden|private_extern).*foo_default' conftest.s > /dev/null; then + ac_cv_visibility_pragma=yes + fi + fi + fi + rm -f conftest.[cs] + ]) + if test "$ac_cv_visibility_pragma" = "yes"; then + AC_CACHE_CHECK(For gcc visibility bug with class-level attributes (GCC bug 26905), + ac_cv_have_visibility_class_bug, + [cat > conftest.c < /dev/null 2>&1 ; then + ac_cv_have_visibility_class_bug=yes + else + if test `egrep -c '@PLT|\\$stub' conftest.S` = 0; then + ac_cv_have_visibility_class_bug=yes + fi + fi + rm -rf conftest.{c,S} + ]) + + AC_CACHE_CHECK(For x86_64 gcc visibility bug with builtins (GCC bug 20297), + ac_cv_have_visibility_builtin_bug, + [cat > conftest.c < +#pragma GCC visibility pop + +__attribute__ ((visibility ("default"))) void Func() { + char c[[100]]; + memset(c, 0, sizeof(c)); +} +EOF + ac_cv_have_visibility_builtin_bug=no + if ! ${CC-cc} ${CFLAGS} ${DSO_PIC_CFLAGS} ${DSO_LDOPTS} -O2 -S -o conftest.S conftest.c > /dev/null 2>&1 ; then + ac_cv_have_visibility_builtin_bug=yes + else + if test `grep -c "@PLT" conftest.S` = 0; then + ac_cv_visibility_builtin_bug=yes + fi + fi + rm -f conftest.{c,S} + ]) + if test "$ac_cv_have_visibility_builtin_bug" = "no" -a \ + "$ac_cv_have_visibility_class_bug" = "no"; then + VISIBILITY_FLAGS='-I$(DIST)/system_wrappers_js -include $(topsrcdir)/config/gcc_hidden.h' + WRAP_SYSTEM_INCLUDES=1 + else + VISIBILITY_FLAGS='-fvisibility=hidden' + fi # have visibility pragma bug + fi # have visibility pragma + fi # have visibility(default) attribute + fi # have visibility(hidden) attribute +fi # GNU_CC + +# visibility hidden flag for Sun Studio on Solaris +if test "$SOLARIS_SUNPRO_CC"; then +VISIBILITY_FLAGS='-xldscope=hidden' +fi # Sun Studio on Solaris + +AC_SUBST(WRAP_SYSTEM_INCLUDES) +AC_SUBST(VISIBILITY_FLAGS) + +dnl Checks for header files. +dnl ======================================================== +AC_HEADER_DIRENT +case "$target_os" in +freebsd*) +# for stuff like -lXshm + CPPFLAGS="${CPPFLAGS} ${X_CFLAGS}" + ;; +esac +AC_CHECK_HEADERS(sys/byteorder.h compat.h getopt.h) +AC_CHECK_HEADERS(sys/bitypes.h memory.h unistd.h) +AC_CHECK_HEADERS(gnu/libc-version.h nl_types.h) +AC_CHECK_HEADERS(malloc.h) +AC_CHECK_HEADERS(X11/XKBlib.h) + +dnl These are all the places some variant of statfs can be hiding. +AC_CHECK_HEADERS(sys/statvfs.h sys/statfs.h sys/vfs.h sys/mount.h) + +dnl Try for MMX support +dnl NB - later gcc versions require -mmmx for this header to be successfully +dnl included (or another option which implies it, such as -march=pentium-mmx) +AC_CHECK_HEADERS(mmintrin.h) + +dnl Check whether the compiler supports the new-style C++ standard +dnl library headers (i.e. ) or needs the old "new.h" +AC_LANG_CPLUSPLUS +NEW_H=new.h +AC_CHECK_HEADER(new, [NEW_H=new]) +AC_DEFINE_UNQUOTED(NEW_H, <$NEW_H>) +AC_LANG_C + +AC_ARG_ENABLE(dtrace, + [ --enable-dtrace build with dtrace support if available (default=no)], + [enable_dtrace="yes"],) +if test "x$enable_dtrace" = "xyes"; then + AC_CHECK_HEADER(sys/sdt.h, HAVE_DTRACE=1) + if test -n "$HAVE_DTRACE"; then + AC_DEFINE(INCLUDE_MOZILLA_DTRACE) + else + AC_MSG_ERROR([dtrace enabled but sys/sdt.h not found]); + fi +fi +AC_SUBST(HAVE_DTRACE) + +case $target in +*-aix4.3*|*-aix5*) + ;; +*) + AC_CHECK_HEADERS(sys/cdefs.h) + ;; +esac + +dnl Checks for libraries. +dnl ======================================================== +case $target in +*-hpux11.*) + ;; +*) + AC_CHECK_LIB(c_r, gethostbyname_r) + ;; +esac + +dnl We don't want to link with libdl even if it's present on OS X, since +dnl it's not used and not part of the default installation. +dnl The same goes for BeOS. OS/2 has dlfcn in libc. +dnl We don't want to link against libm or libpthread on Darwin since +dnl they both are just symlinks to libSystem and explicitly linking +dnl against libSystem causes issues when debugging (see bug 299601). +case $target in +*-darwin*) + ;; +*-beos*) + ;; +*-os2*) + ;; +*) + AC_CHECK_LIB(m, atan) + AC_CHECK_LIB(dl, dlopen, + AC_CHECK_HEADER(dlfcn.h, + LIBS="-ldl $LIBS" + AC_DEFINE(HAVE_LIBDL))) + ;; +esac + +_SAVE_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS -D_GNU_SOURCE" +AC_CHECK_FUNCS(dladdr) +CFLAGS="$_SAVE_CFLAGS" + +if test ! "$GNU_CXX"; then + + case $target in + *-aix*) + AC_CHECK_LIB(C_r, demangle) + ;; + *) + AC_CHECK_LIB(C, demangle) + ;; + esac +fi + +dnl OS/2 has socket in libc. +case $target in +*-os2*) + ;; +*) + AC_CHECK_LIB(socket, socket) +esac + +dnl ======================================================== +dnl = pthread support +dnl = Start by checking whether the system support pthreads +dnl ======================================================== +case "$target_os" in +darwin*) + USE_PTHREADS=1 + ;; +*) + MOZ_CHECK_PTHREADS(pthreads, + USE_PTHREADS=1 _PTHREAD_LDFLAGS="-lpthreads", + MOZ_CHECK_PTHREADS(pthread, + USE_PTHREADS=1 _PTHREAD_LDFLAGS="-lpthread", + MOZ_CHECK_PTHREADS(c_r, + USE_PTHREADS=1 _PTHREAD_LDFLAGS="-lc_r", + MOZ_CHECK_PTHREADS(c, + USE_PTHREADS=1 + ) + ) + ) + ) + ;; +esac + +dnl ======================================================== +dnl Check the command line for --with-pthreads +dnl ======================================================== +MOZ_ARG_WITH_BOOL(pthreads, +[ --with-pthreads Force use of system pthread library with NSPR ], +[ if test "$USE_PTHREADS"x = x; then + AC_MSG_ERROR([ --with-pthreads specified for a system without pthread support ]); +fi], + USE_PTHREADS= + _PTHREAD_LDFLAGS= +) + +dnl ======================================================== +dnl Do the platform specific pthread hackery +dnl ======================================================== +if test "$USE_PTHREADS"x != x +then + dnl + dnl See if -pthread is supported. + dnl + rm -f conftest* + ac_cv_have_dash_pthread=no + AC_MSG_CHECKING(whether ${CC-cc} accepts -pthread) + echo 'int main() { return 0; }' | cat > conftest.c + ${CC-cc} -pthread -o conftest conftest.c > conftest.out 2>&1 + if test $? -eq 0; then + if test -z "`egrep -i '(unrecognize|unknown)' conftest.out | grep pthread`" && test -z "`egrep -i '(error|incorrect)' conftest.out`" ; then + ac_cv_have_dash_pthread=yes + case "$target_os" in + freebsd*) +# Freebsd doesn't use -pthread for compiles, it uses them for linking + ;; + *) + CFLAGS="$CFLAGS -pthread" + CXXFLAGS="$CXXFLAGS -pthread" + ;; + esac + fi + fi + rm -f conftest* + AC_MSG_RESULT($ac_cv_have_dash_pthread) + + dnl + dnl See if -pthreads is supported. + dnl + ac_cv_have_dash_pthreads=no + if test "$ac_cv_have_dash_pthread" = "no"; then + AC_MSG_CHECKING(whether ${CC-cc} accepts -pthreads) + echo 'int main() { return 0; }' | cat > conftest.c + ${CC-cc} -pthreads -o conftest conftest.c > conftest.out 2>&1 + if test $? -eq 0; then + if test -z "`egrep -i '(unrecognize|unknown)' conftest.out | grep pthreads`" && test -z "`egrep -i '(error|incorrect)' conftest.out`" ; then + ac_cv_have_dash_pthreads=yes + CFLAGS="$CFLAGS -pthreads" + CXXFLAGS="$CXXFLAGS -pthreads" + fi + fi + rm -f conftest* + AC_MSG_RESULT($ac_cv_have_dash_pthreads) + fi + + case "$target" in + *-*-freebsd*) + AC_DEFINE(_REENTRANT) + AC_DEFINE(_THREAD_SAFE) + dnl -pthread links in -lc_r, so don't specify it explicitly. + if test "$ac_cv_have_dash_pthread" = "yes"; then + _PTHREAD_LDFLAGS="-pthread" + else + _PTHREAD_LDFLAGS="-lc_r" + fi + ;; + + *-*-openbsd*|*-*-bsdi*) + AC_DEFINE(_REENTRANT) + AC_DEFINE(_THREAD_SAFE) + dnl -pthread links in -lc_r, so don't specify it explicitly. + if test "$ac_cv_have_dash_pthread" = "yes"; then + _PTHREAD_LDFLAGS="-pthread" + fi + ;; + + *-*-linux*|*-*-kfreebsd*-gnu) + AC_DEFINE(_REENTRANT) + ;; + + *-*-nto*) + AC_DEFINE(_REENTRANT) + ;; + + *-aix4.3*|*-aix5*) + AC_DEFINE(_REENTRANT) + ;; + + *-hpux11.*) + AC_DEFINE(_REENTRANT) + ;; + + alpha*-*-osf*) + AC_DEFINE(_REENTRANT) + ;; + + *-*-solaris*) + AC_DEFINE(_REENTRANT) + if test "$SOLARIS_SUNPRO_CC"; then + CFLAGS="$CFLAGS -mt" + CXXFLAGS="$CXXFLAGS -mt" + fi + ;; + esac + LDFLAGS="${_PTHREAD_LDFLAGS} ${LDFLAGS}" +fi + +dnl ======================================================== +dnl See if mmap sees writes +dnl For cross compiling, just define it as no, which is a safe default +dnl ======================================================== +AC_MSG_CHECKING(whether mmap() sees write()s) + +changequote(,) +mmap_test_prog=' + #include + #include + #include + #include + #include + #include + + char fname[] = "conftest.file"; + char zbuff[1024]; /* Fractional page is probably worst case */ + + int main() { + char *map; + int fd; + int i; + unlink(fname); + fd = open(fname, O_RDWR | O_CREAT, 0660); + if(fd<0) return 1; + unlink(fname); + write(fd, zbuff, sizeof(zbuff)); + lseek(fd, 0, SEEK_SET); + map = (char*)mmap(0, sizeof(zbuff), PROT_READ, MAP_SHARED, fd, 0); + if(map==(char*)-1) return 2; + for(i=0; fname[i]; i++) { + int rc = write(fd, &fname[i], 1); + if(map[i]!=fname[i]) return 4; + } + return 0; + } +' +changequote([,]) + +AC_TRY_RUN($mmap_test_prog , result="yes", result="no", result="yes") + +AC_MSG_RESULT("$result") + +if test "$result" = "no"; then + AC_DEFINE(MMAP_MISSES_WRITES) +fi + + +dnl Checks for library functions. +dnl ======================================================== +AC_PROG_GCC_TRADITIONAL +AC_FUNC_MEMCMP + +AC_CHECK_FUNCS([fchmod flockfile getc_unlocked _getc_nolock getpagesize \ + lchown localtime_r lstat64 memmove random rint sbrk snprintf \ + stat64 statvfs statvfs64 strerror strtok_r truncate64]) + +dnl Windows functions, for mingw. +AC_TRY_LINK([#include ], + [SYSTEMTIME st;FILETIME ft;SystemTimeToFileTime(&st,&ft);], + ac_cv_have_systemtimetofiletime="yes", + ac_cv_have_systemtimetofiletime="no") +if test "$ac_cv_have_systemtimetofiletime" = "yes"; then + AC_DEFINE(HAVE_SYSTEMTIMETOFILETIME) +fi +AC_TRY_LINK([#include ], + [FILETIME ft;GetSystemTimeAsFileTime(&ft);], + ac_cv_have_getsystemtimeasfiletime="yes", + ac_cv_have_getsystemtimeasfiletime="no") +if test "$ac_cv_have_getsystemtimeasfiletime" = "yes"; then + AC_DEFINE(HAVE_GETSYSTEMTIMEASFILETIME) +fi + +dnl check for wcrtomb/mbrtowc +dnl ======================================================================= +if test -z "$MACOS_DEPLOYMENT_TARGET" || test "$MACOS_DEPLOYMENT_TARGET" -ge "100300"; then +AC_LANG_SAVE +AC_LANG_CPLUSPLUS +AC_CACHE_CHECK(for wcrtomb, + ac_cv_have_wcrtomb, + [AC_TRY_LINK([#include ], + [mbstate_t ps={0};wcrtomb(0,'f',&ps);], + ac_cv_have_wcrtomb="yes", + ac_cv_have_wcrtomb="no")]) +if test "$ac_cv_have_wcrtomb" = "yes"; then + AC_DEFINE(HAVE_WCRTOMB) +fi +AC_CACHE_CHECK(for mbrtowc, + ac_cv_have_mbrtowc, + [AC_TRY_LINK([#include ], + [mbstate_t ps={0};mbrtowc(0,0,0,&ps);], + ac_cv_have_mbrtowc="yes", + ac_cv_have_mbrtowc="no")]) +if test "$ac_cv_have_mbrtowc" = "yes"; then + AC_DEFINE(HAVE_MBRTOWC) +fi +AC_LANG_RESTORE +fi + +AC_CACHE_CHECK( + [for res_ninit()], + ac_cv_func_res_ninit, + [AC_TRY_LINK([ + #ifdef linux + #define _BSD_SOURCE 1 + #endif + #include + ], + [int foo = res_ninit(&_res);], + [ac_cv_func_res_ninit=yes], + [ac_cv_func_res_ninit=no]) + ]) + +if test "$ac_cv_func_res_ninit" = "yes"; then + AC_DEFINE(HAVE_RES_NINIT) +dnl must add the link line we do something as foolish as this... dougt +dnl else +dnl AC_CHECK_LIB(bind, res_ninit, AC_DEFINE(HAVE_RES_NINIT), +dnl AC_CHECK_LIB(resolv, res_ninit, AC_DEFINE(HAVE_RES_NINIT))) +fi + +AC_LANG_CPLUSPLUS +AC_CACHE_CHECK( + [for gnu_get_libc_version()], + ac_cv_func_gnu_get_libc_version, + [AC_TRY_LINK([ + #ifdef HAVE_GNU_LIBC_VERSION_H + #include + #endif + ], + [const char *glibc_version = gnu_get_libc_version();], + [ac_cv_func_gnu_get_libc_version=yes], + [ac_cv_func_gnu_get_libc_version=no] + )] + ) + +if test "$ac_cv_func_gnu_get_libc_version" = "yes"; then + AC_DEFINE(HAVE_GNU_GET_LIBC_VERSION) +fi + +case $target_os in + os2*|msvc*|mks*|cygwin*|mingw*|darwin*|wince*|beos*) + ;; + *) + +AC_CHECK_LIB(c, iconv, [_ICONV_LIBS="$_ICONV_LIBS"], + AC_CHECK_LIB(iconv, iconv, [_ICONV_LIBS="$_ICONV_LIBS -liconv"], + AC_CHECK_LIB(iconv, libiconv, [_ICONV_LIBS="$_ICONV_LIBS -liconv"]))) +_SAVE_LIBS=$LIBS +LIBS="$LIBS $_ICONV_LIBS" +AC_CACHE_CHECK( + [for iconv()], + ac_cv_func_iconv, + [AC_TRY_LINK([ + #include + #include + ], + [ + iconv_t h = iconv_open("", ""); + iconv(h, NULL, NULL, NULL, NULL); + iconv_close(h); + ], + [ac_cv_func_iconv=yes], + [ac_cv_func_iconv=no] + )] + ) +if test "$ac_cv_func_iconv" = "yes"; then + AC_DEFINE(HAVE_ICONV) + DYNAMIC_XPCOM_LIBS="$DYNAMIC_XPCOM_LIBS $_ICONV_LIBS" + LIBXUL_LIBS="$LIBXUL_LIBS $_ICONV_LIBS" + LIBICONV="$_ICONV_LIBS" + AC_CACHE_CHECK( + [for iconv() with const input], + ac_cv_func_const_iconv, + [AC_TRY_COMPILE([ + #include + #include + ], + [ + const char *input = "testing"; + iconv_t h = iconv_open("", ""); + iconv(h, &input, NULL, NULL, NULL); + iconv_close(h); + ], + [ac_cv_func_const_iconv=yes], + [ac_cv_func_const_iconv=no] + )] + ) + if test "$ac_cv_func_const_iconv" = "yes"; then + AC_DEFINE(HAVE_ICONV_WITH_CONST_INPUT) + fi +fi +LIBS=$_SAVE_LIBS + + ;; +esac + +AC_LANG_C + +dnl ********************** +dnl *** va_copy checks *** +dnl ********************** +dnl we currently check for all three va_copy possibilities, so we get +dnl all results in config.log for bug reports. +AC_MSG_CHECKING(for an implementation of va_copy()) +AC_CACHE_VAL(ac_cv_va_copy,[ + AC_TRY_RUN([ + #include + void f (int i, ...) { + va_list args1, args2; + va_start (args1, i); + va_copy (args2, args1); + if (va_arg (args2, int) != 42 || va_arg (args1, int) != 42) + exit (1); + va_end (args1); va_end (args2); + } + int main() { f (0, 42); return 0; }], + ac_cv_va_copy=yes, + ac_cv_va_copy=no, + ac_cv_va_copy=no + ) +]) +AC_MSG_RESULT($ac_cv_va_copy) +AC_MSG_CHECKING(for an implementation of __va_copy()) +AC_CACHE_VAL(ac_cv___va_copy,[ + AC_TRY_RUN([ + #include + void f (int i, ...) { + va_list args1, args2; + va_start (args1, i); + __va_copy (args2, args1); + if (va_arg (args2, int) != 42 || va_arg (args1, int) != 42) + exit (1); + va_end (args1); va_end (args2); + } + int main() { f (0, 42); return 0; }], + ac_cv___va_copy=yes, + ac_cv___va_copy=no, + ac_cv___va_copy=no + ) +]) +AC_MSG_RESULT($ac_cv___va_copy) +AC_MSG_CHECKING(whether va_lists can be copied by value) +AC_CACHE_VAL(ac_cv_va_val_copy,[ + AC_TRY_RUN([ + #include + void f (int i, ...) { + va_list args1, args2; + va_start (args1, i); + args2 = args1; + if (va_arg (args2, int) != 42 || va_arg (args1, int) != 42) + exit (1); + va_end (args1); va_end (args2); + } + int main() { f (0, 42); return 0; }], + ac_cv_va_val_copy=yes, + ac_cv_va_val_copy=no, + ac_cv_va_val_copy=yes + ) +]) +if test "x$ac_cv_va_copy" = "xyes"; then + AC_DEFINE(VA_COPY, va_copy) + AC_DEFINE(HAVE_VA_COPY) +elif test "x$ac_cv___va_copy" = "xyes"; then + AC_DEFINE(VA_COPY, __va_copy) + AC_DEFINE(HAVE_VA_COPY) +fi + +if test "x$ac_cv_va_val_copy" = "xno"; then + AC_DEFINE(HAVE_VA_LIST_AS_ARRAY) +fi +AC_MSG_RESULT($ac_cv_va_val_copy) + +dnl Check for dll-challenged libc's. +dnl This check is apparently only needed for Linux. +case "$target" in + *-linux*) + dnl =================================================================== + _curdir=`pwd` + export _curdir + rm -rf conftest* _conftest + mkdir _conftest + cat >> conftest.C <<\EOF +#include +#include +#include +#ifdef _dl_loaded +void __dump_link_map(void) { + struct link_map *map = _dl_loaded; + while (NULL != map) {printf("0x%08x %s\n", map->l_addr, map->l_name); map = map->l_next;} +} +int main() { + dlopen("./conftest1.so",RTLD_LAZY); + dlopen("./../_conftest/conftest1.so",RTLD_LAZY); + dlopen("CURDIR/_conftest/conftest1.so",RTLD_LAZY); + dlopen("CURDIR/_conftest/../_conftest/conftest1.so",RTLD_LAZY); + __dump_link_map(); +} +#else +/* _dl_loaded isn't defined, so this should be either a libc5 (glibc1) system, or a glibc2 system that doesn't have the multiple load bug (i.e., RH6.0).*/ +int main() { printf("./conftest1.so\n"); } +#endif +EOF + + $PERL -p -i -e "s/CURDIR/\$ENV{_curdir}/g;" conftest.C + + cat >> conftest1.C <<\EOF +#include +void foo(void) {printf("foo in dll called\n");} +EOF + ${CXX-g++} -fPIC -c -g conftest1.C + ${CXX-g++} -shared -Wl,-h -Wl,conftest1.so -o conftest1.so conftest1.o + ${CXX-g++} -g conftest.C -o conftest -ldl + cp -f conftest1.so conftest _conftest + cd _conftest + if test `./conftest | grep conftest1.so | wc -l` -gt 1 + then + echo + echo "*** Your libc has a bug that can result in loading the same dynamic" + echo "*** library multiple times. This bug is known to be fixed in glibc-2.0.7-32" + echo "*** or later. However, if you choose not to upgrade, the only effect" + echo "*** will be excessive memory usage at runtime." + echo + fi + cd ${_curdir} + rm -rf conftest* _conftest + dnl =================================================================== + ;; +esac + +dnl =================================================================== +dnl ======================================================== +dnl By default, turn rtti and exceptions off on g++/egcs +dnl ======================================================== +if test "$GNU_CXX"; then + + AC_MSG_CHECKING(for C++ exceptions flag) + + dnl They changed -f[no-]handle-exceptions to -f[no-]exceptions in g++ 2.8 + AC_CACHE_VAL(ac_cv_cxx_exceptions_flags, + [echo "int main() { return 0; }" | cat > conftest.C + + ${CXX-g++} ${CXXFLAGS} -c -fno-handle-exceptions conftest.C > conftest.out 2>&1 + + if egrep "warning.*renamed" conftest.out >/dev/null; then + ac_cv_cxx_exceptions_flags=${_COMPILER_PREFIX}-fno-exceptions + else + ac_cv_cxx_exceptions_flags=${_COMPILER_PREFIX}-fno-handle-exceptions + fi + + rm -f conftest*]) + + AC_MSG_RESULT($ac_cv_cxx_exceptions_flags) + _MOZ_EXCEPTIONS_FLAGS_OFF=$ac_cv_cxx_exceptions_flags + _MOZ_EXCEPTIONS_FLAGS_ON=`echo $ac_cv_cxx_exceptions_flags | sed 's|no-||'` +fi + +dnl ======================================================== +dnl Put your C++ language/feature checks below +dnl ======================================================== +AC_LANG_CPLUSPLUS + +HAVE_GCC3_ABI= +if test "$GNU_CC"; then + AC_CACHE_CHECK(for gcc 3.0 ABI, + ac_cv_gcc_three_abi, + [AC_TRY_COMPILE([], + [ +#if defined(__GXX_ABI_VERSION) && __GXX_ABI_VERSION >= 100 /* G++ V3 ABI */ + return 0; +#else +#error Not gcc3. +#endif + ], + ac_cv_gcc_three_abi="yes", + ac_cv_gcc_three_abi="no")]) + if test "$ac_cv_gcc_three_abi" = "yes"; then + TARGET_COMPILER_ABI="${TARGET_COMPILER_ABI-gcc3}" + HAVE_GCC3_ABI=1 + else + TARGET_COMPILER_ABI="${TARGET_COMPILER_ABI-gcc2}" + fi +fi +AC_SUBST(HAVE_GCC3_ABI) + + +AC_CACHE_CHECK(for C++ \"explicit\" keyword, + ac_cv_cpp_explicit, + [AC_TRY_COMPILE(class X { + public: explicit X(int i) : i_(i) {} + private: int i_; + };, + X x(3);, + ac_cv_cpp_explicit=yes, + ac_cv_cpp_explicit=no)]) +if test "$ac_cv_cpp_explicit" = yes ; then + AC_DEFINE(HAVE_CPP_EXPLICIT) +fi + +AC_CACHE_CHECK(for C++ \"typename\" keyword, + ac_cv_cpp_typename, + [AC_TRY_COMPILE(class param { + public: + typedef unsigned long num_type; + }; + + template class tplt { + public: + typedef typename T::num_type t_num_type; + t_num_type foo(typename T::num_type num) { + return num; + } + };, + tplt A; + A.foo(0);, + ac_cv_cpp_typename=yes, + ac_cv_cpp_typename=no)]) +if test "$ac_cv_cpp_typename" = yes ; then + AC_DEFINE(HAVE_CPP_TYPENAME) +fi + +dnl Check for support of modern template specialization syntax +dnl Test code and requirement from scc@netscape.com. +dnl Autoconf cut-and-paste job by waterson@netscape.com +AC_CACHE_CHECK(for modern C++ template specialization syntax support, + ac_cv_cpp_modern_specialize_template_syntax, + [AC_TRY_COMPILE(template struct X { int a; }; + class Y {}; + template <> struct X { double a; };, + X int_x; + X y_x;, + ac_cv_cpp_modern_specialize_template_syntax=yes, + ac_cv_cpp_modern_specialize_template_syntax=no)]) +if test "$ac_cv_cpp_modern_specialize_template_syntax" = yes ; then + AC_DEFINE(HAVE_CPP_MODERN_SPECIALIZE_TEMPLATE_SYNTAX) +fi + + +dnl Some compilers support only full specialization, and some don't. +AC_CACHE_CHECK(whether partial template specialization works, + ac_cv_cpp_partial_specialization, + [AC_TRY_COMPILE(template class Foo {}; + template class Foo {};, + return 0;, + ac_cv_cpp_partial_specialization=yes, + ac_cv_cpp_partial_specialization=no)]) +if test "$ac_cv_cpp_partial_specialization" = yes ; then + AC_DEFINE(HAVE_CPP_PARTIAL_SPECIALIZATION) +fi + +dnl Some compilers have limited support for operators with templates; +dnl specifically, it is necessary to define derived operators when a base +dnl class's operator declaration should suffice. +AC_CACHE_CHECK(whether operators must be re-defined for templates derived from templates, + ac_cv_need_derived_template_operators, + [AC_TRY_COMPILE([template class Base { }; + template + Base operator+(const Base& lhs, const Base& rhs) { return lhs; } + template class Derived : public Base { };], + [Derived a, b; + Base c = a + b; + return 0;], + ac_cv_need_derived_template_operators=no, + ac_cv_need_derived_template_operators=yes)]) +if test "$ac_cv_need_derived_template_operators" = yes ; then + AC_DEFINE(NEED_CPP_DERIVED_TEMPLATE_OPERATORS) +fi + + +dnl Some compilers have trouble detecting that a template class +dnl that derives from another template is actually an instance +dnl of the base class. This test checks for that. +AC_CACHE_CHECK(whether we need to cast a derived template to pass as its base class, + ac_cv_need_cpp_template_cast_to_base, + [AC_TRY_COMPILE([template class Base { }; + template class Derived : public Base { }; + template int foo(const Base&) { return 0; }], + [Derived bar; return foo(bar);], + ac_cv_need_cpp_template_cast_to_base=no, + ac_cv_need_cpp_template_cast_to_base=yes)]) +if test "$ac_cv_need_cpp_template_cast_to_base" = yes ; then + AC_DEFINE(NEED_CPP_TEMPLATE_CAST_TO_BASE) +fi + +dnl Some compilers have trouble resolving the ambiguity between two +dnl functions whose arguments differ only by cv-qualifications. +AC_CACHE_CHECK(whether the compiler can resolve const ambiguities for templates, + ac_cv_can_resolve_const_ambiguity, + [AC_TRY_COMPILE([ + template class ptrClass { + public: T* ptr; + }; + + template T* a(ptrClass *arg) { + return arg->ptr; + } + + template + const T* a(const ptrClass *arg) { + return arg->ptr; + } + ], + [ ptrClass i; + a(&i); ], + ac_cv_can_resolve_const_ambiguity=yes, + ac_cv_can_resolve_const_ambiguity=no)]) +if test "$ac_cv_can_resolve_const_ambiguity" = no ; then + AC_DEFINE(CANT_RESOLVE_CPP_CONST_AMBIGUITY) +fi + +dnl +dnl We don't do exceptions on unix. The only reason this used to be here +dnl is that mozilla/xpcom/tests/TestCOMPtr.cpp has a test which uses +dnl exceptions. But, we turn exceptions off by default and this test breaks. +dnl So im commenting this out until someone writes some artificial +dnl intelligence to detect not only if the compiler has exceptions, but if +dnl they are enabled as well. +dnl +dnl AC_CACHE_CHECK(for C++ \"exceptions\", +dnl ac_cv_cpp_exceptions, +dnl [AC_TRY_COMPILE(class X { public: X() {} }; +dnl static void F() { throw X(); }, +dnl try { F(); } catch(X & e) { }, +dnl ac_cv_cpp_exceptions=yes, +dnl ac_cv_cpp_exceptions=no)]) +dnl if test $ac_cv_cpp_exceptions = yes ; then +dnl AC_DEFINE(HAVE_CPP_EXCEPTIONS) +dnl fi + +dnl Some compilers have marginal |using| support; for example, gcc-2.7.2.3 +dnl supports it well enough to allow us to use it to change access, but not +dnl to resolve ambiguity. The next two tests determine how well the |using| +dnl keyword is supported. +dnl +dnl Check to see if we can change access with |using|. Test both a +dnl legal and an illegal example. +AC_CACHE_CHECK(whether the C++ \"using\" keyword can change access, + ac_cv_cpp_access_changing_using2, + [AC_TRY_COMPILE( + class A { protected: int foo() { return 0; } }; + class B : public A { public: using A::foo; };, + B b; return b.foo();, + [AC_TRY_COMPILE( + class A { public: int foo() { return 1; } }; + class B : public A { private: using A::foo; };, + B b; return b.foo();, + ac_cv_cpp_access_changing_using2=no, + ac_cv_cpp_access_changing_using2=yes)], + ac_cv_cpp_access_changing_using2=no)]) +if test "$ac_cv_cpp_access_changing_using2" = yes ; then + AC_DEFINE(HAVE_CPP_ACCESS_CHANGING_USING) +fi + +dnl Check to see if we can resolve ambiguity with |using|. +AC_CACHE_CHECK(whether the C++ \"using\" keyword resolves ambiguity, + ac_cv_cpp_ambiguity_resolving_using, + [AC_TRY_COMPILE(class X { + public: int go(const X&) {return 3;} + int jo(const X&) {return 3;} + }; + class Y : public X { + public: int go(int) {return 2;} + int jo(int) {return 2;} + using X::jo; + private: using X::go; + };, + X x; Y y; y.jo(x);, + ac_cv_cpp_ambiguity_resolving_using=yes, + ac_cv_cpp_ambiguity_resolving_using=no)]) +if test "$ac_cv_cpp_ambiguity_resolving_using" = yes ; then + AC_DEFINE(HAVE_CPP_AMBIGUITY_RESOLVING_USING) +fi + +dnl Check to see if the |std| namespace is supported. If so, we'll want +dnl to qualify any standard library calls with "std::" to ensure that +dnl those functions can be resolved. +AC_CACHE_CHECK(for \"std::\" namespace, + ac_cv_cpp_namespace_std, + [AC_TRY_COMPILE([#include ], + [return std::min(0, 1);], + ac_cv_cpp_namespace_std=yes, + ac_cv_cpp_namespace_std=no)]) +if test "$ac_cv_cpp_namespace_std" = yes ; then + AC_DEFINE(HAVE_CPP_NAMESPACE_STD) +fi + +dnl Older compilers are overly ambitious with respect to using the standard +dnl template library's |operator!=()| when |operator==()| is defined. In +dnl which case, defining |operator!=()| in addition to |operator==()| causes +dnl ambiguity at compile-time. This test checks for that case. +AC_CACHE_CHECK(whether standard template operator!=() is ambiguous, + ac_cv_cpp_unambiguous_std_notequal, + [AC_TRY_COMPILE([#include + struct T1 {}; + int operator==(const T1&, const T1&) { return 0; } + int operator!=(const T1&, const T1&) { return 0; }], + [T1 a,b; return a != b;], + ac_cv_cpp_unambiguous_std_notequal=unambiguous, + ac_cv_cpp_unambiguous_std_notequal=ambiguous)]) +if test "$ac_cv_cpp_unambiguous_std_notequal" = unambiguous ; then + AC_DEFINE(HAVE_CPP_UNAMBIGUOUS_STD_NOTEQUAL) +fi + + +AC_CACHE_CHECK(for C++ reinterpret_cast, + ac_cv_cpp_reinterpret_cast, + [AC_TRY_COMPILE(struct X { int i; }; + struct Y { int i; };, + X x; X*const z = &x;Y*y = reinterpret_cast(z);, + ac_cv_cpp_reinterpret_cast=yes, + ac_cv_cpp_reinterpret_cast=no)]) +if test "$ac_cv_cpp_reinterpret_cast" = yes ; then + AC_DEFINE(HAVE_CPP_NEW_CASTS) +fi + +dnl See if a dynamic_cast to void* gives the most derived object. +AC_CACHE_CHECK(for C++ dynamic_cast to void*, + ac_cv_cpp_dynamic_cast_void_ptr, + [AC_TRY_RUN([class X { int i; public: virtual ~X() { } }; + class Y { int j; public: virtual ~Y() { } }; + class Z : public X, public Y { int k; }; + + int main() { + Z mdo; + X *subx = (X*)&mdo; + Y *suby = (Y*)&mdo; + return !((((void*)&mdo != (void*)subx) && + ((void*)&mdo == dynamic_cast(subx))) || + (((void*)&mdo != (void*)suby) && + ((void*)&mdo == dynamic_cast(suby)))); + }], + ac_cv_cpp_dynamic_cast_void_ptr=yes, + ac_cv_cpp_dynamic_cast_void_ptr=no, + ac_cv_cpp_dynamic_cast_void_ptr=no)]) +if test "$ac_cv_cpp_dynamic_cast_void_ptr" = yes ; then + AC_DEFINE(HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR) +fi + + +dnl note that this one is reversed - if the test fails, then +dnl we require implementations of unused virtual methods. Which +dnl really blows because it means we'll have useless vtable +dnl bloat. +AC_CACHE_CHECK(whether C++ requires implementation of unused virtual methods, + ac_cv_cpp_unused_required, + [AC_TRY_LINK(class X {private: virtual void never_called();};, + X x;, + ac_cv_cpp_unused_required=no, + ac_cv_cpp_unused_required=yes)]) +if test "$ac_cv_cpp_unused_required" = yes ; then + AC_DEFINE(NEED_CPP_UNUSED_IMPLEMENTATIONS) +fi + + +dnl Some compilers have trouble comparing a constant reference to a templatized +dnl class to zero, and require an explicit operator==() to be defined that takes +dnl an int. This test separates the strong from the weak. + +AC_CACHE_CHECK(for trouble comparing to zero near std::operator!=(), + ac_cv_trouble_comparing_to_zero, + [AC_TRY_COMPILE([#include + template class Foo {}; + class T2; + template int operator==(const T2*, const T&) { return 0; } + template int operator!=(const T2*, const T&) { return 0; }], + [Foo f; return (0 != f);], + ac_cv_trouble_comparing_to_zero=no, + ac_cv_trouble_comparing_to_zero=yes)]) +if test "$ac_cv_trouble_comparing_to_zero" = yes ; then + AC_DEFINE(HAVE_CPP_TROUBLE_COMPARING_TO_ZERO) +fi + + + +dnl End of C++ language/feature checks +AC_LANG_C + +dnl ======================================================== +dnl = Internationalization checks +dnl ======================================================== +dnl +dnl Internationalization and Locale support is different +dnl on various UNIX platforms. Checks for specific i18n +dnl features go here. + +dnl check for LC_MESSAGES +AC_CACHE_CHECK(for LC_MESSAGES, + ac_cv_i18n_lc_messages, + [AC_TRY_COMPILE([#include ], + [int category = LC_MESSAGES;], + ac_cv_i18n_lc_messages=yes, + ac_cv_i18n_lc_messages=no)]) +if test "$ac_cv_i18n_lc_messages" = yes; then + AC_DEFINE(HAVE_I18N_LC_MESSAGES) +fi + +fi # SKIP_COMPILER_CHECKS + +TARGET_XPCOM_ABI= +if test -n "${CPU_ARCH}" -a -n "${TARGET_COMPILER_ABI}"; then + TARGET_XPCOM_ABI="${CPU_ARCH}-${TARGET_COMPILER_ABI}" +fi + +dnl Mozilla specific options +dnl ======================================================== +dnl The macros used for command line options +dnl are defined in build/autoconf/altoptions.m4. + + +dnl ======================================================== +dnl = +dnl = Check for external package dependencies +dnl = +dnl ======================================================== +MOZ_ARG_HEADER(External Packages) + +dnl ======================================================== +dnl = Find the right NSPR to use. +dnl ======================================================== +MOZ_ARG_WITH_BOOL(system-nspr, +[ --with-system-nspr Use an NSPR that is already built and installed. + Use the 'nspr-config' script in the current path, + or look for the script in the directories given with + --with-nspr-exec-prefix or --with-nspr-prefix. + (Those flags are only checked if you specify + --with-system-nspr.)], + _USE_SYSTEM_NSPR=1 ) + +MOZ_ARG_WITH_STRING(nspr-cflags, +[ --with-nspr-cflags=FLAGS Pass FLAGS to CC when building code that uses NSPR. + Use this when there's no accurate nspr-config + script available. This is the case when building + SpiderMonkey as part of the Mozilla tree: the + top-level configure script computes NSPR flags + that accomodate the quirks of that environment.], + NSPR_CFLAGS=$withval) +MOZ_ARG_WITH_STRING(nspr-libs, +[ --with-nspr-libs=LIBS Pass LIBS to LD when linking code that uses NSPR. + See --with-nspr-cflags for more details.], + NSPR_LIBS=$withval) +AC_SUBST(NSPR_CFLAGS) +AC_SUBST(NSPR_LIBS) + +dnl Pass either --with-system-nspr or (--with-nspr-cflags and +dnl --with-nspr-libs), but not both. +if test "$_USE_SYSTEM_NSPR" && (test "$NSPR_CFLAGS" || test "$NSPR_LIBS"); then + AC_MSG_ERROR([--with-system-nspr and --with-nspr-libs/cflags are mutually exclusive. +See 'configure --help'.]) +fi + +if test -n "$_USE_SYSTEM_NSPR"; then + MOZ_NATIVE_NSPR= + AM_PATH_NSPR(4.7.0, [MOZ_NATIVE_NSPR=1]) + if test -z "$MOZ_NATIVE_NSPR"; then + AC_MSG_ERROR([--with-system-nspr given, but configure could not find a suitable NSPR. +Pass --with-nspr-exec-prefix, --with-nspr-prefix, or --with-nspr-cflags/libs. +See 'configure --help'.]) + fi +fi + +if test -n "$MOZ_NATIVE_NSPR"; then + _SAVE_CFLAGS=$CFLAGS + CFLAGS="$CFLAGS $NSPR_CFLAGS" + AC_TRY_COMPILE([#include "prlog.h"], + [#ifndef PR_STATIC_ASSERT + #error PR_STATIC_ASSERT not defined + #endif], + [MOZ_NATIVE_NSPR=1], + AC_MSG_ERROR([system NSPR does not support PR_STATIC_ASSERT])) + CFLAGS=$_SAVE_CFLAGS +fi + +dnl ======================================================== +dnl Use ARM userspace kernel helpers; tell NSPR to enable +dnl their usage and use them in spidermonkey. +dnl ======================================================== +MOZ_ARG_WITH_BOOL(arm-kuser, +[ --with-arm-kuser Use kuser helpers (Linux/ARM only -- requires kernel 2.6.13 or later)], + USE_ARM_KUSER=1, + USE_ARM_KUSER=) +if test -n "$USE_ARM_KUSER"; then + AC_DEFINE(USE_ARM_KUSER) +fi + +dnl ======================================================== +dnl = +dnl = Application +dnl = +dnl ======================================================== + +MOZ_ARG_HEADER(Application) + +BUILD_STATIC_LIBS= +ENABLE_TESTS=1 +MOZ_DBGRINFO_MODULES= + +dnl ======================================================== +dnl = +dnl = Components & Features +dnl = +dnl ======================================================== +MOZ_ARG_HEADER(Components and Features) + +dnl ======================================================== +dnl = Localization +dnl ======================================================== +MOZ_ARG_ENABLE_STRING(ui-locale, +[ --enable-ui-locale=ab-CD + Select the user interface locale (default: en-US)], + MOZ_UI_LOCALE=$enableval ) +AC_SUBST(MOZ_UI_LOCALE) + +dnl ======================================================== +dnl build the tests by default +dnl ======================================================== +MOZ_ARG_DISABLE_BOOL(tests, +[ --disable-tests Do not build test libraries & programs], + ENABLE_TESTS=, + ENABLE_TESTS=1 ) + +dnl ======================================================== +dnl = +dnl = Module specific options +dnl = +dnl ======================================================== +MOZ_ARG_HEADER(Individual module options) + +dnl ======================================================== +dnl = +dnl = Debugging Options +dnl = +dnl ======================================================== +MOZ_ARG_HEADER(Debugging and Optimizations) + +dnl ======================================================== +dnl = Disable building with debug info. +dnl = Debugging is OFF by default +dnl ======================================================== +if test -z "$MOZ_DEBUG_FLAGS" +then + case "$target" in + *-irix*) + if test "$GNU_CC"; then + GCC_VERSION=`$CC -v 2>&1 | awk '/version/ { print $3 }'` + case "$GCC_VERSION" in + 2.95.*) + MOZ_DEBUG_FLAGS="" + ;; + *) + MOZ_DEBUG_FLAGS="-g" + ;; + esac + else + MOZ_DEBUG_FLAGS="-g" + fi + ;; + *) + MOZ_DEBUG_FLAGS="-g" + ;; + esac +fi + +MOZ_ARG_ENABLE_STRING(debug, +[ --enable-debug[=DBG] Enable building with developer debug info + (Using compiler flags DBG)], +[ if test "$enableval" != "no"; then + MOZ_DEBUG=1 + if test -n "$enableval" && test "$enableval" != "yes"; then + MOZ_DEBUG_FLAGS=`echo $enableval | sed -e 's|\\\ | |g'` + fi + else + MOZ_DEBUG= + fi ], + MOZ_DEBUG=) + +MOZ_DEBUG_ENABLE_DEFS="-DDEBUG -D_DEBUG" + case "${target_os}" in + beos*) + MOZ_DEBUG_ENABLE_DEFS="$MOZ_DEBUG_ENABLE_DEFS -DDEBUG_${USER}" + ;; + msvc*|mks*|cygwin*|mingw*|os2*|wince*) + MOZ_DEBUG_ENABLE_DEFS="$MOZ_DEBUG_ENABLE_DEFS -DDEBUG_`echo ${USERNAME} | sed -e 's| |_|g'`" + ;; + *) + MOZ_DEBUG_ENABLE_DEFS="$MOZ_DEBUG_ENABLE_DEFS -DDEBUG_`$WHOAMI`" + ;; + esac +MOZ_DEBUG_ENABLE_DEFS="$MOZ_DEBUG_ENABLE_DEFS -DTRACING" + +MOZ_DEBUG_DISABLE_DEFS="-DNDEBUG -DTRIMMED" + +if test -n "$MOZ_DEBUG"; then + AC_MSG_CHECKING([for valid debug flags]) + _SAVE_CFLAGS=$CFLAGS + CFLAGS="$CFLAGS $MOZ_DEBUG_FLAGS" + AC_TRY_COMPILE([#include ], + [printf("Hello World\n");], + _results=yes, + _results=no) + AC_MSG_RESULT([$_results]) + if test "$_results" = "no"; then + AC_MSG_ERROR([These compiler flags are invalid: $MOZ_DEBUG_FLAGS]) + fi + CFLAGS=$_SAVE_CFLAGS +fi + +dnl ======================================================== +dnl = Enable code optimization. ON by default. +dnl ======================================================== +if test -z "$MOZ_OPTIMIZE_FLAGS"; then + MOZ_OPTIMIZE_FLAGS="-O" +fi + +MOZ_ARG_ENABLE_STRING(optimize, +[ --disable-optimize Disable compiler optimization + --enable-optimize=[OPT] Specify compiler optimization flags [OPT=-O]], +[ if test "$enableval" != "no"; then + MOZ_OPTIMIZE=1 + if test -n "$enableval" && test "$enableval" != "yes"; then + MOZ_OPTIMIZE_FLAGS=`echo "$enableval" | sed -e 's|\\\ | |g'` + MOZ_OPTIMIZE=2 + fi +else + MOZ_OPTIMIZE= +fi ], MOZ_OPTIMIZE=1) + +if test "$COMPILE_ENVIRONMENT"; then +if test -n "$MOZ_OPTIMIZE"; then + AC_MSG_CHECKING([for valid optimization flags]) + _SAVE_CFLAGS=$CFLAGS + CFLAGS="$CFLAGS $MOZ_OPTIMIZE_FLAGS" + AC_TRY_COMPILE([#include ], + [printf("Hello World\n");], + _results=yes, + _results=no) + AC_MSG_RESULT([$_results]) + if test "$_results" = "no"; then + AC_MSG_ERROR([These compiler flags are invalid: $MOZ_OPTIMIZE_FLAGS]) + fi + CFLAGS=$_SAVE_CFLAGS +fi +fi # COMPILE_ENVIRONMENT + +AC_SUBST(MOZ_OPTIMIZE) +AC_SUBST(MOZ_OPTIMIZE_FLAGS) +AC_SUBST(MOZ_OPTIMIZE_LDFLAGS) +AC_SUBST(MOZ_OPTIMIZE_SIZE_TWEAK) + +dnl ======================================================== +dnl = Enable/disable debug for specific modules only +dnl = module names beginning with ^ will be disabled +dnl ======================================================== +MOZ_ARG_ENABLE_STRING(debug-modules, +[ --enable-debug-modules Enable/disable debug info for specific modules], +[ MOZ_DEBUG_MODULES=`echo $enableval| sed 's/,/ /g'` ] ) + +dnl ======================================================== +dnl = Enable/disable generation of debugger info for specific modules only +dnl = the special module name ALL_MODULES can be used to denote all modules +dnl = module names beginning with ^ will be disabled +dnl ======================================================== +MOZ_ARG_ENABLE_STRING(debugger-info-modules, +[ --enable-debugger-info-modules + Enable/disable debugger info for specific modules], +[ for i in `echo $enableval | sed 's/,/ /g'`; do + dnl note that the list of module names is reversed as it is copied + dnl this is important, as it will allow config.mk to interpret stuff like + dnl "^ALL_MODULES xpcom" properly + if test "$i" = "no"; then + i="^ALL_MODULES" + fi + if test "$i" = "yes"; then + i="ALL_MODULES" + fi + MOZ_DBGRINFO_MODULES="$i $MOZ_DBGRINFO_MODULES"; + done ]) + +dnl ======================================================== +dnl Enable Narcissus +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(narcissus, +[ --enable-narcissus Build with Narcissus code enabled], + NARCISSUS=1, + NARCISSUS= ) +if test -n "$NARCISSUS"; then + AC_DEFINE(NARCISSUS) +fi + +dnl ======================================================== +dnl = Enable trace malloc +dnl ======================================================== +NS_TRACE_MALLOC=${MOZ_TRACE_MALLOC} +MOZ_ARG_ENABLE_BOOL(trace-malloc, +[ --enable-trace-malloc Enable malloc tracing], + NS_TRACE_MALLOC=1, + NS_TRACE_MALLOC= ) +if test "$NS_TRACE_MALLOC"; then + # Please, Mr. Linker Man, don't take away our symbol names + MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS= + AC_DEFINE(NS_TRACE_MALLOC) +fi +AC_SUBST(NS_TRACE_MALLOC) + +dnl ======================================================== +dnl = Enable jemalloc +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(jemalloc, +[ --enable-jemalloc Replace memory allocator with jemalloc], + MOZ_MEMORY=1, + MOZ_MEMORY=) + +if test "$NS_TRACE_MALLOC"; then + MOZ_MEMORY= +fi + +if test "$MOZ_MEMORY"; then + + dnl Don't try to run compiler tests on Windows + if test "$OS_ARCH" = "WINNT"; then + if test -z "$HAVE_64BIT_OS"; then + AC_DEFINE_UNQUOTED([MOZ_MEMORY_SIZEOF_PTR_2POW], 2) + else + AC_DEFINE_UNQUOTED([MOZ_MEMORY_SIZEOF_PTR_2POW], 3) + fi + else + AC_CHECK_SIZEOF([int *], [4]) + case "${ac_cv_sizeof_int_p}" in + 4) + AC_DEFINE_UNQUOTED([MOZ_MEMORY_SIZEOF_PTR_2POW], 2) + ;; + 8) + AC_DEFINE_UNQUOTED([MOZ_MEMORY_SIZEOF_PTR_2POW], 3) + ;; + *) + AC_MSG_ERROR([Unexpected pointer size]) + ;; + esac + fi + + AC_DEFINE(MOZ_MEMORY) + if test "x$MOZ_DEBUG" = "x1"; then + AC_DEFINE(MOZ_MEMORY_DEBUG) + fi + dnl The generic feature tests that determine how to compute ncpus are long and + dnl complicated. Therefore, simply define special cpp variables for the + dnl platforms we have special knowledge of. + case "${target_os}" in + darwin*) + AC_DEFINE(MOZ_MEMORY_DARWIN) + ;; + *freebsd*) + AC_DEFINE(MOZ_MEMORY_BSD) + ;; + *linux*) + AC_DEFINE(MOZ_MEMORY_LINUX) + ;; + netbsd*) + AC_DEFINE(MOZ_MEMORY_BSD) + ;; + solaris*) + AC_DEFINE(MOZ_MEMORY_SOLARIS) + ;; + msvc*|mks*|cygwin*|mingw*) + AC_DEFINE(MOZ_MEMORY_WINDOWS) + # the interesting bits will get passed down in MOZ_MEMORY_LDFLAGS + ;; + *wince) + AC_DEFINE(MOZ_MEMORY_WINCE) + AC_DEFINE(MOZ_MEMORY_WINDOWS) + ;; + *) + AC_MSG_ERROR([--enable-jemalloc not supported on ${target}]) + ;; + esac +fi +AC_SUBST(MOZ_MEMORY) +AC_SUBST(MOZ_MEMORY_LDFLAGS) + +dnl ======================================================== +dnl = Use malloc wrapper lib +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(wrap-malloc, +[ --enable-wrap-malloc Wrap malloc calls (gnu linker only)], + _WRAP_MALLOC=1, + _WRAP_MALLOC= ) + +if test -n "$_WRAP_MALLOC"; then + if test "$GNU_CC"; then + WRAP_MALLOC_CFLAGS="${LDFLAGS} -Wl,--wrap -Wl,malloc -Wl,--wrap -Wl,free -Wl,--wrap -Wl,realloc -Wl,--wrap -Wl,__builtin_new -Wl,--wrap -Wl,__builtin_vec_new -Wl,--wrap -Wl,__builtin_delete -Wl,--wrap -Wl,__builtin_vec_delete -Wl,--wrap -Wl,PR_Free -Wl,--wrap -Wl,PR_Malloc -Wl,--wrap -Wl,PR_Calloc -Wl,--wrap -Wl,PR_Realloc" + MKSHLIB='$(CXX) $(DSO_LDOPTS) $(WRAP_MALLOC_CFLAGS) -o $@' + fi +fi + +dnl ======================================================== +dnl = Location of malloc wrapper lib +dnl ======================================================== +MOZ_ARG_WITH_STRING(wrap-malloc, +[ --with-wrap-malloc=DIR Location of malloc wrapper library], + WRAP_MALLOC_LIB=$withval) + +dnl ======================================================== +dnl = Use TraceVis +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(tracevis, +[ --enable-tracevis Enable TraceVis tracing tool (default=no)], + MOZ_TRACEVIS=1, + MOZ_TRACEVIS= ) +if test -n "$MOZ_TRACEVIS"; then + AC_DEFINE(MOZ_TRACEVIS) + if test -z "$ENABLE_JIT"; then + AC_MSG_ERROR([--enable-tracevis is incompatible with --disable-jit]) + fi +fi + + +dnl ======================================================== +dnl = Use Valgrind +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(valgrind, +[ --enable-valgrind Enable Valgrind integration hooks (default=no)], + MOZ_VALGRIND=1, + MOZ_VALGRIND= ) +if test -n "$MOZ_VALGRIND"; then + AC_CHECK_HEADER([valgrind/valgrind.h], [], + AC_MSG_ERROR( + [--enable-valgrind specified but Valgrind is not installed])) + AC_DEFINE(MOZ_VALGRIND) +fi + +dnl ======================================================== +dnl jprof +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(jprof, +[ --enable-jprof Enable jprof profiling tool (needs mozilla/tools/jprof)], + MOZ_JPROF=1, + MOZ_JPROF= ) +if test -n "$MOZ_JPROF"; then + AC_DEFINE(MOZ_JPROF) +fi + +dnl ======================================================== +dnl shark +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(shark, +[ --enable-shark Enable shark remote profiling (needs CHUD framework)], + MOZ_SHARK=1, + MOZ_SHARK= ) +if test -n "$MOZ_SHARK"; then + AC_DEFINE(MOZ_SHARK) +fi + +dnl ======================================================== +dnl callgrind +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(callgrind, +[ --enable-callgrind Enable callgrind profiling], + MOZ_CALLGRIND=1, + MOZ_CALLGRIND= ) +if test -n "$MOZ_CALLGRIND"; then + AC_DEFINE(MOZ_CALLGRIND) +fi + +dnl ======================================================== +dnl vtune +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(vtune, +[ --enable-vtune Enable vtune profiling], + MOZ_VTUNE=1, + MOZ_VTUNE= ) +if test -n "$MOZ_VTUNE"; then + AC_DEFINE(MOZ_VTUNE) +fi + +dnl ======================================================== +dnl Zealous GC +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(gczeal, +[ --enable-gczeal Enable zealous GCing], + JS_GC_ZEAL=1, + JS_GC_ZEAL= ) +if test -n "$JS_GC_ZEAL"; then + AC_DEFINE(JS_GC_ZEAL) +fi + +dnl ======================================================== +dnl = Enable static checking using gcc-dehydra +dnl ======================================================== + +MOZ_ARG_WITH_STRING(static-checking, +[ --with-static-checking=path/to/gcc_dehydra.so + Enable static checking of code using GCC-dehydra], + DEHYDRA_PATH=$withval, + DEHYDRA_PATH= ) + +if test -n "$DEHYDRA_PATH"; then + if test ! -f "$DEHYDRA_PATH"; then + AC_MSG_ERROR([The dehydra plugin is not at the specified path.]) + fi + AC_DEFINE(NS_STATIC_CHECKING) +fi +AC_SUBST(DEHYDRA_PATH) + +dnl ======================================================== +dnl = Enable stripping of libs & executables +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(strip, +[ --enable-strip Enable stripping of libs & executables ], + ENABLE_STRIP=1, + ENABLE_STRIP= ) + +dnl ======================================================== +dnl = Enable stripping of libs & executables when packaging +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(install-strip, +[ --enable-install-strip Enable stripping of libs & executables when packaging ], + PKG_SKIP_STRIP= , + PKG_SKIP_STRIP=1) + +dnl ======================================================== +dnl = +dnl = Profiling and Instrumenting +dnl = +dnl ======================================================== +MOZ_ARG_HEADER(Profiling and Instrumenting) + +dnl ======================================================== +dnl = Enable timeline service, which provides lightweight +dnl = instrumentation of mozilla for performance measurement. +dnl = Timeline is off by default. +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(timeline, +[ --enable-timeline Enable timeline services ], + MOZ_TIMELINE=1, + MOZ_TIMELINE= ) +if test -n "$MOZ_TIMELINE"; then + AC_DEFINE(MOZ_TIMELINE) +fi + +MOZ_ARG_ENABLE_BOOL(insure, +[ --enable-insure Enable insure++ instrumentation (linux only)], + _ENABLE_INSURE=1, + _ENABLE_INSURE= ) +if test -n "$_ENABLE_INSURE"; then + MOZ_INSURE="insure" + MOZ_INSURIFYING=1 + MOZ_INSURE_DIRS="." + MOZ_INSURE_EXCLUDE_DIRS="config" +fi + +MOZ_ARG_WITH_STRING(insure-dirs, +[ --with-insure-dirs=DIRS + Dirs to instrument with insure ], + MOZ_INSURE_DIRS=$withval ) + +MOZ_ARG_WITH_STRING(insure-exclude-dirs, +[ --with-insure-exclude-dirs=DIRS + Dirs to not instrument with insure ], + MOZ_INSURE_EXCLUDE_DIRS="config $withval" ) + +dnl ======================================================== +dnl = Support for Quantify (Windows) +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(quantify, +[ --enable-quantify Enable Quantify support (Windows only) ], + MOZ_QUANTIFY=1, + MOZ_QUANTIFY= ) + +dnl ======================================================== +dnl = Support for demangling undefined symbols +dnl ======================================================== +if test -z "$SKIP_LIBRARY_CHECKS"; then + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_CHECK_FUNCS(__cxa_demangle, HAVE_DEMANGLE=1, HAVE_DEMANGLE=) + AC_LANG_RESTORE +fi + +# Demangle only for debug or trace-malloc builds +MOZ_DEMANGLE_SYMBOLS= +if test "$HAVE_DEMANGLE" -a "$HAVE_GCC3_ABI" && test "$MOZ_DEBUG" -o "$NS_TRACE_MALLOC"; then + MOZ_DEMANGLE_SYMBOLS=1 + AC_DEFINE(MOZ_DEMANGLE_SYMBOLS) +fi +AC_SUBST(MOZ_DEMANGLE_SYMBOLS) + +dnl ======================================================== +dnl = Support for gcc stack unwinding (from gcc 3.3) +dnl ======================================================== +if test "$HAVE_GCC3_ABI" && test -z "$SKIP_LIBRARY_CHECKS"; then + AC_CHECK_HEADER(unwind.h, AC_CHECK_FUNCS(_Unwind_Backtrace)) +fi + +dnl ======================================================== +dnl = +dnl = Misc. Options +dnl = +dnl ======================================================== +MOZ_ARG_HEADER(Misc. Options) + +dnl ======================================================== +dnl update xterm title +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(xterm-updates, +[ --enable-xterm-updates Update XTERM titles with current command.], + MOZ_UPDATE_XTERM=1, + MOZ_UPDATE_XTERM= ) + +if test -z "$SKIP_COMPILER_CHECKS"; then +dnl ======================================================== +dnl = +dnl = Compiler Options +dnl = +dnl ======================================================== +MOZ_ARG_HEADER(Compiler Options) + +dnl ======================================================== +dnl Check for gcc -pipe support +dnl ======================================================== +AC_MSG_CHECKING([for gcc -pipe support]) +if test -n "$GNU_CC" && test -n "$GNU_CXX" && test -n "$GNU_AS"; then + echo '#include ' > dummy-hello.c + echo 'int main() { printf("Hello World\n"); exit(0); }' >> dummy-hello.c + ${CC} -S dummy-hello.c -o dummy-hello.s 2>&5 + cat dummy-hello.s 2> /dev/null | ${AS_BIN} -o dummy-hello.S - 2>&5 + if test $? = 0; then + _res_as_stdin="yes" + else + _res_as_stdin="no" + fi + if test "$_res_as_stdin" = "yes"; then + _SAVE_CFLAGS=$CFLAGS + CFLAGS="$CFLAGS -pipe" + AC_TRY_COMPILE( [ #include ], + [printf("Hello World\n");], + [_res_gcc_pipe="yes"], + [_res_gcc_pipe="no"] ) + CFLAGS=$_SAVE_CFLAGS + fi + if test "$_res_as_stdin" = "yes" && test "$_res_gcc_pipe" = "yes"; then + _res="yes"; + CFLAGS="$CFLAGS -pipe" + CXXFLAGS="$CXXFLAGS -pipe" + else + _res="no" + fi + rm -f dummy-hello.c dummy-hello.s dummy-hello.S dummy-hello a.out + AC_MSG_RESULT([$_res]) +else + AC_MSG_RESULT([no]) +fi + +dnl pass -Wno-long-long to the compiler +MOZ_ARG_ENABLE_BOOL(long-long-warning, +[ --enable-long-long-warning + Warn about use of non-ANSI long long type], + _IGNORE_LONG_LONG_WARNINGS=, + _IGNORE_LONG_LONG_WARNINGS=1) + +if test "$_IGNORE_LONG_LONG_WARNINGS"; then + _SAVE_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS ${_COMPILER_PREFIX}-Wno-long-long" + AC_MSG_CHECKING([whether compiler supports -Wno-long-long]) + AC_TRY_COMPILE([], [return(0);], + [ _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} ${_COMPILER_PREFIX}-Wno-long-long" + _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} ${_COMPILER_PREFIX}-Wno-long-long" + result="yes" ], result="no") + AC_MSG_RESULT([$result]) + CFLAGS="$_SAVE_CFLAGS" +fi + +dnl ======================================================== +dnl Profile guided optimization +dnl ======================================================== +dnl Test for profiling options +dnl Under gcc 3.3, use -fprofile-arcs/-fbranch-probabilities +dnl Under gcc 3.4+, use -fprofile-generate/-fprofile-use + +dnl Provide a switch to disable PGO even when called via profiledbuild. +MOZ_ARG_DISABLE_BOOL(profile-guided-optimization, +[ --disable-profile-guided-optimization + Don't build with PGO even if called via make profiledbuild], +MOZ_PROFILE_GUIDED_OPTIMIZE_DISABLE=1, +MOZ_PROFILE_GUIDED_OPTIMIZE_DISABLE=) + +AC_SUBST(MOZ_PROFILE_GUIDED_OPTIMIZE_DISABLE) + +_SAVE_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS -fprofile-generate" + +AC_MSG_CHECKING([whether C compiler supports -fprofile-generate]) +AC_TRY_COMPILE([], [return 0;], + [ PROFILE_GEN_CFLAGS="-fprofile-generate" + result="yes" ], result="no") +AC_MSG_RESULT([$result]) + +if test $result = "yes"; then + PROFILE_GEN_LDFLAGS="-fprofile-generate" + PROFILE_USE_CFLAGS="-fprofile-use" + PROFILE_USE_LDFLAGS="-fprofile-use" +else + CFLAGS="$_SAVE_CFLAGS -fprofile-arcs" + AC_MSG_CHECKING([whether C compiler supports -fprofile-arcs]) + AC_TRY_COMPILE([], [return 0;], + [ PROFILE_GEN_CFLAGS="-fprofile-arcs" + result="yes" ], result="no") + AC_MSG_RESULT([$result]) + if test $result = "yes"; then + PROFILE_USE_CFLAGS="-fbranch-probabilities" + fi + # don't really care, this is an old GCC + PROFILE_GEN_LDFLAGS= + PROFILE_USE_LDFLAGS= +fi + +CFLAGS="$_SAVE_CFLAGS" + +if test -n "$INTEL_CC"; then + PROFILE_GEN_CFLAGS="-prof-gen -prof-dir ." + PROFILE_GEN_LDFLAGS= + PROFILE_USE_CFLAGS="-prof-use -prof-dir ." + PROFILE_USE_LDFLAGS= +fi + +dnl Sun Studio on Solaris +if test "$SOLARIS_SUNPRO_CC"; then + PROFILE_GEN_CFLAGS="-xprofile=collect:$_objdir/$enable_application" + PROFILE_GEN_LDFLAGS="-xprofile=collect:$_objdir/$enable_application" + if test "$CPU_ARCH" != "sparc"; then + PROFILE_USE_CFLAGS="-xprofile=use:$_objdir/$enable_application" + PROFILE_USE_LDFLAGS="-xprofile=use:$_objdir/$enable_application" + else + PROFILE_USE_CFLAGS="-xlinkopt=2 -xprofile=use:$_objdir/$enable_application" + PROFILE_USE_LDFLAGS="-xlinkopt=2 -xprofile=use:$_objdir/$enable_application" + fi +fi + +AC_SUBST(PROFILE_GEN_CFLAGS) +AC_SUBST(PROFILE_GEN_LDFLAGS) +AC_SUBST(PROFILE_USE_CFLAGS) +AC_SUBST(PROFILE_USE_LDFLAGS) + +AC_LANG_CPLUSPLUS + +dnl ======================================================== +dnl Test for -pedantic bustage +dnl ======================================================== +MOZ_ARG_DISABLE_BOOL(pedantic, +[ --disable-pedantic Issue all warnings demanded by strict ANSI C ], +_PEDANTIC= ) +if test "$_PEDANTIC"; then + _SAVE_CXXFLAGS=$CXXFLAGS + CXXFLAGS="$CXXFLAGS ${_WARNINGS_CXXFLAGS} ${_COMPILER_PREFIX}-pedantic" + AC_MSG_CHECKING([whether C++ compiler has -pedantic long long bug]) + AC_TRY_COMPILE([$configure_static_assert_macros], + [CONFIGURE_STATIC_ASSERT(sizeof(long long) == 8)], + result="no", result="yes" ) + AC_MSG_RESULT([$result]) + CXXFLAGS="$_SAVE_CXXFLAGS" + + case "$result" in + no) + _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} ${_COMPILER_PREFIX}-pedantic" + _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} ${_COMPILER_PREFIX}-pedantic" + ;; + yes) + AC_MSG_ERROR([Your compiler appears to have a known bug where long long is miscompiled when using -pedantic. Reconfigure using --disable-pedantic. ]) + ;; + esac +fi + +dnl ======================================================== +dnl Test for correct temporary object destruction order +dnl ======================================================== +dnl We want to make sure the compiler follows the C++ spec here as +dnl xpcom and the string classes depend on it (bug 235381). +AC_MSG_CHECKING([for correct temporary object destruction order]) +AC_TRY_RUN([ class 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); + } + ], + result="yes", result="no", result="maybe") +AC_MSG_RESULT([$result]) + +if test "$result" = "no"; then + AC_MSG_ERROR([Your compiler does not follow the C++ specification for temporary object destruction order.]) +fi + +dnl ======================================================== +dnl Autoconf test for gcc 2.7.2.x (and maybe others?) so that we don't +dnl provide non-const forms of the operator== for comparing nsCOMPtrs to +dnl raw pointers in nsCOMPtr.h. (VC++ has the same bug.) +dnl ======================================================== +_SAVE_CXXFLAGS=$CXXFLAGS +CXXFLAGS="$CXXFLAGS ${_WARNINGS_CXXFLAGS}" +AC_CACHE_CHECK(for correct overload resolution with const and templates, + ac_nscap_nonconst_opeq_bug, + [AC_TRY_COMPILE([ + template + class Pointer + { + public: + T* myPtr; + }; + + template + int operator==(const Pointer& rhs, U* lhs) + { + return rhs.myPtr == lhs; + } + + template + int operator==(const Pointer& rhs, const U* lhs) + { + return rhs.myPtr == lhs; + } + ], + [ + Pointer foo; + const int* bar; + return foo == bar; + ], + ac_nscap_nonconst_opeq_bug="no", + ac_nscap_nonconst_opeq_bug="yes")]) +CXXFLAGS="$_SAVE_CXXFLAGS" + +if test "$ac_nscap_nonconst_opeq_bug" = "yes" ; then + AC_DEFINE(NSCAP_DONT_PROVIDE_NONCONST_OPEQ) +fi + +dnl ======================================================== +dnl Check for tm_zone, tm_gmtoff in struct tm +dnl ======================================================== +AC_CACHE_CHECK(for tm_zone tm_gmtoff in struct tm, + ac_cv_struct_tm_zone_tm_gmtoff, + [AC_TRY_COMPILE([#include ], + [struct tm tm; tm.tm_zone = 0; tm.tm_gmtoff = 1;], + [ac_cv_struct_tm_zone_tm_gmtoff="yes"], + [ac_cv_struct_tm_zone_tm_gmtoff="no"])]) +if test "$ac_cv_struct_tm_zone_tm_gmtoff" = "yes" ; then + AC_DEFINE(HAVE_TM_ZONE_TM_GMTOFF) +fi + +fi # SKIP_COMPILER_CHECKS + +dnl ======================================================== +dnl C++ rtti +dnl Should be smarter and check that the compiler does indeed have rtti +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(cpp-rtti, +[ --enable-cpp-rtti Enable C++ RTTI ], +[ _MOZ_USE_RTTI=1 ], +[ _MOZ_USE_RTTI= ]) + +if test "$_MOZ_USE_RTTI"; then + _MOZ_RTTI_FLAGS=$_MOZ_RTTI_FLAGS_ON +else + _MOZ_RTTI_FLAGS=$_MOZ_RTTI_FLAGS_OFF +fi + +AC_SUBST(_MOZ_RTTI_FLAGS_ON) + +dnl ======================================================== +dnl C++ exceptions (g++/egcs only - for now) +dnl Should be smarter and check that the compiler does indeed have exceptions +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(cpp-exceptions, +[ --enable-cpp-exceptions Enable C++ exceptions ], +[ _MOZ_CPP_EXCEPTIONS=1 ], +[ _MOZ_CPP_EXCEPTIONS= ]) + +if test "$_MOZ_CPP_EXCEPTIONS"; then + _MOZ_EXCEPTIONS_FLAGS=$_MOZ_EXCEPTIONS_FLAGS_ON +else + _MOZ_EXCEPTIONS_FLAGS=$_MOZ_EXCEPTIONS_FLAGS_OFF +fi + +AC_SUBST(_MOZ_EXCEPTIONS_FLAGS_ON) + +# Irix & OSF native compilers do not like exception declarations +# when exceptions are disabled +if test -n "$MIPSPRO_CXX" -o -n "$COMPAQ_CXX" -o -n "$VACPP"; then + AC_DEFINE(CPP_THROW_NEW, []) +else + AC_DEFINE(CPP_THROW_NEW, [throw()]) +fi +AC_LANG_C + +dnl ======================================================== +dnl = +dnl = Build depencency options +dnl = +dnl ======================================================== +MOZ_ARG_HEADER(Build dependencies) + +dnl ======================================================== +dnl = Do not auto generate dependency info +dnl ======================================================== +MOZ_AUTO_DEPS=1 +MOZ_ARG_DISABLE_BOOL(auto-deps, +[ --disable-auto-deps Do not automatically generate dependency info], + MOZ_AUTO_DEPS=, + MOZ_AUTO_DEPS=1) + +if test -n "$MOZ_AUTO_DEPS"; then +dnl ======================================================== +dnl = Use mkdepend instead of $CC -MD for dependency generation +dnl ======================================================== +_cpp_md_flag= +MOZ_ARG_DISABLE_BOOL(md, +[ --disable-md Do not use compiler-based dependencies ], + [_cpp_md_flag=], + [_cpp_md_flag=1], + [dnl Default is to turn on -MD if using GNU-compatible compilers + if test "$GNU_CC" -a "$GNU_CXX" -a "$OS_ARCH" != "WINNT" -a "$OS_ARCH" != "WINCE"; then + _cpp_md_flag=1 + fi + dnl Default is to use -xM if using Sun Studio on Solaris + if test "$SOLARIS_SUNPRO_CC"; then + _cpp_md_flag=1 + fi]) +if test "$_cpp_md_flag"; then + COMPILER_DEPEND=1 + if test "$OS_ARCH" = "OpenVMS"; then + _DEPEND_CFLAGS='$(subst =, ,$(filter-out %/.pp,-MM=-MD=-MF=$(MDDEPDIR)/$(basename $(@F)).pp))' + else + _DEPEND_CFLAGS='$(filter-out %/.pp,-Wp,-MD,$(MDDEPDIR)/$(basename $(@F)).pp)' + fi + dnl Sun Studio on Solaris use -xM instead of -MD, see config/rules.mk + if test "$SOLARIS_SUNPRO_CC"; then + _DEPEND_CFLAGS= + fi +else + COMPILER_DEPEND= + dnl Don't override this for MSVC + if test -z "$_WIN32_MSVC"; then + _USE_CPP_INCLUDE_FLAG= + _DEFINES_CFLAGS='$(ACDEFINES) -D_JS_CONFDEFS_H_ -DMOZILLA_CLIENT' + _DEFINES_CXXFLAGS='$(ACDEFINES) -D_JS_CONFDEFS_H_ -DMOZILLA_CLIENT' + fi +fi +fi # MOZ_AUTO_DEPS +MDDEPDIR='.deps' +AC_SUBST(MOZ_AUTO_DEPS) +AC_SUBST(COMPILER_DEPEND) +AC_SUBST(MDDEPDIR) + + +dnl ======================================================== +dnl = +dnl = Static Build Options +dnl = +dnl ======================================================== +MOZ_ARG_HEADER(Static build options) + +MOZ_ARG_ENABLE_BOOL(static, +[ --enable-static Enable building of internal static libs], + BUILD_STATIC_LIBS=1, + BUILD_STATIC_LIBS=) + +dnl ======================================================== +dnl = Link js shell to system readline +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(readline, +[ --enable-readline Link js shell to system readline library], + JS_WANT_READLINE=1, + JS_WANT_READLINE= ) + +JS_NATIVE_EDITLINE= +EDITLINE_LIBS= +JS_DISABLE_SHELL= + +case "$target" in +*-wince*|*-mingw*|*-cygwin*|*-msvc*|*-mks*) + NO_EDITLINE=1 + ;; +*-symbian*) + NO_EDITLINE=1 + JS_DISABLE_SHELL=1 + ;; +*) + ;; +esac + +if test -z "$SKIP_LIBRARY_CHECKS" -a -z "$NO_EDITLINE"; then + if test -n "$JS_WANT_READLINE"; then + AC_CHECK_LIB(readline, readline, + EDITLINE_LIBS="-lreadline", + AC_MSG_ERROR([No system readline library found.])) + else + dnl By default, we use editline + JS_NATIVE_EDITLINE=1 + EDITLINE_LIBS='$(DEPTH)/editline/$(LIB_PREFIX)editline.$(LIB_SUFFIX)' + fi + + dnl Either way, we want to build with line editing support. + AC_DEFINE(EDITLINE) +fi +AC_SUBST(JS_NATIVE_EDITLINE) +AC_SUBST(JS_DISABLE_SHELL) +AC_SUBST(EDITLINE_LIBS) + +dnl ======================================================== +dnl = +dnl = Standalone module options +dnl = +dnl ======================================================== +MOZ_ARG_HEADER(Standalone module options (Not for building Mozilla)) + +dnl ======================================================== +if test "$MOZ_DEBUG" || test "$NS_TRACE_MALLOC"; then + MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS= +fi + +MOZ_ARG_WITH_STRING(sync-build-files, +[ --with-sync-build-files=DIR + Check that files in 'config' and 'build' match + their originals in 'DIR/config' and 'DIR/build'. + This helps keep the SpiderMonkey build machinery + in sync with Mozilla's, on which it is based.], +[MOZ_SYNC_BUILD_FILES=$withval ] ) +AC_SUBST(MOZ_SYNC_BUILD_FILES) + +dnl ======================================================== +dnl = +dnl = Maintainer debug option (no --enable equivalent) +dnl = +dnl ======================================================== + +AC_SUBST(AR) +AC_SUBST(AR_FLAGS) +AC_SUBST(AR_LIST) +AC_SUBST(AR_EXTRACT) +AC_SUBST(AR_DELETE) +AC_SUBST(AS) +AC_SUBST(ASFLAGS) +AC_SUBST(AS_DASH_C_FLAG) +AC_SUBST(LD) +AC_SUBST(RC) +AC_SUBST(RCFLAGS) +AC_SUBST(WINDRES) +AC_SUBST(IMPLIB) +AC_SUBST(FILTER) +AC_SUBST(BIN_FLAGS) +AC_SUBST(NS_USE_NATIVE) +AC_SUBST(MOZ_JS_LIBS) +AC_SUBST(MOZ_PSM) +AC_SUBST(MOZ_DEBUG) +AC_SUBST(MOZ_DEBUG_MODULES) +AC_SUBST(MOZ_DEBUG_ENABLE_DEFS) +AC_SUBST(MOZ_DEBUG_DISABLE_DEFS) +AC_SUBST(MOZ_DEBUG_FLAGS) +AC_SUBST(MOZ_DEBUG_LDFLAGS) +AC_SUBST(WARNINGS_AS_ERRORS) +AC_SUBST(MOZ_DBGRINFO_MODULES) +AC_SUBST(MOZ_LEAKY) +AC_SUBST(MOZ_JPROF) +AC_SUBST(MOZ_SHARK) +AC_SUBST(MOZ_CALLGRIND) +AC_SUBST(MOZ_VTUNE) +AC_SUBST(MOZ_XPCTOOLS) +AC_SUBST(MOZ_JSLOADER) +AC_SUBST(MOZ_INSURE) +AC_SUBST(MOZ_INSURE_DIRS) +AC_SUBST(MOZ_INSURE_EXCLUDE_DIRS) +AC_SUBST(MOZ_QUANTIFY) +AC_SUBST(MOZ_INSURIFYING) +AC_SUBST(LIBICONV) + +AC_SUBST(BUILD_STATIC_LIBS) +AC_SUBST(ENABLE_TESTS) + +AC_SUBST(ENABLE_STRIP) +AC_SUBST(PKG_SKIP_STRIP) +AC_SUBST(INCREMENTAL_LINKER) +AC_SUBST(MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS) +AC_SUBST(MOZ_COMPONENT_NSPR_LIBS) + +AC_SUBST(MOZ_FIX_LINK_PATHS) + +AC_SUBST(USE_DEPENDENT_LIBS) + +AC_SUBST(MOZ_BUILD_ROOT) +AC_SUBST(MOZ_OS2_TOOLS) +AC_SUBST(MOZ_OS2_USE_DECLSPEC) + +AC_SUBST(MOZ_POST_DSO_LIB_COMMAND) +AC_SUBST(MOZ_POST_PROGRAM_COMMAND) +AC_SUBST(MOZ_TIMELINE) +AC_SUBST(WINCE) +AC_SUBST(WINCE_WINDOWS_MOBILE) + +AC_SUBST(MOZ_APP_NAME) +AC_SUBST(MOZ_APP_DISPLAYNAME) +AC_SUBST(MOZ_APP_VERSION) + +AC_SUBST(MOZ_PKG_SPECIAL) + +AC_SUBST(MOZILLA_OFFICIAL) + +dnl win32 options +AC_SUBST(MOZ_DEBUG_SYMBOLS) +AC_SUBST(MOZ_MAPINFO) +AC_SUBST(MOZ_BROWSE_INFO) +AC_SUBST(MOZ_TOOLS_DIR) +AC_SUBST(CYGWIN_WRAPPER) +AC_SUBST(AS_PERL) +AC_SUBST(WIN32_REDIST_DIR) +AC_SUBST(PYTHON) + +dnl Echo the CFLAGS to remove extra whitespace. +CFLAGS=`echo \ + $_WARNINGS_CFLAGS \ + $CFLAGS` + +CXXFLAGS=`echo \ + $_MOZ_RTTI_FLAGS \ + $_MOZ_EXCEPTIONS_FLAGS \ + $_WARNINGS_CXXFLAGS \ + $CXXFLAGS` + +COMPILE_CFLAGS=`echo \ + $_DEFINES_CFLAGS \ + $_DEPEND_CFLAGS \ + $COMPILE_CFLAGS` + +COMPILE_CXXFLAGS=`echo \ + $_DEFINES_CXXFLAGS \ + $_DEPEND_CFLAGS \ + $COMPILE_CXXFLAGS` + +AC_SUBST(SYSTEM_MAKEDEPEND) + +AC_SUBST(NSPR_CFLAGS) +AC_SUBST(NSPR_LIBS) +AC_SUBST(MOZ_NATIVE_NSPR) + +AC_SUBST(CFLAGS) +AC_SUBST(CXXFLAGS) +AC_SUBST(CPPFLAGS) +AC_SUBST(COMPILE_CFLAGS) +AC_SUBST(COMPILE_CXXFLAGS) +AC_SUBST(LDFLAGS) +AC_SUBST(LIBS) +AC_SUBST(CROSS_COMPILE) + +AC_SUBST(HOST_CC) +AC_SUBST(HOST_CXX) +AC_SUBST(HOST_CFLAGS) +AC_SUBST(HOST_CXXFLAGS) +AC_SUBST(HOST_OPTIMIZE_FLAGS) +AC_SUBST(HOST_AR) +AC_SUBST(HOST_AR_FLAGS) +AC_SUBST(HOST_LD) +AC_SUBST(HOST_RANLIB) +AC_SUBST(HOST_NSPR_MDCPUCFG) +AC_SUBST(HOST_BIN_SUFFIX) +AC_SUBST(HOST_OS_ARCH) + +AC_SUBST(TARGET_CPU) +AC_SUBST(TARGET_VENDOR) +AC_SUBST(TARGET_OS) +AC_SUBST(TARGET_NSPR_MDCPUCFG) +AC_SUBST(TARGET_MD_ARCH) +AC_SUBST(TARGET_XPCOM_ABI) +AC_SUBST(OS_TARGET) +AC_SUBST(OS_ARCH) +AC_SUBST(OS_RELEASE) +AC_SUBST(OS_TEST) + +AC_SUBST(WRAP_MALLOC_CFLAGS) +AC_SUBST(WRAP_MALLOC_LIB) +AC_SUBST(MKSHLIB) +AC_SUBST(MKCSHLIB) +AC_SUBST(MKSHLIB_FORCE_ALL) +AC_SUBST(MKSHLIB_UNFORCE_ALL) +AC_SUBST(DSO_CFLAGS) +AC_SUBST(DSO_PIC_CFLAGS) +AC_SUBST(DSO_LDOPTS) +AC_SUBST(LIB_PREFIX) +AC_SUBST(DLL_PREFIX) +AC_SUBST(DLL_SUFFIX) +AC_DEFINE_UNQUOTED(MOZ_DLL_SUFFIX, "$DLL_SUFFIX") +AC_SUBST(LIB_SUFFIX) +AC_SUBST(OBJ_SUFFIX) +AC_SUBST(BIN_SUFFIX) +AC_SUBST(ASM_SUFFIX) +AC_SUBST(IMPORT_LIB_SUFFIX) +AC_SUBST(USE_N32) +AC_SUBST(CC_VERSION) +AC_SUBST(CXX_VERSION) +AC_SUBST(MSMANIFEST_TOOL) + +if test "$USING_HCC"; then + CC='${topsrcdir}/build/hcc' + CC="$CC '$_OLDCC'" + CXX='${topsrcdir}/build/hcpp' + CXX="$CXX '$_OLDCXX'" + AC_SUBST(CC) + AC_SUBST(CXX) +fi + +dnl Check for missing components +if test "$COMPILE_ENVIRONMENT"; then +if test "$MOZ_X11"; then + dnl ==================================================== + dnl = Check if X headers exist + dnl ==================================================== + _SAVE_CFLAGS=$CFLAGS + CFLAGS="$CFLAGS $XCFLAGS" + AC_TRY_COMPILE([ + #include + #include + #include + #include + ], + [ + Display *dpy = 0; + if ((dpy = XOpenDisplay(NULL)) == NULL) { + fprintf(stderr, ": can't open %s\n", XDisplayName(NULL)); + exit(1); + } + ], [], + [ AC_MSG_ERROR([Could not compile basic X program.]) ]) + CFLAGS="$_SAVE_CFLAGS" + + if test ! -z "$MISSING_X"; then + AC_MSG_ERROR([ Could not find the following X libraries: $MISSING_X ]); + fi + +fi # MOZ_X11 +fi # COMPILE_ENVIRONMENT + +dnl Set various defines and substitutions +dnl ======================================================== + +if test "$OS_ARCH" = "BeOS"; then + AC_DEFINE(XP_BEOS) + MOZ_MOVEMAIL=1 +elif test "$OS_ARCH" = "Darwin"; then + AC_DEFINE(XP_UNIX) + AC_DEFINE(UNIX_ASYNC_DNS) + MOZ_MOVEMAIL=1 +elif test "$OS_ARCH" = "OpenVMS"; then + AC_DEFINE(XP_UNIX) +elif test "$OS_ARCH" != "WINNT" -a "$OS_ARCH" != "OS2" -a "$OS_ARCH" != "WINCE"; then + AC_DEFINE(XP_UNIX) + AC_DEFINE(UNIX_ASYNC_DNS) + MOZ_MOVEMAIL=1 +fi +AC_SUBST(MOZ_MOVEMAIL) + +AC_ARG_ENABLE(threadsafe, + [ --enable-threadsafe Enable support for multiple threads.], + [AC_DEFINE(JS_THREADSAFE)],) + +if test "$MOZ_DEBUG"; then + AC_DEFINE(MOZ_REFLOW_PERF) + AC_DEFINE(MOZ_REFLOW_PERF_DSP) +fi + +if test "$ACCESSIBILITY" -a "$MOZ_ENABLE_GTK2" ; then + AC_DEFINE(MOZ_ACCESSIBILITY_ATK) + ATK_FULL_VERSION=`$PKG_CONFIG --modversion atk` + ATK_MAJOR_VERSION=`echo ${ATK_FULL_VERSION} | $AWK -F\. '{ print $1 }'` + ATK_MINOR_VERSION=`echo ${ATK_FULL_VERSION} | $AWK -F\. '{ print $2 }'` + ATK_REV_VERSION=`echo ${ATK_FULL_VERSION} | $AWK -F\. '{ print $3 }'` + AC_DEFINE_UNQUOTED(ATK_MAJOR_VERSION, $ATK_MAJOR_VERSION) + AC_DEFINE_UNQUOTED(ATK_MINOR_VERSION, $ATK_MINOR_VERSION) + AC_DEFINE_UNQUOTED(ATK_REV_VERSION, $ATK_REV_VERSION) +fi + +dnl ======================================================== +dnl JavaScript shell +dnl ======================================================== + +AC_HAVE_FUNCS(setlocale) + +dnl ======================================================== +dnl Use cygwin wrapper for win32 builds, except MSYS/MinGW +dnl ======================================================== +case "$host_os" in +mingw*) + WIN_TOP_SRC=`cd $srcdir; pwd -W` + ;; +cygwin*|msvc*|mks*) + HOST_CC="\$(CYGWIN_WRAPPER) $HOST_CC" + HOST_CXX="\$(CYGWIN_WRAPPER) $HOST_CXX" + CC="\$(CYGWIN_WRAPPER) $CC" + CXX="\$(CYGWIN_WRAPPER) $CXX" + CPP="\$(CYGWIN_WRAPPER) $CPP" + LD="\$(CYGWIN_WRAPPER) $LD" + AS="\$(CYGWIN_WRAPPER) $AS" + RC="\$(CYGWIN_WRAPPER) $RC" + CYGDRIVE_MOUNT=`mount -p | awk '{ if (/^\//) { print $1; exit } }'` + WIN_TOP_SRC=`cygpath -a -w $srcdir | sed -e 's|\\\\|/|g'` + ;; +esac + +AC_SUBST(CYGDRIVE_MOUNT) +AC_SUBST(WIN_TOP_SRC) + +AC_SUBST(MOZILLA_VERSION) + +AC_SUBST(ac_configure_args) + +dnl Spit out some output +dnl ======================================================== + +# Save the defines header file before autoconf removes it. +# (Do not add AC_DEFINE calls after this line.) + _CONFIG_TMP=confdefs-tmp.h + _CONFIG_DEFS_H=js-confdefs.h + + cat > $_CONFIG_TMP <<\EOF +/* List of defines generated by configure. Included with preprocessor flag, + * -include, to avoid long list of -D defines on the compile command-line. + * Do not edit. + */ + +#ifndef _JS_CONFDEFS_H_ +#define _JS_CONFDEFS_H_ +EOF + +_EGREP_PATTERN='^#define (' +if test -n "$_NON_GLOBAL_ACDEFINES"; then + for f in $_NON_GLOBAL_ACDEFINES; do + _EGREP_PATTERN="${_EGREP_PATTERN}$f|" + done +fi +_EGREP_PATTERN="${_EGREP_PATTERN}dummy_never_defined)" + + sort confdefs.h | egrep -v "$_EGREP_PATTERN" >> $_CONFIG_TMP + + cat >> $_CONFIG_TMP <<\EOF + +#endif /* _JS_CONFDEFS_H_ */ + +EOF + + # Only write js-confdefs.h when something changes (or it doesn't exist) + if cmp -s $_CONFIG_TMP $_CONFIG_DEFS_H; then + rm $_CONFIG_TMP + else + AC_MSG_RESULT("creating $_CONFIG_DEFS_H") + mv -f $_CONFIG_TMP $_CONFIG_DEFS_H + + echo ==== $_CONFIG_DEFS_H ================================= + cat $_CONFIG_DEFS_H + fi + +dnl Probably shouldn't call this manually but we always want the output of DEFS +rm -f confdefs.h.save +mv confdefs.h confdefs.h.save +egrep -v "$_EGREP_PATTERN" confdefs.h.save > confdefs.h +AC_OUTPUT_MAKE_DEFS() +MOZ_DEFINES=$DEFS +AC_SUBST(MOZ_DEFINES) +rm -f confdefs.h +mv confdefs.h.save confdefs.h + +MAKEFILES=" + Makefile + config/Makefile + config/autoconf.mk + config/mkdepend/Makefile +" + +if test -n "$JS_NATIVE_EDITLINE"; then + MAKEFILES="$MAKEFILES +editline/Makefile +" +fi + +dnl +dnl Run a perl script to quickly create the makefiles. +dnl If it succeeds, it outputs a shell command to set CONFIG_FILES +dnl for the files it cannot handle correctly. This way, config.status +dnl will handle these files. +dnl If it fails, nothing is set and config.status will run as usual. +dnl +dnl This does not change the $MAKEFILES variable. +dnl +dnl OpenVMS gets a line overflow on the long eval command, so use a temp file. +dnl +if test -z "${AS_PERL}"; then +echo $MAKEFILES | ${PERL} $srcdir/build/autoconf/acoutput-fast.pl > conftest.sh +else +echo $MAKEFILES | ${PERL} $srcdir/build/autoconf/acoutput-fast.pl -nowrap --cygwin-srcdir=$srcdir > conftest.sh +fi +. ./conftest.sh +rm conftest.sh + +echo $MAKEFILES > unallmakefiles + +mv -f config/autoconf.mk config/autoconf.mk.orig 2> /dev/null + +AC_OUTPUT($MAKEFILES) + +dnl Prevent the regeneration of autoconf.mk forcing rebuilds of the world +if cmp -s config/autoconf.mk config/autoconf.mk.orig; then + echo "config/autoconf.mk is unchanged" + mv -f config/autoconf.mk.orig config/autoconf.mk 2> /dev/null +else + rm -f config/autoconf.mk.orig 2> /dev/null +fi + +# Produce the js-config script at configure time; see the comments for +# 'js-config' in Makefile.in. +AC_MSG_RESULT(invoking make to create js-config script) +$MAKE js-config diff --git a/ape-server/deps/js/src/dtoa.c b/ape-server/deps/js/src/dtoa.c new file mode 100755 index 0000000..1fdd397 --- /dev/null +++ b/ape-server/deps/js/src/dtoa.c @@ -0,0 +1,3335 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/**************************************************************** + * + * The author of this software is David M. Gay. + * + * Copyright (c) 1991, 2000, 2001 by Lucent Technologies. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + * + ***************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +/* On a machine with IEEE extended-precision registers, it is + * necessary to specify double-precision (53-bit) rounding precision + * before invoking strtod or dtoa. If the machine uses (the equivalent + * of) Intel 80x87 arithmetic, the call + * _control87(PC_53, MCW_PC); + * does this with many compilers. Whether this or another call is + * appropriate depends on the compiler; for this to work, it may be + * necessary to #include "float.h" or another system-dependent header + * file. + */ + +/* strtod for IEEE-, VAX-, and IBM-arithmetic machines. + * + * This strtod returns a nearest machine number to the input decimal + * string (or sets errno to ERANGE). With IEEE arithmetic, ties are + * broken by the IEEE round-even rule. Otherwise ties are broken by + * biased rounding (add half and chop). + * + * Inspired loosely by William D. Clinger's paper "How to Read Floating + * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101]. + * + * Modifications: + * + * 1. We only require IEEE, IBM, or VAX double-precision + * arithmetic (not IEEE double-extended). + * 2. We get by with floating-point arithmetic in a case that + * Clinger missed -- when we're computing d * 10^n + * for a small integer d and the integer n is not too + * much larger than 22 (the maximum integer k for which + * we can represent 10^k exactly), we may be able to + * compute (d*10^k) * 10^(e-k) with just one roundoff. + * 3. Rather than a bit-at-a-time adjustment of the binary + * result in the hard case, we use floating-point + * arithmetic to determine the adjustment to within + * one bit; only in really hard cases do we need to + * compute a second residual. + * 4. Because of 3., we don't need a large table of powers of 10 + * for ten-to-e (just some small tables, e.g. of 10^k + * for 0 <= k <= 22). + */ + +/* + * #define IEEE_8087 for IEEE-arithmetic machines where the least + * significant byte has the lowest address. + * #define IEEE_MC68k for IEEE-arithmetic machines where the most + * significant byte has the lowest address. + * #define Long int on machines with 32-bit ints and 64-bit longs. + * #define IBM for IBM mainframe-style floating-point arithmetic. + * #define VAX for VAX-style floating-point arithmetic (D_floating). + * #define No_leftright to omit left-right logic in fast floating-point + * computation of dtoa. + * #define Honor_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3 + * and strtod and dtoa should round accordingly. + * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3 + * and Honor_FLT_ROUNDS is not #defined. + * #define RND_PRODQUOT to use rnd_prod and rnd_quot (assembly routines + * that use extended-precision instructions to compute rounded + * products and quotients) with IBM. + * #define ROUND_BIASED for IEEE-format with biased rounding. + * #define Inaccurate_Divide for IEEE-format with correctly rounded + * products but inaccurate quotients, e.g., for Intel i860. + * #define NO_LONG_LONG on machines that do not have a "long long" + * integer type (of >= 64 bits). On such machines, you can + * #define Just_16 to store 16 bits per 32-bit Long when doing + * high-precision integer arithmetic. Whether this speeds things + * up or slows things down depends on the machine and the number + * being converted. If long long is available and the name is + * something other than "long long", #define Llong to be the name, + * and if "unsigned Llong" does not work as an unsigned version of + * Llong, #define #ULLong to be the corresponding unsigned type. + * #define KR_headers for old-style C function headers. + * #define Bad_float_h if your system lacks a float.h or if it does not + * define some or all of DBL_DIG, DBL_MAX_10_EXP, DBL_MAX_EXP, + * FLT_RADIX, FLT_ROUNDS, and DBL_MAX. + * #define MALLOC your_malloc, where your_malloc(n) acts like malloc(n) + * if memory is available and otherwise does something you deem + * appropriate. If MALLOC is undefined, malloc will be invoked + * directly -- and assumed always to succeed. Similarly, if you + * want something other than the system's free() to be called to + * recycle memory acquired from MALLOC, #define FREE to be the + * name of the alternate routine. (FREE or free is only called in + * pathological cases, e.g., in a dtoa call after a dtoa return in + * mode 3 with thousands of digits requested.) + * #define Omit_Private_Memory to omit logic (added Jan. 1998) for making + * memory allocations from a private pool of memory when possible. + * When used, the private pool is PRIVATE_MEM bytes long: 2304 bytes, + * unless #defined to be a different length. This default length + * suffices to get rid of MALLOC calls except for unusual cases, + * such as decimal-to-binary conversion of a very long string of + * digits. The longest string dtoa can return is about 751 bytes + * long. For conversions by strtod of strings of 800 digits and + * all dtoa conversions in single-threaded executions with 8-byte + * pointers, PRIVATE_MEM >= 7400 appears to suffice; with 4-byte + * pointers, PRIVATE_MEM >= 7112 appears adequate. + * #define NO_INFNAN_CHECK if you do not wish to have INFNAN_CHECK + * #defined automatically on IEEE systems. On such systems, + * when INFNAN_CHECK is #defined, strtod checks + * for Infinity and NaN (case insensitively). On some systems + * (e.g., some HP systems), it may be necessary to #define NAN_WORD0 + * appropriately -- to the most significant word of a quiet NaN. + * (On HP Series 700/800 machines, -DNAN_WORD0=0x7ff40000 works.) + * When INFNAN_CHECK is #defined and No_Hex_NaN is not #defined, + * strtod also accepts (case insensitively) strings of the form + * NaN(x), where x is a string of hexadecimal digits and spaces; + * if there is only one string of hexadecimal digits, it is taken + * for the 52 fraction bits of the resulting NaN; if there are two + * or more strings of hex digits, the first is for the high 20 bits, + * the second and subsequent for the low 32 bits, with intervening + * white space ignored; but if this results in none of the 52 + * fraction bits being on (an IEEE Infinity symbol), then NAN_WORD0 + * and NAN_WORD1 are used instead. + * #define MULTIPLE_THREADS if the system offers preemptively scheduled + * multiple threads. In this case, you must provide (or suitably + * #define) two locks, acquired by ACQUIRE_DTOA_LOCK(n) and freed + * by FREE_DTOA_LOCK(n) for n = 0 or 1. (The second lock, accessed + * in pow5mult, ensures lazy evaluation of only one copy of high + * powers of 5; omitting this lock would introduce a small + * probability of wasting memory, but would otherwise be harmless.) + * You must also invoke freedtoa(s) to free the value s returned by + * dtoa. You may do so whether or not MULTIPLE_THREADS is #defined. + * #define NO_IEEE_Scale to disable new (Feb. 1997) logic in strtod that + * avoids underflows on inputs whose result does not underflow. + * If you #define NO_IEEE_Scale on a machine that uses IEEE-format + * floating-point numbers and flushes underflows to zero rather + * than implementing gradual underflow, then you must also #define + * Sudden_Underflow. + * #define USE_LOCALE to use the current locale's decimal_point value. + * #define SET_INEXACT if IEEE arithmetic is being used and extra + * computation should be done to set the inexact flag when the + * result is inexact and avoid setting inexact when the result + * is exact. In this case, dtoa.c must be compiled in + * an environment, perhaps provided by #include "dtoa.c" in a + * suitable wrapper, that defines two functions, + * int get_inexact(void); + * void clear_inexact(void); + * such that get_inexact() returns a nonzero value if the + * inexact bit is already set, and clear_inexact() sets the + * inexact bit to 0. When SET_INEXACT is #defined, strtod + * also does extra computations to set the underflow and overflow + * flags when appropriate (i.e., when the result is tiny and + * inexact or when it is a numeric value rounded to +-infinity). + * #define NO_ERRNO if strtod should not assign errno = ERANGE when + * the result overflows to +-Infinity or underflows to 0. + */ + +#ifndef Long +#define Long long +#endif +#ifndef ULong +typedef unsigned Long ULong; +#endif + +#ifdef DEBUG +#include "stdio.h" +#define Bug(x) {fprintf(stderr, "%s\n", x); exit(1);} +#endif + +#include "stdlib.h" +#include "string.h" + +#ifdef USE_LOCALE +#include "locale.h" +#endif + +#ifdef MALLOC +#ifdef KR_headers +extern char *MALLOC(); +#else +extern void *MALLOC(size_t); +#endif +#else +#define MALLOC malloc +#endif + +#ifndef Omit_Private_Memory +#ifndef PRIVATE_MEM +#define PRIVATE_MEM 2304 +#endif +#define PRIVATE_mem ((PRIVATE_MEM+sizeof(double)-1)/sizeof(double)) +static double private_mem[PRIVATE_mem], *pmem_next = private_mem; +#endif + +#undef IEEE_Arith +#undef Avoid_Underflow +#ifdef IEEE_MC68k +#define IEEE_Arith +#endif +#ifdef IEEE_8087 +#define IEEE_Arith +#endif + +#ifdef IEEE_Arith +#ifndef NO_INFNAN_CHECK +#undef INFNAN_CHECK +#define INFNAN_CHECK +#endif +#else +#undef INFNAN_CHECK +#endif + +#include "errno.h" + +#ifdef Bad_float_h + +#ifdef IEEE_Arith +#define DBL_DIG 15 +#define DBL_MAX_10_EXP 308 +#define DBL_MAX_EXP 1024 +#define FLT_RADIX 2 +#endif /*IEEE_Arith*/ + +#ifdef IBM +#define DBL_DIG 16 +#define DBL_MAX_10_EXP 75 +#define DBL_MAX_EXP 63 +#define FLT_RADIX 16 +#define DBL_MAX 7.2370055773322621e+75 +#endif + +#ifdef VAX +#define DBL_DIG 16 +#define DBL_MAX_10_EXP 38 +#define DBL_MAX_EXP 127 +#define FLT_RADIX 2 +#define DBL_MAX 1.7014118346046923e+38 +#endif + +#ifndef LONG_MAX +#define LONG_MAX 2147483647 +#endif + +#else /* ifndef Bad_float_h */ +#include "float.h" +#endif /* Bad_float_h */ + +#ifndef __MATH_H__ +#include "math.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef CONST +#ifdef KR_headers +#define CONST /* blank */ +#else +#define CONST const +#endif +#endif + +#if defined(IEEE_8087) + defined(IEEE_MC68k) + defined(VAX) + defined(IBM) != 1 +Exactly one of IEEE_8087, IEEE_MC68k, VAX, or IBM should be defined. +#endif + +typedef union { double d; ULong L[2]; } U; + +#define dval(x) ((x).d) +#ifdef IEEE_8087 +#define word0(x) ((x).L[1]) +#define word1(x) ((x).L[0]) +#else +#define word0(x) ((x).L[0]) +#define word1(x) ((x).L[1]) +#endif + +/* The following definition of Storeinc is appropriate for MIPS processors. + * An alternative that might be better on some machines is + * #define Storeinc(a,b,c) (*a++ = b << 16 | c & 0xffff) + */ +#if defined(IEEE_8087) + defined(VAX) +#define Storeinc(a,b,c) (((unsigned short *)a)[1] = (unsigned short)b, \ +((unsigned short *)a)[0] = (unsigned short)c, a++) +#else +#define Storeinc(a,b,c) (((unsigned short *)a)[0] = (unsigned short)b, \ +((unsigned short *)a)[1] = (unsigned short)c, a++) +#endif + +/* #define P DBL_MANT_DIG */ +/* Ten_pmax = floor(P*log(2)/log(5)) */ +/* Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16 */ +/* Quick_max = floor((P-1)*log(FLT_RADIX)/log(10) - 1) */ +/* Int_max = floor(P*log(FLT_RADIX)/log(10) - 1) */ + +#ifdef IEEE_Arith +#define Exp_shift 20 +#define Exp_shift1 20 +#define Exp_msk1 0x100000 +#define Exp_msk11 0x100000 +#define Exp_mask 0x7ff00000 +#define P 53 +#define Bias 1023 +#define Emin (-1022) +#define Exp_1 0x3ff00000 +#define Exp_11 0x3ff00000 +#define Ebits 11 +#define Frac_mask 0xfffff +#define Frac_mask1 0xfffff +#define Ten_pmax 22 +#define Bletch 0x10 +#define Bndry_mask 0xfffff +#define Bndry_mask1 0xfffff +#define LSB 1 +#define Sign_bit 0x80000000 +#define Log2P 1 +#define Tiny0 0 +#define Tiny1 1 +#define Quick_max 14 +#define Int_max 14 +#ifndef NO_IEEE_Scale +#define Avoid_Underflow +#ifdef Flush_Denorm /* debugging option */ +#undef Sudden_Underflow +#endif +#endif + +#ifndef Flt_Rounds +#ifdef FLT_ROUNDS +#define Flt_Rounds FLT_ROUNDS +#else +#define Flt_Rounds 1 +#endif +#endif /*Flt_Rounds*/ + +#ifdef Honor_FLT_ROUNDS +#define Rounding rounding +#undef Check_FLT_ROUNDS +#define Check_FLT_ROUNDS +#else +#define Rounding Flt_Rounds +#endif + +#else /* ifndef IEEE_Arith */ +#undef Check_FLT_ROUNDS +#undef Honor_FLT_ROUNDS +#undef SET_INEXACT +#undef Sudden_Underflow +#define Sudden_Underflow +#ifdef IBM +#undef Flt_Rounds +#define Flt_Rounds 0 +#define Exp_shift 24 +#define Exp_shift1 24 +#define Exp_msk1 0x1000000 +#define Exp_msk11 0x1000000 +#define Exp_mask 0x7f000000 +#define P 14 +#define Bias 65 +#define Exp_1 0x41000000 +#define Exp_11 0x41000000 +#define Ebits 8 /* exponent has 7 bits, but 8 is the right value in b2d */ +#define Frac_mask 0xffffff +#define Frac_mask1 0xffffff +#define Bletch 4 +#define Ten_pmax 22 +#define Bndry_mask 0xefffff +#define Bndry_mask1 0xffffff +#define LSB 1 +#define Sign_bit 0x80000000 +#define Log2P 4 +#define Tiny0 0x100000 +#define Tiny1 0 +#define Quick_max 14 +#define Int_max 15 +#else /* VAX */ +#undef Flt_Rounds +#define Flt_Rounds 1 +#define Exp_shift 23 +#define Exp_shift1 7 +#define Exp_msk1 0x80 +#define Exp_msk11 0x800000 +#define Exp_mask 0x7f80 +#define P 56 +#define Bias 129 +#define Exp_1 0x40800000 +#define Exp_11 0x4080 +#define Ebits 8 +#define Frac_mask 0x7fffff +#define Frac_mask1 0xffff007f +#define Ten_pmax 24 +#define Bletch 2 +#define Bndry_mask 0xffff007f +#define Bndry_mask1 0xffff007f +#define LSB 0x10000 +#define Sign_bit 0x8000 +#define Log2P 1 +#define Tiny0 0x80 +#define Tiny1 0 +#define Quick_max 15 +#define Int_max 15 +#endif /* IBM, VAX */ +#endif /* IEEE_Arith */ + +#ifndef IEEE_Arith +#define ROUND_BIASED +#endif + +#ifdef RND_PRODQUOT +#define rounded_product(a,b) a = rnd_prod(a, b) +#define rounded_quotient(a,b) a = rnd_quot(a, b) +#ifdef KR_headers +extern double rnd_prod(), rnd_quot(); +#else +extern double rnd_prod(double, double), rnd_quot(double, double); +#endif +#else +#define rounded_product(a,b) a *= b +#define rounded_quotient(a,b) a /= b +#endif + +#define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1)) +#define Big1 0xffffffff + +#ifndef Pack_32 +#define Pack_32 +#endif + +#ifdef KR_headers +#define FFFFFFFF ((((unsigned long)0xffff)<<16)|(unsigned long)0xffff) +#else +#define FFFFFFFF 0xffffffffUL +#endif + +#ifdef NO_LONG_LONG +#undef ULLong +#ifdef Just_16 +#undef Pack_32 +/* When Pack_32 is not defined, we store 16 bits per 32-bit Long. + * This makes some inner loops simpler and sometimes saves work + * during multiplications, but it often seems to make things slightly + * slower. Hence the default is now to store 32 bits per Long. + */ +#endif +#else /* long long available */ +#ifndef Llong +#define Llong long long +#endif +#ifndef ULLong +#define ULLong unsigned Llong +#endif +#endif /* NO_LONG_LONG */ + +#ifndef MULTIPLE_THREADS +#define ACQUIRE_DTOA_LOCK(n) /*nothing*/ +#define FREE_DTOA_LOCK(n) /*nothing*/ +#endif + +#define Kmax 7 + + struct +Bigint { + struct Bigint *next; + int k, maxwds, sign, wds; + ULong x[1]; + }; + + typedef struct Bigint Bigint; + + static Bigint *freelist[Kmax+1]; + + static Bigint * +Balloc +#ifdef KR_headers + (k) int k; +#else + (int k) +#endif +{ + int x; + Bigint *rv; +#ifndef Omit_Private_Memory + size_t len; +#endif + + ACQUIRE_DTOA_LOCK(0); + /* The k > Kmax case does not need ACQUIRE_DTOA_LOCK(0), */ + /* but this case seems very unlikely. */ + if (k <= Kmax && (rv = freelist[k])) + freelist[k] = rv->next; + else { + x = 1 << k; +#ifdef Omit_Private_Memory + rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(ULong)); +#else + len = (sizeof(Bigint) + (x-1)*sizeof(ULong) + sizeof(double) - 1) + /sizeof(double); + if (k <= Kmax && pmem_next - private_mem + len <= PRIVATE_mem) { + rv = (Bigint*)pmem_next; + pmem_next += len; + } + else + rv = (Bigint*)MALLOC(len*sizeof(double)); +#endif + rv->k = k; + rv->maxwds = x; + } + FREE_DTOA_LOCK(0); + rv->sign = rv->wds = 0; + return rv; + } + + static void +Bfree +#ifdef KR_headers + (v) Bigint *v; +#else + (Bigint *v) +#endif +{ + if (v) { + if (v->k > Kmax) +#ifdef FREE + FREE((void*)v); +#else + free((void*)v); +#endif + else { + ACQUIRE_DTOA_LOCK(0); + v->next = freelist[v->k]; + freelist[v->k] = v; + FREE_DTOA_LOCK(0); + } + } + } + +#define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \ +y->wds*sizeof(Long) + 2*sizeof(int)) + + static Bigint * +multadd +#ifdef KR_headers + (b, m, a) Bigint *b; int m, a; +#else + (Bigint *b, int m, int a) /* multiply by m and add a */ +#endif +{ + int i, wds; +#ifdef ULLong + ULong *x; + ULLong carry, y; +#else + ULong carry, *x, y; +#ifdef Pack_32 + ULong xi, z; +#endif +#endif + Bigint *b1; + + wds = b->wds; + x = b->x; + i = 0; + carry = a; + do { +#ifdef ULLong + y = *x * (ULLong)m + carry; + carry = y >> 32; + *x++ = (ULong) y & FFFFFFFF; +#else +#ifdef Pack_32 + xi = *x; + y = (xi & 0xffff) * m + carry; + z = (xi >> 16) * m + (y >> 16); + carry = z >> 16; + *x++ = (z << 16) + (y & 0xffff); +#else + y = *x * m + carry; + carry = y >> 16; + *x++ = y & 0xffff; +#endif +#endif + } + while(++i < wds); + if (carry) { + if (wds >= b->maxwds) { + b1 = Balloc(b->k+1); + Bcopy(b1, b); + Bfree(b); + b = b1; + } + b->x[wds++] = (ULong) carry; + b->wds = wds; + } + return b; + } + + static Bigint * +s2b +#ifdef KR_headers + (s, nd0, nd, y9) CONST char *s; int nd0, nd; ULong y9; +#else + (CONST char *s, int nd0, int nd, ULong y9) +#endif +{ + Bigint *b; + int i, k; + Long x, y; + + x = (nd + 8) / 9; + for(k = 0, y = 1; x > y; y <<= 1, k++) ; +#ifdef Pack_32 + b = Balloc(k); + b->x[0] = y9; + b->wds = 1; +#else + b = Balloc(k+1); + b->x[0] = y9 & 0xffff; + b->wds = (b->x[1] = y9 >> 16) ? 2 : 1; +#endif + + i = 9; + if (9 < nd0) { + s += 9; + do b = multadd(b, 10, *s++ - '0'); + while(++i < nd0); + s++; + } + else + s += 10; + for(; i < nd; i++) + b = multadd(b, 10, *s++ - '0'); + return b; + } + + static int +hi0bits +#ifdef KR_headers + (x) register ULong x; +#else + (register ULong x) +#endif +{ + register int k = 0; + + if (!(x & 0xffff0000)) { + k = 16; + x <<= 16; + } + if (!(x & 0xff000000)) { + k += 8; + x <<= 8; + } + if (!(x & 0xf0000000)) { + k += 4; + x <<= 4; + } + if (!(x & 0xc0000000)) { + k += 2; + x <<= 2; + } + if (!(x & 0x80000000)) { + k++; + if (!(x & 0x40000000)) + return 32; + } + return k; + } + + static int +lo0bits +#ifdef KR_headers + (y) ULong *y; +#else + (ULong *y) +#endif +{ + register int k; + register ULong x = *y; + + if (x & 7) { + if (x & 1) + return 0; + if (x & 2) { + *y = x >> 1; + return 1; + } + *y = x >> 2; + return 2; + } + k = 0; + if (!(x & 0xffff)) { + k = 16; + x >>= 16; + } + if (!(x & 0xff)) { + k += 8; + x >>= 8; + } + if (!(x & 0xf)) { + k += 4; + x >>= 4; + } + if (!(x & 0x3)) { + k += 2; + x >>= 2; + } + if (!(x & 1)) { + k++; + x >>= 1; + if (!x) + return 32; + } + *y = x; + return k; + } + + static Bigint * +i2b +#ifdef KR_headers + (i) int i; +#else + (int i) +#endif +{ + Bigint *b; + + b = Balloc(1); + b->x[0] = i; + b->wds = 1; + return b; + } + + static Bigint * +mult +#ifdef KR_headers + (a, b) Bigint *a, *b; +#else + (Bigint *a, Bigint *b) +#endif +{ + Bigint *c; + int k, wa, wb, wc; + ULong *x, *xa, *xae, *xb, *xbe, *xc, *xc0; + ULong y; +#ifdef ULLong + ULLong carry, z; +#else + ULong carry, z; +#ifdef Pack_32 + ULong z2; +#endif +#endif + + if (a->wds < b->wds) { + c = a; + a = b; + b = c; + } + k = a->k; + wa = a->wds; + wb = b->wds; + wc = wa + wb; + if (wc > a->maxwds) + k++; + c = Balloc(k); + for(x = c->x, xa = x + wc; x < xa; x++) + *x = 0; + xa = a->x; + xae = xa + wa; + xb = b->x; + xbe = xb + wb; + xc0 = c->x; +#ifdef ULLong + for(; xb < xbe; xc0++) { + if ((y = *xb++)) { + x = xa; + xc = xc0; + carry = 0; + do { + z = *x++ * (ULLong)y + *xc + carry; + carry = z >> 32; + *xc++ = (ULong) z & FFFFFFFF; + } + while(x < xae); + *xc = (ULong) carry; + } + } +#else +#ifdef Pack_32 + for(; xb < xbe; xb++, xc0++) { + if (y = *xb & 0xffff) { + x = xa; + xc = xc0; + carry = 0; + do { + z = (*x & 0xffff) * y + (*xc & 0xffff) + carry; + carry = z >> 16; + z2 = (*x++ >> 16) * y + (*xc >> 16) + carry; + carry = z2 >> 16; + Storeinc(xc, z2, z); + } + while(x < xae); + *xc = carry; + } + if (y = *xb >> 16) { + x = xa; + xc = xc0; + carry = 0; + z2 = *xc; + do { + z = (*x & 0xffff) * y + (*xc >> 16) + carry; + carry = z >> 16; + Storeinc(xc, z, z2); + z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry; + carry = z2 >> 16; + } + while(x < xae); + *xc = z2; + } + } +#else + for(; xb < xbe; xc0++) { + if (y = *xb++) { + x = xa; + xc = xc0; + carry = 0; + do { + z = *x++ * y + *xc + carry; + carry = z >> 16; + *xc++ = z & 0xffff; + } + while(x < xae); + *xc = carry; + } + } +#endif +#endif + for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ; + c->wds = wc; + return c; + } + + static Bigint *p5s; + + static Bigint * +pow5mult +#ifdef KR_headers + (b, k) Bigint *b; int k; +#else + (Bigint *b, int k) +#endif +{ + Bigint *b1, *p5, *p51; + int i; + static int p05[3] = { 5, 25, 125 }; + + if ((i = k & 3)) + b = multadd(b, p05[i-1], 0); + + if (!(k >>= 2)) + return b; + if (!(p5 = p5s)) { + /* first time */ +#ifdef MULTIPLE_THREADS + ACQUIRE_DTOA_LOCK(1); + if (!(p5 = p5s)) { + p5 = p5s = i2b(625); + p5->next = 0; + } + FREE_DTOA_LOCK(1); +#else + p5 = p5s = i2b(625); + p5->next = 0; +#endif + } + for(;;) { + if (k & 1) { + b1 = mult(b, p5); + Bfree(b); + b = b1; + } + if (!(k >>= 1)) + break; + if (!(p51 = p5->next)) { +#ifdef MULTIPLE_THREADS + ACQUIRE_DTOA_LOCK(1); + if (!(p51 = p5->next)) { + p51 = p5->next = mult(p5,p5); + p51->next = 0; + } + FREE_DTOA_LOCK(1); +#else + p51 = p5->next = mult(p5,p5); + p51->next = 0; +#endif + } + p5 = p51; + } + return b; + } + + static Bigint * +lshift +#ifdef KR_headers + (b, k) Bigint *b; int k; +#else + (Bigint *b, int k) +#endif +{ + int i, k1, n, n1; + Bigint *b1; + ULong *x, *x1, *xe, z; + +#ifdef Pack_32 + n = k >> 5; +#else + n = k >> 4; +#endif + k1 = b->k; + n1 = n + b->wds + 1; + for(i = b->maxwds; n1 > i; i <<= 1) + k1++; + b1 = Balloc(k1); + x1 = b1->x; + for(i = 0; i < n; i++) + *x1++ = 0; + x = b->x; + xe = x + b->wds; +#ifdef Pack_32 + if (k &= 0x1f) { + k1 = 32 - k; + z = 0; + do { + *x1++ = *x << k | z; + z = *x++ >> k1; + } + while(x < xe); + if ((*x1 = z)) + ++n1; + } +#else + if (k &= 0xf) { + k1 = 16 - k; + z = 0; + do { + *x1++ = *x << k & 0xffff | z; + z = *x++ >> k1; + } + while(x < xe); + if (*x1 = z) + ++n1; + } +#endif + else do + *x1++ = *x++; + while(x < xe); + b1->wds = n1 - 1; + Bfree(b); + return b1; + } + + static int +cmp +#ifdef KR_headers + (a, b) Bigint *a, *b; +#else + (Bigint *a, Bigint *b) +#endif +{ + ULong *xa, *xa0, *xb, *xb0; + int i, j; + + i = a->wds; + j = b->wds; +#ifdef DEBUG + if (i > 1 && !a->x[i-1]) + Bug("cmp called with a->x[a->wds-1] == 0"); + if (j > 1 && !b->x[j-1]) + Bug("cmp called with b->x[b->wds-1] == 0"); +#endif + if (i -= j) + return i; + xa0 = a->x; + xa = xa0 + j; + xb0 = b->x; + xb = xb0 + j; + for(;;) { + if (*--xa != *--xb) + return *xa < *xb ? -1 : 1; + if (xa <= xa0) + break; + } + return 0; + } + + static Bigint * +diff +#ifdef KR_headers + (a, b) Bigint *a, *b; +#else + (Bigint *a, Bigint *b) +#endif +{ + Bigint *c; + int i, wa, wb; + ULong *xa, *xae, *xb, *xbe, *xc; +#ifdef ULLong + ULLong borrow, y; +#else + ULong borrow, y; +#ifdef Pack_32 + ULong z; +#endif +#endif + + i = cmp(a,b); + if (!i) { + c = Balloc(0); + c->wds = 1; + c->x[0] = 0; + return c; + } + if (i < 0) { + c = a; + a = b; + b = c; + i = 1; + } + else + i = 0; + c = Balloc(a->k); + c->sign = i; + wa = a->wds; + xa = a->x; + xae = xa + wa; + wb = b->wds; + xb = b->x; + xbe = xb + wb; + xc = c->x; + borrow = 0; +#ifdef ULLong + do { + y = (ULLong)*xa++ - *xb++ - borrow; + borrow = y >> 32 & (ULong)1; + *xc++ = (ULong) y & FFFFFFFF; + } + while(xb < xbe); + while(xa < xae) { + y = *xa++ - borrow; + borrow = y >> 32 & (ULong)1; + *xc++ = (ULong) y & FFFFFFFF; + } +#else +#ifdef Pack_32 + do { + y = (*xa & 0xffff) - (*xb & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*xa++ >> 16) - (*xb++ >> 16) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(xc, z, y); + } + while(xb < xbe); + while(xa < xae) { + y = (*xa & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*xa++ >> 16) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(xc, z, y); + } +#else + do { + y = *xa++ - *xb++ - borrow; + borrow = (y & 0x10000) >> 16; + *xc++ = y & 0xffff; + } + while(xb < xbe); + while(xa < xae) { + y = *xa++ - borrow; + borrow = (y & 0x10000) >> 16; + *xc++ = y & 0xffff; + } +#endif +#endif + while(!*--xc) + wa--; + c->wds = wa; + return c; + } + + static double +ulp +#ifdef KR_headers + (x) U x; +#else + (U x) +#endif +{ + register Long L; + U a; + + L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1; +#ifndef Avoid_Underflow +#ifndef Sudden_Underflow + if (L > 0) { +#endif +#endif +#ifdef IBM + L |= Exp_msk1 >> 4; +#endif + word0(a) = L; + word1(a) = 0; +#ifndef Avoid_Underflow +#ifndef Sudden_Underflow + } + else { + L = -L >> Exp_shift; + if (L < Exp_shift) { + word0(a) = 0x80000 >> L; + word1(a) = 0; + } + else { + word0(a) = 0; + L -= Exp_shift; + word1(a) = L >= 31 ? 1 : 1 << 31 - L; + } + } +#endif +#endif + return dval(a); + } + + static double +b2d +#ifdef KR_headers + (a, e) Bigint *a; int *e; +#else + (Bigint *a, int *e) +#endif +{ + ULong *xa, *xa0, w, y, z; + int k; + U d; +#ifdef VAX + ULong d0, d1; +#else +#define d0 word0(d) +#define d1 word1(d) +#endif + + xa0 = a->x; + xa = xa0 + a->wds; + y = *--xa; +#ifdef DEBUG + if (!y) Bug("zero y in b2d"); +#endif + k = hi0bits(y); + *e = 32 - k; +#ifdef Pack_32 + if (k < Ebits) { + d0 = Exp_1 | y >> (Ebits - k); + w = xa > xa0 ? *--xa : 0; + d1 = y << ((32-Ebits) + k) | w >> (Ebits - k); + goto ret_d; + } + z = xa > xa0 ? *--xa : 0; + if (k -= Ebits) { + d0 = Exp_1 | y << k | z >> (32 - k); + y = xa > xa0 ? *--xa : 0; + d1 = z << k | y >> (32 - k); + } + else { + d0 = Exp_1 | y; + d1 = z; + } +#else + if (k < Ebits + 16) { + z = xa > xa0 ? *--xa : 0; + d0 = Exp_1 | y << k - Ebits | z >> Ebits + 16 - k; + w = xa > xa0 ? *--xa : 0; + y = xa > xa0 ? *--xa : 0; + d1 = z << k + 16 - Ebits | w << k - Ebits | y >> 16 + Ebits - k; + goto ret_d; + } + z = xa > xa0 ? *--xa : 0; + w = xa > xa0 ? *--xa : 0; + k -= Ebits + 16; + d0 = Exp_1 | y << k + 16 | z << k | w >> 16 - k; + y = xa > xa0 ? *--xa : 0; + d1 = w << k + 16 | y << k; +#endif + ret_d: +#ifdef VAX + word0(d) = d0 >> 16 | d0 << 16; + word1(d) = d1 >> 16 | d1 << 16; +#else +#undef d0 +#undef d1 +#endif + return dval(d); + } + + static Bigint * +d2b +#ifdef KR_headers + (d, e, bits) U d; int *e, *bits; +#else + (U d, int *e, int *bits) +#endif +{ + Bigint *b; + int de, k; + ULong *x, y, z; +#ifndef Sudden_Underflow + int i; +#endif +#ifdef VAX + ULong d0, d1; + d0 = word0(d) >> 16 | word0(d) << 16; + d1 = word1(d) >> 16 | word1(d) << 16; +#else +#define d0 word0(d) +#define d1 word1(d) +#endif + +#ifdef Pack_32 + b = Balloc(1); +#else + b = Balloc(2); +#endif + x = b->x; + + z = d0 & Frac_mask; + d0 &= 0x7fffffff; /* clear sign bit, which we ignore */ +#ifdef Sudden_Underflow + de = (int)(d0 >> Exp_shift); +#ifndef IBM + z |= Exp_msk11; +#endif +#else + if ((de = (int)(d0 >> Exp_shift))) + z |= Exp_msk1; +#endif +#ifdef Pack_32 + if ((y = d1)) { + if ((k = lo0bits(&y))) { + x[0] = y | z << (32 - k); + z >>= k; + } + else + x[0] = y; +#ifndef Sudden_Underflow + i = +#endif + b->wds = (x[1] = z) ? 2 : 1; + } + else { +#ifdef DEBUG + if (!z) + Bug("Zero passed to d2b"); +#endif + k = lo0bits(&z); + x[0] = z; +#ifndef Sudden_Underflow + i = +#endif + b->wds = 1; + k += 32; + } +#else + if (y = d1) { + if (k = lo0bits(&y)) + if (k >= 16) { + x[0] = y | z << 32 - k & 0xffff; + x[1] = z >> k - 16 & 0xffff; + x[2] = z >> k; + i = 2; + } + else { + x[0] = y & 0xffff; + x[1] = y >> 16 | z << 16 - k & 0xffff; + x[2] = z >> k & 0xffff; + x[3] = z >> k+16; + i = 3; + } + else { + x[0] = y & 0xffff; + x[1] = y >> 16; + x[2] = z & 0xffff; + x[3] = z >> 16; + i = 3; + } + } + else { +#ifdef DEBUG + if (!z) + Bug("Zero passed to d2b"); +#endif + k = lo0bits(&z); + if (k >= 16) { + x[0] = z; + i = 0; + } + else { + x[0] = z & 0xffff; + x[1] = z >> 16; + i = 1; + } + k += 32; + } + while(!x[i]) + --i; + b->wds = i + 1; +#endif +#ifndef Sudden_Underflow + if (de) { +#endif +#ifdef IBM + *e = (de - Bias - (P-1) << 2) + k; + *bits = 4*P + 8 - k - hi0bits(word0(d) & Frac_mask); +#else + *e = de - Bias - (P-1) + k; + *bits = P - k; +#endif +#ifndef Sudden_Underflow + } + else { + *e = de - Bias - (P-1) + 1 + k; +#ifdef Pack_32 + *bits = 32*i - hi0bits(x[i-1]); +#else + *bits = (i+2)*16 - hi0bits(x[i]); +#endif + } +#endif + return b; + } +#undef d0 +#undef d1 + + static double +ratio +#ifdef KR_headers + (a, b) Bigint *a, *b; +#else + (Bigint *a, Bigint *b) +#endif +{ + U da, db; + int k, ka, kb; + + dval(da) = b2d(a, &ka); + dval(db) = b2d(b, &kb); +#ifdef Pack_32 + k = ka - kb + 32*(a->wds - b->wds); +#else + k = ka - kb + 16*(a->wds - b->wds); +#endif +#ifdef IBM + if (k > 0) { + word0(da) += (k >> 2)*Exp_msk1; + if (k &= 3) + dval(da) *= 1 << k; + } + else { + k = -k; + word0(db) += (k >> 2)*Exp_msk1; + if (k &= 3) + dval(db) *= 1 << k; + } +#else + if (k > 0) + word0(da) += k*Exp_msk1; + else { + k = -k; + word0(db) += k*Exp_msk1; + } +#endif + return dval(da) / dval(db); + } + + static CONST double +tens[] = { + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, + 1e20, 1e21, 1e22 +#ifdef VAX + , 1e23, 1e24 +#endif + }; + + static CONST double +#ifdef IEEE_Arith +bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; +static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128, +#ifdef Avoid_Underflow + 9007199254740992.*9007199254740992.e-256 + /* = 2^106 * 1e-53 */ +#else + 1e-256 +#endif + }; +/* The factor of 2^53 in tinytens[4] helps us avoid setting the underflow */ +/* flag unnecessarily. It leads to a song and dance at the end of strtod. */ +#define Scale_Bit 0x10 +#define n_bigtens 5 +#else +#ifdef IBM +bigtens[] = { 1e16, 1e32, 1e64 }; +static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64 }; +#define n_bigtens 3 +#else +bigtens[] = { 1e16, 1e32 }; +static CONST double tinytens[] = { 1e-16, 1e-32 }; +#define n_bigtens 2 +#endif +#endif + +#ifdef INFNAN_CHECK + +#ifndef NAN_WORD0 +#define NAN_WORD0 0x7ff80000 +#endif + +#ifndef NAN_WORD1 +#define NAN_WORD1 0 +#endif + + static int +match +#ifdef KR_headers + (sp, t) char **sp, *t; +#else + (CONST char **sp, CONST char *t) +#endif +{ + int c, d; + CONST char *s = *sp; + + while((d = *t++)) { + if ((c = *++s) >= 'A' && c <= 'Z') + c += 'a' - 'A'; + if (c != d) + return 0; + } + *sp = s + 1; + return 1; + } + +#ifndef No_Hex_NaN + static void +hexnan +#ifdef KR_headers + (rvp, sp) U *rvp; CONST char **sp; +#else + (U *rvp, CONST char **sp) +#endif +{ + ULong c, x[2]; + CONST char *s; + int havedig, udx0, xshift; + + x[0] = x[1] = 0; + havedig = xshift = 0; + udx0 = 1; + s = *sp; + /* allow optional initial 0x or 0X */ + while((c = *(CONST unsigned char*)(s+1)) && c <= ' ') + ++s; + if (s[1] == '0' && (s[2] == 'x' || s[2] == 'X')) + s += 2; + while((c = *(CONST unsigned char*)++s)) { + if (c >= '0' && c <= '9') + c -= '0'; + else if (c >= 'a' && c <= 'f') + c += 10 - 'a'; + else if (c >= 'A' && c <= 'F') + c += 10 - 'A'; + else if (c <= ' ') { + if (udx0 && havedig) { + udx0 = 0; + xshift = 1; + } + continue; + } +#ifdef GDTOA_NON_PEDANTIC_NANCHECK + else if (/*(*/ c == ')' && havedig) { + *sp = s + 1; + break; + } + else + return; /* invalid form: don't change *sp */ +#else + else { + do { + if (/*(*/ c == ')') { + *sp = s + 1; + break; + } + } while((c = *++s)); + break; + } +#endif + havedig = 1; + if (xshift) { + xshift = 0; + x[0] = x[1]; + x[1] = 0; + } + if (udx0) + x[0] = (x[0] << 4) | (x[1] >> 28); + x[1] = (x[1] << 4) | c; + } + if ((x[0] &= 0xfffff) || x[1]) { + word0(*rvp) = Exp_mask | x[0]; + word1(*rvp) = x[1]; + } + } +#endif /*No_Hex_NaN*/ +#endif /* INFNAN_CHECK */ + + static double +_strtod +#ifdef KR_headers + (s00, se) CONST char *s00; char **se; +#else + (CONST char *s00, char **se) +#endif +{ +#ifdef Avoid_Underflow + int scale; +#endif + int bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, dsign, + e, e1, esign, i, j, k, nd, nd0, nf, nz, nz0, sign; + CONST char *s, *s0, *s1; + double aadj, adj; + U aadj1, rv, rv0; + Long L; + ULong y, z; + Bigint *bb, *bb1, *bd, *bd0, *bs, *delta; +#ifdef SET_INEXACT + int inexact, oldinexact; +#endif +#ifdef Honor_FLT_ROUNDS + int rounding; +#endif +#ifdef USE_LOCALE + CONST char *s2; +#endif + +#ifdef __GNUC__ + delta = bb = bd = bs = 0; +#endif + + sign = nz0 = nz = 0; + dval(rv) = 0.; + for(s = s00;;s++) switch(*s) { + case '-': + sign = 1; + /* no break */ + case '+': + if (*++s) + goto break2; + /* no break */ + case 0: + goto ret0; + case '\t': + case '\n': + case '\v': + case '\f': + case '\r': + case ' ': + continue; + default: + goto break2; + } + break2: + if (*s == '0') { + nz0 = 1; + while(*++s == '0') ; + if (!*s) + goto ret; + } + s0 = s; + y = z = 0; + for(nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++) + if (nd < 9) + y = 10*y + c - '0'; + else if (nd < 16) + z = 10*z + c - '0'; + nd0 = nd; +#ifdef USE_LOCALE + s1 = localeconv()->decimal_point; + if (c == *s1) { + c = '.'; + if (*++s1) { + s2 = s; + for(;;) { + if (*++s2 != *s1) { + c = 0; + break; + } + if (!*++s1) { + s = s2; + break; + } + } + } + } +#endif + if (c == '.') { + c = *++s; + if (!nd) { + for(; c == '0'; c = *++s) + nz++; + if (c > '0' && c <= '9') { + s0 = s; + nf += nz; + nz = 0; + goto have_dig; + } + goto dig_done; + } + for(; c >= '0' && c <= '9'; c = *++s) { + have_dig: + nz++; + if (c -= '0') { + nf += nz; + for(i = 1; i < nz; i++) + if (nd++ < 9) + y *= 10; + else if (nd <= DBL_DIG + 1) + z *= 10; + if (nd++ < 9) + y = 10*y + c; + else if (nd <= DBL_DIG + 1) + z = 10*z + c; + nz = 0; + } + } + } + dig_done: + e = 0; + if (c == 'e' || c == 'E') { + if (!nd && !nz && !nz0) { + goto ret0; + } + s00 = s; + esign = 0; + switch(c = *++s) { + case '-': + esign = 1; + case '+': + c = *++s; + } + if (c >= '0' && c <= '9') { + while(c == '0') + c = *++s; + if (c > '0' && c <= '9') { + L = c - '0'; + s1 = s; + while((c = *++s) >= '0' && c <= '9') + L = 10*L + c - '0'; + if (s - s1 > 8 || L > 19999) + /* Avoid confusion from exponents + * so large that e might overflow. + */ + e = 19999; /* safe for 16 bit ints */ + else + e = (int)L; + if (esign) + e = -e; + } + else + e = 0; + } + else + s = s00; + } + if (!nd) { + if (!nz && !nz0) { +#ifdef INFNAN_CHECK + /* Check for Nan and Infinity */ + switch(c) { + case 'i': + case 'I': + if (match(&s,"nf")) { + --s; + if (!match(&s,"inity")) + ++s; + word0(rv) = 0x7ff00000; + word1(rv) = 0; + goto ret; + } + break; + case 'n': + case 'N': + if (match(&s, "an")) { + word0(rv) = NAN_WORD0; + word1(rv) = NAN_WORD1; +#ifndef No_Hex_NaN + if (*s == '(') /*)*/ + hexnan(&rv, &s); +#endif + goto ret; + } + } +#endif /* INFNAN_CHECK */ + ret0: + s = s00; + sign = 0; + } + goto ret; + } + e1 = e -= nf; + + /* Now we have nd0 digits, starting at s0, followed by a + * decimal point, followed by nd-nd0 digits. The number we're + * after is the integer represented by those digits times + * 10**e */ + + if (!nd0) + nd0 = nd; + k = nd < DBL_DIG + 1 ? nd : DBL_DIG + 1; + dval(rv) = y; + if (k > 9) { +#ifdef SET_INEXACT + if (k > DBL_DIG) + oldinexact = get_inexact(); +#endif + dval(rv) = tens[k - 9] * dval(rv) + z; + } + bd0 = 0; + if (nd <= DBL_DIG +#ifndef RND_PRODQUOT +#ifndef Honor_FLT_ROUNDS + && Flt_Rounds == 1 +#endif +#endif + ) { + if (!e) + goto ret; + if (e > 0) { + if (e <= Ten_pmax) { +#ifdef VAX + goto vax_ovfl_check; +#else +#ifdef Honor_FLT_ROUNDS + /* round correctly FLT_ROUNDS = 2 or 3 */ + if (sign) { + rv = -rv; + sign = 0; + } +#endif + /* rv = */ rounded_product(dval(rv), tens[e]); + goto ret; +#endif + } + i = DBL_DIG - nd; + if (e <= Ten_pmax + i) { + /* A fancier test would sometimes let us do + * this for larger i values. + */ +#ifdef Honor_FLT_ROUNDS + /* round correctly FLT_ROUNDS = 2 or 3 */ + if (sign) { + rv = -rv; + sign = 0; + } +#endif + e -= i; + dval(rv) *= tens[i]; +#ifdef VAX + /* VAX exponent range is so narrow we must + * worry about overflow here... + */ + vax_ovfl_check: + word0(rv) -= P*Exp_msk1; + /* rv = */ rounded_product(dval(rv), tens[e]); + if ((word0(rv) & Exp_mask) + > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) + goto ovfl; + word0(rv) += P*Exp_msk1; +#else + /* rv = */ rounded_product(dval(rv), tens[e]); +#endif + goto ret; + } + } +#ifndef Inaccurate_Divide + else if (e >= -Ten_pmax) { +#ifdef Honor_FLT_ROUNDS + /* round correctly FLT_ROUNDS = 2 or 3 */ + if (sign) { + rv = -rv; + sign = 0; + } +#endif + /* rv = */ rounded_quotient(dval(rv), tens[-e]); + goto ret; + } +#endif + } + e1 += nd - k; + +#ifdef IEEE_Arith +#ifdef SET_INEXACT + inexact = 1; + if (k <= DBL_DIG) + oldinexact = get_inexact(); +#endif +#ifdef Avoid_Underflow + scale = 0; +#endif +#ifdef Honor_FLT_ROUNDS + if ((rounding = Flt_Rounds) >= 2) { + if (sign) + rounding = rounding == 2 ? 0 : 2; + else + if (rounding != 2) + rounding = 0; + } +#endif +#endif /*IEEE_Arith*/ + + /* Get starting approximation = rv * 10**e1 */ + + if (e1 > 0) { + if ((i = e1 & 15)) + dval(rv) *= tens[i]; + if (e1 &= ~15) { + if (e1 > DBL_MAX_10_EXP) { + ovfl: +#ifndef NO_ERRNO + errno = ERANGE; +#endif + /* Can't trust HUGE_VAL */ +#ifdef IEEE_Arith +#ifdef Honor_FLT_ROUNDS + switch(rounding) { + case 0: /* toward 0 */ + case 3: /* toward -infinity */ + word0(rv) = Big0; + word1(rv) = Big1; + break; + default: + word0(rv) = Exp_mask; + word1(rv) = 0; + } +#else /*Honor_FLT_ROUNDS*/ + word0(rv) = Exp_mask; + word1(rv) = 0; +#endif /*Honor_FLT_ROUNDS*/ +#ifdef SET_INEXACT + /* set overflow bit */ + dval(rv0) = 1e300; + dval(rv0) *= dval(rv0); +#endif +#else /*IEEE_Arith*/ + word0(rv) = Big0; + word1(rv) = Big1; +#endif /*IEEE_Arith*/ + if (bd0) + goto retfree; + goto ret; + } + e1 >>= 4; + for(j = 0; e1 > 1; j++, e1 >>= 1) + if (e1 & 1) + dval(rv) *= bigtens[j]; + /* The last multiplication could overflow. */ + word0(rv) -= P*Exp_msk1; + dval(rv) *= bigtens[j]; + if ((z = word0(rv) & Exp_mask) + > Exp_msk1*(DBL_MAX_EXP+Bias-P)) + goto ovfl; + if (z > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) { + /* set to largest number */ + /* (Can't trust DBL_MAX) */ + word0(rv) = Big0; + word1(rv) = Big1; + } + else + word0(rv) += P*Exp_msk1; + } + } + else if (e1 < 0) { + e1 = -e1; + if ((i = e1 & 15)) + dval(rv) /= tens[i]; + if (e1 >>= 4) { + if (e1 >= 1 << n_bigtens) + goto undfl; +#ifdef Avoid_Underflow + if (e1 & Scale_Bit) + scale = 2*P; + for(j = 0; e1 > 0; j++, e1 >>= 1) + if (e1 & 1) + dval(rv) *= tinytens[j]; + if (scale && (j = 2*P + 1 - ((word0(rv) & Exp_mask) + >> Exp_shift)) > 0) { + /* scaled rv is denormal; zap j low bits */ + if (j >= 32) { + word1(rv) = 0; + if (j >= 53) + word0(rv) = (P+2)*Exp_msk1; + else + word0(rv) &= 0xffffffff << (j-32); + } + else + word1(rv) &= 0xffffffff << j; + } +#else + for(j = 0; e1 > 1; j++, e1 >>= 1) + if (e1 & 1) + dval(rv) *= tinytens[j]; + /* The last multiplication could underflow. */ + dval(rv0) = dval(rv); + dval(rv) *= tinytens[j]; + if (!dval(rv)) { + dval(rv) = 2.*dval(rv0); + dval(rv) *= tinytens[j]; +#endif + if (!dval(rv)) { + undfl: + dval(rv) = 0.; +#ifndef NO_ERRNO + errno = ERANGE; +#endif + if (bd0) + goto retfree; + goto ret; + } +#ifndef Avoid_Underflow + word0(rv) = Tiny0; + word1(rv) = Tiny1; + /* The refinement below will clean + * this approximation up. + */ + } +#endif + } + } + + /* Now the hard part -- adjusting rv to the correct value.*/ + + /* Put digits into bd: true value = bd * 10^e */ + + bd0 = s2b(s0, nd0, nd, y); + + for(;;) { + bd = Balloc(bd0->k); + Bcopy(bd, bd0); + bb = d2b(rv, &bbe, &bbbits); /* rv = bb * 2^bbe */ + bs = i2b(1); + + if (e >= 0) { + bb2 = bb5 = 0; + bd2 = bd5 = e; + } + else { + bb2 = bb5 = -e; + bd2 = bd5 = 0; + } + if (bbe >= 0) + bb2 += bbe; + else + bd2 -= bbe; + bs2 = bb2; +#ifdef Honor_FLT_ROUNDS + if (rounding != 1) + bs2++; +#endif +#ifdef Avoid_Underflow + j = bbe - scale; + i = j + bbbits - 1; /* logb(rv) */ + if (i < Emin) /* denormal */ + j += P - Emin; + else + j = P + 1 - bbbits; +#else /*Avoid_Underflow*/ +#ifdef Sudden_Underflow +#ifdef IBM + j = 1 + 4*P - 3 - bbbits + ((bbe + bbbits - 1) & 3); +#else + j = P + 1 - bbbits; +#endif +#else /*Sudden_Underflow*/ + j = bbe; + i = j + bbbits - 1; /* logb(rv) */ + if (i < Emin) /* denormal */ + j += P - Emin; + else + j = P + 1 - bbbits; +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow*/ + bb2 += j; + bd2 += j; +#ifdef Avoid_Underflow + bd2 += scale; +#endif + i = bb2 < bd2 ? bb2 : bd2; + if (i > bs2) + i = bs2; + if (i > 0) { + bb2 -= i; + bd2 -= i; + bs2 -= i; + } + if (bb5 > 0) { + bs = pow5mult(bs, bb5); + bb1 = mult(bs, bb); + Bfree(bb); + bb = bb1; + } + if (bb2 > 0) + bb = lshift(bb, bb2); + if (bd5 > 0) + bd = pow5mult(bd, bd5); + if (bd2 > 0) + bd = lshift(bd, bd2); + if (bs2 > 0) + bs = lshift(bs, bs2); + delta = diff(bb, bd); + dsign = delta->sign; + delta->sign = 0; + i = cmp(delta, bs); +#ifdef Honor_FLT_ROUNDS + if (rounding != 1) { + if (i < 0) { + /* Error is less than an ulp */ + if (!delta->x[0] && delta->wds <= 1) { + /* exact */ +#ifdef SET_INEXACT + inexact = 0; +#endif + break; + } + if (rounding) { + if (dsign) { + adj = 1.; + goto apply_adj; + } + } + else if (!dsign) { + adj = -1.; + if (!word1(rv) + && !(word0(rv) & Frac_mask)) { + y = word0(rv) & Exp_mask; +#ifdef Avoid_Underflow + if (!scale || y > 2*P*Exp_msk1) +#else + if (y) +#endif + { + delta = lshift(delta,Log2P); + if (cmp(delta, bs) <= 0) + adj = -0.5; + } + } + apply_adj: +#ifdef Avoid_Underflow + if (scale && (y = word0(rv) & Exp_mask) + <= 2*P*Exp_msk1) + word0(adj) += (2*P+1)*Exp_msk1 - y; +#else +#ifdef Sudden_Underflow + if ((word0(rv) & Exp_mask) <= + P*Exp_msk1) { + word0(rv) += P*Exp_msk1; + dval(rv) += adj*ulp(rv); + word0(rv) -= P*Exp_msk1; + } + else +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow*/ + dval(rv) += adj*ulp(rv); + } + break; + } + adj = ratio(delta, bs); + if (adj < 1.) + adj = 1.; + if (adj <= 0x7ffffffe) { + /* adj = rounding ? ceil(adj) : floor(adj); */ + y = adj; + if (y != adj) { + if (!((rounding>>1) ^ dsign)) + y++; + adj = y; + } + } +#ifdef Avoid_Underflow + if (scale && (y = word0(rv) & Exp_mask) <= 2*P*Exp_msk1) + word0(adj) += (2*P+1)*Exp_msk1 - y; +#else +#ifdef Sudden_Underflow + if ((word0(rv) & Exp_mask) <= P*Exp_msk1) { + word0(rv) += P*Exp_msk1; + adj *= ulp(rv); + if (dsign) + dval(rv) += adj; + else + dval(rv) -= adj; + word0(rv) -= P*Exp_msk1; + goto cont; + } +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow*/ + adj *= ulp(rv); + if (dsign) + dval(rv) += adj; + else + dval(rv) -= adj; + goto cont; + } +#endif /*Honor_FLT_ROUNDS*/ + + if (i < 0) { + /* Error is less than half an ulp -- check for + * special case of mantissa a power of two. + */ + if (dsign || word1(rv) || word0(rv) & Bndry_mask +#ifdef IEEE_Arith +#ifdef Avoid_Underflow + || (word0(rv) & Exp_mask) <= (2*P+1)*Exp_msk1 +#else + || (word0(rv) & Exp_mask) <= Exp_msk1 +#endif +#endif + ) { +#ifdef SET_INEXACT + if (!delta->x[0] && delta->wds <= 1) + inexact = 0; +#endif + break; + } + if (!delta->x[0] && delta->wds <= 1) { + /* exact result */ +#ifdef SET_INEXACT + inexact = 0; +#endif + break; + } + delta = lshift(delta,Log2P); + if (cmp(delta, bs) > 0) + goto drop_down; + break; + } + if (i == 0) { + /* exactly half-way between */ + if (dsign) { + if ((word0(rv) & Bndry_mask1) == Bndry_mask1 + && word1(rv) == ( +#ifdef Avoid_Underflow + (scale && (y = word0(rv) & Exp_mask) <= 2*P*Exp_msk1) + ? (0xffffffff & (0xffffffff << (2*P+1-(y>>Exp_shift)))) : +#endif + 0xffffffff)) { + /*boundary case -- increment exponent*/ + word0(rv) = (word0(rv) & Exp_mask) + + Exp_msk1 +#ifdef IBM + | Exp_msk1 >> 4 +#endif + ; + word1(rv) = 0; +#ifdef Avoid_Underflow + dsign = 0; +#endif + break; + } + } + else if (!(word0(rv) & Bndry_mask) && !word1(rv)) { + drop_down: + /* boundary case -- decrement exponent */ +#ifdef Sudden_Underflow /*{{*/ + L = word0(rv) & Exp_mask; +#ifdef IBM + if (L < Exp_msk1) +#else +#ifdef Avoid_Underflow + if (L <= (scale ? (2*P+1)*Exp_msk1 : Exp_msk1)) +#else + if (L <= Exp_msk1) +#endif /*Avoid_Underflow*/ +#endif /*IBM*/ + goto undfl; + L -= Exp_msk1; +#else /*Sudden_Underflow}{*/ +#ifdef Avoid_Underflow + if (scale) { + L = word0(rv) & Exp_mask; + if (L <= (2*P+1)*Exp_msk1) { + if (L > (P+2)*Exp_msk1) + /* round even ==> */ + /* accept rv */ + break; + /* rv = smallest denormal */ + goto undfl; + } + } +#endif /*Avoid_Underflow*/ + L = (word0(rv) & Exp_mask) - Exp_msk1; +#endif /*Sudden_Underflow}}*/ + word0(rv) = L | Bndry_mask1; + word1(rv) = 0xffffffff; +#ifdef IBM + goto cont; +#else + break; +#endif + } +#ifndef ROUND_BIASED + if (!(word1(rv) & LSB)) + break; +#endif + if (dsign) + dval(rv) += ulp(rv); +#ifndef ROUND_BIASED + else { + dval(rv) -= ulp(rv); +#ifndef Sudden_Underflow + if (!dval(rv)) + goto undfl; +#endif + } +#ifdef Avoid_Underflow + dsign = 1 - dsign; +#endif +#endif + break; + } + if ((aadj = ratio(delta, bs)) <= 2.) { + if (dsign) + aadj = dval(aadj1) = 1.; + else if (word1(rv) || word0(rv) & Bndry_mask) { +#ifndef Sudden_Underflow + if (word1(rv) == Tiny1 && !word0(rv)) + goto undfl; +#endif + aadj = 1.; + dval(aadj1) = -1.; + } + else { + /* special case -- power of FLT_RADIX to be */ + /* rounded down... */ + + if (aadj < 2./FLT_RADIX) + aadj = 1./FLT_RADIX; + else + aadj *= 0.5; + dval(aadj1) = -aadj; + } + } + else { + aadj *= 0.5; + dval(aadj1) = dsign ? aadj : -aadj; +#ifdef Check_FLT_ROUNDS + switch(Rounding) { + case 2: /* towards +infinity */ + dval(aadj1) -= 0.5; + break; + case 0: /* towards 0 */ + case 3: /* towards -infinity */ + dval(aadj1) += 0.5; + } +#else + if (Flt_Rounds == 0) + dval(aadj1) += 0.5; +#endif /*Check_FLT_ROUNDS*/ + } + y = word0(rv) & Exp_mask; + + /* Check for overflow */ + + if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) { + dval(rv0) = dval(rv); + word0(rv) -= P*Exp_msk1; + adj = dval(aadj1) * ulp(rv); + dval(rv) += adj; + if ((word0(rv) & Exp_mask) >= + Exp_msk1*(DBL_MAX_EXP+Bias-P)) { + if (word0(rv0) == Big0 && word1(rv0) == Big1) + goto ovfl; + word0(rv) = Big0; + word1(rv) = Big1; + goto cont; + } + else + word0(rv) += P*Exp_msk1; + } + else { +#ifdef Avoid_Underflow + if (scale && y <= 2*P*Exp_msk1) { + if (aadj <= 0x7fffffff) { + if ((z = (ULong) aadj) <= 0) + z = 1; + aadj = z; + dval(aadj1) = dsign ? aadj : -aadj; + } + word0(aadj1) += (2*P+1)*Exp_msk1 - y; + } + adj = dval(aadj1) * ulp(rv); + dval(rv) += adj; +#else +#ifdef Sudden_Underflow + if ((word0(rv) & Exp_mask) <= P*Exp_msk1) { + dval(rv0) = dval(rv); + word0(rv) += P*Exp_msk1; + adj = dval(aadj1) * ulp(rv); + dval(rv) += adj; +#ifdef IBM + if ((word0(rv) & Exp_mask) < P*Exp_msk1) +#else + if ((word0(rv) & Exp_mask) <= P*Exp_msk1) +#endif + { + if (word0(rv0) == Tiny0 + && word1(rv0) == Tiny1) + goto undfl; + word0(rv) = Tiny0; + word1(rv) = Tiny1; + goto cont; + } + else + word0(rv) -= P*Exp_msk1; + } + else { + adj = dval(aadj1) * ulp(rv); + dval(rv) += adj; + } +#else /*Sudden_Underflow*/ + /* Compute adj so that the IEEE rounding rules will + * correctly round rv + adj in some half-way cases. + * If rv * ulp(rv) is denormalized (i.e., + * y <= (P-1)*Exp_msk1), we must adjust aadj to avoid + * trouble from bits lost to denormalization; + * example: 1.2e-307 . + */ + if (y <= (P-1)*Exp_msk1 && aadj > 1.) { + dval(aadj1) = (double)(int)(aadj + 0.5); + if (!dsign) + dval(aadj1) = -dval(aadj1); + } + adj = dval(aadj1) * ulp(rv); + dval(rv) += adj; +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow*/ + } + z = word0(rv) & Exp_mask; +#ifndef SET_INEXACT +#ifdef Avoid_Underflow + if (!scale) +#endif + if (y == z) { + /* Can we stop now? */ + L = (Long)aadj; + aadj -= L; + /* The tolerances below are conservative. */ + if (dsign || word1(rv) || word0(rv) & Bndry_mask) { + if (aadj < .4999999 || aadj > .5000001) + break; + } + else if (aadj < .4999999/FLT_RADIX) + break; + } +#endif + cont: + Bfree(bb); + Bfree(bd); + Bfree(bs); + Bfree(delta); + } +#ifdef SET_INEXACT + if (inexact) { + if (!oldinexact) { + word0(rv0) = Exp_1 + (70 << Exp_shift); + word1(rv0) = 0; + dval(rv0) += 1.; + } + } + else if (!oldinexact) + clear_inexact(); +#endif +#ifdef Avoid_Underflow + if (scale) { + word0(rv0) = Exp_1 - 2*P*Exp_msk1; + word1(rv0) = 0; + dval(rv) *= dval(rv0); +#ifndef NO_ERRNO + /* try to avoid the bug of testing an 8087 register value */ + if (word0(rv) == 0 && word1(rv) == 0) + errno = ERANGE; +#endif + } +#endif /* Avoid_Underflow */ +#ifdef SET_INEXACT + if (inexact && !(word0(rv) & Exp_mask)) { + /* set underflow bit */ + dval(rv0) = 1e-300; + dval(rv0) *= dval(rv0); + } +#endif + retfree: + Bfree(bb); + Bfree(bd); + Bfree(bs); + Bfree(bd0); + Bfree(delta); + ret: + if (se) + *se = (char *)s; + return sign ? -dval(rv) : dval(rv); + } + + static int +quorem +#ifdef KR_headers + (b, S) Bigint *b, *S; +#else + (Bigint *b, Bigint *S) +#endif +{ + int n; + ULong *bx, *bxe, q, *sx, *sxe; +#ifdef ULLong + ULLong borrow, carry, y, ys; +#else + ULong borrow, carry, y, ys; +#ifdef Pack_32 + ULong si, z, zs; +#endif +#endif + + n = S->wds; +#ifdef DEBUG + /*debug*/ if (b->wds > n) + /*debug*/ Bug("oversize b in quorem"); +#endif + if (b->wds < n) + return 0; + sx = S->x; + sxe = sx + --n; + bx = b->x; + bxe = bx + n; + q = *bxe / (*sxe + 1); /* ensure q <= true quotient */ +#ifdef DEBUG + /*debug*/ if (q > 9) + /*debug*/ Bug("oversized quotient in quorem"); +#endif + if (q) { + borrow = 0; + carry = 0; + do { +#ifdef ULLong + ys = *sx++ * (ULLong)q + carry; + carry = ys >> 32; + y = *bx - (ys & FFFFFFFF) - borrow; + borrow = y >> 32 & (ULong)1; + *bx++ = (ULong) y & FFFFFFFF; +#else +#ifdef Pack_32 + si = *sx++; + ys = (si & 0xffff) * q + carry; + zs = (si >> 16) * q + (ys >> 16); + carry = zs >> 16; + y = (*bx & 0xffff) - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*bx >> 16) - (zs & 0xffff) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(bx, z, y); +#else + ys = *sx++ * q + carry; + carry = ys >> 16; + y = *bx - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + *bx++ = y & 0xffff; +#endif +#endif + } + while(sx <= sxe); + if (!*bxe) { + bx = b->x; + while(--bxe > bx && !*bxe) + --n; + b->wds = n; + } + } + if (cmp(b, S) >= 0) { + q++; + borrow = 0; + carry = 0; + bx = b->x; + sx = S->x; + do { +#ifdef ULLong + ys = *sx++ + carry; + carry = ys >> 32; + y = *bx - (ys & FFFFFFFF) - borrow; + borrow = y >> 32 & (ULong)1; + *bx++ = (ULong) y & FFFFFFFF; +#else +#ifdef Pack_32 + si = *sx++; + ys = (si & 0xffff) + carry; + zs = (si >> 16) + (ys >> 16); + carry = zs >> 16; + y = (*bx & 0xffff) - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*bx >> 16) - (zs & 0xffff) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(bx, z, y); +#else + ys = *sx++ + carry; + carry = ys >> 16; + y = *bx - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + *bx++ = y & 0xffff; +#endif +#endif + } + while(sx <= sxe); + bx = b->x; + bxe = bx + n; + if (!*bxe) { + while(--bxe > bx && !*bxe) + --n; + b->wds = n; + } + } + return q; + } + +#ifndef MULTIPLE_THREADS + static char *dtoa_result; +#endif + + static char * +#ifdef KR_headers +rv_alloc(i) int i; +#else +rv_alloc(int i) +#endif +{ + int j, k, *r; + + j = sizeof(ULong); + for(k = 0; + sizeof(Bigint) - sizeof(ULong) - sizeof(int) + j <= (unsigned) i; + j <<= 1) + k++; + r = (int*)Balloc(k); + *r = k; + return +#ifndef MULTIPLE_THREADS + dtoa_result = +#endif + (char *)(r+1); + } + + static char * +#ifdef KR_headers +nrv_alloc(s, rve, n) char *s, **rve; int n; +#else +nrv_alloc(CONST char *s, char **rve, int n) +#endif +{ + char *rv, *t; + + t = rv = rv_alloc(n); + while((*t = *s++)) t++; + if (rve) + *rve = t; + return rv; + } + +/* freedtoa(s) must be used to free values s returned by dtoa + * when MULTIPLE_THREADS is #defined. It should be used in all cases, + * but for consistency with earlier versions of dtoa, it is optional + * when MULTIPLE_THREADS is not defined. + */ + + void +#ifdef KR_headers +freedtoa(s) char *s; +#else +freedtoa(char *s) +#endif +{ + Bigint *b = (Bigint *)((int *)s - 1); + b->maxwds = 1 << (b->k = *(int*)b); + Bfree(b); +#ifndef MULTIPLE_THREADS + if (s == dtoa_result) + dtoa_result = 0; +#endif + } + +/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string. + * + * Inspired by "How to Print Floating-Point Numbers Accurately" by + * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 112-126]. + * + * Modifications: + * 1. Rather than iterating, we use a simple numeric overestimate + * to determine k = floor(log10(d)). We scale relevant + * quantities using O(log2(k)) rather than O(k) multiplications. + * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't + * try to generate digits strictly left to right. Instead, we + * compute with fewer bits and propagate the carry if necessary + * when rounding the final digit up. This is often faster. + * 3. Under the assumption that input will be rounded nearest, + * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22. + * That is, we allow equality in stopping tests when the + * round-nearest rule will give the same floating-point value + * as would satisfaction of the stopping test with strict + * inequality. + * 4. We remove common factors of powers of 2 from relevant + * quantities. + * 5. When converting floating-point integers less than 1e16, + * we use floating-point arithmetic rather than resorting + * to multiple-precision integers. + * 6. When asked to produce fewer than 15 digits, we first try + * to get by with floating-point arithmetic; we resort to + * multiple-precision integer arithmetic only if we cannot + * guarantee that the floating-point calculation has given + * the correctly rounded result. For k requested digits and + * "uniformly" distributed input, the probability is + * something like 10^(k-15) that we must resort to the Long + * calculation. + */ + + static char * +dtoa +#ifdef KR_headers + (d, mode, ndigits, decpt, sign, rve) + U d; int mode, ndigits, *decpt, *sign; char **rve; +#else + (U d, int mode, int ndigits, int *decpt, int *sign, char **rve) +#endif +{ + /* Arguments ndigits, decpt, sign are similar to those + of ecvt and fcvt; trailing zeros are suppressed from + the returned string. If not null, *rve is set to point + to the end of the return value. If d is +-Infinity or NaN, + then *decpt is set to 9999. + + mode: + 0 ==> shortest string that yields d when read in + and rounded to nearest. + 1 ==> like 0, but with Steele & White stopping rule; + e.g. with IEEE P754 arithmetic , mode 0 gives + 1e23 whereas mode 1 gives 9.999999999999999e22. + 2 ==> max(1,ndigits) significant digits. This gives a + return value similar to that of ecvt, except + that trailing zeros are suppressed. + 3 ==> through ndigits past the decimal point. This + gives a return value similar to that from fcvt, + except that trailing zeros are suppressed, and + ndigits can be negative. + 4,5 ==> similar to 2 and 3, respectively, but (in + round-nearest mode) with the tests of mode 0 to + possibly return a shorter string that rounds to d. + With IEEE arithmetic and compilation with + -DHonor_FLT_ROUNDS, modes 4 and 5 behave the same + as modes 2 and 3 when FLT_ROUNDS != 1. + 6-9 ==> Debugging modes similar to mode - 4: don't try + fast floating-point estimate (if applicable). + + Values of mode other than 0-9 are treated as mode 0. + + Sufficient space is allocated to the return value + to hold the suppressed trailing zeros. + */ + + int bbits, b2, b5, be, dig, i, ieps, ilim, ilim0, ilim1, + j, j1, k, k0, k_check, leftright, m2, m5, s2, s5, + spec_case, try_quick; + Long L; +#ifndef Sudden_Underflow + int denorm; + ULong x; +#endif + Bigint *b, *b1, *delta, *mlo, *mhi, *S; + U d2, eps; + double ds; + char *s, *s0; +#ifdef Honor_FLT_ROUNDS + int rounding; +#endif +#ifdef SET_INEXACT + int inexact, oldinexact; +#endif + +#ifdef __GNUC__ + ilim = ilim1 = 0; + mlo = NULL; +#endif + +#ifndef MULTIPLE_THREADS + if (dtoa_result) { + freedtoa(dtoa_result); + dtoa_result = 0; + } +#endif + + if (word0(d) & Sign_bit) { + /* set sign for everything, including 0's and NaNs */ + *sign = 1; + word0(d) &= ~Sign_bit; /* clear sign bit */ + } + else + *sign = 0; + +#if defined(IEEE_Arith) + defined(VAX) +#ifdef IEEE_Arith + if ((word0(d) & Exp_mask) == Exp_mask) +#else + if (word0(d) == 0x8000) +#endif + { + /* Infinity or NaN */ + *decpt = 9999; +#ifdef IEEE_Arith + if (!word1(d) && !(word0(d) & 0xfffff)) + return nrv_alloc("Infinity", rve, 8); +#endif + return nrv_alloc("NaN", rve, 3); + } +#endif +#ifdef IBM + dval(d) += 0; /* normalize */ +#endif + if (!dval(d)) { + *decpt = 1; + return nrv_alloc("0", rve, 1); + } + +#ifdef SET_INEXACT + try_quick = oldinexact = get_inexact(); + inexact = 1; +#endif +#ifdef Honor_FLT_ROUNDS + if ((rounding = Flt_Rounds) >= 2) { + if (*sign) + rounding = rounding == 2 ? 0 : 2; + else + if (rounding != 2) + rounding = 0; + } +#endif + + b = d2b(d, &be, &bbits); +#ifdef Sudden_Underflow + i = (int)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1)); +#else + if ((i = (int)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1)))) { +#endif + dval(d2) = dval(d); + word0(d2) &= Frac_mask1; + word0(d2) |= Exp_11; +#ifdef IBM + if (j = 11 - hi0bits(word0(d2) & Frac_mask)) + dval(d2) /= 1 << j; +#endif + + /* log(x) ~=~ log(1.5) + (x-1.5)/1.5 + * log10(x) = log(x) / log(10) + * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10)) + * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2) + * + * This suggests computing an approximation k to log10(d) by + * + * k = (i - Bias)*0.301029995663981 + * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 ); + * + * We want k to be too large rather than too small. + * The error in the first-order Taylor series approximation + * is in our favor, so we just round up the constant enough + * to compensate for any error in the multiplication of + * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077, + * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14, + * adding 1e-13 to the constant term more than suffices. + * Hence we adjust the constant term to 0.1760912590558. + * (We could get a more accurate k by invoking log10, + * but this is probably not worthwhile.) + */ + + i -= Bias; +#ifdef IBM + i <<= 2; + i += j; +#endif +#ifndef Sudden_Underflow + denorm = 0; + } + else { + /* d is denormalized */ + + i = bbits + be + (Bias + (P-1) - 1); + x = i > 32 ? word0(d) << (64 - i) | word1(d) >> (i - 32) + : word1(d) << (32 - i); + dval(d2) = x; + word0(d2) -= 31*Exp_msk1; /* adjust exponent */ + i -= (Bias + (P-1) - 1) + 1; + denorm = 1; + } +#endif + ds = (dval(d2)-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981; + k = (int)ds; + if (ds < 0. && ds != k) + k--; /* want k = floor(ds) */ + k_check = 1; + if (k >= 0 && k <= Ten_pmax) { + if (dval(d) < tens[k]) + k--; + k_check = 0; + } + j = bbits - i - 1; + if (j >= 0) { + b2 = 0; + s2 = j; + } + else { + b2 = -j; + s2 = 0; + } + if (k >= 0) { + b5 = 0; + s5 = k; + s2 += k; + } + else { + b2 -= k; + b5 = -k; + s5 = 0; + } + if (mode < 0 || mode > 9) + mode = 0; + +#ifndef SET_INEXACT +#ifdef Check_FLT_ROUNDS + try_quick = Rounding == 1; +#else + try_quick = 1; +#endif +#endif /*SET_INEXACT*/ + + if (mode > 5) { + mode -= 4; + try_quick = 0; + } + leftright = 1; + switch(mode) { + case 0: + case 1: + ilim = ilim1 = -1; + i = 18; + ndigits = 0; + break; + case 2: + leftright = 0; + /* no break */ + case 4: + if (ndigits <= 0) + ndigits = 1; + ilim = ilim1 = i = ndigits; + break; + case 3: + leftright = 0; + /* no break */ + case 5: + i = ndigits + k + 1; + ilim = i; + ilim1 = i - 1; + if (i <= 0) + i = 1; + } + s = s0 = rv_alloc(i); + +#ifdef Honor_FLT_ROUNDS + if (mode > 1 && rounding != 1) + leftright = 0; +#endif + + if (ilim >= 0 && ilim <= Quick_max && try_quick) { + + /* Try to get by with floating-point arithmetic. */ + + i = 0; + dval(d2) = dval(d); + k0 = k; + ilim0 = ilim; + ieps = 2; /* conservative */ + if (k > 0) { + ds = tens[k&0xf]; + j = k >> 4; + if (j & Bletch) { + /* prevent overflows */ + j &= Bletch - 1; + dval(d) /= bigtens[n_bigtens-1]; + ieps++; + } + for(; j; j >>= 1, i++) + if (j & 1) { + ieps++; + ds *= bigtens[i]; + } + dval(d) /= ds; + } + else if ((j1 = -k)) { + dval(d) *= tens[j1 & 0xf]; + for(j = j1 >> 4; j; j >>= 1, i++) + if (j & 1) { + ieps++; + dval(d) *= bigtens[i]; + } + } + if (k_check && dval(d) < 1. && ilim > 0) { + if (ilim1 <= 0) + goto fast_failed; + ilim = ilim1; + k--; + dval(d) *= 10.; + ieps++; + } + dval(eps) = ieps*dval(d) + 7.; + word0(eps) -= (P-1)*Exp_msk1; + if (ilim == 0) { + S = mhi = 0; + dval(d) -= 5.; + if (dval(d) > dval(eps)) + goto one_digit; + if (dval(d) < -dval(eps)) + goto no_digits; + goto fast_failed; + } +#ifndef No_leftright + if (leftright) { + /* Use Steele & White method of only + * generating digits needed. + */ + dval(eps) = 0.5/tens[ilim-1] - dval(eps); + for(i = 0;;) { + L = (ULong) dval(d); + dval(d) -= L; + *s++ = '0' + (int)L; + if (dval(d) < dval(eps)) + goto ret1; + if (1. - dval(d) < dval(eps)) + goto bump_up; + if (++i >= ilim) + break; + dval(eps) *= 10.; + dval(d) *= 10.; + } + } + else { +#endif + /* Generate ilim digits, then fix them up. */ + dval(eps) *= tens[ilim-1]; + for(i = 1;; i++, dval(d) *= 10.) { + L = (Long)(dval(d)); + if (!(dval(d) -= L)) + ilim = i; + *s++ = '0' + (int)L; + if (i == ilim) { + if (dval(d) > 0.5 + dval(eps)) + goto bump_up; + else if (dval(d) < 0.5 - dval(eps)) { + while(*--s == '0'); + s++; + goto ret1; + } + break; + } + } +#ifndef No_leftright + } +#endif + fast_failed: + s = s0; + dval(d) = dval(d2); + k = k0; + ilim = ilim0; + } + + /* Do we have a "small" integer? */ + + if (be >= 0 && k <= Int_max) { + /* Yes. */ + ds = tens[k]; + if (ndigits < 0 && ilim <= 0) { + S = mhi = 0; + if (ilim < 0 || dval(d) < 5*ds) + goto no_digits; + goto one_digit; + } + for(i = 1;; i++, dval(d) *= 10.) { + L = (Long)(dval(d) / ds); + dval(d) -= L*ds; +#ifdef Check_FLT_ROUNDS + /* If FLT_ROUNDS == 2, L will usually be high by 1 */ + if (dval(d) < 0) { + L--; + dval(d) += ds; + } +#endif + *s++ = '0' + (int)L; + if (!dval(d)) { +#ifdef SET_INEXACT + inexact = 0; +#endif + break; + } + if (i == ilim) { +#ifdef Honor_FLT_ROUNDS + if (mode > 1) + switch(rounding) { + case 0: goto ret1; + case 2: goto bump_up; + } +#endif + dval(d) += dval(d); + if (dval(d) > ds || (dval(d) == ds && L & 1)) { + bump_up: + while(*--s == '9') + if (s == s0) { + k++; + *s = '0'; + break; + } + ++*s++; + } + break; + } + } + goto ret1; + } + + m2 = b2; + m5 = b5; + mhi = mlo = 0; + if (leftright) { + i = +#ifndef Sudden_Underflow + denorm ? be + (Bias + (P-1) - 1 + 1) : +#endif +#ifdef IBM + 1 + 4*P - 3 - bbits + ((bbits + be - 1) & 3); +#else + 1 + P - bbits; +#endif + b2 += i; + s2 += i; + mhi = i2b(1); + } + if (m2 > 0 && s2 > 0) { + i = m2 < s2 ? m2 : s2; + b2 -= i; + m2 -= i; + s2 -= i; + } + if (b5 > 0) { + if (leftright) { + if (m5 > 0) { + mhi = pow5mult(mhi, m5); + b1 = mult(mhi, b); + Bfree(b); + b = b1; + } + if ((j = b5 - m5)) + b = pow5mult(b, j); + } + else + b = pow5mult(b, b5); + } + S = i2b(1); + if (s5 > 0) + S = pow5mult(S, s5); + + /* Check for special case that d is a normalized power of 2. */ + + spec_case = 0; + if ((mode < 2 || leftright) +#ifdef Honor_FLT_ROUNDS + && rounding == 1 +#endif + ) { + if (!word1(d) && !(word0(d) & Bndry_mask) +#ifndef Sudden_Underflow + && word0(d) & (Exp_mask & ~Exp_msk1) +#endif + ) { + /* The special case */ + b2 += Log2P; + s2 += Log2P; + spec_case = 1; + } + } + + /* Arrange for convenient computation of quotients: + * shift left if necessary so divisor has 4 leading 0 bits. + * + * Perhaps we should just compute leading 28 bits of S once + * and for all and pass them and a shift to quorem, so it + * can do shifts and ors to compute the numerator for q. + */ +#ifdef Pack_32 + if ((i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0x1f)) + i = 32 - i; +#else + if (i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0xf) + i = 16 - i; +#endif + if (i > 4) { + i -= 4; + b2 += i; + m2 += i; + s2 += i; + } + else if (i < 4) { + i += 28; + b2 += i; + m2 += i; + s2 += i; + } + if (b2 > 0) + b = lshift(b, b2); + if (s2 > 0) + S = lshift(S, s2); + if (k_check) { + if (cmp(b,S) < 0) { + k--; + b = multadd(b, 10, 0); /* we botched the k estimate */ + if (leftright) + mhi = multadd(mhi, 10, 0); + ilim = ilim1; + } + } + if (ilim <= 0 && (mode == 3 || mode == 5)) { + if (ilim < 0 || cmp(b,S = multadd(S,5,0)) < 0) { + /* no digits, fcvt style */ + no_digits: + /* MOZILLA CHANGE: Always return a non-empty string. */ + *s++ = '0'; + k = 0; + goto ret; + } + one_digit: + *s++ = '1'; + k++; + goto ret; + } + if (leftright) { + if (m2 > 0) + mhi = lshift(mhi, m2); + + /* Compute mlo -- check for special case + * that d is a normalized power of 2. + */ + + mlo = mhi; + if (spec_case) { + mhi = Balloc(mhi->k); + Bcopy(mhi, mlo); + mhi = lshift(mhi, Log2P); + } + + for(i = 1;;i++) { + dig = quorem(b,S) + '0'; + /* Do we yet have the shortest decimal string + * that will round to d? + */ + j = cmp(b, mlo); + delta = diff(S, mhi); + j1 = delta->sign ? 1 : cmp(b, delta); + Bfree(delta); +#ifndef ROUND_BIASED + if (j1 == 0 && mode != 1 && !(word1(d) & 1) +#ifdef Honor_FLT_ROUNDS + && rounding >= 1 +#endif + ) { + if (dig == '9') + goto round_9_up; + if (j > 0) + dig++; +#ifdef SET_INEXACT + else if (!b->x[0] && b->wds <= 1) + inexact = 0; +#endif + *s++ = dig; + goto ret; + } +#endif + if (j < 0 || (j == 0 && mode != 1 +#ifndef ROUND_BIASED + && !(word1(d) & 1) +#endif + )) { + if (!b->x[0] && b->wds <= 1) { +#ifdef SET_INEXACT + inexact = 0; +#endif + goto accept_dig; + } +#ifdef Honor_FLT_ROUNDS + if (mode > 1) + switch(rounding) { + case 0: goto accept_dig; + case 2: goto keep_dig; + } +#endif /*Honor_FLT_ROUNDS*/ + if (j1 > 0) { + b = lshift(b, 1); + j1 = cmp(b, S); + if ((j1 > 0 || (j1 == 0 && dig & 1)) + && dig++ == '9') + goto round_9_up; + } + accept_dig: + *s++ = dig; + goto ret; + } + if (j1 > 0) { +#ifdef Honor_FLT_ROUNDS + if (!rounding) + goto accept_dig; +#endif + if (dig == '9') { /* possible if i == 1 */ + round_9_up: + *s++ = '9'; + goto roundoff; + } + *s++ = dig + 1; + goto ret; + } +#ifdef Honor_FLT_ROUNDS + keep_dig: +#endif + *s++ = dig; + if (i == ilim) + break; + b = multadd(b, 10, 0); + if (mlo == mhi) + mlo = mhi = multadd(mhi, 10, 0); + else { + mlo = multadd(mlo, 10, 0); + mhi = multadd(mhi, 10, 0); + } + } + } + else + for(i = 1;; i++) { + *s++ = dig = quorem(b,S) + '0'; + if (!b->x[0] && b->wds <= 1) { +#ifdef SET_INEXACT + inexact = 0; +#endif + goto ret; + } + if (i >= ilim) + break; + b = multadd(b, 10, 0); + } + + /* Round off last digit */ + +#ifdef Honor_FLT_ROUNDS + switch(rounding) { + case 0: goto trimzeros; + case 2: goto roundoff; + } +#endif + b = lshift(b, 1); + j = cmp(b, S); + if (j >= 0) { /* ECMA compatible rounding needed by Spidermonkey */ + roundoff: + while(*--s == '9') + if (s == s0) { + k++; + *s++ = '1'; + goto ret; + } + ++*s++; + } + else { +#ifdef Honor_FLT_ROUNDS + trimzeros: +#endif + while(*--s == '0'); + s++; + } + ret: + Bfree(S); + if (mhi) { + if (mlo && mlo != mhi) + Bfree(mlo); + Bfree(mhi); + } + ret1: +#ifdef SET_INEXACT + if (inexact) { + if (!oldinexact) { + word0(d) = Exp_1 + (70 << Exp_shift); + word1(d) = 0; + dval(d) += 1.; + } + } + else if (!oldinexact) + clear_inexact(); +#endif + Bfree(b); + *s = 0; + *decpt = k + 1; + if (rve) + *rve = s; + return s0; + } +#ifdef __cplusplus +} +#endif diff --git a/ape-server/deps/js/src/editline/Makefile b/ape-server/deps/js/src/editline/Makefile new file mode 100755 index 0000000..500fdab --- /dev/null +++ b/ape-server/deps/js/src/editline/Makefile @@ -0,0 +1,55 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Spidermonkey build system. +# +# The Initial Developer of the Original Code is +# The Mozilla Foundation. +# Portions created by the Initial Developer are Copyright (C) 2008 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Ted Mielczarek +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = .. +topsrcdir = .. +srcdir = . +VPATH = . + +include $(DEPTH)/config/autoconf.mk + +LIBRARY_NAME = editline +FORCE_STATIC_LIB = 1 + +CSRCS = editline.c sysunix.c + +DEFINES += -DANSI_ARROWS -DHAVE_TCGETATTR -DHIDE -DUSE_DIRENT -DSYS_UNIX \ + -DHAVE_STDLIB -DUNIQUE_HISTORY + +include $(topsrcdir)/config/rules.mk diff --git a/ape-server/deps/js/src/editline/Makefile.in b/ape-server/deps/js/src/editline/Makefile.in new file mode 100755 index 0000000..acf5a6d --- /dev/null +++ b/ape-server/deps/js/src/editline/Makefile.in @@ -0,0 +1,55 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Spidermonkey build system. +# +# The Initial Developer of the Original Code is +# The Mozilla Foundation. +# Portions created by the Initial Developer are Copyright (C) 2008 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Ted Mielczarek +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = .. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +LIBRARY_NAME = editline +FORCE_STATIC_LIB = 1 + +CSRCS = editline.c sysunix.c + +DEFINES += -DANSI_ARROWS -DHAVE_TCGETATTR -DHIDE -DUSE_DIRENT -DSYS_UNIX \ + -DHAVE_STDLIB -DUNIQUE_HISTORY + +include $(topsrcdir)/config/rules.mk diff --git a/ape-server/deps/js/src/editline/Makefile.ref b/ape-server/deps/js/src/editline/Makefile.ref new file mode 100755 index 0000000..c902c88 --- /dev/null +++ b/ape-server/deps/js/src/editline/Makefile.ref @@ -0,0 +1,143 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Simmule Turner and Rich Salz. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Copyright 1992,1993 Simmule Turner and Rich Salz. All rights reserved. +# +# This software is not subject to any license of the American Telephone +# and Telegraph Company or of the Regents of the University of California. +# +# Permission is granted to anyone to use this software for any purpose on +# any computer system, and to alter it and redistribute it freely, subject +# to the following restrictions: +# 1. The authors are not responsible for the consequences of use of this +# software, no matter how awful, even if they arise from flaws in it. +# 2. The origin of this software must not be misrepresented, either by +# explicit claim or by omission. Since few users ever read sources, +# credits must appear in the documentation. +# 3. Altered versions must be plainly marked as such, and must not be +# misrepresented as being the original software. Since few users +# ever read sources, credits must appear in the documentation. +# 4. This notice may not be removed or altered. +# + +# +# Unix makefile for editline library. +# + +## Set your options: +## -DANSI_ARROWS ANSI arrows keys work like emacs. +## -DHAVE_STDLIB Have . +## -DHAVE_TCGETATTR Have tcgetattr(), tcsetattr(). +## -DHAVE_TERMIO Have "struct termio" and +## (If neither of above two, we use and BSD ioctl's) +## -DHIDE Make static functions static (non debug). +## -DHIST_SIZE=n History size. +## -DNEED_STRDUP Don't have strdup(). +## -DUNIQUE_HISTORY Don't save command if same as last one. +## -DUSE_DIRENT Use , not ? +## -DUSE_TERMCAP Use the termcap library for terminal size +## see LDFLAGS, below, if you set this. +## -DNEED_PERROR Don't have perror() (used in testit) + +## If you have -DUSE_TERMCAP, set this as appropriate: +#LDFLAGS = -ltermlib +#LDFLAGS = -ltermcap + +DEFS = -DANSI_ARROWS -DHAVE_TCGETATTR -DHIDE -DUSE_DIRENT -DSYS_UNIX \ + -DHAVE_STDLIB -DUNIQUE_HISTORY + +DEPTH = .. + +include $(DEPTH)/config.mk + +LOCAL_OBJDIR = $(OBJDIR)/editline + +# +# Default IEEE libm +# +CFLAGS += -DXP_UNIX $(OPTIMIZER) $(OS_CFLAGS) $(DEFINES) $(INCLUDES) \ + -DJSFILE $(XCFLAGS) $(DEFS) + +INCFILES = editline.h +.INIT: $(INCFILES) +.KEEP_STATE: +EDITLINE_CFILES = editline.c sysunix.c + +EDITLINE_OBJS = $(addprefix $(LOCAL_OBJDIR)/, $(EDITLINE_CFILES:.c=.o)) + +LIBRARY = $(LOCAL_OBJDIR)/libedit.a + +all: $(LIBRARY) + +export: + +# make objects to depen on $(LOCAL_OBJDIR) only when it exists +$(EDITLINE_OBJS) : $(filter-out $(wildcard $(LOCAL_OBJDIR)), $(LOCAL_OBJDIR)) + +$(LOCAL_OBJDIR) : + mkdir -p $@ + +$(LOCAL_OBJDIR)/%: %.c + $(CC) -o $@ $(CFLAGS) $*.c $(LDFLAGS) + +$(LOCAL_OBJDIR)/%.o: %.c + $(CC) -o $@ -c $(CFLAGS) $*.c + +$(LOCAL_OBJDIR)/%.o: %.s + $(AS) -o $@ $(ASFLAGS) $*.s + +$(LIBRARY): $(EDITLINE_OBJS) + $(AR) rv $@ $? + $(RANLIB) $@ + +#libedit.a : $(EDITLINE_OBJS) +# $(AR) cru $(LOCAL_OBJDIR)/libedit.a $(EDITLINE_OBJS) +# $(RANLIB) $(LOCAL_OBJDIR)/libedit.a + +clean: + rm -rf $(EDITLINE_OBJS) $(EDITLINE_OBJS:.o=.d) + +clobber: clean + rm -rf $(LIBRARY) $(DEPENDENCIES) + if test -d $(LOCAL_OBJDIR); then rmdir $(LOCAL_OBJDIR); fi + +SUFFIXES: .i +%.i: %.c + $(CC) -C -E $(CFLAGS) $< > $*.i diff --git a/ape-server/deps/js/src/editline/README b/ape-server/deps/js/src/editline/README new file mode 100755 index 0000000..53ec359 --- /dev/null +++ b/ape-server/deps/js/src/editline/README @@ -0,0 +1,83 @@ +The files in this directory provide simple line-editing and history +support for the standalone javascript engine, through the 'editline' +library. + +editline has only been enabled for those platforms on which it is +known to build; to try it on a different platform, define JS_EDITLINE +before building. Line editing (and js.c) is not a supported feature +of the javascript library, so your mileage my vary. + +The editline API is a compatible subset of the FSF readline API; if +you have readline installed and would like to link to that instead, +define JS_READLINE. Note that the readline library is distributed +under the GPL, so any resulting binaries are not legally +distributable. + +The editline files used here have been modified to work with the js +build system and to quiet some compiler warnings, and also to remove +filename-completion support. + +If anyone knows of a more recent version of these files, or a site on +which they are being maintained, please let me know! + +Mike McCabe, mccabe@netscape.com + + +The original README file distributed with the editline library follows. + + + +This is a line-editing library. It can be linked into almost any +program to provide command-line editing and recall. + +It is call-compatible with the FSF readline library, but it is a +fraction of the size (and offers fewer features). It does not use +standard I/O. It is distributed under a "C News-like" copyright. + +Configuration is done in the Makefile. Type "make testit" to get +a small slow shell for testing. + +An earlier version was distributed with Byron's rc. Principal +changes over that version include: + Faster. + Is eight-bit clean (thanks to brendan@cs.widener.edu) + Written in K&R C, but ANSI compliant (gcc all warnings) + Propagates EOF properly; rc trip test now passes + Doesn't need or use or provide memmove. + More robust + Calling sequence changed to be compatible with readline. + Test program, new manpage, better configuration + More system-independant; includes Unix and OS-9 support. + +This contains some changes since the posting to comp.sources.misc: + Bugfix for completion on absolute pathnames. + Better handling of M-n versus showing raw 8bit chars. + Better signal handling. + Now supports termios/termio/sgttyb ioctl's. + Add M-m command to toggle how 8bit data is displayed. + +There is one known bug: + History-searching redraws the line wrong if the text + retrieved is shorter then the prompt. + +Enjoy, + Rich $alz + + + Copyright 1992,1993 Simmule Turner and Rich Salz. All rights reserved. + + This software is not subject to any license of the American Telephone + and Telegraph Company or of the Regents of the University of California. + + Permission is granted to anyone to use this software for any purpose on + any computer system, and to alter it and redistribute it freely, subject + to the following restrictions: + 1. The authors are not responsible for the consequences of use of this + software, no matter how awful, even if they arise from flaws in it. + 2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. Since few users ever read sources, + credits must appear in the documentation. + 3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. Since few users + ever read sources, credits must appear in the documentation. + 4. This notice may not be removed or altered. diff --git a/ape-server/deps/js/src/editline/editline.3 b/ape-server/deps/js/src/editline/editline.3 new file mode 100755 index 0000000..21a72ca --- /dev/null +++ b/ape-server/deps/js/src/editline/editline.3 @@ -0,0 +1,175 @@ +.TH EDITLINE 3 +.SH NAME +editline \- command-line editing library with history +.SH SYNOPSIS +.nf +.B "char *" +.B "readline(prompt)" +.B " char *prompt;" + +.B "void" +.B "add_history(line)" +.B " char *line;" +.fi +.SH DESCRIPTION +.I Editline +is a library that provides an line-editing interface with text recall. +It is intended to be compatible with the +.I readline +library provided by the Free Software Foundation, but much smaller. +The bulk of this manual page describes the user interface. +.PP +The +.I readline +routine returns a line of text with the trailing newline removed. +The data is returned in a buffer allocated with +.IR malloc (3), +so the space should be released with +.IR free (3) +when the calling program is done with it. +Before accepting input from the user, the specified +.I prompt +is displayed on the terminal. +.PP +The +.I add_history +routine makes a copy of the specified +.I line +and adds it to the internal history list. +.SS "User Interface" +A program that uses this library provides a simple emacs-like editing +interface to its users. +A line may be edited before it is sent to the calling program by typing either +control characters or escape sequences. +A control character, shown as a caret followed by a letter, is typed by +holding down the ``control'' key while the letter is typed. +For example, ``^A'' is a control-A. +An escape sequence is entered by typing the ``escape'' key followed by one or +more characters. +The escape key is abbreviated as ``ESC.'' +Note that unlike control keys, case matters in escape sequences; ``ESC\ F'' +is not the same as ``ESC\ f''. +.PP +An editing command may be typed anywhere on the line, not just at the +beginning. +In addition, a return may also be typed anywhere on the line, not just at +the end. +.PP +Most editing commands may be given a repeat count, +.IR n , +where +.I n +is a number. +To enter a repeat count, type the escape key, the number, and then +the command to execute. +For example, ``ESC\ 4\ ^f'' moves forward four characters. +If a command may be given a repeat count then the text ``[n]'' is given at the +end of its description. +.PP +The following control characters are accepted: +.RS +.nf +.ta \w'ESC DEL 'u +^A Move to the beginning of the line +^B Move left (backwards) [n] +^D Delete character [n] +^E Move to end of line +^F Move right (forwards) [n] +^G Ring the bell +^H Delete character before cursor (backspace key) [n] +^I Complete filename (tab key); see below +^J Done with line (return key) +^K Kill to end of line (or column [n]) +^L Redisplay line +^M Done with line (alternate return key) +^N Get next line from history [n] +^P Get previous line from history [n] +^R Search backward (forward if [n]) through history for text; +\& must start line if text begins with an uparrow +^T Transpose characters +^V Insert next character, even if it is an edit command +^W Wipe to the mark +^X^X Exchange current location and mark +^Y Yank back last killed text +^[ Start an escape sequence (escape key) +^]c Move forward to next character ``c'' +^? Delete character before cursor (delete key) [n] +.fi +.RE +.PP +The following escape sequences are provided. +.RS +.nf +.ta \w'ESC DEL 'u +ESC\ ^H Delete previous word (backspace key) [n] +ESC\ DEL Delete previous word (delete key) [n] +ESC\ SP Set the mark (space key); see ^X^X and ^Y above +ESC\ \. Get the last (or [n]'th) word from previous line +ESC\ \? Show possible completions; see below +ESC\ < Move to start of history +ESC\ > Move to end of history +ESC\ b Move backward a word [n] +ESC\ d Delete word under cursor [n] +ESC\ f Move forward a word [n] +ESC\ l Make word lowercase [n] +ESC\ m Toggle if 8bit chars display normally or with ``M\-'' prefix +ESC\ u Make word uppercase [n] +ESC\ y Yank back last killed text +ESC\ v Show library version +ESC\ w Make area up to mark yankable +ESC\ nn Set repeat count to the number nn +ESC\ C Read from environment variable ``_C_'', where C is +\& an uppercase letter +.fi +.RE +.PP +The +.I editline +library has a small macro facility. +If you type the escape key followed by an uppercase letter, +.IR C , +then the contents of the environment variable +.I _C_ +are read in as if you had typed them at the keyboard. +For example, if the variable +.I _L_ +contains the following: +.RS +^A^Kecho '^V^[[H^V^[[2J'^M +.RE +Then typing ``ESC L'' will move to the beginning of the line, kill the +entire line, enter the echo command needed to clear the terminal (if your +terminal is like a VT-100), and send the line back to the shell. +.PP +The +.I editline +library also does filename completion. +Suppose the root directory has the following files in it: +.RS +.nf +.ta \w'core 'u +bin vmunix +core vmunix.old +.fi +.RE +If you type ``rm\ /v'' and then the tab key. +.I Editline +will then finish off as much of the name as possible by adding ``munix''. +Because the name is not unique, it will then beep. +If you type the escape key and a question mark, it will display the +two choices. +If you then type a period and a tab, the library will finish off the filename +for you: +.RS +.nf +.RI "rm /v[TAB]" munix .TAB old +.fi +.RE +The tab key is shown by ``[TAB]'' and the automatically-entered text +is shown in italics. +.SH "BUGS AND LIMITATIONS" +Cannot handle lines more than 80 columns. +.SH AUTHORS +Simmule R. Turner +and Rich $alz . +Original manual page by DaviD W. Sanderson . diff --git a/ape-server/deps/js/src/editline/editline.c b/ape-server/deps/js/src/editline/editline.c new file mode 100755 index 0000000..6feeaef --- /dev/null +++ b/ape-server/deps/js/src/editline/editline.c @@ -0,0 +1,1371 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Simmule Turner and Rich Salz. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Copyright 1992,1993 Simmule Turner and Rich Salz. All rights reserved. + * + * This software is not subject to any license of the American Telephone + * and Telegraph Company or of the Regents of the University of California. + * + * Permission is granted to anyone to use this software for any purpose on + * any computer system, and to alter it and redistribute it freely, subject + * to the following restrictions: + * 1. The authors are not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits must appear in the documentation. + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits must appear in the documentation. + * 4. This notice may not be removed or altered. + */ + + +/* +** Main editing routines for editline library. +*/ +#include "editline.h" +#include +#include +#include + +/* +** Manifest constants. +*/ +#define SCREEN_WIDTH 80 +#define SCREEN_ROWS 24 +#define NO_ARG (-1) +#define DEL 127 +#define CTL(x) ((x) & 0x1F) +#define ISCTL(x) ((x) && (x) < ' ') +#define UNCTL(x) ((x) + 64) +#define META(x) ((x) | 0x80) +#define ISMETA(x) ((x) & 0x80) +#define UNMETA(x) ((x) & 0x7F) +#if !defined(HIST_SIZE) +#define HIST_SIZE 20 +#endif /* !defined(HIST_SIZE) */ + +/* +** Command status codes. +*/ +typedef enum _STATUS { + CSdone, CSeof, CSmove, CSdispatch, CSstay, CSsignal +} STATUS; + +/* +** The type of case-changing to perform. +*/ +typedef enum _CASE { + TOupper, TOlower +} CASE; + +/* +** Key to command mapping. +*/ +typedef struct _KEYMAP { + CHAR Key; + STATUS (*Function)(); +} KEYMAP; + +/* +** Command history structure. +*/ +typedef struct _HISTORY { + int Size; + int Pos; + CHAR *Lines[HIST_SIZE]; +} HISTORY; + +/* +** Globals. +*/ +int rl_eof; +int rl_erase; +int rl_intr; +int rl_kill; +int rl_quit; + +STATIC CHAR NIL[] = ""; +STATIC CONST CHAR *Input = NIL; +STATIC CHAR *Line; +STATIC CONST char *Prompt; +STATIC CHAR *Yanked; +STATIC char *Screen; +STATIC char NEWLINE[]= CRLF; +STATIC HISTORY H; +STATIC int Repeat; +STATIC int End; +STATIC int Mark; +STATIC int OldPoint; +STATIC int Point; +STATIC int PushBack; +STATIC int Pushed; +STATIC int Signal; +FORWARD KEYMAP Map[32]; +FORWARD KEYMAP MetaMap[16]; +STATIC SIZE_T Length; +STATIC SIZE_T ScreenCount; +STATIC SIZE_T ScreenSize; +STATIC char *backspace; +STATIC int TTYwidth; +STATIC int TTYrows; + +/* Display print 8-bit chars as `M-x' or as the actual 8-bit char? */ +int rl_meta_chars = 0; + +/* +** Declarations. +*/ +STATIC CHAR *editinput(); +#if defined(USE_TERMCAP) +#include +#include +#include +#endif /* defined(USE_TERMCAP) */ + +/* +** TTY input/output functions. +*/ + +STATIC void +TTYflush() +{ + if (ScreenCount) { + (void)write(1, Screen, ScreenCount); + ScreenCount = 0; + } +} + +STATIC void +TTYput(c) + CHAR c; +{ + Screen[ScreenCount] = c; + if (++ScreenCount >= ScreenSize - 1) { + ScreenSize += SCREEN_INC; + RENEW(Screen, char, ScreenSize); + } +} + +STATIC void +TTYputs(p) + CHAR *p; +{ + while (*p) + TTYput(*p++); +} + +STATIC void +TTYshow(c) + CHAR c; +{ + if (c == DEL) { + TTYput('^'); + TTYput('?'); + } + else if (ISCTL(c)) { + TTYput('^'); + TTYput(UNCTL(c)); + } + else if (rl_meta_chars && ISMETA(c)) { + TTYput('M'); + TTYput('-'); + TTYput(UNMETA(c)); + } + else + TTYput(c); +} + +STATIC void +TTYstring(p) + CHAR *p; +{ + while (*p) + TTYshow(*p++); +} + +STATIC unsigned int +TTYget() +{ + CHAR c; + + TTYflush(); + if (Pushed) { + Pushed = 0; + return PushBack; + } + if (*Input) + return *Input++; + return read(0, &c, (SIZE_T)1) == 1 ? c : EOF; +} + +#define TTYback() (backspace ? TTYputs((CHAR *)backspace) : TTYput('\b')) + +STATIC void +TTYbackn(n) + int n; +{ + while (--n >= 0) + TTYback(); +} + +STATIC void +TTYinfo() +{ + static int init; +#if defined(USE_TERMCAP) + char *term; + char buff[2048]; + char *bp, *p; +#endif /* defined(USE_TERMCAP) */ +#if defined(TIOCGWINSZ) + struct winsize W; +#endif /* defined(TIOCGWINSZ) */ + + if (init) { +#if defined(TIOCGWINSZ) + /* Perhaps we got resized. */ + if (ioctl(0, TIOCGWINSZ, &W) >= 0 + && W.ws_col > 0 && W.ws_row > 0) { + TTYwidth = (int)W.ws_col; + TTYrows = (int)W.ws_row; + } +#endif /* defined(TIOCGWINSZ) */ + return; + } + init++; + + TTYwidth = TTYrows = 0; +#if defined(USE_TERMCAP) + bp = &buff[0]; + if ((term = getenv("TERM")) == NULL) + term = "dumb"; + if (tgetent(buff, term) < 0) { + TTYwidth = SCREEN_WIDTH; + TTYrows = SCREEN_ROWS; + return; + } + p = tgetstr("le", &bp); + backspace = p ? strdup(p) : NULL; + TTYwidth = tgetnum("co"); + TTYrows = tgetnum("li"); +#endif /* defined(USE_TERMCAP) */ + +#if defined(TIOCGWINSZ) + if (ioctl(0, TIOCGWINSZ, &W) >= 0) { + TTYwidth = (int)W.ws_col; + TTYrows = (int)W.ws_row; + } +#endif /* defined(TIOCGWINSZ) */ + + if (TTYwidth <= 0 || TTYrows <= 0) { + TTYwidth = SCREEN_WIDTH; + TTYrows = SCREEN_ROWS; + } +} + + +STATIC void +reposition() +{ + int i; + CHAR *p; + + TTYput('\r'); + TTYputs((CONST CHAR *)Prompt); + for (i = Point, p = Line; --i >= 0; p++) + TTYshow(*p); +} + +STATIC void +left(Change) + STATUS Change; +{ + TTYback(); + if (Point) { + if (ISCTL(Line[Point - 1])) + TTYback(); + else if (rl_meta_chars && ISMETA(Line[Point - 1])) { + TTYback(); + TTYback(); + } + } + if (Change == CSmove) + Point--; +} + +STATIC void +right(Change) + STATUS Change; +{ + TTYshow(Line[Point]); + if (Change == CSmove) + Point++; +} + +STATIC STATUS +ring_bell() +{ + TTYput('\07'); + TTYflush(); + return CSstay; +} + +STATIC STATUS +do_macro(c) + unsigned int c; +{ + CHAR name[4]; + + name[0] = '_'; + name[1] = c; + name[2] = '_'; + name[3] = '\0'; + + if ((Input = (CHAR *)getenv((char *)name)) == NULL) { + Input = NIL; + return ring_bell(); + } + return CSstay; +} + +STATIC STATUS +do_forward(move) + STATUS move; +{ + int i; + CHAR *p; + + i = 0; + do { + p = &Line[Point]; + for ( ; Point < End && (*p == ' ' || !isalnum(*p)); Point++, p++) + if (move == CSmove) + right(CSstay); + + for (; Point < End && isalnum(*p); Point++, p++) + if (move == CSmove) + right(CSstay); + + if (Point == End) + break; + } while (++i < Repeat); + + return CSstay; +} + +STATIC STATUS +do_case(type) + CASE type; +{ + int i; + int end; + int count; + CHAR *p; + + (void)do_forward(CSstay); + if (OldPoint != Point) { + if ((count = Point - OldPoint) < 0) + count = -count; + Point = OldPoint; + if ((end = Point + count) > End) + end = End; + for (i = Point, p = &Line[i]; i < end; i++, p++) { + if (type == TOupper) { + if (islower(*p)) + *p = toupper(*p); + } + else if (isupper(*p)) + *p = tolower(*p); + right(CSmove); + } + } + return CSstay; +} + +STATIC STATUS +case_down_word() +{ + return do_case(TOlower); +} + +STATIC STATUS +case_up_word() +{ + return do_case(TOupper); +} + +STATIC void +ceol() +{ + int extras; + int i; + CHAR *p; + + for (extras = 0, i = Point, p = &Line[i]; i <= End; i++, p++) { + TTYput(' '); + if (ISCTL(*p)) { + TTYput(' '); + extras++; + } + else if (rl_meta_chars && ISMETA(*p)) { + TTYput(' '); + TTYput(' '); + extras += 2; + } + } + + for (i += extras; i > Point; i--) + TTYback(); +} + +STATIC void +clear_line() +{ + Point = -strlen(Prompt); + TTYput('\r'); + ceol(); + Point = 0; + End = 0; + Line[0] = '\0'; +} + +STATIC STATUS +insert_string(p) + CHAR *p; +{ + SIZE_T len; + int i; + CHAR *new; + CHAR *q; + + len = strlen((char *)p); + if (End + len >= Length) { + if ((new = NEW(CHAR, Length + len + MEM_INC)) == NULL) + return CSstay; + if (Length) { + COPYFROMTO(new, Line, Length); + DISPOSE(Line); + } + Line = new; + Length += len + MEM_INC; + } + + for (q = &Line[Point], i = End - Point; --i >= 0; ) + q[len + i] = q[i]; + COPYFROMTO(&Line[Point], p, len); + End += len; + Line[End] = '\0'; + TTYstring(&Line[Point]); + Point += len; + + return Point == End ? CSstay : CSmove; +} + +STATIC STATUS +redisplay() +{ + TTYputs((CONST CHAR *)NEWLINE); + TTYputs((CONST CHAR *)Prompt); + TTYstring(Line); + return CSmove; +} + +STATIC STATUS +toggle_meta_mode() +{ + rl_meta_chars = ! rl_meta_chars; + return redisplay(); +} + + +STATIC CHAR * +next_hist() +{ + return H.Pos >= H.Size - 1 ? NULL : H.Lines[++H.Pos]; +} + +STATIC CHAR * +prev_hist() +{ + return H.Pos == 0 ? NULL : H.Lines[--H.Pos]; +} + +STATIC STATUS +do_insert_hist(p) + CHAR *p; +{ + if (p == NULL) + return ring_bell(); + Point = 0; + reposition(); + ceol(); + End = 0; + return insert_string(p); +} + +STATIC STATUS +do_hist(move) + CHAR *(*move)(); +{ + CHAR *p; + int i; + + i = 0; + do { + if ((p = (*move)()) == NULL) + return ring_bell(); + } while (++i < Repeat); + return do_insert_hist(p); +} + +STATIC STATUS +h_next() +{ + return do_hist(next_hist); +} + +STATIC STATUS +h_prev() +{ + return do_hist(prev_hist); +} + +STATIC STATUS +h_first() +{ + return do_insert_hist(H.Lines[H.Pos = 0]); +} + +STATIC STATUS +h_last() +{ + return do_insert_hist(H.Lines[H.Pos = H.Size - 1]); +} + +/* +** Return zero if pat appears as a substring in text. +*/ +STATIC int +substrcmp(text, pat, len) + char *text; + char *pat; + int len; +{ + char c; + + if ((c = *pat) == '\0') + return *text == '\0'; + for ( ; *text; text++) + if (*text == c && strncmp(text, pat, len) == 0) + return 0; + return 1; +} + +STATIC CHAR * +search_hist(search, move) + CHAR *search; + CHAR *(*move)(); +{ + static CHAR *old_search; + int len; + int pos; + int (*match)(); + char *pat; + + /* Save or get remembered search pattern. */ + if (search && *search) { + if (old_search) + DISPOSE(old_search); + old_search = (CHAR *)strdup((char *)search); + } + else { + if (old_search == NULL || *old_search == '\0') + return NULL; + search = old_search; + } + + /* Set up pattern-finder. */ + if (*search == '^') { + match = strncmp; + pat = (char *)(search + 1); + } + else { + match = substrcmp; + pat = (char *)search; + } + len = strlen(pat); + + for (pos = H.Pos; (*move)() != NULL; ) + if ((*match)((char *)H.Lines[H.Pos], pat, len) == 0) + return H.Lines[H.Pos]; + H.Pos = pos; + return NULL; +} + +STATIC STATUS +h_search() +{ + static int Searching; + CONST char *old_prompt; + CHAR *(*move)(); + CHAR *p; + + if (Searching) + return ring_bell(); + Searching = 1; + + clear_line(); + old_prompt = Prompt; + Prompt = "Search: "; + TTYputs((CONST CHAR *)Prompt); + move = Repeat == NO_ARG ? prev_hist : next_hist; + p = editinput(); + Prompt = old_prompt; + Searching = 0; + TTYputs((CONST CHAR *)Prompt); + if (p == NULL && Signal > 0) { + Signal = 0; + clear_line(); + return redisplay(); + } + p = search_hist(p, move); + clear_line(); + if (p == NULL) { + (void)ring_bell(); + return redisplay(); + } + return do_insert_hist(p); +} + +STATIC STATUS +fd_char() +{ + int i; + + i = 0; + do { + if (Point >= End) + break; + right(CSmove); + } while (++i < Repeat); + return CSstay; +} + +STATIC void +save_yank(begin, i) + int begin; + int i; +{ + if (Yanked) { + DISPOSE(Yanked); + Yanked = NULL; + } + + if (i < 1) + return; + + if ((Yanked = NEW(CHAR, (SIZE_T)i + 1)) != NULL) { + COPYFROMTO(Yanked, &Line[begin], i); + Yanked[i] = '\0'; + } +} + +STATIC STATUS +delete_string(count) + int count; +{ + int i; + CHAR *p; + + if (count <= 0 || End == Point) + return ring_bell(); + + if (count == 1 && Point == End - 1) { + /* Optimize common case of delete at end of line. */ + End--; + p = &Line[Point]; + i = 1; + TTYput(' '); + if (ISCTL(*p)) { + i = 2; + TTYput(' '); + } + else if (rl_meta_chars && ISMETA(*p)) { + i = 3; + TTYput(' '); + TTYput(' '); + } + TTYbackn(i); + *p = '\0'; + return CSmove; + } + if (Point + count > End && (count = End - Point) <= 0) + return CSstay; + + if (count > 1) + save_yank(Point, count); + + for (p = &Line[Point], i = End - (Point + count) + 1; --i >= 0; p++) + p[0] = p[count]; + ceol(); + End -= count; + TTYstring(&Line[Point]); + return CSmove; +} + +STATIC STATUS +bk_char() +{ + int i; + + i = 0; + do { + if (Point == 0) + break; + left(CSmove); + } while (++i < Repeat); + + return CSstay; +} + +STATIC STATUS +bk_del_char() +{ + int i; + + i = 0; + do { + if (Point == 0) + break; + left(CSmove); + } while (++i < Repeat); + + return delete_string(i); +} + +STATIC STATUS +kill_line() +{ + int i; + + if (Repeat != NO_ARG) { + if (Repeat < Point) { + i = Point; + Point = Repeat; + reposition(); + (void)delete_string(i - Point); + } + else if (Repeat > Point) { + right(CSmove); + (void)delete_string(Repeat - Point - 1); + } + return CSmove; + } + + save_yank(Point, End - Point); + Line[Point] = '\0'; + ceol(); + End = Point; + return CSstay; +} + +STATIC STATUS +insert_char(c) + int c; +{ + STATUS s; + CHAR buff[2]; + CHAR *p; + CHAR *q; + int i; + + if (Repeat == NO_ARG || Repeat < 2) { + buff[0] = c; + buff[1] = '\0'; + return insert_string(buff); + } + + if ((p = NEW(CHAR, Repeat + 1)) == NULL) + return CSstay; + for (i = Repeat, q = p; --i >= 0; ) + *q++ = c; + *q = '\0'; + Repeat = 0; + s = insert_string(p); + DISPOSE(p); + return s; +} + +STATIC STATUS +meta() +{ + unsigned int c; + KEYMAP *kp; + + if ((c = TTYget()) == EOF) + return CSeof; +#if defined(ANSI_ARROWS) + /* Also include VT-100 arrows. */ + if (c == '[' || c == 'O') + switch (c = TTYget()) { + default: return ring_bell(); + case EOF: return CSeof; + case 'A': return h_prev(); + case 'B': return h_next(); + case 'C': return fd_char(); + case 'D': return bk_char(); + } +#endif /* defined(ANSI_ARROWS) */ + + if (isdigit(c)) { + for (Repeat = c - '0'; (c = TTYget()) != EOF && isdigit(c); ) + Repeat = Repeat * 10 + c - '0'; + Pushed = 1; + PushBack = c; + return CSstay; + } + + if (isupper(c)) + return do_macro(c); + for (OldPoint = Point, kp = MetaMap; kp->Function; kp++) + if (kp->Key == c) + return (*kp->Function)(); + + return ring_bell(); +} + +STATIC STATUS +emacs(c) + unsigned int c; +{ + STATUS s; + KEYMAP *kp; + + if (rl_meta_chars && ISMETA(c)) { + Pushed = 1; + PushBack = UNMETA(c); + return meta(); + } + for (kp = Map; kp->Function; kp++) + if (kp->Key == c) + break; + s = kp->Function ? (*kp->Function)() : insert_char((int)c); + if (!Pushed) + /* No pushback means no repeat count; hacky, but true. */ + Repeat = NO_ARG; + return s; +} + +STATIC STATUS +TTYspecial(c) + unsigned int c; +{ + if (ISMETA(c)) + return CSdispatch; + + if (c == rl_erase || c == DEL) + return bk_del_char(); + if (c == rl_kill) { + if (Point != 0) { + Point = 0; + reposition(); + } + Repeat = NO_ARG; + return kill_line(); + } + if (c == rl_eof && Point == 0 && End == 0) + return CSeof; + if (c == rl_intr) { + Signal = SIGINT; + return CSsignal; + } + if (c == rl_quit) { + Signal = SIGQUIT; + return CSeof; + } + + return CSdispatch; +} + +STATIC CHAR * +editinput() +{ + unsigned int c; + + Repeat = NO_ARG; + OldPoint = Point = Mark = End = 0; + Line[0] = '\0'; + + Signal = -1; + while ((c = TTYget()) != EOF) + switch (TTYspecial(c)) { + case CSdone: + return Line; + case CSeof: + return NULL; + case CSsignal: + return (CHAR *)""; + case CSmove: + reposition(); + break; + case CSdispatch: + switch (emacs(c)) { + case CSdone: + return Line; + case CSeof: + return NULL; + case CSsignal: + return (CHAR *)""; + case CSmove: + reposition(); + break; + case CSdispatch: + case CSstay: + break; + } + break; + case CSstay: + break; + } + if (strlen(Line)) + return Line; + free(Line); + return NULL; +} + +STATIC void +hist_add(p) + CHAR *p; +{ + int i; + + if ((p = (CHAR *)strdup((char *)p)) == NULL) + return; + if (H.Size < HIST_SIZE) + H.Lines[H.Size++] = p; + else { + DISPOSE(H.Lines[0]); + for (i = 0; i < HIST_SIZE - 1; i++) + H.Lines[i] = H.Lines[i + 1]; + H.Lines[i] = p; + } + H.Pos = H.Size - 1; +} + +/* +** For compatibility with FSF readline. +*/ +/* ARGSUSED0 */ +void +rl_reset_terminal(p) + char *p; +{ +} + +void +rl_initialize() +{ +} + +char * +readline(prompt) + CONST char *prompt; +{ + CHAR *line; + int s; + + if (Line == NULL) { + Length = MEM_INC; + if ((Line = NEW(CHAR, Length)) == NULL) + return NULL; + } + + TTYinfo(); + rl_ttyset(0); + hist_add(NIL); + ScreenSize = SCREEN_INC; + Screen = NEW(char, ScreenSize); + Prompt = prompt ? prompt : (char *)NIL; + TTYputs((CONST CHAR *)Prompt); + if ((line = editinput()) != NULL) { + line = (CHAR *)strdup((char *)line); + TTYputs((CHAR *)NEWLINE); + TTYflush(); + } + rl_ttyset(1); + DISPOSE(Screen); + DISPOSE(H.Lines[--H.Size]); + if (Signal > 0) { + s = Signal; + Signal = 0; + (void)kill(getpid(), s); + } + return (char *)line; +} + +void +add_history(p) + char *p; +{ + if (p == NULL || *p == '\0') + return; + +#if defined(UNIQUE_HISTORY) + if (H.Size && strcmp(p, H.Lines[H.Size - 1]) == 0) + return; +#endif /* defined(UNIQUE_HISTORY) */ + hist_add((CHAR *)p); +} + + +STATIC STATUS +beg_line() +{ + if (Point) { + Point = 0; + return CSmove; + } + return CSstay; +} + +STATIC STATUS +del_char() +{ + return delete_string(Repeat == NO_ARG ? 1 : Repeat); +} + +STATIC STATUS +end_line() +{ + if (Point != End) { + Point = End; + return CSmove; + } + return CSstay; +} + +STATIC STATUS +accept_line() +{ + Line[End] = '\0'; + return CSdone; +} + +STATIC STATUS +transpose() +{ + CHAR c; + + if (Point) { + if (Point == End) + left(CSmove); + c = Line[Point - 1]; + left(CSstay); + Line[Point - 1] = Line[Point]; + TTYshow(Line[Point - 1]); + Line[Point++] = c; + TTYshow(c); + } + return CSstay; +} + +STATIC STATUS +quote() +{ + unsigned int c; + + return (c = TTYget()) == EOF ? CSeof : insert_char((int)c); +} + +STATIC STATUS +wipe() +{ + int i; + + if (Mark > End) + return ring_bell(); + + if (Point > Mark) { + i = Point; + Point = Mark; + Mark = i; + reposition(); + } + + return delete_string(Mark - Point); +} + +STATIC STATUS +mk_set() +{ + Mark = Point; + return CSstay; +} + +STATIC STATUS +exchange() +{ + unsigned int c; + + if ((c = TTYget()) != CTL('X')) + return c == EOF ? CSeof : ring_bell(); + + if ((c = Mark) <= End) { + Mark = Point; + Point = c; + return CSmove; + } + return CSstay; +} + +STATIC STATUS +yank() +{ + if (Yanked && *Yanked) + return insert_string(Yanked); + return CSstay; +} + +STATIC STATUS +copy_region() +{ + if (Mark > End) + return ring_bell(); + + if (Point > Mark) + save_yank(Mark, Point - Mark); + else + save_yank(Point, Mark - Point); + + return CSstay; +} + +STATIC STATUS +move_to_char() +{ + unsigned int c; + int i; + CHAR *p; + + if ((c = TTYget()) == EOF) + return CSeof; + for (i = Point + 1, p = &Line[i]; i < End; i++, p++) + if (*p == c) { + Point = i; + return CSmove; + } + return CSstay; +} + +STATIC STATUS +fd_word() +{ + return do_forward(CSmove); +} + +STATIC STATUS +fd_kill_word() +{ + int i; + + (void)do_forward(CSstay); + if (OldPoint != Point) { + i = Point - OldPoint; + Point = OldPoint; + return delete_string(i); + } + return CSstay; +} + +STATIC STATUS +bk_word() +{ + int i; + CHAR *p; + + i = 0; + do { + for (p = &Line[Point]; p > Line && !isalnum(p[-1]); p--) + left(CSmove); + + for (; p > Line && p[-1] != ' ' && isalnum(p[-1]); p--) + left(CSmove); + + if (Point == 0) + break; + } while (++i < Repeat); + + return CSstay; +} + +STATIC STATUS +bk_kill_word() +{ + (void)bk_word(); + if (OldPoint != Point) + return delete_string(OldPoint - Point); + return CSstay; +} + +STATIC int +argify(line, avp) + CHAR *line; + CHAR ***avp; +{ + CHAR *c; + CHAR **p; + CHAR **new; + int ac; + int i; + + i = MEM_INC; + if ((*avp = p = NEW(CHAR*, i))== NULL) + return 0; + + for (c = line; isspace(*c); c++) + continue; + if (*c == '\n' || *c == '\0') + return 0; + + for (ac = 0, p[ac++] = c; *c && *c != '\n'; ) { + if (isspace(*c)) { + *c++ = '\0'; + if (*c && *c != '\n') { + if (ac + 1 == i) { + new = NEW(CHAR*, i + MEM_INC); + if (new == NULL) { + p[ac] = NULL; + return ac; + } + COPYFROMTO(new, p, i * sizeof (char **)); + i += MEM_INC; + DISPOSE(p); + *avp = p = new; + } + p[ac++] = c; + } + } + else + c++; + } + *c = '\0'; + p[ac] = NULL; + return ac; +} + +STATIC STATUS +last_argument() +{ + CHAR **av; + CHAR *p; + STATUS s; + int ac; + + if (H.Size == 1 || (p = H.Lines[H.Size - 2]) == NULL) + return ring_bell(); + + if ((p = (CHAR *)strdup((char *)p)) == NULL) + return CSstay; + ac = argify(p, &av); + + if (Repeat != NO_ARG) + s = Repeat < ac ? insert_string(av[Repeat]) : ring_bell(); + else + s = ac ? insert_string(av[ac - 1]) : CSstay; + + if (ac) + DISPOSE(av); + DISPOSE(p); + return s; +} + +STATIC KEYMAP Map[32] = { + { CTL('@'), ring_bell }, + { CTL('A'), beg_line }, + { CTL('B'), bk_char }, + { CTL('D'), del_char }, + { CTL('E'), end_line }, + { CTL('F'), fd_char }, + { CTL('G'), ring_bell }, + { CTL('H'), bk_del_char }, + { CTL('J'), accept_line }, + { CTL('K'), kill_line }, + { CTL('L'), redisplay }, + { CTL('M'), accept_line }, + { CTL('N'), h_next }, + { CTL('O'), ring_bell }, + { CTL('P'), h_prev }, + { CTL('Q'), ring_bell }, + { CTL('R'), h_search }, + { CTL('S'), ring_bell }, + { CTL('T'), transpose }, + { CTL('U'), ring_bell }, + { CTL('V'), quote }, + { CTL('W'), wipe }, + { CTL('X'), exchange }, + { CTL('Y'), yank }, + { CTL('Z'), ring_bell }, + { CTL('['), meta }, + { CTL(']'), move_to_char }, + { CTL('^'), ring_bell }, + { CTL('_'), ring_bell }, + { 0, NULL } +}; + +STATIC KEYMAP MetaMap[16]= { + { CTL('H'), bk_kill_word }, + { DEL, bk_kill_word }, + { ' ', mk_set }, + { '.', last_argument }, + { '<', h_first }, + { '>', h_last }, + { 'b', bk_word }, + { 'd', fd_kill_word }, + { 'f', fd_word }, + { 'l', case_down_word }, + { 'm', toggle_meta_mode }, + { 'u', case_up_word }, + { 'y', yank }, + { 'w', copy_region }, + { 0, NULL } +}; + diff --git a/ape-server/deps/js/src/editline/editline.h b/ape-server/deps/js/src/editline/editline.h new file mode 100755 index 0000000..e820049 --- /dev/null +++ b/ape-server/deps/js/src/editline/editline.h @@ -0,0 +1,135 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Simmule Turner and Rich Salz. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Copyright 1992,1993 Simmule Turner and Rich Salz. All rights reserved. + * + * This software is not subject to any license of the American Telephone + * and Telegraph Company or of the Regents of the University of California. + * + * Permission is granted to anyone to use this software for any purpose on + * any computer system, and to alter it and redistribute it freely, subject + * to the following restrictions: + * 1. The authors are not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits must appear in the documentation. + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits must appear in the documentation. + * 4. This notice may not be removed or altered. + */ + +/* +** Internal header file for editline library. +*/ +#include +#if defined(HAVE_STDLIB) +#include +#include +#endif /* defined(HAVE_STDLIB) */ +#if defined(SYS_UNIX) +#include "unix.h" +#endif /* defined(SYS_UNIX) */ +#if defined(SYS_OS9) +#include "os9.h" +#endif /* defined(SYS_OS9) */ + +#if !defined(SIZE_T) +#define SIZE_T unsigned int +#endif /* !defined(SIZE_T) */ + +typedef unsigned char CHAR; + +#if defined(HIDE) +#define STATIC static +#else +#define STATIC /* NULL */ +#endif /* !defined(HIDE) */ + +#if !defined(CONST) +#if defined(__STDC__) +#define CONST const +#else +#define CONST +#endif /* defined(__STDC__) */ +#endif /* !defined(CONST) */ + + +#define MEM_INC 64 +#define SCREEN_INC 256 + +#define DISPOSE(p) free((char *)(p)) +#define NEW(T, c) \ + ((T *)malloc((unsigned int)(sizeof (T) * (c)))) +#define RENEW(p, T, c) \ + (p = (T *)realloc((char *)(p), (unsigned int)(sizeof (T) * (c)))) +#define COPYFROMTO(new, p, len) \ + (void)memcpy((char *)(new), (char *)(p), (int)(len)) + + +/* +** Variables and routines internal to this package. +*/ +extern int rl_eof; +extern int rl_erase; +extern int rl_intr; +extern int rl_kill; +extern int rl_quit; +extern char *rl_complete(); +extern int rl_list_possib(); +extern void rl_ttyset(); +extern void rl_add_slash(); + +#if !defined(HAVE_STDLIB) +extern char *getenv(); +extern char *malloc(); +extern char *realloc(); +extern char *memcpy(); +extern char *strcat(); +extern char *strchr(); +extern char *strrchr(); +extern char *strcpy(); +extern char *strdup(); +extern int strcmp(); +extern int strlen(); +extern int strncmp(); +#endif /* !defined(HAVE_STDLIB) */ + diff --git a/ape-server/deps/js/src/editline/sysunix.c b/ape-server/deps/js/src/editline/sysunix.c new file mode 100755 index 0000000..17227ea --- /dev/null +++ b/ape-server/deps/js/src/editline/sysunix.c @@ -0,0 +1,182 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Simmule Turner and Rich Salz. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Copyright 1992,1993 Simmule Turner and Rich Salz. All rights reserved. + * + * This software is not subject to any license of the American Telephone + * and Telegraph Company or of the Regents of the University of California. + * + * Permission is granted to anyone to use this software for any purpose on + * any computer system, and to alter it and redistribute it freely, subject + * to the following restrictions: + * 1. The authors are not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits must appear in the documentation. + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits must appear in the documentation. + * 4. This notice may not be removed or altered. + */ + + +/* +** Unix system-dependant routines for editline library. +*/ +#include "editline.h" + +#if defined(HAVE_TCGETATTR) +#include + +void +rl_ttyset(Reset) + int Reset; +{ + static struct termios old; + struct termios new; + + if (Reset == 0) { + (void)tcgetattr(0, &old); + rl_erase = old.c_cc[VERASE]; + rl_kill = old.c_cc[VKILL]; + rl_eof = old.c_cc[VEOF]; + rl_intr = old.c_cc[VINTR]; + rl_quit = old.c_cc[VQUIT]; + + new = old; + new.c_cc[VINTR] = -1; + new.c_cc[VQUIT] = -1; + new.c_lflag &= ~(ECHO | ICANON); + new.c_iflag &= ~(ISTRIP | INPCK); + new.c_cc[VMIN] = 1; + new.c_cc[VTIME] = 0; + (void)tcsetattr(0, TCSADRAIN, &new); + } + else + (void)tcsetattr(0, TCSADRAIN, &old); +} + +#else +#if defined(HAVE_TERMIO) +#include + +void +rl_ttyset(Reset) + int Reset; +{ + static struct termio old; + struct termio new; + + if (Reset == 0) { + (void)ioctl(0, TCGETA, &old); + rl_erase = old.c_cc[VERASE]; + rl_kill = old.c_cc[VKILL]; + rl_eof = old.c_cc[VEOF]; + rl_intr = old.c_cc[VINTR]; + rl_quit = old.c_cc[VQUIT]; + + new = old; + new.c_cc[VINTR] = -1; + new.c_cc[VQUIT] = -1; + new.c_lflag &= ~(ECHO | ICANON); + new.c_iflag &= ~(ISTRIP | INPCK); + new.c_cc[VMIN] = 1; + new.c_cc[VTIME] = 0; + (void)ioctl(0, TCSETAW, &new); + } + else + (void)ioctl(0, TCSETAW, &old); +} + +#else +#include + +void +rl_ttyset(Reset) + int Reset; +{ + static struct sgttyb old_sgttyb; + static struct tchars old_tchars; + struct sgttyb new_sgttyb; + struct tchars new_tchars; + + if (Reset == 0) { + (void)ioctl(0, TIOCGETP, &old_sgttyb); + rl_erase = old_sgttyb.sg_erase; + rl_kill = old_sgttyb.sg_kill; + + (void)ioctl(0, TIOCGETC, &old_tchars); + rl_eof = old_tchars.t_eofc; + rl_intr = old_tchars.t_intrc; + rl_quit = old_tchars.t_quitc; + + new_sgttyb = old_sgttyb; + new_sgttyb.sg_flags &= ~ECHO; + new_sgttyb.sg_flags |= RAW; +#if defined(PASS8) + new_sgttyb.sg_flags |= PASS8; +#endif /* defined(PASS8) */ + (void)ioctl(0, TIOCSETP, &new_sgttyb); + + new_tchars = old_tchars; + new_tchars.t_intrc = -1; + new_tchars.t_quitc = -1; + (void)ioctl(0, TIOCSETC, &new_tchars); + } + else { + (void)ioctl(0, TIOCSETP, &old_sgttyb); + (void)ioctl(0, TIOCSETC, &old_tchars); + } +} +#endif /* defined(HAVE_TERMIO) */ +#endif /* defined(HAVE_TCGETATTR) */ + +void +rl_add_slash(path, p) + char *path; + char *p; +{ + struct stat Sb; + + if (stat(path, &Sb) >= 0) + (void)strcat(p, S_ISDIR(Sb.st_mode) ? "/" : " "); +} + diff --git a/ape-server/deps/js/src/editline/unix.h b/ape-server/deps/js/src/editline/unix.h new file mode 100755 index 0000000..c99b729 --- /dev/null +++ b/ape-server/deps/js/src/editline/unix.h @@ -0,0 +1,82 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Simmule Turner and Rich Salz. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Copyright 1992,1993 Simmule Turner and Rich Salz. All rights reserved. + * + * This software is not subject to any license of the American Telephone + * and Telegraph Company or of the Regents of the University of California. + * + * Permission is granted to anyone to use this software for any purpose on + * any computer system, and to alter it and redistribute it freely, subject + * to the following restrictions: + * 1. The authors are not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits must appear in the documentation. + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits must appear in the documentation. + * 4. This notice may not be removed or altered. + */ + + +/* +** Editline system header file for Unix. +*/ + +#define CRLF "\r\n" +#define FORWARD STATIC + +#include +#include + +#if defined(USE_DIRENT) +#include +typedef struct dirent DIRENTRY; +#else +#include +typedef struct direct DIRENTRY; +#endif /* defined(USE_DIRENT) */ + +#if !defined(S_ISDIR) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif /* !defined(S_ISDIR) */ + diff --git a/ape-server/deps/js/src/find-child.py b/ape-server/deps/js/src/find-child.py new file mode 100755 index 0000000..5aabbfc --- /dev/null +++ b/ape-server/deps/js/src/find-child.py @@ -0,0 +1,65 @@ +#!/usr/bin/python +# +# The output of this script is a splicemap. +# +# A splicemap is used by the mercurial 'convert' extension to direct its +# splicing operation. +# +# The script assumes you already have a destination repository +# containing a conversion somewhere in its history, but that you've +# possibly mixed some new commits on top of that conversion. It outputs +# a splicemap that picks up the conversion where it left off (with the +# first descendant, in the source repo, of the src rev given by --start, +# if or rather the first descendant that would be included by the +# conversion's filemap) and connects them to the current tip of the +# destination repo. +# + +from mercurial import ui, hg +from hgext.convert.filemap import filemapper +from optparse import OptionParser + + +parser = OptionParser() + +parser.add_option("-s", "--src", dest="src", + help="source repository", metavar="REPO") + +parser.add_option("-d", "--dst", dest="dst", + help="destination repository", metavar="REPO") + +parser.add_option("-t", "--start", dest="start", + help="starting revid in source repository", metavar="REV") + +parser.add_option("-f", "--filemap", dest="filemap", + help="filemap used in conversion", metavar="PATH") + +(options, args) = parser.parse_args() + +if not (options.src and options.dst and options.start): + parser.print_help() + exit(1) + +u = ui.ui() + +src_repo = hg.repository(u, options.src) +dst_repo = hg.repository(u, options.dst) + +fm = None +if options.filemap: + fm = filemapper(u, options.filemap) + +last_converted_src = src_repo[options.start] + +dst_tip = dst_repo.changectx(dst_repo.changelog.tip()).hex() +revs = last_converted_src.children() + +while len(revs) != 0: + tmp = revs + revs = [] + for child in tmp: + for f in child.files(): + if (not fm) or fm(f): + u.write("%s %s\n" % (child.hex(), dst_tip)) + exit(0); + revs.extend(child.children()) diff --git a/ape-server/deps/js/src/imacro_asm.js.in b/ape-server/deps/js/src/imacro_asm.js.in new file mode 100755 index 0000000..b0a83b9 --- /dev/null +++ b/ape-server/deps/js/src/imacro_asm.js.in @@ -0,0 +1,404 @@ +/* vim: set sw=4 ts=8 et tw=78 ft=javascript: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the TraceMonkey IMacro Assembler. + * + * The Initial Developer of the Original Code is + * Brendan Eich . + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * An imacro (interpreter-macro) assembler in JS, with a light dusting of C + * pre-processor. We depend on the snarf function from the js shell, defined + * originally for the Narcissus metacircular evaluator, now unconditionally + * compiled into the shell. + * + * Filename suffix conventions, used by Makefile.in rules: + * .js.in C-pre-processed JS source + * .jsasm SpiderMonkey JS assembly source, which could be input to other + * assemblers than imacro_asm.js, hence the generic suffix! + * .c.out C source output by imacro_asm.js + */ + +#define ASSERT(cond) _ASSERT(cond, #cond) + +function _ASSERT(cond, message) { + if (!cond) + throw new Error("Assertion failed: " + message); +} + +const js_arguments_str = "arguments"; +const js_new_str = "new"; +const js_typeof_str = "typeof"; +const js_void_str = "void"; +const js_null_str = "null"; +const js_this_str = "this"; +const js_false_str = "false"; +const js_true_str = "true"; +const js_throw_str = "throw"; +const js_in_str = "in"; +const js_instanceof_str = "instanceof"; +const js_getter_str = "getter"; +const js_setter_str = "setter"; + +#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ + {jsop: #op, opcode: val, opname: name, opsrc: token, oplen: length, \ + pops: nuses, pushes: ndefs, precedence: prec, flags: #format}, + +const NULL = null; + +const opinfo = [ +#include "jsopcode.tbl" +]; + +const opname2info = {}; +const jsop2opcode = {}; + +for (let i = 0; i < opinfo.length; i++) { + let info = opinfo[i]; + ASSERT(info.opcode == i); + opname2info[info.opname] = info; + jsop2opcode[info.jsop] = info.opcode; +} + +function format_offset(n, w) { + let s = n.toString(); + while (s.length < w) + s = ' ' + s; + return s; +} + +function immediate(op) { + let info = op.info; + let imm1Expr = /^\(/.test(op.imm1); + if (info.flags.indexOf("JOF_ATOM") >= 0) { + if (/^(?:void|object|function|string|number|boolean)$/.test(op.imm1)) + return "0, COMMON_TYPE_ATOM_INDEX(JSTYPE_" + op.imm1.toUpperCase() + ")"; + return "0, COMMON_ATOM_INDEX(" + op.imm1 + ")"; + } + if (info.flags.indexOf("JOF_JUMP") >= 0) { + ASSERT(!imm1Expr); + return ((op.target >> 8) & 0xff) + ", " + (op.target & 0xff); + } + if (info.flags.indexOf("JOF_UINT8") >= 0 || + info.flags.indexOf("JOF_INT8") >= 0) { + if (imm1Expr) + return op.imm1; + if (isNaN(Number(op.imm1)) || Number(op.imm1) != parseInt(op.imm1)) + throw new Error("invalid 8-bit operand: " + op.imm1); + return (op.imm1 & 0xff); + } + if (info.flags.indexOf("JOF_UINT16") >= 0) { + if (imm1Expr) + return '(_ & 0xff00) >> 8, (_ & 0xff)'.replace(/_/g, op.imm1); + return ((op.imm1 & 0xff00) >> 8) + ", " + (op.imm1 & 0xff); + } + throw new Error(info.jsop + " format not yet implemented"); +} + +function simulate_cfg(imacro, depth, i) { + while (i < imacro.code.length) { + let op = imacro.code[i]; + depth -= (op.info.pops < 0) ? 2 + Number(op.imm1) : op.info.pops; + depth += op.info.pushes; + + if (imacro.depths.hasOwnProperty(i) && imacro.depths[i] != depth) + throw Error("Mismatched depth at " + imacro.filename + ":" + op.line); + + /* + * Underflowing depth isn't necessarily fatal; most of the imacros + * assume they are called with N>0 args so some assume it's ok to go + * to some depth imacro.maxdepth) + imacro.maxdepth = depth; + imacro.depths[i] = depth; + + if (op.hasOwnProperty("target_index")) { + if (op.target_index <= i) + throw Error("Backward jump at " + imacro.filename + ":" + op.line); + + simulate_cfg(imacro, depth, op.target_index); + + if (op.info.opname == "goto" || op.info.opname == "gotox") + return; + } + ++i; + } +} + +/* + * Syntax (spaces are significant only to delimit tokens): + * + * Assembly ::= (Directive? '\n')* + * Directive ::= (name ':')? Operation + * Operation ::= opname Operands? + * Operands ::= Operand (',' Operand)* + * Operand ::= name | number | '(' Expr ')' + * Expr ::= a constant-expression in the C++ language + * containing no parentheses + * + * We simplify given line structure and the maximum of one immediate operand, + * by parsing using split and regexps. For ease of parsing, parentheses are + * banned in an Expr for now, even in quotes or a C++ comment. + * + * Pseudo-ops start with . and include .igroup and .imacro, terminated by .end. + * .imacro must nest in .igroup, neither nests in itself. See imacros.jsasm for + * examples. + */ +const line_regexp_parts = [ + "^(?:(\\w+):)?", // optional label at start of line + "\\s*(\\.?\\w+)", // optional spaces, (pseudo-)opcode + "(?:\\s+(\\w+|\\([^)]*\\)))?", // optional first immediate operand + "(?:\\s+([\\w-]+|\\([^)]*\\)))?", // optional second immediate operand + "(?:\\s*(?:#.*))?$" // optional spaces and comment +]; + +const line_regexp = new RegExp(line_regexp_parts.join("")); + +function assemble(filename) { + let igroup = null, imacro = null; + let opcode2extra = []; + let igroups = []; + + print("/* GENERATED BY imacro_asm.js -- DO NOT EDIT!!! */"); + + let s = snarf(filename); + let a = s.split('\n'); + for (let i = 0; i < a.length; i++) { + if (/^\s*(?:#.*)?$/.test(a[i])) + continue; + let m = line_regexp.exec(a[i]); + if (!m) + throw new Error(a[i]); + + let [, label, opname, imm1, imm2] = m; + + if (opname[0] == '.') { + if (label) + throw new Error("invalid label " + label + " before " + opname); + + switch (opname) { + case ".igroup": + if (!imm1) + throw new Error("missing .igroup name"); + if (igroup) + throw new Error("nested .igroup " + imm1); + let oprange = imm2.match(/^(\w+)(?:-(\w+))?$/); + if (!oprange) + throw new Error("invalid igroup operator range " + imm2); + let firstop = jsop2opcode[oprange[1]]; + igroup = { + name: imm1, + firstop: firstop, + lastop: oprange[2] ? jsop2opcode[oprange[2]] : firstop, + imacros: [] + }; + break; + + case ".imacro": + if (!igroup) + throw new Error(".imacro outside of .igroup"); + if (!imm1) + throw new Error("missing .imacro name"); + if (imacro) + throw new Error("nested .imacro " + imm1); + imacro = { + name: imm1, + offset: 0, + code: [], + labeldefs: {}, + labeldef_indexes: {}, + labelrefs: {}, + filename: filename, + depths: {} + }; + break; + + case ".end": + if (!imacro) { + if (!igroup) + throw new Error(".end without prior .igroup or .imacro"); + if (imm1 && (imm1 != igroup.name || imm2)) + throw new Error(".igroup/.end name mismatch"); + + let maxdepth = 0; + + print("static struct {"); + for (let j = 0; j < igroup.imacros.length; j++) { + imacro = igroup.imacros[j]; + print(" jsbytecode " + imacro.name + "[" + imacro.offset + "];"); + } + print("} " + igroup.name + "_imacros = {"); + + for (let j = 0; j < igroup.imacros.length; j++) { + let depth = 0; + + imacro = igroup.imacros[j]; + print(" {"); + for (let k = 0; k < imacro.code.length; k++) { + let op = imacro.code[k]; + + print("/*" + format_offset(op.offset,2) + "*/ " + op.info.jsop + + (op.imm1 ? ", " + immediate(op) : "") + ","); + + } + + imacro.maxdepth = 0; + simulate_cfg(imacro, 0, 0); + if (imacro.maxdepth > maxdepth) + maxdepth = imacro.maxdepth; + + print(" },"); + } + + print("};"); + + let opcode = igroup.firstop; + let oplast = igroup.lastop; + do { + opcode2extra[opcode] = maxdepth; + } while (opcode++ != oplast); + igroups.push(igroup); + igroup = null; + } else { + ASSERT(igroup); + + if (imm1 && imm1 != imacro.name) + throw new Error(".imacro/.end name mismatch"); + + // Backpatch the forward references to labels that must now be defined. + for (label in imacro.labelrefs) { + if (!imacro.labelrefs.hasOwnProperty(label)) + continue; + if (!imacro.labeldefs.hasOwnProperty(label)) + throw new Error("label " + label + " used but not defined"); + let link = imacro.labelrefs[label]; + ASSERT(link >= 0); + for (;;) { + let op = imacro.code[link]; + ASSERT(op); + ASSERT(op.hasOwnProperty('target')); + let next = op.target; + op.target = imacro.labeldefs[label] - op.offset; + op.target_index = imacro.labeldef_indexes[label]; + if (next < 0) + break; + link = next; + } + } + + igroup.imacros.push(imacro); + } + imacro = null; + break; + + default: + throw new Error("unknown pseudo-op " + opname); + } + continue; + } + + if (!opname2info.hasOwnProperty(opname)) + throw new Error("unknown opcode " + opname + (label ? " (label " + label + ")" : "")); + + let info = opname2info[opname]; + if (info.oplen == -1) + throw new Error("unimplemented opcode " + opname); + + if (!imacro) + throw new Error("opcode " + opname + " outside of .imacro"); + + // Blacklist ops that may or must use an atomized double immediate. + switch (info.opname) { + case "double": + case "lookupswitch": + case "lookupswitchx": + throw new Error(op.opname + " opcode not yet supported"); + } + + if (label) { + imacro.labeldefs[label] = imacro.offset; + imacro.labeldef_indexes[label] = imacro.code.length; + } + + let op = {offset: imacro.offset, info: info, imm1: imm1, imm2: imm2, line:(i+1) }; + if (info.flags.indexOf("JOF_JUMP") >= 0) { + if (imacro.labeldefs.hasOwnProperty(imm1)) { + // Backward reference can be resolved right away, no backpatching needed. + op.target = imacro.labeldefs[imm1] - op.offset; + op.target_index = imacro.labeldef_indexes[imm1]; + } else { + // Link op into the .target-linked backpatch chain at labelrefs[imm1]. + // The linked list terminates with a -1 sentinel. + op.target = imacro.labelrefs.hasOwnProperty(imm1) ? imacro.labelrefs[imm1] : -1; + imacro.labelrefs[imm1] = imacro.code.length; + } + } + + imacro.code.push(op); + imacro.offset += info.oplen; + } + + print("uint8 js_opcode2extra[JSOP_LIMIT] = {"); + for (let i = 0; i < opinfo.length; i++) { + print(" " + ((i in opcode2extra) ? opcode2extra[i] : "0") + + ", /* " + opinfo[i].jsop + " */"); + } + print("};"); + + print("#define JSOP_IS_IMACOP(x) (0 \\"); + for (let i in opcode2extra) + print(" || x == " + opinfo[i].jsop + " \\"); + print(")"); + + print("jsbytecode*\njs_GetImacroStart(jsbytecode* pc) {"); + for each (let g in igroups) { + for each (let m in g.imacros) { + let start = g.name + "_imacros." + m.name; + print(" if (size_t(pc - " + start + ") < " + m.offset + ") return " + start + ";"); + } + } + print(" return NULL;"); + print("}"); +} + +for (let i = 0; i < arguments.length; i++) { + try { + assemble(arguments[i]); + } catch (e) { + print(e.name + ": " + e.message + "\n" + e.stack); + } +} diff --git a/ape-server/deps/js/src/imacros.c.out b/ape-server/deps/js/src/imacros.c.out new file mode 100755 index 0000000..89e22d9 --- /dev/null +++ b/ape-server/deps/js/src/imacros.c.out @@ -0,0 +1,987 @@ +/* GENERATED BY imacro_asm.js -- DO NOT EDIT!!! */ +static struct { + jsbytecode any_obj[36]; + jsbytecode obj_any[38]; +} equality_imacros = { + { +/* 0*/ JSOP_DUP, +/* 1*/ JSOP_DUP, +/* 2*/ JSOP_GETPROP, 0, COMMON_ATOM_INDEX(valueOf), +/* 5*/ JSOP_IFPRIMTOP, 0, 18, +/* 8*/ JSOP_SWAP, +/* 9*/ JSOP_STRING, 0, COMMON_TYPE_ATOM_INDEX(JSTYPE_VOID), +/*12*/ JSOP_CALL, 0, 1, +/*15*/ JSOP_IFPRIMTOP, 0, 17, +/*18*/ JSOP_POP, +/*19*/ JSOP_DUP, +/*20*/ JSOP_GOTO, 0, 4, +/*23*/ JSOP_POP, +/*24*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString), +/*27*/ JSOP_CALL, 0, 0, +/*30*/ JSOP_PRIMTOP, (JSTYPE_NUMBER), +/*32*/ JSOP_SWAP, +/*33*/ JSOP_POP, +/*34*/ JSOP_IMACOP, +/*35*/ JSOP_STOP, + }, + { +/* 0*/ JSOP_SWAP, +/* 1*/ JSOP_DUP, +/* 2*/ JSOP_DUP, +/* 3*/ JSOP_GETPROP, 0, COMMON_ATOM_INDEX(valueOf), +/* 6*/ JSOP_IFPRIMTOP, 0, 18, +/* 9*/ JSOP_SWAP, +/*10*/ JSOP_STRING, 0, COMMON_TYPE_ATOM_INDEX(JSTYPE_VOID), +/*13*/ JSOP_CALL, 0, 1, +/*16*/ JSOP_IFPRIMTOP, 0, 17, +/*19*/ JSOP_POP, +/*20*/ JSOP_DUP, +/*21*/ JSOP_GOTO, 0, 4, +/*24*/ JSOP_POP, +/*25*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString), +/*28*/ JSOP_CALL, 0, 0, +/*31*/ JSOP_PRIMTOP, (JSTYPE_NUMBER), +/*33*/ JSOP_SWAP, +/*34*/ JSOP_POP, +/*35*/ JSOP_SWAP, +/*36*/ JSOP_IMACOP, +/*37*/ JSOP_STOP, + }, +}; +static struct { + jsbytecode any_obj[36]; + jsbytecode obj_any[38]; + jsbytecode obj_obj[72]; +} binary_imacros = { + { +/* 0*/ JSOP_DUP, +/* 1*/ JSOP_DUP, +/* 2*/ JSOP_GETPROP, 0, COMMON_ATOM_INDEX(valueOf), +/* 5*/ JSOP_IFPRIMTOP, 0, 18, +/* 8*/ JSOP_SWAP, +/* 9*/ JSOP_STRING, 0, COMMON_TYPE_ATOM_INDEX(JSTYPE_NUMBER), +/*12*/ JSOP_CALL, 0, 1, +/*15*/ JSOP_IFPRIMTOP, 0, 17, +/*18*/ JSOP_POP, +/*19*/ JSOP_DUP, +/*20*/ JSOP_GOTO, 0, 4, +/*23*/ JSOP_POP, +/*24*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString), +/*27*/ JSOP_CALL, 0, 0, +/*30*/ JSOP_PRIMTOP, (JSTYPE_NUMBER), +/*32*/ JSOP_SWAP, +/*33*/ JSOP_POP, +/*34*/ JSOP_IMACOP, +/*35*/ JSOP_STOP, + }, + { +/* 0*/ JSOP_SWAP, +/* 1*/ JSOP_DUP, +/* 2*/ JSOP_DUP, +/* 3*/ JSOP_GETPROP, 0, COMMON_ATOM_INDEX(valueOf), +/* 6*/ JSOP_IFPRIMTOP, 0, 18, +/* 9*/ JSOP_SWAP, +/*10*/ JSOP_STRING, 0, COMMON_TYPE_ATOM_INDEX(JSTYPE_NUMBER), +/*13*/ JSOP_CALL, 0, 1, +/*16*/ JSOP_IFPRIMTOP, 0, 17, +/*19*/ JSOP_POP, +/*20*/ JSOP_DUP, +/*21*/ JSOP_GOTO, 0, 4, +/*24*/ JSOP_POP, +/*25*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString), +/*28*/ JSOP_CALL, 0, 0, +/*31*/ JSOP_PRIMTOP, (JSTYPE_NUMBER), +/*33*/ JSOP_SWAP, +/*34*/ JSOP_POP, +/*35*/ JSOP_SWAP, +/*36*/ JSOP_IMACOP, +/*37*/ JSOP_STOP, + }, + { +/* 0*/ JSOP_SWAP, +/* 1*/ JSOP_DUP, +/* 2*/ JSOP_DUP, +/* 3*/ JSOP_GETPROP, 0, COMMON_ATOM_INDEX(valueOf), +/* 6*/ JSOP_IFPRIMTOP, 0, 18, +/* 9*/ JSOP_SWAP, +/*10*/ JSOP_STRING, 0, COMMON_TYPE_ATOM_INDEX(JSTYPE_NUMBER), +/*13*/ JSOP_CALL, 0, 1, +/*16*/ JSOP_IFPRIMTOP, 0, 17, +/*19*/ JSOP_POP, +/*20*/ JSOP_DUP, +/*21*/ JSOP_GOTO, 0, 4, +/*24*/ JSOP_POP, +/*25*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString), +/*28*/ JSOP_CALL, 0, 0, +/*31*/ JSOP_PRIMTOP, (JSTYPE_NUMBER), +/*33*/ JSOP_SWAP, +/*34*/ JSOP_POP, +/*35*/ JSOP_SWAP, +/*36*/ JSOP_DUP, +/*37*/ JSOP_DUP, +/*38*/ JSOP_GETPROP, 0, COMMON_ATOM_INDEX(valueOf), +/*41*/ JSOP_IFPRIMTOP, 0, 18, +/*44*/ JSOP_SWAP, +/*45*/ JSOP_STRING, 0, COMMON_TYPE_ATOM_INDEX(JSTYPE_NUMBER), +/*48*/ JSOP_CALL, 0, 1, +/*51*/ JSOP_IFPRIMTOP, 0, 17, +/*54*/ JSOP_POP, +/*55*/ JSOP_DUP, +/*56*/ JSOP_GOTO, 0, 4, +/*59*/ JSOP_POP, +/*60*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString), +/*63*/ JSOP_CALL, 0, 0, +/*66*/ JSOP_PRIMTOP, (JSTYPE_NUMBER), +/*68*/ JSOP_SWAP, +/*69*/ JSOP_POP, +/*70*/ JSOP_IMACOP, +/*71*/ JSOP_STOP, + }, +}; +static struct { + jsbytecode any_obj[36]; + jsbytecode obj_any[38]; + jsbytecode obj_obj[72]; +} add_imacros = { + { +/* 0*/ JSOP_DUP, +/* 1*/ JSOP_DUP, +/* 2*/ JSOP_GETPROP, 0, COMMON_ATOM_INDEX(valueOf), +/* 5*/ JSOP_IFPRIMTOP, 0, 18, +/* 8*/ JSOP_SWAP, +/* 9*/ JSOP_STRING, 0, COMMON_TYPE_ATOM_INDEX(JSTYPE_VOID), +/*12*/ JSOP_CALL, 0, 1, +/*15*/ JSOP_IFPRIMTOP, 0, 17, +/*18*/ JSOP_POP, +/*19*/ JSOP_DUP, +/*20*/ JSOP_GOTO, 0, 4, +/*23*/ JSOP_POP, +/*24*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString), +/*27*/ JSOP_CALL, 0, 0, +/*30*/ JSOP_PRIMTOP, (JSTYPE_VOID), +/*32*/ JSOP_SWAP, +/*33*/ JSOP_POP, +/*34*/ JSOP_ADD, +/*35*/ JSOP_STOP, + }, + { +/* 0*/ JSOP_SWAP, +/* 1*/ JSOP_DUP, +/* 2*/ JSOP_DUP, +/* 3*/ JSOP_GETPROP, 0, COMMON_ATOM_INDEX(valueOf), +/* 6*/ JSOP_IFPRIMTOP, 0, 18, +/* 9*/ JSOP_SWAP, +/*10*/ JSOP_STRING, 0, COMMON_TYPE_ATOM_INDEX(JSTYPE_VOID), +/*13*/ JSOP_CALL, 0, 1, +/*16*/ JSOP_IFPRIMTOP, 0, 17, +/*19*/ JSOP_POP, +/*20*/ JSOP_DUP, +/*21*/ JSOP_GOTO, 0, 4, +/*24*/ JSOP_POP, +/*25*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString), +/*28*/ JSOP_CALL, 0, 0, +/*31*/ JSOP_PRIMTOP, (JSTYPE_VOID), +/*33*/ JSOP_SWAP, +/*34*/ JSOP_POP, +/*35*/ JSOP_SWAP, +/*36*/ JSOP_ADD, +/*37*/ JSOP_STOP, + }, + { +/* 0*/ JSOP_SWAP, +/* 1*/ JSOP_DUP, +/* 2*/ JSOP_DUP, +/* 3*/ JSOP_GETPROP, 0, COMMON_ATOM_INDEX(valueOf), +/* 6*/ JSOP_IFPRIMTOP, 0, 18, +/* 9*/ JSOP_SWAP, +/*10*/ JSOP_STRING, 0, COMMON_TYPE_ATOM_INDEX(JSTYPE_VOID), +/*13*/ JSOP_CALL, 0, 1, +/*16*/ JSOP_IFPRIMTOP, 0, 17, +/*19*/ JSOP_POP, +/*20*/ JSOP_DUP, +/*21*/ JSOP_GOTO, 0, 4, +/*24*/ JSOP_POP, +/*25*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString), +/*28*/ JSOP_CALL, 0, 0, +/*31*/ JSOP_PRIMTOP, (JSTYPE_VOID), +/*33*/ JSOP_SWAP, +/*34*/ JSOP_POP, +/*35*/ JSOP_SWAP, +/*36*/ JSOP_DUP, +/*37*/ JSOP_DUP, +/*38*/ JSOP_GETPROP, 0, COMMON_ATOM_INDEX(valueOf), +/*41*/ JSOP_IFPRIMTOP, 0, 18, +/*44*/ JSOP_SWAP, +/*45*/ JSOP_STRING, 0, COMMON_TYPE_ATOM_INDEX(JSTYPE_VOID), +/*48*/ JSOP_CALL, 0, 1, +/*51*/ JSOP_IFPRIMTOP, 0, 17, +/*54*/ JSOP_POP, +/*55*/ JSOP_DUP, +/*56*/ JSOP_GOTO, 0, 4, +/*59*/ JSOP_POP, +/*60*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString), +/*63*/ JSOP_CALL, 0, 0, +/*66*/ JSOP_PRIMTOP, (JSTYPE_VOID), +/*68*/ JSOP_SWAP, +/*69*/ JSOP_POP, +/*70*/ JSOP_ADD, +/*71*/ JSOP_STOP, + }, +}; +static struct { + jsbytecode sign[41]; +} unary_imacros = { + { +/* 0*/ JSOP_DUP, +/* 1*/ JSOP_DUP, +/* 2*/ JSOP_GETPROP, 0, COMMON_ATOM_INDEX(valueOf), +/* 5*/ JSOP_IFPRIMTOP, 0, 23, +/* 8*/ JSOP_SWAP, +/* 9*/ JSOP_STRING, 0, COMMON_TYPE_ATOM_INDEX(JSTYPE_NUMBER), +/*12*/ JSOP_CALL, 0, 1, +/*15*/ JSOP_IFPRIMTOP, 0, 8, +/*18*/ JSOP_POP, +/*19*/ JSOP_DUP, +/*20*/ JSOP_GOTO, 0, 9, +/*23*/ JSOP_SWAP, +/*24*/ JSOP_POP, +/*25*/ JSOP_GOTO, 0, 14, +/*28*/ JSOP_POP, +/*29*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString), +/*32*/ JSOP_CALL, 0, 0, +/*35*/ JSOP_PRIMTOP, (JSTYPE_NUMBER), +/*37*/ JSOP_SWAP, +/*38*/ JSOP_POP, +/*39*/ JSOP_IMACOP, +/*40*/ JSOP_STOP, + }, +}; +static struct { + jsbytecode String[38]; +} call_imacros = { + { +/* 0*/ JSOP_DUP, +/* 1*/ JSOP_DUP, +/* 2*/ JSOP_GETPROP, 0, COMMON_ATOM_INDEX(toString), +/* 5*/ JSOP_IFPRIMTOP, 0, 15, +/* 8*/ JSOP_SWAP, +/* 9*/ JSOP_CALL, 0, 0, +/*12*/ JSOP_IFPRIMTOP, 0, 20, +/*15*/ JSOP_POP, +/*16*/ JSOP_DUP, +/*17*/ JSOP_GOTO, 0, 4, +/*20*/ JSOP_POP, +/*21*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(valueOf), +/*24*/ JSOP_STRING, 0, COMMON_TYPE_ATOM_INDEX(JSTYPE_STRING), +/*27*/ JSOP_CALL, 0, 1, +/*30*/ JSOP_PRIMTOP, (JSTYPE_STRING), +/*32*/ JSOP_SWAP, +/*33*/ JSOP_POP, +/*34*/ JSOP_CALL, 0, 1, +/*37*/ JSOP_STOP, + }, +}; +static struct { + jsbytecode String[38]; +} new_imacros = { + { +/* 0*/ JSOP_DUP, +/* 1*/ JSOP_DUP, +/* 2*/ JSOP_GETPROP, 0, COMMON_ATOM_INDEX(toString), +/* 5*/ JSOP_IFPRIMTOP, 0, 15, +/* 8*/ JSOP_SWAP, +/* 9*/ JSOP_CALL, 0, 0, +/*12*/ JSOP_IFPRIMTOP, 0, 20, +/*15*/ JSOP_POP, +/*16*/ JSOP_DUP, +/*17*/ JSOP_GOTO, 0, 4, +/*20*/ JSOP_POP, +/*21*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(valueOf), +/*24*/ JSOP_STRING, 0, COMMON_TYPE_ATOM_INDEX(JSTYPE_STRING), +/*27*/ JSOP_CALL, 0, 1, +/*30*/ JSOP_PRIMTOP, (JSTYPE_STRING), +/*32*/ JSOP_SWAP, +/*33*/ JSOP_POP, +/*34*/ JSOP_NEW, 0, 1, +/*37*/ JSOP_STOP, + }, +}; +static struct { + jsbytecode apply0[8]; + jsbytecode apply1[12]; + jsbytecode apply2[16]; + jsbytecode apply3[21]; + jsbytecode apply4[26]; + jsbytecode apply5[31]; + jsbytecode apply6[36]; + jsbytecode apply7[41]; + jsbytecode apply8[46]; + jsbytecode call0[7]; + jsbytecode call1[7]; + jsbytecode call2[7]; + jsbytecode call3[7]; + jsbytecode call4[7]; + jsbytecode call5[7]; + jsbytecode call6[7]; + jsbytecode call7[7]; + jsbytecode call8[7]; +} apply_imacros = { + { +/* 0*/ JSOP_PICK, 3, +/* 2*/ JSOP_POP, +/* 3*/ JSOP_POP, +/* 4*/ JSOP_CALL, 0, 0, +/* 7*/ JSOP_STOP, + }, + { +/* 0*/ JSOP_PICK, 3, +/* 2*/ JSOP_POP, +/* 3*/ JSOP_DUP, +/* 4*/ JSOP_ZERO, +/* 5*/ JSOP_GETELEM, +/* 6*/ JSOP_SWAP, +/* 7*/ JSOP_POP, +/* 8*/ JSOP_CALL, 0, 1, +/*11*/ JSOP_STOP, + }, + { +/* 0*/ JSOP_PICK, 3, +/* 2*/ JSOP_POP, +/* 3*/ JSOP_DUP, +/* 4*/ JSOP_ZERO, +/* 5*/ JSOP_GETELEM, +/* 6*/ JSOP_SWAP, +/* 7*/ JSOP_DUP, +/* 8*/ JSOP_ONE, +/* 9*/ JSOP_GETELEM, +/*10*/ JSOP_SWAP, +/*11*/ JSOP_POP, +/*12*/ JSOP_CALL, 0, 2, +/*15*/ JSOP_STOP, + }, + { +/* 0*/ JSOP_PICK, 3, +/* 2*/ JSOP_POP, +/* 3*/ JSOP_DUP, +/* 4*/ JSOP_ZERO, +/* 5*/ JSOP_GETELEM, +/* 6*/ JSOP_SWAP, +/* 7*/ JSOP_DUP, +/* 8*/ JSOP_ONE, +/* 9*/ JSOP_GETELEM, +/*10*/ JSOP_SWAP, +/*11*/ JSOP_DUP, +/*12*/ JSOP_INT8, 2, +/*14*/ JSOP_GETELEM, +/*15*/ JSOP_SWAP, +/*16*/ JSOP_POP, +/*17*/ JSOP_CALL, 0, 3, +/*20*/ JSOP_STOP, + }, + { +/* 0*/ JSOP_PICK, 3, +/* 2*/ JSOP_POP, +/* 3*/ JSOP_DUP, +/* 4*/ JSOP_ZERO, +/* 5*/ JSOP_GETELEM, +/* 6*/ JSOP_SWAP, +/* 7*/ JSOP_DUP, +/* 8*/ JSOP_ONE, +/* 9*/ JSOP_GETELEM, +/*10*/ JSOP_SWAP, +/*11*/ JSOP_DUP, +/*12*/ JSOP_INT8, 2, +/*14*/ JSOP_GETELEM, +/*15*/ JSOP_SWAP, +/*16*/ JSOP_DUP, +/*17*/ JSOP_INT8, 3, +/*19*/ JSOP_GETELEM, +/*20*/ JSOP_SWAP, +/*21*/ JSOP_POP, +/*22*/ JSOP_CALL, 0, 4, +/*25*/ JSOP_STOP, + }, + { +/* 0*/ JSOP_PICK, 3, +/* 2*/ JSOP_POP, +/* 3*/ JSOP_DUP, +/* 4*/ JSOP_ZERO, +/* 5*/ JSOP_GETELEM, +/* 6*/ JSOP_SWAP, +/* 7*/ JSOP_DUP, +/* 8*/ JSOP_ONE, +/* 9*/ JSOP_GETELEM, +/*10*/ JSOP_SWAP, +/*11*/ JSOP_DUP, +/*12*/ JSOP_INT8, 2, +/*14*/ JSOP_GETELEM, +/*15*/ JSOP_SWAP, +/*16*/ JSOP_DUP, +/*17*/ JSOP_INT8, 3, +/*19*/ JSOP_GETELEM, +/*20*/ JSOP_SWAP, +/*21*/ JSOP_DUP, +/*22*/ JSOP_INT8, 4, +/*24*/ JSOP_GETELEM, +/*25*/ JSOP_SWAP, +/*26*/ JSOP_POP, +/*27*/ JSOP_CALL, 0, 5, +/*30*/ JSOP_STOP, + }, + { +/* 0*/ JSOP_PICK, 3, +/* 2*/ JSOP_POP, +/* 3*/ JSOP_DUP, +/* 4*/ JSOP_ZERO, +/* 5*/ JSOP_GETELEM, +/* 6*/ JSOP_SWAP, +/* 7*/ JSOP_DUP, +/* 8*/ JSOP_ONE, +/* 9*/ JSOP_GETELEM, +/*10*/ JSOP_SWAP, +/*11*/ JSOP_DUP, +/*12*/ JSOP_INT8, 2, +/*14*/ JSOP_GETELEM, +/*15*/ JSOP_SWAP, +/*16*/ JSOP_DUP, +/*17*/ JSOP_INT8, 3, +/*19*/ JSOP_GETELEM, +/*20*/ JSOP_SWAP, +/*21*/ JSOP_DUP, +/*22*/ JSOP_INT8, 4, +/*24*/ JSOP_GETELEM, +/*25*/ JSOP_SWAP, +/*26*/ JSOP_DUP, +/*27*/ JSOP_INT8, 5, +/*29*/ JSOP_GETELEM, +/*30*/ JSOP_SWAP, +/*31*/ JSOP_POP, +/*32*/ JSOP_CALL, 0, 6, +/*35*/ JSOP_STOP, + }, + { +/* 0*/ JSOP_PICK, 3, +/* 2*/ JSOP_POP, +/* 3*/ JSOP_DUP, +/* 4*/ JSOP_ZERO, +/* 5*/ JSOP_GETELEM, +/* 6*/ JSOP_SWAP, +/* 7*/ JSOP_DUP, +/* 8*/ JSOP_ONE, +/* 9*/ JSOP_GETELEM, +/*10*/ JSOP_SWAP, +/*11*/ JSOP_DUP, +/*12*/ JSOP_INT8, 2, +/*14*/ JSOP_GETELEM, +/*15*/ JSOP_SWAP, +/*16*/ JSOP_DUP, +/*17*/ JSOP_INT8, 3, +/*19*/ JSOP_GETELEM, +/*20*/ JSOP_SWAP, +/*21*/ JSOP_DUP, +/*22*/ JSOP_INT8, 4, +/*24*/ JSOP_GETELEM, +/*25*/ JSOP_SWAP, +/*26*/ JSOP_DUP, +/*27*/ JSOP_INT8, 5, +/*29*/ JSOP_GETELEM, +/*30*/ JSOP_SWAP, +/*31*/ JSOP_DUP, +/*32*/ JSOP_INT8, 6, +/*34*/ JSOP_GETELEM, +/*35*/ JSOP_SWAP, +/*36*/ JSOP_POP, +/*37*/ JSOP_CALL, 0, 7, +/*40*/ JSOP_STOP, + }, + { +/* 0*/ JSOP_PICK, 3, +/* 2*/ JSOP_POP, +/* 3*/ JSOP_DUP, +/* 4*/ JSOP_ZERO, +/* 5*/ JSOP_GETELEM, +/* 6*/ JSOP_SWAP, +/* 7*/ JSOP_DUP, +/* 8*/ JSOP_ONE, +/* 9*/ JSOP_GETELEM, +/*10*/ JSOP_SWAP, +/*11*/ JSOP_DUP, +/*12*/ JSOP_INT8, 2, +/*14*/ JSOP_GETELEM, +/*15*/ JSOP_SWAP, +/*16*/ JSOP_DUP, +/*17*/ JSOP_INT8, 3, +/*19*/ JSOP_GETELEM, +/*20*/ JSOP_SWAP, +/*21*/ JSOP_DUP, +/*22*/ JSOP_INT8, 4, +/*24*/ JSOP_GETELEM, +/*25*/ JSOP_SWAP, +/*26*/ JSOP_DUP, +/*27*/ JSOP_INT8, 5, +/*29*/ JSOP_GETELEM, +/*30*/ JSOP_SWAP, +/*31*/ JSOP_DUP, +/*32*/ JSOP_INT8, 6, +/*34*/ JSOP_GETELEM, +/*35*/ JSOP_SWAP, +/*36*/ JSOP_DUP, +/*37*/ JSOP_INT8, 7, +/*39*/ JSOP_GETELEM, +/*40*/ JSOP_SWAP, +/*41*/ JSOP_POP, +/*42*/ JSOP_CALL, 0, 8, +/*45*/ JSOP_STOP, + }, + { +/* 0*/ JSOP_SWAP, +/* 1*/ JSOP_POP, +/* 2*/ JSOP_NULL, +/* 3*/ JSOP_CALL, 0, 0, +/* 6*/ JSOP_STOP, + }, + { +/* 0*/ JSOP_PICK, 2, +/* 2*/ JSOP_POP, +/* 3*/ JSOP_CALL, 0, 0, +/* 6*/ JSOP_STOP, + }, + { +/* 0*/ JSOP_PICK, 3, +/* 2*/ JSOP_POP, +/* 3*/ JSOP_CALL, 0, 1, +/* 6*/ JSOP_STOP, + }, + { +/* 0*/ JSOP_PICK, 4, +/* 2*/ JSOP_POP, +/* 3*/ JSOP_CALL, 0, 2, +/* 6*/ JSOP_STOP, + }, + { +/* 0*/ JSOP_PICK, 5, +/* 2*/ JSOP_POP, +/* 3*/ JSOP_CALL, 0, 3, +/* 6*/ JSOP_STOP, + }, + { +/* 0*/ JSOP_PICK, 6, +/* 2*/ JSOP_POP, +/* 3*/ JSOP_CALL, 0, 4, +/* 6*/ JSOP_STOP, + }, + { +/* 0*/ JSOP_PICK, 7, +/* 2*/ JSOP_POP, +/* 3*/ JSOP_CALL, 0, 5, +/* 6*/ JSOP_STOP, + }, + { +/* 0*/ JSOP_PICK, 8, +/* 2*/ JSOP_POP, +/* 3*/ JSOP_CALL, 0, 6, +/* 6*/ JSOP_STOP, + }, + { +/* 0*/ JSOP_PICK, 9, +/* 2*/ JSOP_POP, +/* 3*/ JSOP_CALL, 0, 7, +/* 6*/ JSOP_STOP, + }, +}; +static struct { + jsbytecode for_in[13]; + jsbytecode for_each[13]; + jsbytecode for_in_native[10]; + jsbytecode for_each_native[10]; +} iter_imacros = { + { +/* 0*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(iterator), +/* 3*/ JSOP_INT8, (JSITER_ENUMERATE), +/* 5*/ JSOP_CALL, 0, 1, +/* 8*/ JSOP_OBJTOP, ((JSMSG_BAD_ITERATOR_RETURN) & 0xff00) >> 8, ((JSMSG_BAD_ITERATOR_RETURN) & 0xff), +/*11*/ JSOP_PUSH, +/*12*/ JSOP_STOP, + }, + { +/* 0*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(iterator), +/* 3*/ JSOP_INT8, (JSITER_ENUMERATE|JSITER_FOREACH), +/* 5*/ JSOP_CALL, 0, 1, +/* 8*/ JSOP_OBJTOP, ((JSMSG_BAD_ITERATOR_RETURN) & 0xff00) >> 8, ((JSMSG_BAD_ITERATOR_RETURN) & 0xff), +/*11*/ JSOP_PUSH, +/*12*/ JSOP_STOP, + }, + { +/* 0*/ JSOP_CALLBUILTIN, ((JSBUILTIN_ObjectToIterator) & 0xff00) >> 8, ((JSBUILTIN_ObjectToIterator) & 0xff), +/* 3*/ JSOP_INT8, (JSITER_ENUMERATE), +/* 5*/ JSOP_CALL, 0, 1, +/* 8*/ JSOP_PUSH, +/* 9*/ JSOP_STOP, + }, + { +/* 0*/ JSOP_CALLBUILTIN, ((JSBUILTIN_ObjectToIterator) & 0xff00) >> 8, ((JSBUILTIN_ObjectToIterator) & 0xff), +/* 3*/ JSOP_INT8, (JSITER_ENUMERATE|JSITER_FOREACH), +/* 5*/ JSOP_CALL, 0, 1, +/* 8*/ JSOP_PUSH, +/* 9*/ JSOP_STOP, + }, +}; +static struct { + jsbytecode custom_iter_next[12]; + jsbytecode native_iter_next[12]; +} nextiter_imacros = { + { +/* 0*/ JSOP_POP, +/* 1*/ JSOP_DUP, +/* 2*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(next), +/* 5*/ JSOP_CALL, 0, 0, +/* 8*/ JSOP_DUP, +/* 9*/ JSOP_HOLE, +/*10*/ JSOP_STRICTNE, +/*11*/ JSOP_STOP, + }, + { +/* 0*/ JSOP_POP, +/* 1*/ JSOP_DUP, +/* 2*/ JSOP_CALLBUILTIN, ((JSBUILTIN_CallIteratorNext) & 0xff00) >> 8, ((JSBUILTIN_CallIteratorNext) & 0xff), +/* 5*/ JSOP_CALL, 0, 0, +/* 8*/ JSOP_DUP, +/* 9*/ JSOP_HOLE, +/*10*/ JSOP_STRICTNE, +/*11*/ JSOP_STOP, + }, +}; +static struct { + jsbytecode string[38]; +} defvalue_imacros = { + { +/* 0*/ JSOP_SWAP, +/* 1*/ JSOP_DUP, +/* 2*/ JSOP_DUP, +/* 3*/ JSOP_GETPROP, 0, COMMON_ATOM_INDEX(valueOf), +/* 6*/ JSOP_IFPRIMTOP, 0, 18, +/* 9*/ JSOP_SWAP, +/*10*/ JSOP_STRING, 0, COMMON_TYPE_ATOM_INDEX(JSTYPE_VOID), +/*13*/ JSOP_CALL, 0, 1, +/*16*/ JSOP_IFPRIMTOP, 0, 17, +/*19*/ JSOP_POP, +/*20*/ JSOP_DUP, +/*21*/ JSOP_GOTO, 0, 4, +/*24*/ JSOP_POP, +/*25*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString), +/*28*/ JSOP_CALL, 0, 0, +/*31*/ JSOP_PRIMTOP, (JSTYPE_VOID), +/*33*/ JSOP_SWAP, +/*34*/ JSOP_POP, +/*35*/ JSOP_SWAP, +/*36*/ JSOP_IMACOP, +/*37*/ JSOP_STOP, + }, +}; +uint8 js_opcode2extra[JSOP_LIMIT] = { + 0, /* JSOP_NOP */ + 0, /* JSOP_PUSH */ + 0, /* JSOP_POPV */ + 0, /* JSOP_ENTERWITH */ + 0, /* JSOP_LEAVEWITH */ + 0, /* JSOP_RETURN */ + 0, /* JSOP_GOTO */ + 0, /* JSOP_IFEQ */ + 0, /* JSOP_IFNE */ + 0, /* JSOP_ARGUMENTS */ + 0, /* JSOP_FORARG */ + 0, /* JSOP_FORLOCAL */ + 0, /* JSOP_DUP */ + 0, /* JSOP_DUP2 */ + 0, /* JSOP_SETCONST */ + 3, /* JSOP_BITOR */ + 3, /* JSOP_BITXOR */ + 3, /* JSOP_BITAND */ + 3, /* JSOP_EQ */ + 3, /* JSOP_NE */ + 3, /* JSOP_LT */ + 3, /* JSOP_LE */ + 3, /* JSOP_GT */ + 3, /* JSOP_GE */ + 3, /* JSOP_LSH */ + 3, /* JSOP_RSH */ + 3, /* JSOP_URSH */ + 3, /* JSOP_ADD */ + 3, /* JSOP_SUB */ + 3, /* JSOP_MUL */ + 3, /* JSOP_DIV */ + 3, /* JSOP_MOD */ + 0, /* JSOP_NOT */ + 0, /* JSOP_BITNOT */ + 3, /* JSOP_NEG */ + 3, /* JSOP_POS */ + 0, /* JSOP_DELNAME */ + 0, /* JSOP_DELPROP */ + 0, /* JSOP_DELELEM */ + 0, /* JSOP_TYPEOF */ + 0, /* JSOP_VOID */ + 0, /* JSOP_INCNAME */ + 0, /* JSOP_INCPROP */ + 0, /* JSOP_INCELEM */ + 0, /* JSOP_DECNAME */ + 0, /* JSOP_DECPROP */ + 0, /* JSOP_DECELEM */ + 0, /* JSOP_NAMEINC */ + 0, /* JSOP_PROPINC */ + 0, /* JSOP_ELEMINC */ + 0, /* JSOP_NAMEDEC */ + 0, /* JSOP_PROPDEC */ + 0, /* JSOP_ELEMDEC */ + 0, /* JSOP_GETPROP */ + 0, /* JSOP_SETPROP */ + 0, /* JSOP_GETELEM */ + 0, /* JSOP_SETELEM */ + 0, /* JSOP_CALLNAME */ + 3, /* JSOP_CALL */ + 0, /* JSOP_NAME */ + 0, /* JSOP_DOUBLE */ + 0, /* JSOP_STRING */ + 0, /* JSOP_ZERO */ + 0, /* JSOP_ONE */ + 0, /* JSOP_NULL */ + 0, /* JSOP_THIS */ + 0, /* JSOP_FALSE */ + 0, /* JSOP_TRUE */ + 0, /* JSOP_OR */ + 0, /* JSOP_AND */ + 0, /* JSOP_TABLESWITCH */ + 0, /* JSOP_LOOKUPSWITCH */ + 0, /* JSOP_STRICTEQ */ + 0, /* JSOP_STRICTNE */ + 0, /* JSOP_SETCALL */ + 2, /* JSOP_ITER */ + 2, /* JSOP_NEXTITER */ + 0, /* JSOP_ENDITER */ + 8, /* JSOP_APPLY */ + 0, /* JSOP_SWAP */ + 0, /* JSOP_OBJECT */ + 0, /* JSOP_POP */ + 3, /* JSOP_NEW */ + 0, /* JSOP_TRAP */ + 0, /* JSOP_GETARG */ + 0, /* JSOP_SETARG */ + 0, /* JSOP_GETLOCAL */ + 0, /* JSOP_SETLOCAL */ + 0, /* JSOP_UINT16 */ + 0, /* JSOP_NEWINIT */ + 0, /* JSOP_ENDINIT */ + 0, /* JSOP_INITPROP */ + 0, /* JSOP_INITELEM */ + 0, /* JSOP_DEFSHARP */ + 0, /* JSOP_USESHARP */ + 0, /* JSOP_INCARG */ + 0, /* JSOP_DECARG */ + 0, /* JSOP_ARGINC */ + 0, /* JSOP_ARGDEC */ + 0, /* JSOP_INCLOCAL */ + 0, /* JSOP_DECLOCAL */ + 0, /* JSOP_LOCALINC */ + 0, /* JSOP_LOCALDEC */ + 0, /* JSOP_IMACOP */ + 0, /* JSOP_FORNAME */ + 0, /* JSOP_FORPROP */ + 0, /* JSOP_FORELEM */ + 0, /* JSOP_POPN */ + 0, /* JSOP_BINDNAME */ + 0, /* JSOP_SETNAME */ + 0, /* JSOP_THROW */ + 0, /* JSOP_IN */ + 0, /* JSOP_INSTANCEOF */ + 0, /* JSOP_DEBUGGER */ + 0, /* JSOP_GOSUB */ + 0, /* JSOP_RETSUB */ + 0, /* JSOP_EXCEPTION */ + 0, /* JSOP_LINENO */ + 0, /* JSOP_CONDSWITCH */ + 0, /* JSOP_CASE */ + 0, /* JSOP_DEFAULT */ + 0, /* JSOP_EVAL */ + 0, /* JSOP_ENUMELEM */ + 0, /* JSOP_GETTER */ + 0, /* JSOP_SETTER */ + 0, /* JSOP_DEFFUN */ + 0, /* JSOP_DEFCONST */ + 0, /* JSOP_DEFVAR */ + 0, /* JSOP_LAMBDA */ + 0, /* JSOP_CALLEE */ + 0, /* JSOP_SETLOCALPOP */ + 0, /* JSOP_PICK */ + 0, /* JSOP_TRY */ + 0, /* JSOP_FINALLY */ + 0, /* JSOP_GETDSLOT */ + 0, /* JSOP_CALLDSLOT */ + 0, /* JSOP_ARGSUB */ + 0, /* JSOP_ARGCNT */ + 0, /* JSOP_DEFLOCALFUN */ + 0, /* JSOP_GOTOX */ + 0, /* JSOP_IFEQX */ + 0, /* JSOP_IFNEX */ + 0, /* JSOP_ORX */ + 0, /* JSOP_ANDX */ + 0, /* JSOP_GOSUBX */ + 0, /* JSOP_CASEX */ + 0, /* JSOP_DEFAULTX */ + 0, /* JSOP_TABLESWITCHX */ + 0, /* JSOP_LOOKUPSWITCHX */ + 0, /* JSOP_BACKPATCH */ + 0, /* JSOP_BACKPATCH_POP */ + 0, /* JSOP_THROWING */ + 0, /* JSOP_SETRVAL */ + 0, /* JSOP_RETRVAL */ + 0, /* JSOP_GETGVAR */ + 0, /* JSOP_SETGVAR */ + 0, /* JSOP_INCGVAR */ + 0, /* JSOP_DECGVAR */ + 0, /* JSOP_GVARINC */ + 0, /* JSOP_GVARDEC */ + 0, /* JSOP_REGEXP */ + 0, /* JSOP_DEFXMLNS */ + 0, /* JSOP_ANYNAME */ + 0, /* JSOP_QNAMEPART */ + 0, /* JSOP_QNAMECONST */ + 0, /* JSOP_QNAME */ + 0, /* JSOP_TOATTRNAME */ + 0, /* JSOP_TOATTRVAL */ + 0, /* JSOP_ADDATTRNAME */ + 0, /* JSOP_ADDATTRVAL */ + 0, /* JSOP_BINDXMLNAME */ + 0, /* JSOP_SETXMLNAME */ + 0, /* JSOP_XMLNAME */ + 0, /* JSOP_DESCENDANTS */ + 0, /* JSOP_FILTER */ + 0, /* JSOP_ENDFILTER */ + 0, /* JSOP_TOXML */ + 0, /* JSOP_TOXMLLIST */ + 0, /* JSOP_XMLTAGEXPR */ + 0, /* JSOP_XMLELTEXPR */ + 0, /* JSOP_XMLOBJECT */ + 0, /* JSOP_XMLCDATA */ + 0, /* JSOP_XMLCOMMENT */ + 0, /* JSOP_XMLPI */ + 0, /* JSOP_CALLPROP */ + 0, /* JSOP_GETUPVAR */ + 0, /* JSOP_CALLUPVAR */ + 0, /* JSOP_DELDESC */ + 0, /* JSOP_UINT24 */ + 0, /* JSOP_INDEXBASE */ + 0, /* JSOP_RESETBASE */ + 0, /* JSOP_RESETBASE0 */ + 0, /* JSOP_STARTXML */ + 0, /* JSOP_STARTXMLEXPR */ + 0, /* JSOP_CALLELEM */ + 0, /* JSOP_STOP */ + 0, /* JSOP_GETXPROP */ + 0, /* JSOP_CALLXMLNAME */ + 0, /* JSOP_TYPEOFEXPR */ + 0, /* JSOP_ENTERBLOCK */ + 0, /* JSOP_LEAVEBLOCK */ + 0, /* JSOP_IFPRIMTOP */ + 0, /* JSOP_PRIMTOP */ + 0, /* JSOP_GENERATOR */ + 0, /* JSOP_YIELD */ + 0, /* JSOP_ARRAYPUSH */ + 0, /* JSOP_GETFUNNS */ + 0, /* JSOP_ENUMCONSTELEM */ + 0, /* JSOP_LEAVEBLOCKEXPR */ + 0, /* JSOP_GETTHISPROP */ + 0, /* JSOP_GETARGPROP */ + 0, /* JSOP_GETLOCALPROP */ + 0, /* JSOP_INDEXBASE1 */ + 0, /* JSOP_INDEXBASE2 */ + 0, /* JSOP_INDEXBASE3 */ + 0, /* JSOP_CALLGVAR */ + 0, /* JSOP_CALLLOCAL */ + 0, /* JSOP_CALLARG */ + 0, /* JSOP_CALLBUILTIN */ + 0, /* JSOP_INT8 */ + 0, /* JSOP_INT32 */ + 0, /* JSOP_LENGTH */ + 0, /* JSOP_NEWARRAY */ + 0, /* JSOP_HOLE */ + 0, /* JSOP_DEFFUN_FC */ + 0, /* JSOP_DEFLOCALFUN_FC */ + 0, /* JSOP_LAMBDA_FC */ + 0, /* JSOP_OBJTOP */ + 0, /* JSOP_TRACE */ + 0, /* JSOP_GETUPVAR_DBG */ + 0, /* JSOP_CALLUPVAR_DBG */ + 0, /* JSOP_DEFFUN_DBGFC */ + 0, /* JSOP_DEFLOCALFUN_DBGFC */ + 0, /* JSOP_LAMBDA_DBGFC */ + 3, /* JSOP_CONCATN */ + 0, /* JSOP_SETMETHOD */ + 0, /* JSOP_INITMETHOD */ + 0, /* JSOP_SHARPINIT */ +}; +#define JSOP_IS_IMACOP(x) (0 \ + || x == JSOP_BITOR \ + || x == JSOP_BITXOR \ + || x == JSOP_BITAND \ + || x == JSOP_EQ \ + || x == JSOP_NE \ + || x == JSOP_LT \ + || x == JSOP_LE \ + || x == JSOP_GT \ + || x == JSOP_GE \ + || x == JSOP_LSH \ + || x == JSOP_RSH \ + || x == JSOP_URSH \ + || x == JSOP_ADD \ + || x == JSOP_SUB \ + || x == JSOP_MUL \ + || x == JSOP_DIV \ + || x == JSOP_MOD \ + || x == JSOP_NEG \ + || x == JSOP_POS \ + || x == JSOP_CALL \ + || x == JSOP_ITER \ + || x == JSOP_NEXTITER \ + || x == JSOP_APPLY \ + || x == JSOP_NEW \ + || x == JSOP_CONCATN \ +) +jsbytecode* +js_GetImacroStart(jsbytecode* pc) { + if (size_t(pc - equality_imacros.any_obj) < 36) return equality_imacros.any_obj; + if (size_t(pc - equality_imacros.obj_any) < 38) return equality_imacros.obj_any; + if (size_t(pc - binary_imacros.any_obj) < 36) return binary_imacros.any_obj; + if (size_t(pc - binary_imacros.obj_any) < 38) return binary_imacros.obj_any; + if (size_t(pc - binary_imacros.obj_obj) < 72) return binary_imacros.obj_obj; + if (size_t(pc - add_imacros.any_obj) < 36) return add_imacros.any_obj; + if (size_t(pc - add_imacros.obj_any) < 38) return add_imacros.obj_any; + if (size_t(pc - add_imacros.obj_obj) < 72) return add_imacros.obj_obj; + if (size_t(pc - unary_imacros.sign) < 41) return unary_imacros.sign; + if (size_t(pc - call_imacros.String) < 38) return call_imacros.String; + if (size_t(pc - new_imacros.String) < 38) return new_imacros.String; + if (size_t(pc - apply_imacros.apply0) < 8) return apply_imacros.apply0; + if (size_t(pc - apply_imacros.apply1) < 12) return apply_imacros.apply1; + if (size_t(pc - apply_imacros.apply2) < 16) return apply_imacros.apply2; + if (size_t(pc - apply_imacros.apply3) < 21) return apply_imacros.apply3; + if (size_t(pc - apply_imacros.apply4) < 26) return apply_imacros.apply4; + if (size_t(pc - apply_imacros.apply5) < 31) return apply_imacros.apply5; + if (size_t(pc - apply_imacros.apply6) < 36) return apply_imacros.apply6; + if (size_t(pc - apply_imacros.apply7) < 41) return apply_imacros.apply7; + if (size_t(pc - apply_imacros.apply8) < 46) return apply_imacros.apply8; + if (size_t(pc - apply_imacros.call0) < 7) return apply_imacros.call0; + if (size_t(pc - apply_imacros.call1) < 7) return apply_imacros.call1; + if (size_t(pc - apply_imacros.call2) < 7) return apply_imacros.call2; + if (size_t(pc - apply_imacros.call3) < 7) return apply_imacros.call3; + if (size_t(pc - apply_imacros.call4) < 7) return apply_imacros.call4; + if (size_t(pc - apply_imacros.call5) < 7) return apply_imacros.call5; + if (size_t(pc - apply_imacros.call6) < 7) return apply_imacros.call6; + if (size_t(pc - apply_imacros.call7) < 7) return apply_imacros.call7; + if (size_t(pc - apply_imacros.call8) < 7) return apply_imacros.call8; + if (size_t(pc - iter_imacros.for_in) < 13) return iter_imacros.for_in; + if (size_t(pc - iter_imacros.for_each) < 13) return iter_imacros.for_each; + if (size_t(pc - iter_imacros.for_in_native) < 10) return iter_imacros.for_in_native; + if (size_t(pc - iter_imacros.for_each_native) < 10) return iter_imacros.for_each_native; + if (size_t(pc - nextiter_imacros.custom_iter_next) < 12) return nextiter_imacros.custom_iter_next; + if (size_t(pc - nextiter_imacros.native_iter_next) < 12) return nextiter_imacros.native_iter_next; + if (size_t(pc - defvalue_imacros.string) < 38) return defvalue_imacros.string; + return NULL; +} diff --git a/ape-server/deps/js/src/imacros.jsasm b/ape-server/deps/js/src/imacros.jsasm new file mode 100755 index 0000000..a7ed5ff --- /dev/null +++ b/ape-server/deps/js/src/imacros.jsasm @@ -0,0 +1,728 @@ +# -*- indent-tabs-mode: nil; -*- +# vim: set sw=4 ts=8 et tw=78 ft=asm: +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is the TraceMonkey IMacro Assembler. +# +# The Initial Developer of the Original Code is +# Brendan Eich . +# Portions created by the Initial Developer are Copyright (C) 2008 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +.igroup equality JSOP_EQ-JSOP_NE + + .imacro any_obj # any obj + dup # any obj obj + dup # any obj obj obj + getprop valueOf # any obj obj valueOf + ifprimtop 1 # any obj obj valueOf + swap # any obj valueOf obj + string void # any obj valueOf obj "void" + call 1 # any obj rval + ifprimtop 3 # any obj rval + pop # any obj + dup # any obj obj + goto 2 +1: pop # any obj obj +2: callprop toString # any obj toString obj + call 0 # any obj rval + primtop (JSTYPE_NUMBER) # any obj rval +3: swap # any rval obj + pop # any rval + imacop # eqval + stop + .end + + .imacro obj_any # obj any + swap # any obj + dup # any obj obj + dup # any obj obj obj + getprop valueOf # any obj obj valueOf + ifprimtop 1 # any obj obj valueOf + swap # any obj valueOf obj + string void # any obj valueOf obj "void" + call 1 # any obj lval + ifprimtop 3 # any obj lval + pop # any obj + dup # any obj obj + goto 2 +1: pop # any obj obj +2: callprop toString # any obj toString obj + call 0 # any obj lval + primtop (JSTYPE_NUMBER) # any obj rval +3: swap # any lval obj + pop # any lval + swap # lval any + imacop # eqval + stop + .end + +.end equality + +.igroup binary JSOP_BITOR-JSOP_MOD + + .imacro any_obj # any obj + dup # any obj obj + dup # any obj obj obj + getprop valueOf # any obj obj valueOf + ifprimtop 1 # any obj obj valueOf + swap # any obj valueOf obj + string number # any obj valueOf obj "number" + call 1 # any obj rval + ifprimtop 3 # any obj rval + pop # any obj + dup # any obj obj + goto 2 +1: pop # any obj obj +2: callprop toString # any obj toString obj + call 0 # any obj rval + primtop (JSTYPE_NUMBER) # any obj rval +3: swap # any rval obj + pop # any rval + imacop # bval + stop + .end + + .imacro obj_any # obj any + swap # any obj + dup # any obj obj + dup # any obj obj obj + getprop valueOf # any obj obj valueOf + ifprimtop 1 # any obj obj valueOf + swap # any obj valueOf obj + string number # any obj valueOf obj "number" + call 1 # any obj lval + ifprimtop 3 # any obj lval + pop # any obj + dup # any obj obj + goto 2 +1: pop # any obj obj +2: callprop toString # any obj toString obj + call 0 # any obj lval + primtop (JSTYPE_NUMBER) # any obj lval +3: swap # any lval obj + pop # any lval + swap # lval any + imacop # bval + stop + .end + + .imacro obj_obj # obj1 obj2 + swap # obj2 obj1 + dup # obj2 obj1 obj1 + dup # obj2 obj1 obj1 obj1 + getprop valueOf # obj2 obj1 obj1 valueOf + ifprimtop 1 # obj2 obj1 obj1 valueOf + swap # obj2 obj1 valueOf obj1 + string number # obj2 obj1 valueOf obj1 "number" + call 1 # obj2 obj1 lval + ifprimtop 3 # obj2 obj1 lval + pop # obj2 obj1 + dup # obj2 obj1 obj1 + goto 2 +1: pop # obj2 obj1 obj1 +2: callprop toString # obj2 obj1 toString obj1 + call 0 # obj2 obj1 lval + primtop (JSTYPE_NUMBER) # obj2 obj1 lval +3: swap # obj2 lval obj1 + pop # obj2 lval + swap # lval obj2 + dup # lval obj1 obj1 + dup # lval obj obj obj + getprop valueOf # lval obj obj valueOf + ifprimtop 4 # lval obj obj valueOf + swap # lval obj valueOf obj + string number # lval obj valueOf obj "number" + call 1 # lval obj rval + ifprimtop 6 # lval obj rval + pop # lval obj + dup # lval obj obj + goto 5 +4: pop # lval obj obj +5: callprop toString # lval obj toString obj + call 0 # lval obj rval + primtop (JSTYPE_NUMBER) # lval obj rval +6: swap # lval rval obj + pop # lval rval + imacop # bval + stop + .end + +.end binary + +.igroup add JSOP_ADD + + .imacro any_obj # any obj + dup # any obj obj + dup # any obj obj obj + getprop valueOf # any obj obj valueOf + ifprimtop 1 # any obj obj valueOf + swap # any obj valueOf obj + string void # any obj valueOf obj "void" + call 1 # any obj rval + ifprimtop 3 # any obj rval + pop # any obj + dup # any obj obj + goto 2 +1: pop # any obj obj +2: callprop toString # any obj toString obj + call 0 # any obj rval + primtop (JSTYPE_VOID) # any obj rval +3: swap # any rval obj + pop # any rval + add # aval + stop + .end + + .imacro obj_any # obj any + swap # any obj + dup # any obj obj + dup # any obj obj obj + getprop valueOf # any obj obj valueOf + ifprimtop 1 # any obj obj valueOf + swap # any obj valueOf obj + string void # any obj valueOf obj "void" + call 1 # any obj lval + ifprimtop 3 # any obj lval + pop # any obj + dup # any obj obj + goto 2 +1: pop # any obj obj +2: callprop toString # any obj toString obj + call 0 # any obj lval + primtop (JSTYPE_VOID) # any obj lval +3: swap # any lval obj + pop # any lval + swap # lval any + add # aval + stop + .end + + .imacro obj_obj # obj1 obj2 + swap # obj2 obj1 + dup # obj2 obj1 obj1 + dup # obj2 obj1 obj1 obj1 + getprop valueOf # obj2 obj1 obj1 valueOf + ifprimtop 1 # obj2 obj1 obj1 valueOf + swap # obj2 obj1 valueOf obj1 + string void # obj2 obj1 valueOf obj1 "void" + call 1 # obj2 obj1 lval + ifprimtop 3 # obj2 obj1 lval + pop # obj2 obj1 + dup # obj2 obj1 obj1 + goto 2 +1: pop # obj2 obj1 obj1 +2: callprop toString # obj2 obj1 toString obj1 + call 0 # obj2 obj1 lval + primtop (JSTYPE_VOID) # obj2 obj1 lval +3: swap # obj2 lval obj1 + pop # obj2 lval + swap # lval obj2 + dup # lval obj obj + dup # lval obj obj obj + getprop valueOf # lval obj obj valueOf + ifprimtop 4 # lval obj obj valueOf + swap # lval obj valueOf obj + string void # lval obj valueOf obj "void" + call 1 # lval obj rval + ifprimtop 6 # lval obj rval + pop # lval obj + dup # lval obj obj + goto 5 +4: pop # lval obj obj +5: callprop toString # lval obj toString obj + call 0 # lval obj rval + primtop (JSTYPE_VOID) # lval obj rval +6: swap # lval rval obj + pop # lval rval + add # aval + stop + .end + +.end add + +.igroup unary JSOP_NEG-JSOP_POS + + .imacro sign # obj + dup # obj obj + dup # obj obj obj + getprop valueOf # obj obj valueOf + ifprimtop 2 # obj obj valueOf + swap # obj valueOf obj + string number # obj valueOf obj "number" + call 1 # obj lval + ifprimtop 1 # obj lval + pop # obj + dup # obj obj + goto 3 +1: swap # lval obj + pop # lval + goto 4 +2: pop # obj obj +3: callprop toString # obj toString obj + call 0 # obj lval + primtop (JSTYPE_NUMBER) # obj lval + swap # lval obj + pop # lval +4: imacop # aval + stop + .end + +.end unary + +.igroup call JSOP_CALL + + .imacro String # String this obj + dup # String this obj obj + dup # String this obj obj obj + getprop toString # String this obj obj toString + ifprimtop 1 # String this obj obj toString + swap # String this obj toString obj + call 0 # String this obj rval + ifprimtop 3 # String this obj rval + pop # String this obj + dup # String this obj obj + goto 2 +1: pop # String this obj obj +2: callprop valueOf # String this obj valueOf obj + string string # String this obj valueOf obj "string" + call 1 # String this obj rval + primtop (JSTYPE_STRING) # String this obj rval +3: swap # String this rval obj + pop # String this rval + call 1 # str + stop # str + .end + +.end call + +.igroup new JSOP_NEW + + .imacro String # String this obj + dup # String this obj obj + dup # String this obj obj obj + getprop toString # String this obj obj toString + ifprimtop 1 # String this obj obj toString + swap # String this obj toString obj + call 0 # String this obj rval + ifprimtop 3 # String this obj rval + pop # String this obj + dup # String this obj obj + goto 2 +1: pop # String this obj obj +2: callprop valueOf # String this obj valueOf obj + string string # String this obj valueOf obj "string" + call 1 # String this obj rval + primtop (JSTYPE_STRING) # String this obj rval +3: swap # String this rval obj + pop # String this rval + new 1 # strobj + stop # strobj + .end + +.end new + +.igroup apply JSOP_APPLY + + .imacro apply0 # apply fun this arr + pick 3 # fun this arr apply + pop # fun this arr + pop # fun this + call 0 # + stop # + .end # + + .imacro apply1 # apply fun this arr + pick 3 # fun this arr apply + pop # fun this arr + dup # fun this arr arr + zero # fun this arr arr 0 + getelem # fun this arr arg0 + swap # fun this arg0 arr + pop # fun this arg0 + call 1 # + stop # + .end # + + .imacro apply2 # apply fun this arr + pick 3 # fun this arr apply + pop # fun this arr + dup # fun this arr arr + zero # fun this arr arr 0 + getelem # fun this arr arg0 + swap # fun this arg0 arr + dup # fun this arg0 arr arr + one # fun this arg0 arr arr 1 + getelem # fun this arg0 arr arg1 + swap # fun this arg0 arg1 arr + pop # fun this arg0 arg1 + call 2 # + stop # + .end # + + .imacro apply3 # apply fun this arr + pick 3 # fun this arr apply + pop # fun this arr + dup # fun this arr arr + zero # fun this arr arr 0 + getelem # fun this arr arg0 + swap # fun this arg0 arr + dup # fun this arg0 arr arr + one # fun this arg0 arr arr 1 + getelem # fun this arg0 arr arg1 + swap # fun this arg0 arg1 arr + dup # fun this arg0 arg1 arr arr + int8 2 # fun this arg0 arg1 arr arr 2 + getelem # fun this arg0 arg1 arr arg2 + swap # fun this arg0 arg1 arg2 arr + pop # fun this arg0 arg1 arg2 + call 3 # + stop # + .end # + + .imacro apply4 # apply fun this arr + pick 3 # fun this arr apply + pop # fun this arr + dup # fun this arr arr + zero # fun this arr arr 0 + getelem # fun this arr arg0 + swap # fun this arg0 arr + dup # fun this arg0 arr arr + one # fun this arg0 arr arr 1 + getelem # fun this arg0 arr arg1 + swap # fun this arg0 arg1 arr + dup # fun this arg0 arg1 arr arr + int8 2 # fun this arg0 arg1 arr arr 2 + getelem # fun this arg0 arg1 arr arg2 + swap # fun this arg0 arg1 arg2 arr + dup # fun this arg0 arg1 arg2 arr arr + int8 3 # fun this arg0 arg1 arg2 arr arr 3 + getelem # fun this arg0 arg1 arg2 arr arg3 + swap # fun this arg0 arg1 arg2 arg3 arr + pop # fun this arg0 arg1 arg2 arg3 + call 4 # + stop # + .end # + + .imacro apply5 # apply fun this arr + pick 3 # fun this arr apply + pop # fun this arr + dup # fun this arr arr + zero # fun this arr arr 0 + getelem # fun this arr arg0 + swap # fun this arg0 arr + dup # fun this arg0 arr arr + one # fun this arg0 arr arr 1 + getelem # fun this arg0 arr arg1 + swap # fun this arg0 arg1 arr + dup # fun this arg0 arg1 arr arr + int8 2 # fun this arg0 arg1 arr arr 2 + getelem # fun this arg0 arg1 arr arg2 + swap # fun this arg0 arg1 arg2 arr + dup # fun this arg0 arg1 arg2 arr arr + int8 3 # fun this arg0 arg1 arg2 arr arr 3 + getelem # fun this arg0 arg1 arg2 arr arg3 + swap # fun this arg0 arg1 arg2 arg3 arr + dup # fun this arg0 arg1 arg2 arg3 arr arr + int8 4 # fun this arg0 arg1 arg2 arg3 arr arr 4 + getelem # fun this arg0 arg1 arg2 arg3 arr arg4 + swap # fun this arg0 arg1 arg2 arg3 arg4 arr + pop # fun this arg0 arg1 arg2 arg3 arg4 + call 5 # + stop # + .end # + + .imacro apply6 # apply fun this arr + pick 3 # fun this arr apply + pop # fun this arr + dup # fun this arr arr + zero # fun this arr arr 0 + getelem # fun this arr arg0 + swap # fun this arg0 arr + dup # fun this arg0 arr arr + one # fun this arg0 arr arr 1 + getelem # fun this arg0 arr arg1 + swap # fun this arg0 arg1 arr + dup # fun this arg0 arg1 arr arr + int8 2 # fun this arg0 arg1 arr arr 2 + getelem # fun this arg0 arg1 arr arg2 + swap # fun this arg0 arg1 arg2 arr + dup # fun this arg0 arg1 arg2 arr arr + int8 3 # fun this arg0 arg1 arg2 arr arr 3 + getelem # fun this arg0 arg1 arg2 arr arg3 + swap # fun this arg0 arg1 arg2 arg3 arr + dup # fun this arg0 arg1 arg2 arg3 arr arr + int8 4 # fun this arg0 arg1 arg2 arg3 arr arr 4 + getelem # fun this arg0 arg1 arg2 arg3 arr arg4 + swap # fun this arg0 arg1 arg2 arg3 arg4 arr + dup # fun this arg0 arg1 arg2 arg3 arg4 arr arr + int8 5 # fun this arg0 arg1 arg2 arg3 arg4 arr arr 5 + getelem # fun this arg0 arg1 arg2 arg3 arg4 arr arg5 + swap # fun this arg0 arg1 arg2 arg3 arg4 arg5 arr + pop # fun this arg0 arg1 arg2 arg3 arg4 arg5 + call 6 # + stop # + .end # + + .imacro apply7 # apply fun this arr + pick 3 # fun this arr apply + pop # fun this arr + dup # fun this arr arr + zero # fun this arr arr 0 + getelem # fun this arr arg0 + swap # fun this arg0 arr + dup # fun this arg0 arr arr + one # fun this arg0 arr arr 1 + getelem # fun this arg0 arr arg1 + swap # fun this arg0 arg1 arr + dup # fun this arg0 arg1 arr arr + int8 2 # fun this arg0 arg1 arr arr 2 + getelem # fun this arg0 arg1 arr arg2 + swap # fun this arg0 arg1 arg2 arr + dup # fun this arg0 arg1 arg2 arr arr + int8 3 # fun this arg0 arg1 arg2 arr arr 3 + getelem # fun this arg0 arg1 arg2 arr arg3 + swap # fun this arg0 arg1 arg2 arg3 arr + dup # fun this arg0 arg1 arg2 arg3 arr arr + int8 4 # fun this arg0 arg1 arg2 arg3 arr arr 4 + getelem # fun this arg0 arg1 arg2 arg3 arr arg4 + swap # fun this arg0 arg1 arg2 arg3 arg4 arr + dup # fun this arg0 arg1 arg2 arg3 arg4 arr arr + int8 5 # fun this arg0 arg1 arg2 arg3 arg4 arr arr 5 + getelem # fun this arg0 arg1 arg2 arg3 arg4 arr arg5 + swap # fun this arg0 arg1 arg2 arg3 arg4 arg5 arr + dup # fun this arg0 arg1 arg2 arg3 arg4 arg5 arr arr + int8 6 # fun this arg0 arg1 arg2 arg3 arg4 arg5 arr arr 6 + getelem # fun this arg0 arg1 arg2 arg3 arg4 arg5 arr arg6 + swap # fun this arg0 arg1 arg2 arg3 arg4 arg5 arg6 arr + pop # fun this arg0 arg1 arg2 arg3 arg4 arg5 arg6 + call 7 # + stop # + .end # + + .imacro apply8 # apply fun this arr + pick 3 # fun this arr apply + pop # fun this arr + dup # fun this arr arr + zero # fun this arr arr 0 + getelem # fun this arr arg0 + swap # fun this arg0 arr + dup # fun this arg0 arr arr + one # fun this arg0 arr arr 1 + getelem # fun this arg0 arr arg1 + swap # fun this arg0 arg1 arr + dup # fun this arg0 arg1 arr arr + int8 2 # fun this arg0 arg1 arr arr 2 + getelem # fun this arg0 arg1 arr arg2 + swap # fun this arg0 arg1 arg2 arr + dup # fun this arg0 arg1 arg2 arr arr + int8 3 # fun this arg0 arg1 arg2 arr arr 3 + getelem # fun this arg0 arg1 arg2 arr arg3 + swap # fun this arg0 arg1 arg2 arg3 arr + dup # fun this arg0 arg1 arg2 arg3 arr arr + int8 4 # fun this arg0 arg1 arg2 arg3 arr arr 4 + getelem # fun this arg0 arg1 arg2 arg3 arr arg4 + swap # fun this arg0 arg1 arg2 arg3 arg4 arr + dup # fun this arg0 arg1 arg2 arg3 arg4 arr arr + int8 5 # fun this arg0 arg1 arg2 arg3 arg4 arr arr 5 + getelem # fun this arg0 arg1 arg2 arg3 arg4 arr arg5 + swap # fun this arg0 arg1 arg2 arg3 arg4 arg5 arr + dup # fun this arg0 arg1 arg2 arg3 arg4 arg5 arr arr + int8 6 # fun this arg0 arg1 arg2 arg3 arg4 arg5 arr arr 6 + getelem # fun this arg0 arg1 arg2 arg3 arg4 arg5 arr arg6 + swap # fun this arg0 arg1 arg2 arg3 arg4 arg5 arg6 arr + dup # fun this arg0 arg1 arg2 arg3 arg4 arg5 arg6 arr arr + int8 7 # fun this arg0 arg1 arg2 arg3 arg4 arg5 arg6 arr arr 7 + getelem # fun this arg0 arg1 arg2 arg3 arg4 arg5 arg6 arr arg7 + swap # fun this arg0 arg1 arg2 arg3 arg4 arg5 arg6 arg7 arr + pop # fun this arg0 arg1 arg2 arg3 arg4 arg5 arg6 arg7 + call 8 # + stop # + .end # + + .imacro call0 # call fun + swap # fun call + pop # fun + null # fun this + call 0 # + stop # + .end # + + .imacro call1 # call fun this + pick 2 # fun this call + pop # fun this + call 0 # + stop # + .end # + + .imacro call2 # call fun this arg0 + pick 3 # fun this arg0 call + pop # fun this arg0 + call 1 # + stop # + .end # + + .imacro call3 # call fun this arg0 arg1 + pick 4 # fun this arg0 arg1 call + pop # fun this arg0 arg1 + call 2 # + stop # + .end # + + .imacro call4 # call fun this arg0 arg1 arg2 + pick 5 # fun this arg0 arg1 arg2 call + pop # fun this arg0 arg1 arg2 + call 3 # + stop # + .end # + + .imacro call5 # call fun this arg0 arg1 arg2 arg3 + pick 6 # fun this arg0 arg1 arg2 arg3 call + pop # fun this arg0 arg1 arg2 arg3 + call 4 # + stop # + .end # + + .imacro call6 # call fun this arg0 arg1 arg2 arg3 arg4 + pick 7 # fun this arg0 arg1 arg2 arg3 arg4 call + pop # fun this arg0 arg1 arg2 arg3 arg4 + call 5 # + stop # + .end # + + .imacro call7 # call fun this arg0 arg1 arg2 arg3 arg4 arg5 + pick 8 # fun this arg0 arg1 arg2 arg3 arg4 arg5 call + pop # fun this arg0 arg1 arg2 arg3 arg4 arg5 + call 6 # + stop # + .end # + + .imacro call8 # call fun this arg0 arg1 arg2 arg3 arg4 arg5 arg6 + pick 9 # fun this arg0 arg1 arg2 arg3 arg4 arg5 arg6 call + pop # fun this arg0 arg1 arg2 arg3 arg4 arg5 arg6 + call 7 # + stop # + .end # + +.end apply + +.igroup iter JSOP_ITER + + .imacro for_in # obj + callprop iterator # fun obj + int8 (JSITER_ENUMERATE) # fun obj flags + call 1 # iterobj + objtop (JSMSG_BAD_ITERATOR_RETURN) # iterobj + push # iterobj undef + stop + .end + + .imacro for_each # obj + callprop iterator # fun obj + int8 (JSITER_ENUMERATE|JSITER_FOREACH) # fun obj flags + call 1 # iterobj + objtop (JSMSG_BAD_ITERATOR_RETURN) # iterobj + push # iterobj undef + stop + .end + + .imacro for_in_native # obj + callbuiltin (JSBUILTIN_ObjectToIterator) # fun obj + int8 (JSITER_ENUMERATE) # fun obj flags + call 1 # iterobj + push # iterobj undef + stop + .end + + .imacro for_each_native # obj + callbuiltin (JSBUILTIN_ObjectToIterator) # fun obj + int8 (JSITER_ENUMERATE|JSITER_FOREACH) # fun obj flags + call 1 # iterobj + push # iterobj undef + stop + .end + +.end iter + +.igroup nextiter JSOP_NEXTITER + + .imacro custom_iter_next # iterobj prevval + pop # iterobj + dup # iterobj iterobj + callprop next # iterobj fun iterobj + call 0 # iterobj nextval + dup # iterobj nextval? nextval? + hole # iterobj nextval? nextval? hole + strictne # iterobj nextval? boolean + stop + .end + + .imacro native_iter_next # iterobj prevval + pop # iterobj + dup # iterobj iterobj + callbuiltin (JSBUILTIN_CallIteratorNext) # iterobj fun iterobj + call 0 # iterobj nextval? + dup # iterobj nextval? nextval? + hole # iterobj nextval? nextval? hole + strictne # iterobj nextval? boolean + stop + .end + +.end nextiter + + +# Force accounting for unused argument 'N' by touching it with 'swap'. +.igroup defvalue JSOP_CONCATN + + .imacro string # [arg1..argN] argI I + swap # [arg1..argN] I argI + dup # [arg1..argN] I argI argI + dup # [arg1..argN] I argI argI argI + getprop valueOf # [arg1..argN] I argI argI valueOf + ifprimtop 1 # [arg1..argN] I argI argI valueOf + swap # [arg1..argN] I argI valueOf argI + string void # [arg1..argN] I argI valueOf argI "void" + call 1 # [arg1..argN] I argI val + ifprimtop 3 # [arg1..argN] I argI val + pop # [arg1..argN] I argI + dup # [arg1..argN] I argI argI + goto 2 +1: pop # [arg1..argN] I argI argI +2: callprop toString # [arg1..argN] I argI toString argI + call 0 # [arg1..argN] I argI val + primtop (JSTYPE_VOID) # [arg1..argN] I argI val +3: swap # [arg1..argN] I val argI + pop # [arg1..argN] I val + swap # [arg1..argN] val I + imacop # catstr + stop + .end + +.end defvalue diff --git a/ape-server/deps/js/src/javascript-trace.d b/ape-server/deps/js/src/javascript-trace.d new file mode 100755 index 0000000..258c6cd --- /dev/null +++ b/ape-server/deps/js/src/javascript-trace.d @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Copyright (C) 2007 Sun Microsystems, Inc. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * javascript provider probes + * + * function-entry (filename, classname, funcname) + * function-info (filename, classname, funcname, lineno, + * runfilename, runlineno) + * function-args (filename, classname, funcname, argc, argv, argv0, + * argv1, argv2, argv3, argv4) + * function-rval (filename, classname, funcname, lineno, rval, rval0) + * function-return (filename, classname, funcname) + * object-create-start (filename, classname) + * object-create (filename, classname, *object, rlineno) + * object-create-done (filename, classname) + * object-finalize (NULL, classname, *object) + * execute-start (filename, lineno) + * execute-done (filename, lineno) + */ + +provider javascript { + probe function__entry(char *, char *, char *); + probe function__info(char *, char *, char *, int, char *, int); + probe function__args(char *, char *, char *, int, void *, void *, void *, + void *, void *, void *); + probe function__rval(char *, char *, char *, int, void *, void *); + probe function__return(char *, char *, char *); + probe object__create__start(char *, char *); + probe object__create__done(char *, char *); + /* XXX must use unsigned longs here instead of uintptr_t for OS X + (Apple radar: 5194316 & 5565198) */ + probe object__create(char *, char *, unsigned long, int); + probe object__finalize(char *, char *, unsigned long); + probe execute__start(char *, int); + probe execute__done(char *, int); +}; + +/* +#pragma D attributes Unstable/Unstable/Common provider mozilla provider +#pragma D attributes Private/Private/Unknown provider mozilla module +#pragma D attributes Private/Private/Unknown provider mozilla function +#pragma D attributes Unstable/Unstable/Common provider mozilla name +#pragma D attributes Unstable/Unstable/Common provider mozilla args +*/ + diff --git a/ape-server/deps/js/src/jitstats.tbl b/ape-server/deps/js/src/jitstats.tbl new file mode 100755 index 0000000..1f85faf --- /dev/null +++ b/ape-server/deps/js/src/jitstats.tbl @@ -0,0 +1,97 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=0 ft=C: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released + * June 22, 2008. + * + * The Initial Developer of the Original Code is + * Brian Crowder + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* NB: Keep this list synced with jitstatHandler in trace-test.js. */ +/** + * Proper use of this file: Consumers must define JITSTAT; they can optionally + * also define MONITOR_JITSTAT or RECORDER_JITSTAT or both to do separate + * things with stats that are monitor or recorder specific. If those are not + * defined, they will be automatically defined to JITSTAT. + */ +#ifdef DEFINED_MONITOR_JITSTAT +#error "How did that happen?" +#endif + +#ifdef DEFINED_RECORDER_JITSTAT +#error "How did that happen?" +#endif + +#ifndef MONITOR_JITSTAT +#define MONITOR_JITSTAT(_ident, _name) JITSTAT(_ident) +#define DEFINED_MONITOR_JITSTAT +#endif + +#ifndef RECORDER_JITSTAT +#define RECORDER_JITSTAT(_ident, _name) JITSTAT(_ident) +#define DEFINED_RECORDER_JITSTAT +#endif + +RECORDER_JITSTAT(recorderStarted, "started") +RECORDER_JITSTAT(recorderAborted, "aborted") +RECORDER_JITSTAT(traceCompleted, "completed") +MONITOR_JITSTAT(sideExitIntoInterpreter, "exits") +MONITOR_JITSTAT(timeoutIntoInterpreter, "timeouts") +MONITOR_JITSTAT(typeMapMismatchAtEntry, "type mismatch") +RECORDER_JITSTAT(returnToDifferentLoopHeader, "different header") +MONITOR_JITSTAT(traceTriggered, "triggered") +MONITOR_JITSTAT(globalShapeMismatchAtEntry, "global mismatch") +RECORDER_JITSTAT(treesTrashed, "trees trashed") +RECORDER_JITSTAT(slotPromoted, "slot promoted") +RECORDER_JITSTAT(unstableLoopVariable, "unstable loop variable") +RECORDER_JITSTAT(breakLoopExits, "breaks") +RECORDER_JITSTAT(returnLoopExits, "returns") +RECORDER_JITSTAT(mergedLoopExits, "merged loop exits") +RECORDER_JITSTAT(noCompatInnerTrees, "unstableInnerCalls") +RECORDER_JITSTAT(blacklisted, "blacklisted") +MONITOR_JITSTAT(cacheFlushed, "flushed") +JITSTAT(archIsIA32) +JITSTAT(archIsAMD64) +JITSTAT(archIs64BIT) +JITSTAT(archIsARM) +JITSTAT(archIsSPARC) +JITSTAT(archIsPPC) + +#ifdef DEFINED_MONITOR_JITSTAT +#undef DEFINED_MONITOR_JITSTAT +#undef MONITOR_JITSTAT +#endif + +#ifdef DEFINED_RECORDER_JITSTAT +#undef DEFINED_RECORDER_JITSTAT +#undef RECORDER_JITSTAT +#endif diff --git a/ape-server/deps/js/src/js-confdefs.h b/ape-server/deps/js/src/js-confdefs.h new file mode 100644 index 0000000..839873c --- /dev/null +++ b/ape-server/deps/js/src/js-confdefs.h @@ -0,0 +1,97 @@ +/* List of defines generated by configure. Included with preprocessor flag, + * -include, to avoid long list of -D defines on the compile command-line. + * Do not edit. + */ + +#ifndef _JS_CONFDEFS_H_ +#define _JS_CONFDEFS_H_ + +#define AVMPLUS_IA32 1 +#define AVMPLUS_LINUX 1 +#define AVMPLUS_UNIX 1 +#define CPP_THROW_NEW throw() +#define D_INO d_ino +#define EDITLINE 1 +#define FEATURE_NANOJIT 1 +#define HAVE_CPP_ACCESS_CHANGING_USING 1 +#define HAVE_CPP_AMBIGUITY_RESOLVING_USING 1 +#define HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR 1 +#define HAVE_CPP_EXPLICIT 1 +#define HAVE_CPP_MODERN_SPECIALIZE_TEMPLATE_SYNTAX 1 +#define HAVE_CPP_NAMESPACE_STD 1 +#define HAVE_CPP_NEW_CASTS 1 +#define HAVE_CPP_PARTIAL_SPECIALIZATION 1 +#define HAVE_CPP_TYPENAME 1 +#define HAVE_CPP_UNAMBIGUOUS_STD_NOTEQUAL 1 +#define HAVE_DIRENT_H 1 +#define HAVE_DLADDR 1 +#define HAVE_FCHMOD 1 +#define HAVE_FLOCKFILE 1 +#define HAVE_GETC_UNLOCKED 1 +#define HAVE_GETOPT_H 1 +#define HAVE_GETPAGESIZE 1 +#define HAVE_GNU_GET_LIBC_VERSION 1 +#define HAVE_GNU_LIBC_VERSION_H 1 +#define HAVE_I18N_LC_MESSAGES 1 +#define HAVE_ICONV 1 +#define HAVE_INT16_T 1 +#define HAVE_INT32_T 1 +#define HAVE_INT64_T 1 +#define HAVE_LCHOWN 1 +#define HAVE_LIBDL 1 +#define HAVE_LIBM 1 +#define HAVE_LOCALTIME_R 1 +#define HAVE_LSTAT64 1 +#define HAVE_MALLOC_H 1 +#define HAVE_MBRTOWC 1 +#define HAVE_MEMMOVE 1 +#define HAVE_MEMORY_H 1 +#define HAVE_NL_TYPES_H 1 +#define HAVE_RANDOM 1 +#define HAVE_RES_NINIT 1 +#define HAVE_RINT 1 +#define HAVE_SBRK 1 +#define HAVE_SETLOCALE 1 +#define HAVE_SIGINFO_T 1 +#define HAVE_SNPRINTF 1 +#define HAVE_STAT64 1 +#define HAVE_STATVFS 1 +#define HAVE_STATVFS64 1 +#define HAVE_STRERROR 1 +#define HAVE_STRTOK_R 1 +#define HAVE_ST_BLKSIZE 1 +#define HAVE_SYS_BITYPES_H 1 +#define HAVE_SYS_CDEFS_H 1 +#define HAVE_SYS_MOUNT_H 1 +#define HAVE_SYS_STATFS_H 1 +#define HAVE_SYS_STATVFS_H 1 +#define HAVE_SYS_VFS_H 1 +#define HAVE_TM_ZONE_TM_GMTOFF 1 +#define HAVE_TRUNCATE64 1 +#define HAVE_UINT 1 +#define HAVE_UNAME_DOMAINNAME_FIELD 1 +#define HAVE_UNISTD_H 1 +#define HAVE_VA_COPY 1 +#define HAVE_VISIBILITY_ATTRIBUTE 1 +#define HAVE_VISIBILITY_HIDDEN_ATTRIBUTE 1 +#define HAVE_WCRTOMB 1 +#define HAVE__UNWIND_BACKTRACE 1 +#define HAVE___CXA_DEMANGLE 1 +#define JS_ALIGN_OF_POINTER 4 +#define JS_BITS_PER_WORD_LOG2 5 +#define JS_BYTES_PER_DOUBLE 8 +#define JS_BYTES_PER_WORD 4 +#define JS_HAVE_STDINT_H 1 +#define JS_TRACER 1 +#define MOZ_DLL_SUFFIX ".so" +#define NEED_CPP_UNUSED_IMPLEMENTATIONS 1 +#define NEW_H +#define STDC_HEADERS 1 +#define UNIX_ASYNC_DNS 1 +#define VA_COPY va_copy +#define XP_UNIX 1 +#define X_DISPLAY_MISSING 1 +#define _REENTRANT 1 + +#endif /* _JS_CONFDEFS_H_ */ + diff --git a/ape-server/deps/js/src/js-config b/ape-server/deps/js/src/js-config new file mode 100755 index 0000000..f264b92 --- /dev/null +++ b/ape-server/deps/js/src/js-config @@ -0,0 +1,111 @@ +#!/bin/sh + +prefix='/usr/local' +mozilla_version='' +LIBRARY_NAME='mozjs' +NSPR_CFLAGS='' +JS_CONFIG_LIBS=' -ldl -lm -lm -ldl ' +MOZ_JS_LIBS='-L/usr/local/lib -lmozjs' + +usage() +{ + cat <&2 +fi + +while test $# -gt 0; do + case "$1" in + -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) optarg= ;; + esac + + case $1 in + --prefix=*) + prefix=$optarg + ;; + --prefix) + echo_prefix=yes + ;; + --exec-prefix=*) + exec_prefix=$optarg + ;; + --exec-prefix) + echo_exec_prefix=yes + ;; + --includedir=*) + includedir=$optarg + ;; + --includedir) + echo_includedir=yes + ;; + --libdir=*) + libdir=$optarg + ;; + --libdir) + echo_libdir=yes + ;; + --version) + echo "$mozilla_version" + ;; + --cflags) + echo_cflags=yes + ;; + --libs) + echo_libs=yes + ;; + *) + usage 1 1>&2 + ;; + esac + shift +done + +# Set variables that may be dependent upon other variables +if test -z "$exec_prefix"; then + exec_prefix=/usr/local +fi +if test -z "$includedir"; then + includedir=/usr/local/include +fi +if test -z "$libdir"; then + libdir=/usr/local/lib +fi + +if test "$echo_prefix" = "yes"; then + echo $prefix +fi + +if test "$echo_exec_prefix" = "yes"; then + echo $exec_prefix +fi + +if test "$echo_includedir" = "yes"; then + echo $includedir +fi + +if test "$echo_libdir" = "yes"; then + echo $libdir +fi + +if test "$echo_cflags" = "yes"; then + echo "-I$includedir/js $NSPR_CFLAGS" +fi + +if test "$echo_libs" = "yes"; then + echo "$MOZ_JS_LIBS $JS_CONFIG_LIBS" +fi diff --git a/ape-server/deps/js/src/js-config.h b/ape-server/deps/js/src/js-config.h new file mode 100644 index 0000000..326ff7a --- /dev/null +++ b/ape-server/deps/js/src/js-config.h @@ -0,0 +1,85 @@ +/* js-config.h. Generated automatically by configure. */ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef js_config_h___ +#define js_config_h___ + +/* Definitions set at build time that affect SpiderMonkey's public API. + This header file is generated by the SpiderMonkey configure script, + and installed along with jsapi.h. */ + +/* Define to 1 if SpiderMonkey should support multi-threaded clients. */ +/* #undef JS_THREADSAFE */ + +/* Define to 1 if SpiderMonkey should support the ability to perform + entirely too much GC. */ +/* #undef JS_GC_ZEAL */ + +/* Define to 1 if the standard header is present and + useable. See jstypes.h and jsstdint.h. */ +#define JS_HAVE_STDINT_H 1 + +/* Define to 1 if the N-byte __intN types are defined by the + compiler. */ +/* #undef JS_HAVE___INTN */ + +/* Define to 1 if #including provides definitions for + intptr_t and uintptr_t. */ +/* #undef JS_STDDEF_H_HAS_INTPTR_T */ + +/* Define to 1 if #including provides definitions for + intptr_t and uintptr_t. */ +/* #undef JS_CRTDEFS_H_HAS_INTPTR_T */ + +/* The configure script defines these if it doesn't #define + JS_HAVE_STDINT_H. */ +/* #undef JS_INT8_TYPE */ +/* #undef JS_INT16_TYPE */ +/* #undef JS_INT32_TYPE */ +/* #undef JS_INT64_TYPE */ +/* #undef JS_INTPTR_TYPE */ +#define JS_BYTES_PER_WORD 4 + +/* Some mozilla code uses JS-friend APIs that depend on JS_TRACER being + correct. */ +#define JS_TRACER 1 + +#endif /* js_config_h___ */ diff --git a/ape-server/deps/js/src/js-config.h.in b/ape-server/deps/js/src/js-config.h.in new file mode 100755 index 0000000..5e667e3 --- /dev/null +++ b/ape-server/deps/js/src/js-config.h.in @@ -0,0 +1,84 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef js_config_h___ +#define js_config_h___ + +/* Definitions set at build time that affect SpiderMonkey's public API. + This header file is generated by the SpiderMonkey configure script, + and installed along with jsapi.h. */ + +/* Define to 1 if SpiderMonkey should support multi-threaded clients. */ +#undef JS_THREADSAFE + +/* Define to 1 if SpiderMonkey should support the ability to perform + entirely too much GC. */ +#undef JS_GC_ZEAL + +/* Define to 1 if the standard header is present and + useable. See jstypes.h and jsstdint.h. */ +#undef JS_HAVE_STDINT_H + +/* Define to 1 if the N-byte __intN types are defined by the + compiler. */ +#undef JS_HAVE___INTN + +/* Define to 1 if #including provides definitions for + intptr_t and uintptr_t. */ +#undef JS_STDDEF_H_HAS_INTPTR_T + +/* Define to 1 if #including provides definitions for + intptr_t and uintptr_t. */ +#undef JS_CRTDEFS_H_HAS_INTPTR_T + +/* The configure script defines these if it doesn't #define + JS_HAVE_STDINT_H. */ +#undef JS_INT8_TYPE +#undef JS_INT16_TYPE +#undef JS_INT32_TYPE +#undef JS_INT64_TYPE +#undef JS_INTPTR_TYPE +#undef JS_BYTES_PER_WORD + +/* Some mozilla code uses JS-friend APIs that depend on JS_TRACER being + correct. */ +#undef JS_TRACER + +#endif /* js_config_h___ */ diff --git a/ape-server/deps/js/src/js-config.in b/ape-server/deps/js/src/js-config.in new file mode 100755 index 0000000..59b4719 --- /dev/null +++ b/ape-server/deps/js/src/js-config.in @@ -0,0 +1,111 @@ +#!/bin/sh + +prefix='@prefix@' +mozilla_version='@MOZILLA_VERSION@' +LIBRARY_NAME='@LIBRARY_NAME@' +NSPR_CFLAGS='@NSPR_CFLAGS@' +JS_CONFIG_LIBS='@JS_CONFIG_LIBS@' +MOZ_JS_LIBS='@MOZ_JS_LIBS@' + +usage() +{ + cat <&2 +fi + +while test $# -gt 0; do + case "$1" in + -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) optarg= ;; + esac + + case $1 in + --prefix=*) + prefix=$optarg + ;; + --prefix) + echo_prefix=yes + ;; + --exec-prefix=*) + exec_prefix=$optarg + ;; + --exec-prefix) + echo_exec_prefix=yes + ;; + --includedir=*) + includedir=$optarg + ;; + --includedir) + echo_includedir=yes + ;; + --libdir=*) + libdir=$optarg + ;; + --libdir) + echo_libdir=yes + ;; + --version) + echo "$mozilla_version" + ;; + --cflags) + echo_cflags=yes + ;; + --libs) + echo_libs=yes + ;; + *) + usage 1 1>&2 + ;; + esac + shift +done + +# Set variables that may be dependent upon other variables +if test -z "$exec_prefix"; then + exec_prefix=@exec_prefix@ +fi +if test -z "$includedir"; then + includedir=@includedir@ +fi +if test -z "$libdir"; then + libdir=@libdir@ +fi + +if test "$echo_prefix" = "yes"; then + echo $prefix +fi + +if test "$echo_exec_prefix" = "yes"; then + echo $exec_prefix +fi + +if test "$echo_includedir" = "yes"; then + echo $includedir +fi + +if test "$echo_libdir" = "yes"; then + echo $libdir +fi + +if test "$echo_cflags" = "yes"; then + echo "-I$includedir/js $NSPR_CFLAGS" +fi + +if test "$echo_libs" = "yes"; then + echo "$MOZ_JS_LIBS $JS_CONFIG_LIBS" +fi diff --git a/ape-server/deps/js/src/js.mdp b/ape-server/deps/js/src/js.mdp new file mode 100755 index 0000000000000000000000000000000000000000..8da64fb6b61a5a047ac54c88020cf397fa7eaa25 GIT binary patch literal 17922 zcmeI3-A)rh6vxj}3RDD&d|#MMV&sNEPuNv)f8dOnd+p zzn{e$6YhNi?|2EX)acnQgDY|a;b!UCWV@63|IgWTeltmvNxQmvcQFOMum#Wqbafqp z-*j~~frGB@o|<2Sd3X%hU>ZUe!_g;E=^g^HFT9`K(=~N!2=qcf47Th_&0ktvsJALV z_P(WOMQm5lkO&X~B0vO)01+SpM1Tko0U|&IhyW2tJb^Ek)njD=KEonp`@bi?A8O!q z1k%ulCqM&`flDw5mtp91;p9#-48~ysCSmFfIVdEl1TuK`n>sB1>yua)(V<}Uhmc4P zcSRbk+nKzQfmYspKJfu8zqq~EY)EJ*Ka7_jq5K_pU7#=+iY1irQ=5_ zgd-eN#w&D8u6T+EtRSS)b*yH+AvBjqme*(*KeV_l*AcAJW?MollVk6%p(w>Jw*z)x z_)YH!+#57ZbuttWi;dn9FZC^YhC811meAXtdTR2&Lus@lZ3f(~vHXs(Yes#K>PSa@ zAf10BKcF^Ql!2%UjeJfj{v%ql#&Z14pThVJje)pM!Z~T3exYT!Q`Yh5WefIa3Z)W; z;i}*oJ&Se>Tj7}Q2(Rte52nhlt(C8~1D2ng`+uFoJ=%+R-yV9tJ==Y!JgEEm>X5bG% C_`cBq literal 0 HcmV?d00001 diff --git a/ape-server/deps/js/src/js.msg b/ape-server/deps/js/src/js.msg new file mode 100755 index 0000000..bff0d08 --- /dev/null +++ b/ape-server/deps/js/src/js.msg @@ -0,0 +1,319 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * This is the JavaScript error message file. + * + * The format for each JS error message is: + * + * MSG_DEF(, , , , + * ) + * + * where ; + * is a legal C identifer that will be used in the + * JS engine source. + * + * is an unique integral value identifying this error. + * + * is an integer literal specifying the total number of + * replaceable arguments in the following format string. + * + * is an exception index from the enum in jsexn.c; + * JSEXN_NONE for none. The given exception index will be raised by the + * engine when the corresponding error occurs. + * + * is a string literal, optionally containing sequences + * {X} where X is an integer representing the argument number that will + * be replaced with a string value when the error is reported. + * + * e.g. + * + * MSG_DEF(JSMSG_NOT_A_SUBSPECIES, 73, JSEXN_NONE, 2, + * "{0} is not a member of the {1} family") + * + * can be used: + * + * JS_ReportErrorNumber(JSMSG_NOT_A_SUBSPECIES, "Rhino", "Monkey"); + * + * to report: + * + * "Rhino is not a member of the Monkey family" + * + * Before adding a new MSG_DEF at the end, look for JSMSG_UNUSED free + * index placeholders in the middle of the list. + */ + +MSG_DEF(JSMSG_NOT_AN_ERROR, 0, 0, JSEXN_NONE, "") +MSG_DEF(JSMSG_NOT_DEFINED, 1, 1, JSEXN_REFERENCEERR, "{0} is not defined") +MSG_DEF(JSMSG_INACTIVE, 2, 0, JSEXN_INTERNALERR, "nothing active on context") +MSG_DEF(JSMSG_MORE_ARGS_NEEDED, 3, 3, JSEXN_TYPEERR, "{0} requires more than {1} argument{2}") +MSG_DEF(JSMSG_BAD_CHAR, 4, 1, JSEXN_INTERNALERR, "invalid format character {0}") +MSG_DEF(JSMSG_BAD_TYPE, 5, 1, JSEXN_TYPEERR, "unknown type {0}") +MSG_DEF(JSMSG_ALLOC_OVERFLOW, 6, 0, JSEXN_INTERNALERR, "allocation size overflow") +MSG_DEF(JSMSG_CANT_UNLOCK, 7, 0, JSEXN_INTERNALERR, "can't unlock memory") +MSG_DEF(JSMSG_INCOMPATIBLE_PROTO, 8, 3, JSEXN_TYPEERR, "{0}.prototype.{1} called on incompatible {2}") +MSG_DEF(JSMSG_NO_CONSTRUCTOR, 9, 1, JSEXN_TYPEERR, "{0} has no constructor") +MSG_DEF(JSMSG_CANT_ALIAS, 10, 3, JSEXN_TYPEERR, "can't alias {0} to {1} in class {2}") +MSG_DEF(JSMSG_NOT_SCRIPTED_FUNCTION, 11, 1, JSEXN_TYPEERR, "{0} is not a scripted function") +MSG_DEF(JSMSG_BAD_SORT_ARG, 12, 0, JSEXN_TYPEERR, "invalid Array.prototype.sort argument") +MSG_DEF(JSMSG_BAD_ATOMIC_NUMBER, 13, 1, JSEXN_INTERNALERR, "internal error: no index for atom {0}") +MSG_DEF(JSMSG_TOO_MANY_LITERALS, 14, 0, JSEXN_INTERNALERR, "too many literals") +MSG_DEF(JSMSG_CANT_WATCH, 15, 1, JSEXN_TYPEERR, "can't watch non-native objects of class {0}") +MSG_DEF(JSMSG_STACK_UNDERFLOW, 16, 2, JSEXN_INTERNALERR, "internal error compiling {0}: stack underflow at pc {1}") +MSG_DEF(JSMSG_NEED_DIET, 17, 1, JSEXN_INTERNALERR, "{0} too large") +MSG_DEF(JSMSG_TOO_MANY_LOCAL_ROOTS, 18, 0, JSEXN_ERR, "out of local root space") +MSG_DEF(JSMSG_READ_ONLY, 19, 1, JSEXN_ERR, "{0} is read-only") +MSG_DEF(JSMSG_BAD_FORMAL, 20, 0, JSEXN_SYNTAXERR, "malformed formal parameter") +MSG_DEF(JSMSG_BAD_ITERATOR, 21, 3, JSEXN_TYPEERR, "{0} has invalid {1} value {2}") +MSG_DEF(JSMSG_NOT_FUNCTION, 22, 1, JSEXN_TYPEERR, "{0} is not a function") +MSG_DEF(JSMSG_NOT_CONSTRUCTOR, 23, 1, JSEXN_TYPEERR, "{0} is not a constructor") +MSG_DEF(JSMSG_SCRIPT_STACK_QUOTA, 24, 0, JSEXN_INTERNALERR, "script stack space quota is exhausted") +MSG_DEF(JSMSG_TOO_DEEP, 25, 1, JSEXN_INTERNALERR, "{0} nested too deeply") +MSG_DEF(JSMSG_OVER_RECURSED, 26, 0, JSEXN_INTERNALERR, "too much recursion") +MSG_DEF(JSMSG_IN_NOT_OBJECT, 27, 1, JSEXN_TYPEERR, "invalid 'in' operand {0}") +MSG_DEF(JSMSG_BAD_NEW_RESULT, 28, 1, JSEXN_TYPEERR, "invalid new expression result {0}") +MSG_DEF(JSMSG_BAD_SHARP_DEF, 29, 1, JSEXN_ERR, "invalid sharp variable definition #{0}=") +MSG_DEF(JSMSG_BAD_SHARP_USE, 30, 1, JSEXN_ERR, "invalid sharp variable use #{0}#") +MSG_DEF(JSMSG_BAD_INSTANCEOF_RHS, 31, 1, JSEXN_TYPEERR, "invalid 'instanceof' operand {0}") +MSG_DEF(JSMSG_BAD_BYTECODE, 32, 1, JSEXN_INTERNALERR, "unimplemented JavaScript bytecode {0}") +MSG_DEF(JSMSG_BAD_RADIX, 33, 1, JSEXN_ERR, "illegal radix {0}") +MSG_DEF(JSMSG_PAREN_BEFORE_LET, 34, 0, JSEXN_SYNTAXERR, "missing ( before let head") +MSG_DEF(JSMSG_CANT_CONVERT, 35, 1, JSEXN_ERR, "can't convert {0} to an integer") +MSG_DEF(JSMSG_CYCLIC_VALUE, 36, 1, JSEXN_ERR, "cyclic {0} value") +MSG_DEF(JSMSG_COMPILE_EXECED_SCRIPT, 37, 0, JSEXN_TYPEERR, "cannot compile over a script that is currently executing") +MSG_DEF(JSMSG_CANT_CONVERT_TO, 38, 2, JSEXN_TYPEERR, "can't convert {0} to {1}") +MSG_DEF(JSMSG_NO_PROPERTIES, 39, 1, JSEXN_TYPEERR, "{0} has no properties") +MSG_DEF(JSMSG_CANT_FIND_CLASS, 40, 1, JSEXN_TYPEERR, "can't find class id {0}") +MSG_DEF(JSMSG_CANT_XDR_CLASS, 41, 1, JSEXN_TYPEERR, "can't XDR class {0}") +MSG_DEF(JSMSG_BYTECODE_TOO_BIG, 42, 2, JSEXN_INTERNALERR, "bytecode {0} too large (limit {1})") +MSG_DEF(JSMSG_UNKNOWN_FORMAT, 43, 1, JSEXN_INTERNALERR, "unknown bytecode format {0}") +MSG_DEF(JSMSG_TOO_MANY_CON_ARGS, 44, 0, JSEXN_SYNTAXERR, "too many constructor arguments") +MSG_DEF(JSMSG_TOO_MANY_FUN_ARGS, 45, 0, JSEXN_SYNTAXERR, "too many function arguments") +MSG_DEF(JSMSG_BAD_QUANTIFIER, 46, 1, JSEXN_SYNTAXERR, "invalid quantifier {0}") +MSG_DEF(JSMSG_MIN_TOO_BIG, 47, 1, JSEXN_SYNTAXERR, "overlarge minimum {0}") +MSG_DEF(JSMSG_MAX_TOO_BIG, 48, 1, JSEXN_SYNTAXERR, "overlarge maximum {0}") +MSG_DEF(JSMSG_OUT_OF_ORDER, 49, 1, JSEXN_SYNTAXERR, "maximum {0} less than minimum") +MSG_DEF(JSMSG_BAD_DESTRUCT_DECL, 50, 0, JSEXN_SYNTAXERR, "missing = in destructuring declaration") +MSG_DEF(JSMSG_BAD_DESTRUCT_ASS, 51, 0, JSEXN_SYNTAXERR, "invalid destructuring assignment operator") +MSG_DEF(JSMSG_PAREN_AFTER_LET, 52, 0, JSEXN_SYNTAXERR, "missing ) after let head") +MSG_DEF(JSMSG_CURLY_AFTER_LET, 53, 0, JSEXN_SYNTAXERR, "missing } after let block") +MSG_DEF(JSMSG_MISSING_PAREN, 54, 0, JSEXN_SYNTAXERR, "unterminated parenthetical") +MSG_DEF(JSMSG_UNTERM_CLASS, 55, 1, JSEXN_SYNTAXERR, "unterminated character class {0}") +MSG_DEF(JSMSG_TRAILING_SLASH, 56, 0, JSEXN_SYNTAXERR, "trailing \\ in regular expression") +MSG_DEF(JSMSG_BAD_CLASS_RANGE, 57, 0, JSEXN_SYNTAXERR, "invalid range in character class") +MSG_DEF(JSMSG_BAD_REGEXP_FLAG, 58, 1, JSEXN_SYNTAXERR, "invalid regular expression flag {0}") +MSG_DEF(JSMSG_NO_INPUT, 59, 5, JSEXN_SYNTAXERR, "no input for /{0}/{1}{2}{3}{4}") +MSG_DEF(JSMSG_CANT_OPEN, 60, 2, JSEXN_ERR, "can't open {0}: {1}") +MSG_DEF(JSMSG_BAD_STRING_MASK, 61, 1, JSEXN_ERR, "invalid string escape mask {0}") +MSG_DEF(JSMSG_UNMATCHED_RIGHT_PAREN, 62, 0, JSEXN_SYNTAXERR, "unmatched ) in regular expression") +MSG_DEF(JSMSG_END_OF_DATA, 63, 0, JSEXN_INTERNALERR, "unexpected end of data") +MSG_DEF(JSMSG_SEEK_BEYOND_START, 64, 0, JSEXN_INTERNALERR, "illegal seek beyond start") +MSG_DEF(JSMSG_SEEK_BEYOND_END, 65, 0, JSEXN_INTERNALERR, "illegal seek beyond end") +MSG_DEF(JSMSG_END_SEEK, 66, 0, JSEXN_INTERNALERR, "illegal end-based seek") +MSG_DEF(JSMSG_WHITHER_WHENCE, 67, 1, JSEXN_INTERNALERR, "unknown seek whence: {0}") +MSG_DEF(JSMSG_BAD_SCRIPT_MAGIC, 68, 0, JSEXN_INTERNALERR, "bad script XDR magic number") +MSG_DEF(JSMSG_PAREN_BEFORE_FORMAL, 69, 0, JSEXN_SYNTAXERR, "missing ( before formal parameters") +MSG_DEF(JSMSG_MISSING_FORMAL, 70, 0, JSEXN_SYNTAXERR, "missing formal parameter") +MSG_DEF(JSMSG_PAREN_AFTER_FORMAL, 71, 0, JSEXN_SYNTAXERR, "missing ) after formal parameters") +MSG_DEF(JSMSG_CURLY_BEFORE_BODY, 72, 0, JSEXN_SYNTAXERR, "missing { before function body") +MSG_DEF(JSMSG_CURLY_AFTER_BODY, 73, 0, JSEXN_SYNTAXERR, "missing } after function body") +MSG_DEF(JSMSG_PAREN_BEFORE_COND, 74, 0, JSEXN_SYNTAXERR, "missing ( before condition") +MSG_DEF(JSMSG_PAREN_AFTER_COND, 75, 0, JSEXN_SYNTAXERR, "missing ) after condition") +MSG_DEF(JSMSG_DESTRUCT_DUP_ARG, 76, 0, JSEXN_SYNTAXERR, "duplicate argument is mixed with destructuring pattern") +MSG_DEF(JSMSG_NAME_AFTER_DOT, 77, 0, JSEXN_SYNTAXERR, "missing name after . operator") +MSG_DEF(JSMSG_BRACKET_IN_INDEX, 78, 0, JSEXN_SYNTAXERR, "missing ] in index expression") +MSG_DEF(JSMSG_XML_WHOLE_PROGRAM, 79, 0, JSEXN_SYNTAXERR, "XML cannot be the whole program") +MSG_DEF(JSMSG_PAREN_BEFORE_SWITCH, 80, 0, JSEXN_SYNTAXERR, "missing ( before switch expression") +MSG_DEF(JSMSG_PAREN_AFTER_SWITCH, 81, 0, JSEXN_SYNTAXERR, "missing ) after switch expression") +MSG_DEF(JSMSG_CURLY_BEFORE_SWITCH, 82, 0, JSEXN_SYNTAXERR, "missing { before switch body") +MSG_DEF(JSMSG_COLON_AFTER_CASE, 83, 0, JSEXN_SYNTAXERR, "missing : after case label") +MSG_DEF(JSMSG_WHILE_AFTER_DO, 84, 0, JSEXN_SYNTAXERR, "missing while after do-loop body") +MSG_DEF(JSMSG_PAREN_AFTER_FOR, 85, 0, JSEXN_SYNTAXERR, "missing ( after for") +MSG_DEF(JSMSG_SEMI_AFTER_FOR_INIT, 86, 0, JSEXN_SYNTAXERR, "missing ; after for-loop initializer") +MSG_DEF(JSMSG_SEMI_AFTER_FOR_COND, 87, 0, JSEXN_SYNTAXERR, "missing ; after for-loop condition") +MSG_DEF(JSMSG_PAREN_AFTER_FOR_CTRL, 88, 0, JSEXN_SYNTAXERR, "missing ) after for-loop control") +MSG_DEF(JSMSG_CURLY_BEFORE_TRY, 89, 0, JSEXN_SYNTAXERR, "missing { before try block") +MSG_DEF(JSMSG_CURLY_AFTER_TRY, 90, 0, JSEXN_SYNTAXERR, "missing } after try block") +MSG_DEF(JSMSG_PAREN_BEFORE_CATCH, 91, 0, JSEXN_SYNTAXERR, "missing ( before catch") +MSG_DEF(JSMSG_CATCH_IDENTIFIER, 92, 0, JSEXN_SYNTAXERR, "missing identifier in catch") +MSG_DEF(JSMSG_PAREN_AFTER_CATCH, 93, 0, JSEXN_SYNTAXERR, "missing ) after catch") +MSG_DEF(JSMSG_CURLY_BEFORE_CATCH, 94, 0, JSEXN_SYNTAXERR, "missing { before catch block") +MSG_DEF(JSMSG_CURLY_AFTER_CATCH, 95, 0, JSEXN_SYNTAXERR, "missing } after catch block") +MSG_DEF(JSMSG_CURLY_BEFORE_FINALLY, 96, 0, JSEXN_SYNTAXERR, "missing { before finally block") +MSG_DEF(JSMSG_CURLY_AFTER_FINALLY, 97, 0, JSEXN_SYNTAXERR, "missing } after finally block") +MSG_DEF(JSMSG_CATCH_OR_FINALLY, 98, 0, JSEXN_SYNTAXERR, "missing catch or finally after try") +MSG_DEF(JSMSG_PAREN_BEFORE_WITH, 99, 0, JSEXN_SYNTAXERR, "missing ( before with-statement object") +MSG_DEF(JSMSG_PAREN_AFTER_WITH, 100, 0, JSEXN_SYNTAXERR, "missing ) after with-statement object") +MSG_DEF(JSMSG_CURLY_IN_COMPOUND, 101, 0, JSEXN_SYNTAXERR, "missing } in compound statement") +MSG_DEF(JSMSG_NO_VARIABLE_NAME, 102, 0, JSEXN_SYNTAXERR, "missing variable name") +MSG_DEF(JSMSG_COLON_IN_COND, 103, 0, JSEXN_SYNTAXERR, "missing : in conditional expression") +MSG_DEF(JSMSG_PAREN_AFTER_ARGS, 104, 0, JSEXN_SYNTAXERR, "missing ) after argument list") +MSG_DEF(JSMSG_BRACKET_AFTER_LIST, 105, 0, JSEXN_SYNTAXERR, "missing ] after element list") +MSG_DEF(JSMSG_COLON_AFTER_ID, 106, 0, JSEXN_SYNTAXERR, "missing : after property id") +MSG_DEF(JSMSG_CURLY_AFTER_LIST, 107, 0, JSEXN_SYNTAXERR, "missing } after property list") +MSG_DEF(JSMSG_PAREN_IN_PAREN, 108, 0, JSEXN_SYNTAXERR, "missing ) in parenthetical") +MSG_DEF(JSMSG_SEMI_BEFORE_STMNT, 109, 0, JSEXN_SYNTAXERR, "missing ; before statement") +MSG_DEF(JSMSG_NO_RETURN_VALUE, 110, 1, JSEXN_TYPEERR, "function {0} does not always return a value") +MSG_DEF(JSMSG_DUPLICATE_FORMAL, 111, 1, JSEXN_SYNTAXERR, "duplicate formal argument {0}") +MSG_DEF(JSMSG_EQUAL_AS_ASSIGN, 112, 1, JSEXN_SYNTAXERR, "test for equality (==) mistyped as assignment (=)?{0}") +MSG_DEF(JSMSG_OPTIMIZED_CLOSURE_LEAK, 113, 0, JSEXN_INTERNALERR, "cannot access optimized closure") +MSG_DEF(JSMSG_TOO_MANY_DEFAULTS, 114, 0, JSEXN_SYNTAXERR, "more than one switch default") +MSG_DEF(JSMSG_TOO_MANY_CASES, 115, 0, JSEXN_INTERNALERR, "too many switch cases") +MSG_DEF(JSMSG_BAD_SWITCH, 116, 0, JSEXN_SYNTAXERR, "invalid switch statement") +MSG_DEF(JSMSG_BAD_FOR_LEFTSIDE, 117, 0, JSEXN_SYNTAXERR, "invalid for/in left-hand side") +MSG_DEF(JSMSG_CATCH_AFTER_GENERAL, 118, 0, JSEXN_SYNTAXERR, "catch after unconditional catch") +MSG_DEF(JSMSG_CATCH_WITHOUT_TRY, 119, 0, JSEXN_SYNTAXERR, "catch without try") +MSG_DEF(JSMSG_FINALLY_WITHOUT_TRY, 120, 0, JSEXN_SYNTAXERR, "finally without try") +MSG_DEF(JSMSG_LABEL_NOT_FOUND, 121, 0, JSEXN_SYNTAXERR, "label not found") +MSG_DEF(JSMSG_TOUGH_BREAK, 122, 0, JSEXN_SYNTAXERR, "unlabeled break must be inside loop or switch") +MSG_DEF(JSMSG_BAD_CONTINUE, 123, 0, JSEXN_SYNTAXERR, "continue must be inside loop") +MSG_DEF(JSMSG_BAD_RETURN_OR_YIELD, 124, 1, JSEXN_SYNTAXERR, "{0} not in function") +MSG_DEF(JSMSG_BAD_LABEL, 125, 0, JSEXN_SYNTAXERR, "invalid label") +MSG_DEF(JSMSG_DUPLICATE_LABEL, 126, 0, JSEXN_SYNTAXERR, "duplicate label") +MSG_DEF(JSMSG_VAR_HIDES_ARG, 127, 1, JSEXN_TYPEERR, "variable {0} redeclares argument") +MSG_DEF(JSMSG_BAD_VAR_INIT, 128, 0, JSEXN_SYNTAXERR, "invalid variable initialization") +MSG_DEF(JSMSG_BAD_LEFTSIDE_OF_ASS, 129, 0, JSEXN_SYNTAXERR, "invalid assignment left-hand side") +MSG_DEF(JSMSG_BAD_OPERAND, 130, 1, JSEXN_SYNTAXERR, "invalid {0} operand") +MSG_DEF(JSMSG_BAD_PROP_ID, 131, 0, JSEXN_SYNTAXERR, "invalid property id") +MSG_DEF(JSMSG_RESERVED_ID, 132, 1, JSEXN_SYNTAXERR, "{0} is a reserved identifier") +MSG_DEF(JSMSG_SYNTAX_ERROR, 133, 0, JSEXN_SYNTAXERR, "syntax error") +MSG_DEF(JSMSG_BAD_SHARP_VAR_DEF, 134, 0, JSEXN_SYNTAXERR, "invalid sharp variable definition") +MSG_DEF(JSMSG_BAD_PROTOTYPE, 135, 1, JSEXN_TYPEERR, "'prototype' property of {0} is not an object") +MSG_DEF(JSMSG_MISSING_EXPONENT, 136, 0, JSEXN_SYNTAXERR, "missing exponent") +MSG_DEF(JSMSG_OUT_OF_MEMORY, 137, 0, JSEXN_ERR, "out of memory") +MSG_DEF(JSMSG_UNTERMINATED_STRING, 138, 0, JSEXN_SYNTAXERR, "unterminated string literal") +MSG_DEF(JSMSG_TOO_MANY_PARENS, 139, 0, JSEXN_INTERNALERR, "too many parentheses in regular expression") +MSG_DEF(JSMSG_UNTERMINATED_COMMENT, 140, 0, JSEXN_SYNTAXERR, "unterminated comment") +MSG_DEF(JSMSG_UNTERMINATED_REGEXP, 141, 0, JSEXN_SYNTAXERR, "unterminated regular expression literal") +MSG_DEF(JSMSG_BAD_CLONE_FUNOBJ_SCOPE, 142, 0, JSEXN_TYPEERR, "bad cloned function scope chain") +MSG_DEF(JSMSG_SHARPVAR_TOO_BIG, 143, 0, JSEXN_SYNTAXERR, "overlarge sharp variable number") +MSG_DEF(JSMSG_ILLEGAL_CHARACTER, 144, 0, JSEXN_SYNTAXERR, "illegal character") +MSG_DEF(JSMSG_BAD_OCTAL, 145, 1, JSEXN_SYNTAXERR, "{0} is not a legal ECMA-262 octal constant") +MSG_DEF(JSMSG_BAD_INDIRECT_CALL, 146, 1, JSEXN_EVALERR, "function {0} must be called directly, and not by way of a function of another name") +MSG_DEF(JSMSG_UNCAUGHT_EXCEPTION, 147, 1, JSEXN_INTERNALERR, "uncaught exception: {0}") +MSG_DEF(JSMSG_INVALID_BACKREF, 148, 0, JSEXN_SYNTAXERR, "non-octal digit in an escape sequence that doesn't match a back-reference") +MSG_DEF(JSMSG_BAD_BACKREF, 149, 0, JSEXN_SYNTAXERR, "back-reference exceeds number of capturing parentheses") +MSG_DEF(JSMSG_PRECISION_RANGE, 150, 1, JSEXN_RANGEERR, "precision {0} out of range") +MSG_DEF(JSMSG_BAD_GETTER_OR_SETTER, 151, 1, JSEXN_SYNTAXERR, "invalid {0} usage") +MSG_DEF(JSMSG_BAD_ARRAY_LENGTH, 152, 0, JSEXN_RANGEERR, "invalid array length") +MSG_DEF(JSMSG_CANT_DESCRIBE_PROPS, 153, 1, JSEXN_TYPEERR, "can't describe non-native properties of class {0}") +MSG_DEF(JSMSG_BAD_APPLY_ARGS, 154, 1, JSEXN_TYPEERR, "second argument to Function.prototype.{0} must be an array") +MSG_DEF(JSMSG_REDECLARED_VAR, 155, 2, JSEXN_TYPEERR, "redeclaration of {0} {1}") +MSG_DEF(JSMSG_UNDECLARED_VAR, 156, 1, JSEXN_REFERENCEERR, "assignment to undeclared variable {0}") +MSG_DEF(JSMSG_ANON_NO_RETURN_VALUE, 157, 0, JSEXN_TYPEERR, "anonymous function does not always return a value") +MSG_DEF(JSMSG_DEPRECATED_USAGE, 158, 1, JSEXN_REFERENCEERR, "deprecated {0} usage") +MSG_DEF(JSMSG_BAD_URI, 159, 0, JSEXN_URIERR, "malformed URI sequence") +MSG_DEF(JSMSG_GETTER_ONLY, 160, 0, JSEXN_TYPEERR, "setting a property that has only a getter") +MSG_DEF(JSMSG_IDSTART_AFTER_NUMBER, 161, 0, JSEXN_SYNTAXERR, "identifier starts immediately after numeric literal") +MSG_DEF(JSMSG_UNDEFINED_PROP, 162, 1, JSEXN_REFERENCEERR, "reference to undefined property {0}") +MSG_DEF(JSMSG_USELESS_EXPR, 163, 0, JSEXN_TYPEERR, "useless expression") +MSG_DEF(JSMSG_REDECLARED_PARAM, 164, 1, JSEXN_TYPEERR, "redeclaration of formal parameter {0}") +MSG_DEF(JSMSG_NEWREGEXP_FLAGGED, 165, 0, JSEXN_TYPEERR, "can't supply flags when constructing one RegExp from another") +MSG_DEF(JSMSG_RESERVED_SLOT_RANGE, 166, 0, JSEXN_RANGEERR, "reserved slot index out of range") +MSG_DEF(JSMSG_CANT_DECODE_PRINCIPALS, 167, 0, JSEXN_INTERNALERR, "can't decode JSPrincipals") +MSG_DEF(JSMSG_CANT_SEAL_OBJECT, 168, 1, JSEXN_ERR, "can't seal {0} objects") +MSG_DEF(JSMSG_TOO_MANY_CATCH_VARS, 169, 0, JSEXN_SYNTAXERR, "too many catch variables") +MSG_DEF(JSMSG_BAD_XML_MARKUP, 170, 0, JSEXN_SYNTAXERR, "invalid XML markup") +MSG_DEF(JSMSG_BAD_XML_CHARACTER, 171, 0, JSEXN_SYNTAXERR, "illegal XML character") +MSG_DEF(JSMSG_BAD_DEFAULT_XML_NAMESPACE,172,0,JSEXN_SYNTAXERR, "invalid default XML namespace") +MSG_DEF(JSMSG_BAD_XML_NAME_SYNTAX, 173, 0, JSEXN_SYNTAXERR, "invalid XML name") +MSG_DEF(JSMSG_BRACKET_AFTER_ATTR_EXPR,174, 0, JSEXN_SYNTAXERR, "missing ] after attribute expression") +MSG_DEF(JSMSG_NESTING_GENERATOR, 175, 1, JSEXN_TYPEERR, "already executing generator {0}") +MSG_DEF(JSMSG_CURLY_IN_XML_EXPR, 176, 0, JSEXN_SYNTAXERR, "missing } in XML expression") +MSG_DEF(JSMSG_BAD_XML_NAMESPACE, 177, 1, JSEXN_TYPEERR, "invalid XML namespace {0}") +MSG_DEF(JSMSG_BAD_XML_ATTR_NAME, 178, 1, JSEXN_TYPEERR, "invalid XML attribute name {0}") +MSG_DEF(JSMSG_BAD_XML_NAME, 179, 1, JSEXN_TYPEERR, "invalid XML name {0}") +MSG_DEF(JSMSG_BAD_XML_CONVERSION, 180, 1, JSEXN_TYPEERR, "can't convert {0} to XML") +MSG_DEF(JSMSG_BAD_XMLLIST_CONVERSION, 181, 1, JSEXN_TYPEERR, "can't convert {0} to XMLList") +MSG_DEF(JSMSG_BAD_GENERATOR_SEND, 182, 1, JSEXN_TYPEERR, "attempt to send {0} to newborn generator") +MSG_DEF(JSMSG_NO_ASSIGN_IN_XML_ATTR, 183, 0, JSEXN_SYNTAXERR, "missing = in XML attribute") +MSG_DEF(JSMSG_BAD_XML_ATTR_VALUE, 184, 0, JSEXN_SYNTAXERR, "invalid XML attribute value") +MSG_DEF(JSMSG_XML_TAG_NAME_MISMATCH, 185, 1, JSEXN_SYNTAXERR, "XML tag name mismatch (expected {0})") +MSG_DEF(JSMSG_BAD_XML_TAG_SYNTAX, 186, 0, JSEXN_SYNTAXERR, "invalid XML tag syntax") +MSG_DEF(JSMSG_BAD_XML_LIST_SYNTAX, 187, 0, JSEXN_SYNTAXERR, "invalid XML list syntax") +MSG_DEF(JSMSG_INCOMPATIBLE_METHOD, 188, 3, JSEXN_TYPEERR, "{0} {1} called on incompatible {2}") +MSG_DEF(JSMSG_CANT_SET_XML_ATTRS, 189, 0, JSEXN_INTERNALERR, "can't set XML property attributes") +MSG_DEF(JSMSG_END_OF_XML_SOURCE, 190, 0, JSEXN_SYNTAXERR, "unexpected end of XML source") +MSG_DEF(JSMSG_END_OF_XML_ENTITY, 191, 0, JSEXN_SYNTAXERR, "unexpected end of XML entity") +MSG_DEF(JSMSG_BAD_XML_QNAME, 192, 0, JSEXN_SYNTAXERR, "invalid XML qualified name") +MSG_DEF(JSMSG_BAD_FOR_EACH_LOOP, 193, 0, JSEXN_SYNTAXERR, "invalid for each loop") +MSG_DEF(JSMSG_BAD_XMLLIST_PUT, 194, 1, JSEXN_TYPEERR, "can't set property {0} in XMLList") +MSG_DEF(JSMSG_UNKNOWN_XML_ENTITY, 195, 1, JSEXN_TYPEERR, "unknown XML entity {0}") +MSG_DEF(JSMSG_BAD_XML_NCR, 196, 1, JSEXN_TYPEERR, "malformed XML character {0}") +MSG_DEF(JSMSG_UNDEFINED_XML_NAME, 197, 1, JSEXN_REFERENCEERR, "reference to undefined XML name {0}") +MSG_DEF(JSMSG_DUPLICATE_XML_ATTR, 198, 1, JSEXN_TYPEERR, "duplicate XML attribute {0}") +MSG_DEF(JSMSG_TOO_MANY_LOCALS, 199, 0, JSEXN_SYNTAXERR, "too many local variables") +MSG_DEF(JSMSG_ARRAY_INIT_TOO_BIG, 200, 0, JSEXN_INTERNALERR, "array initialiser too large") +MSG_DEF(JSMSG_REGEXP_TOO_COMPLEX, 201, 0, JSEXN_INTERNALERR, "regular expression too complex") +MSG_DEF(JSMSG_BUFFER_TOO_SMALL, 202, 0, JSEXN_INTERNALERR, "buffer too small") +MSG_DEF(JSMSG_BAD_SURROGATE_CHAR, 203, 1, JSEXN_TYPEERR, "bad surrogate character {0}") +MSG_DEF(JSMSG_UTF8_CHAR_TOO_LARGE, 204, 1, JSEXN_TYPEERR, "UTF-8 character {0} too large") +MSG_DEF(JSMSG_MALFORMED_UTF8_CHAR, 205, 1, JSEXN_TYPEERR, "malformed UTF-8 character sequence at offset {0}") +MSG_DEF(JSMSG_USER_DEFINED_ERROR, 206, 0, JSEXN_ERR, "JS_ReportError was called") +MSG_DEF(JSMSG_WRONG_CONSTRUCTOR, 207, 1, JSEXN_TYPEERR, "wrong constructor called for {0}") +MSG_DEF(JSMSG_BAD_GENERATOR_RETURN, 208, 1, JSEXN_TYPEERR, "generator function {0} returns a value") +MSG_DEF(JSMSG_BAD_ANON_GENERATOR_RETURN, 209, 0, JSEXN_TYPEERR, "anonymous generator function returns a value") +MSG_DEF(JSMSG_NAME_AFTER_FOR_PAREN, 210, 0, JSEXN_SYNTAXERR, "missing name after for (") +MSG_DEF(JSMSG_IN_AFTER_FOR_NAME, 211, 0, JSEXN_SYNTAXERR, "missing in after for") +MSG_DEF(JSMSG_BAD_ITERATOR_RETURN, 212, 1, JSEXN_TYPEERR, "{0}.__iterator__ returned a primitive value") +MSG_DEF(JSMSG_KEYWORD_NOT_NS, 213, 0, JSEXN_SYNTAXERR, "keyword is used as namespace") +MSG_DEF(JSMSG_BAD_GENERATOR_YIELD, 214, 1, JSEXN_TYPEERR, "yield from closing generator {0}") +MSG_DEF(JSMSG_BAD_GENERATOR_SYNTAX, 215, 1, JSEXN_SYNTAXERR, "{0} expression must be parenthesized") +MSG_DEF(JSMSG_ARRAY_COMP_LEFTSIDE, 216, 0, JSEXN_SYNTAXERR, "invalid array comprehension left-hand side") +MSG_DEF(JSMSG_NON_XML_FILTER, 217, 1, JSEXN_TYPEERR, "XML filter is applied to non-XML value {0}") +MSG_DEF(JSMSG_EMPTY_ARRAY_REDUCE, 218, 0, JSEXN_TYPEERR, "reduce of empty array with no initial value") +MSG_DEF(JSMSG_NON_LIST_XML_METHOD, 219, 2, JSEXN_TYPEERR, "cannot call {0} method on an XML list with {1} elements") +MSG_DEF(JSMSG_BAD_DELETE_OPERAND, 220, 0, JSEXN_SYNTAXERR, "invalid delete operand") +MSG_DEF(JSMSG_BAD_INCOP_OPERAND, 221, 0, JSEXN_SYNTAXERR, "invalid increment/decrement operand") +MSG_DEF(JSMSG_UNEXPECTED_TYPE, 222, 2, JSEXN_TYPEERR, "{0} is {1}") +MSG_DEF(JSMSG_LET_DECL_NOT_IN_BLOCK, 223, 0, JSEXN_SYNTAXERR, "let declaration not directly within block") +MSG_DEF(JSMSG_BAD_OBJECT_INIT, 224, 0, JSEXN_SYNTAXERR, "invalid object initializer") +MSG_DEF(JSMSG_CANT_SET_ARRAY_ATTRS, 225, 0, JSEXN_INTERNALERR, "can't set attributes on indexed array properties") +MSG_DEF(JSMSG_EVAL_ARITY, 226, 0, JSEXN_TYPEERR, "eval accepts only one parameter") +MSG_DEF(JSMSG_MISSING_FUN_ARG, 227, 2, JSEXN_TYPEERR, "missing argument {0} when calling function {1}") +MSG_DEF(JSMSG_JSON_BAD_PARSE, 228, 0, JSEXN_SYNTAXERR, "JSON.parse") +MSG_DEF(JSMSG_JSON_BAD_STRINGIFY, 229, 0, JSEXN_ERR, "JSON.stringify") +MSG_DEF(JSMSG_XDR_CLOSURE_WRAPPER, 230, 1, JSEXN_INTERNALERR, "can't XDR closure wrapper for function {0}") +MSG_DEF(JSMSG_NOT_NONNULL_OBJECT, 231, 0, JSEXN_TYPEERR, "value is not a non-null object") +MSG_DEF(JSMSG_DEPRECATED_OCTAL, 232, 0, JSEXN_SYNTAXERR, "octal literals and octal escape sequences are deprecated") +MSG_DEF(JSMSG_STRICT_CODE_WITH, 233, 0, JSEXN_SYNTAXERR, "strict mode code may not contain 'with' statements") +MSG_DEF(JSMSG_DUPLICATE_PROPERTY, 234, 1, JSEXN_SYNTAXERR, "property name {0} appears more than once in object literal") +MSG_DEF(JSMSG_DEPRECATED_DELETE_OPERAND, 235, 0, JSEXN_SYNTAXERR, "Applying the 'delete' operator to an unqualified name is deprecated") +MSG_DEF(JSMSG_DEPRECATED_ASSIGN, 236, 1, JSEXN_SYNTAXERR, "assignment to {0} is deprecated") +MSG_DEF(JSMSG_BAD_BINDING, 237, 1, JSEXN_SYNTAXERR, "redefining {0} is deprecated") diff --git a/ape-server/deps/js/src/jsapi.cpp b/ape-server/deps/js/src/jsapi.cpp new file mode 100755 index 0000000..00de732 --- /dev/null +++ b/ape-server/deps/js/src/jsapi.cpp @@ -0,0 +1,5929 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * JavaScript API. + */ +#include +#include +#include +#include +#include "jstypes.h" +#include "jsstdint.h" +#include "jsarena.h" /* Added by JSIFY */ +#include "jsutil.h" /* Added by JSIFY */ +#include "jsclist.h" +#include "jsdhash.h" +#include "jsprf.h" +#include "jsapi.h" +#include "jsarray.h" +#include "jsatom.h" +#include "jsbool.h" +#include "jsbuiltins.h" +#include "jscntxt.h" +#include "jsversion.h" +#include "jsdate.h" +#include "jsdtoa.h" +#include "jsemit.h" +#include "jsexn.h" +#include "jsfun.h" +#include "jsgc.h" +#include "jsinterp.h" +#include "jsiter.h" +#include "jslock.h" +#include "jsmath.h" +#include "jsnum.h" +#include "json.h" +#include "jsobj.h" +#include "jsopcode.h" +#include "jsparse.h" +#include "jsregexp.h" +#include "jsscan.h" +#include "jsscope.h" +#include "jsscript.h" +#include "jsstr.h" +#include "jstask.h" +#include "jstracer.h" +#include "jsdbgapi.h" +#include "prmjtime.h" +#include "jsstaticcheck.h" +#include "jsvector.h" + +#include "jsatominlines.h" +#include "jsscopeinlines.h" + +#if JS_HAS_FILE_OBJECT +#include "jsfile.h" +#endif + +#if JS_HAS_XML_SUPPORT +#include "jsxml.h" +#endif + +#ifdef HAVE_VA_LIST_AS_ARRAY +#define JS_ADDRESSOF_VA_LIST(ap) ((va_list *)(ap)) +#else +#define JS_ADDRESSOF_VA_LIST(ap) (&(ap)) +#endif + +#if defined(JS_THREADSAFE) +#define CHECK_REQUEST(cx) \ + JS_ASSERT((cx)->requestDepth || (cx)->thread == (cx)->runtime->gcThread) +#else +#define CHECK_REQUEST(cx) ((void)0) +#endif + +/* Check that we can cast JSObject* as jsval without tag bit manipulations. */ +JS_STATIC_ASSERT(JSVAL_OBJECT == 0); + +/* Check that JSVAL_TRACE_KIND works. */ +JS_STATIC_ASSERT(JSVAL_TRACE_KIND(JSVAL_OBJECT) == JSTRACE_OBJECT); +JS_STATIC_ASSERT(JSVAL_TRACE_KIND(JSVAL_DOUBLE) == JSTRACE_DOUBLE); +JS_STATIC_ASSERT(JSVAL_TRACE_KIND(JSVAL_STRING) == JSTRACE_STRING); + +JS_PUBLIC_API(int64) +JS_Now() +{ + return PRMJ_Now(); +} + +JS_PUBLIC_API(jsval) +JS_GetNaNValue(JSContext *cx) +{ + return cx->runtime->NaNValue; +} + +JS_PUBLIC_API(jsval) +JS_GetNegativeInfinityValue(JSContext *cx) +{ + return cx->runtime->negativeInfinityValue; +} + +JS_PUBLIC_API(jsval) +JS_GetPositiveInfinityValue(JSContext *cx) +{ + return cx->runtime->positiveInfinityValue; +} + +JS_PUBLIC_API(jsval) +JS_GetEmptyStringValue(JSContext *cx) +{ + return STRING_TO_JSVAL(cx->runtime->emptyString); +} + +static JSBool +TryArgumentFormatter(JSContext *cx, const char **formatp, JSBool fromJS, + jsval **vpp, va_list *app) +{ + const char *format; + JSArgumentFormatMap *map; + + format = *formatp; + for (map = cx->argumentFormatMap; map; map = map->next) { + if (!strncmp(format, map->format, map->length)) { + *formatp = format + map->length; + return map->formatter(cx, format, fromJS, vpp, app); + } + } + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_CHAR, format); + return JS_FALSE; +} + +JS_PUBLIC_API(JSBool) +JS_ConvertArguments(JSContext *cx, uintN argc, jsval *argv, const char *format, + ...) +{ + va_list ap; + JSBool ok; + + va_start(ap, format); + ok = JS_ConvertArgumentsVA(cx, argc, argv, format, ap); + va_end(ap); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_ConvertArgumentsVA(JSContext *cx, uintN argc, jsval *argv, + const char *format, va_list ap) +{ + jsval *sp; + JSBool required; + char c; + JSFunction *fun; + jsdouble d; + JSString *str; + JSObject *obj; + + CHECK_REQUEST(cx); + sp = argv; + required = JS_TRUE; + while ((c = *format++) != '\0') { + if (isspace(c)) + continue; + if (c == '/') { + required = JS_FALSE; + continue; + } + if (sp == argv + argc) { + if (required) { + fun = js_ValueToFunction(cx, &argv[-2], 0); + if (fun) { + char numBuf[12]; + JS_snprintf(numBuf, sizeof numBuf, "%u", argc); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_MORE_ARGS_NEEDED, + JS_GetFunctionName(fun), numBuf, + (argc == 1) ? "" : "s"); + } + return JS_FALSE; + } + break; + } + switch (c) { + case 'b': + *va_arg(ap, JSBool *) = js_ValueToBoolean(*sp); + break; + case 'c': + if (!JS_ValueToUint16(cx, *sp, va_arg(ap, uint16 *))) + return JS_FALSE; + break; + case 'i': + if (!JS_ValueToECMAInt32(cx, *sp, va_arg(ap, int32 *))) + return JS_FALSE; + break; + case 'u': + if (!JS_ValueToECMAUint32(cx, *sp, va_arg(ap, uint32 *))) + return JS_FALSE; + break; + case 'j': + if (!JS_ValueToInt32(cx, *sp, va_arg(ap, int32 *))) + return JS_FALSE; + break; + case 'd': + if (!JS_ValueToNumber(cx, *sp, va_arg(ap, jsdouble *))) + return JS_FALSE; + break; + case 'I': + if (!JS_ValueToNumber(cx, *sp, &d)) + return JS_FALSE; + *va_arg(ap, jsdouble *) = js_DoubleToInteger(d); + break; + case 's': + case 'S': + case 'W': + str = js_ValueToString(cx, *sp); + if (!str) + return JS_FALSE; + *sp = STRING_TO_JSVAL(str); + if (c == 's') { + const char *bytes = js_GetStringBytes(cx, str); + if (!bytes) + return JS_FALSE; + *va_arg(ap, const char **) = bytes; + } else if (c == 'W') { + const jschar *chars = js_GetStringChars(cx, str); + if (!chars) + return JS_FALSE; + *va_arg(ap, const jschar **) = chars; + } else { + *va_arg(ap, JSString **) = str; + } + break; + case 'o': + if (!js_ValueToObject(cx, *sp, &obj)) + return JS_FALSE; + *sp = OBJECT_TO_JSVAL(obj); + *va_arg(ap, JSObject **) = obj; + break; + case 'f': + obj = js_ValueToFunctionObject(cx, sp, 0); + if (!obj) + return JS_FALSE; + *sp = OBJECT_TO_JSVAL(obj); + *va_arg(ap, JSFunction **) = GET_FUNCTION_PRIVATE(cx, obj); + break; + case 'v': + *va_arg(ap, jsval *) = *sp; + break; + case '*': + break; + default: + format--; + if (!TryArgumentFormatter(cx, &format, JS_TRUE, &sp, + JS_ADDRESSOF_VA_LIST(ap))) { + return JS_FALSE; + } + /* NB: the formatter already updated sp, so we continue here. */ + continue; + } + sp++; + } + return JS_TRUE; +} + +JS_PUBLIC_API(jsval *) +JS_PushArguments(JSContext *cx, void **markp, const char *format, ...) +{ + va_list ap; + jsval *argv; + + va_start(ap, format); + argv = JS_PushArgumentsVA(cx, markp, format, ap); + va_end(ap); + return argv; +} + +JS_PUBLIC_API(jsval *) +JS_PushArgumentsVA(JSContext *cx, void **markp, const char *format, va_list ap) +{ + uintN argc; + jsval *argv, *sp; + char c; + const char *cp; + JSString *str; + JSFunction *fun; + JSStackHeader *sh; + + CHECK_REQUEST(cx); + *markp = NULL; + argc = 0; + for (cp = format; (c = *cp) != '\0'; cp++) { + /* + * Count non-space non-star characters as individual jsval arguments. + * This may over-allocate stack, but we'll fix below. + */ + if (isspace(c) || c == '*') + continue; + argc++; + } + js_LeaveTrace(cx); + sp = js_AllocStack(cx, argc, markp); + if (!sp) + return NULL; + argv = sp; + while ((c = *format++) != '\0') { + if (isspace(c) || c == '*') + continue; + switch (c) { + case 'b': + *sp = BOOLEAN_TO_JSVAL((JSBool) va_arg(ap, int)); + break; + case 'c': + *sp = INT_TO_JSVAL((uint16) va_arg(ap, unsigned int)); + break; + case 'i': + case 'j': + /* + * Use JS_New{Double,Number}Value here and in the next two cases, + * not js_New{Double,Number}InRootedValue, as sp may point to an + * unrooted location. + */ + if (!JS_NewNumberValue(cx, (jsdouble) va_arg(ap, int32), sp)) + goto bad; + break; + case 'u': + if (!JS_NewNumberValue(cx, (jsdouble) va_arg(ap, uint32), sp)) + goto bad; + break; + case 'd': + case 'I': + if (!JS_NewDoubleValue(cx, va_arg(ap, jsdouble), sp)) + goto bad; + break; + case 's': + str = JS_NewStringCopyZ(cx, va_arg(ap, char *)); + if (!str) + goto bad; + *sp = STRING_TO_JSVAL(str); + break; + case 'W': + str = JS_NewUCStringCopyZ(cx, va_arg(ap, jschar *)); + if (!str) + goto bad; + *sp = STRING_TO_JSVAL(str); + break; + case 'S': + str = va_arg(ap, JSString *); + *sp = STRING_TO_JSVAL(str); + break; + case 'o': + *sp = OBJECT_TO_JSVAL(va_arg(ap, JSObject *)); + break; + case 'f': + fun = va_arg(ap, JSFunction *); + *sp = fun ? OBJECT_TO_JSVAL(FUN_OBJECT(fun)) : JSVAL_NULL; + break; + case 'v': + *sp = va_arg(ap, jsval); + break; + default: + format--; + if (!TryArgumentFormatter(cx, &format, JS_FALSE, &sp, + JS_ADDRESSOF_VA_LIST(ap))) { + goto bad; + } + /* NB: the formatter already updated sp, so we continue here. */ + continue; + } + sp++; + } + + /* + * We may have overallocated stack due to a multi-character format code + * handled by a JSArgumentFormatter. Give back that stack space! + */ + JS_ASSERT(sp <= argv + argc); + if (sp < argv + argc) { + /* Return slots not pushed to the current stack arena. */ + cx->stackPool.current->avail = (jsuword)sp; + + /* Reduce the count of slots the GC will scan in this stack segment. */ + sh = cx->stackHeaders; + JS_ASSERT(JS_STACK_SEGMENT(sh) + sh->nslots == argv + argc); + sh->nslots -= argc - (sp - argv); + } + return argv; + +bad: + js_FreeStack(cx, *markp); + return NULL; +} + +JS_PUBLIC_API(void) +JS_PopArguments(JSContext *cx, void *mark) +{ + CHECK_REQUEST(cx); + JS_ASSERT_NOT_ON_TRACE(cx); + js_FreeStack(cx, mark); +} + +JS_PUBLIC_API(JSBool) +JS_AddArgumentFormatter(JSContext *cx, const char *format, + JSArgumentFormatter formatter) +{ + size_t length; + JSArgumentFormatMap **mpp, *map; + + length = strlen(format); + mpp = &cx->argumentFormatMap; + while ((map = *mpp) != NULL) { + /* Insert before any shorter string to match before prefixes. */ + if (map->length < length) + break; + if (map->length == length && !strcmp(map->format, format)) + goto out; + mpp = &map->next; + } + map = (JSArgumentFormatMap *) cx->malloc(sizeof *map); + if (!map) + return JS_FALSE; + map->format = format; + map->length = length; + map->next = *mpp; + *mpp = map; +out: + map->formatter = formatter; + return JS_TRUE; +} + +JS_PUBLIC_API(void) +JS_RemoveArgumentFormatter(JSContext *cx, const char *format) +{ + size_t length; + JSArgumentFormatMap **mpp, *map; + + length = strlen(format); + mpp = &cx->argumentFormatMap; + while ((map = *mpp) != NULL) { + if (map->length == length && !strcmp(map->format, format)) { + *mpp = map->next; + cx->free(map); + return; + } + mpp = &map->next; + } +} + +JS_PUBLIC_API(JSBool) +JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp) +{ + JSBool ok; + JSObject *obj; + JSString *str; + jsdouble d, *dp; + + CHECK_REQUEST(cx); + switch (type) { + case JSTYPE_VOID: + *vp = JSVAL_VOID; + ok = JS_TRUE; + break; + case JSTYPE_OBJECT: + ok = js_ValueToObject(cx, v, &obj); + if (ok) + *vp = OBJECT_TO_JSVAL(obj); + break; + case JSTYPE_FUNCTION: + *vp = v; + obj = js_ValueToFunctionObject(cx, vp, JSV2F_SEARCH_STACK); + ok = (obj != NULL); + break; + case JSTYPE_STRING: + str = js_ValueToString(cx, v); + ok = (str != NULL); + if (ok) + *vp = STRING_TO_JSVAL(str); + break; + case JSTYPE_NUMBER: + ok = JS_ValueToNumber(cx, v, &d); + if (ok) { + dp = js_NewWeaklyRootedDouble(cx, d); + ok = (dp != NULL); + if (ok) + *vp = DOUBLE_TO_JSVAL(dp); + } + break; + case JSTYPE_BOOLEAN: + *vp = BOOLEAN_TO_JSVAL(js_ValueToBoolean(v)); + return JS_TRUE; + default: { + char numBuf[12]; + JS_snprintf(numBuf, sizeof numBuf, "%d", (int)type); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_TYPE, + numBuf); + ok = JS_FALSE; + break; + } + } + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_ValueToObject(JSContext *cx, jsval v, JSObject **objp) +{ + CHECK_REQUEST(cx); + return js_ValueToObject(cx, v, objp); +} + +JS_PUBLIC_API(JSFunction *) +JS_ValueToFunction(JSContext *cx, jsval v) +{ + CHECK_REQUEST(cx); + return js_ValueToFunction(cx, &v, JSV2F_SEARCH_STACK); +} + +JS_PUBLIC_API(JSFunction *) +JS_ValueToConstructor(JSContext *cx, jsval v) +{ + CHECK_REQUEST(cx); + return js_ValueToFunction(cx, &v, JSV2F_SEARCH_STACK); +} + +JS_PUBLIC_API(JSString *) +JS_ValueToString(JSContext *cx, jsval v) +{ + CHECK_REQUEST(cx); + return js_ValueToString(cx, v); +} + +JS_PUBLIC_API(JSString *) +JS_ValueToSource(JSContext *cx, jsval v) +{ + CHECK_REQUEST(cx); + return js_ValueToSource(cx, v); +} + +JS_PUBLIC_API(JSBool) +JS_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp) +{ + CHECK_REQUEST(cx); + + JSAutoTempValueRooter tvr(cx, v); + *dp = js_ValueToNumber(cx, tvr.addr()); + return !JSVAL_IS_NULL(tvr.value()); +} + +JS_PUBLIC_API(JSBool) +JS_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip) +{ + CHECK_REQUEST(cx); + + JSAutoTempValueRooter tvr(cx, v); + *ip = js_ValueToECMAInt32(cx, tvr.addr()); + return !JSVAL_IS_NULL(tvr.value()); +} + +JS_PUBLIC_API(JSBool) +JS_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip) +{ + CHECK_REQUEST(cx); + + JSAutoTempValueRooter tvr(cx, v); + *ip = js_ValueToECMAUint32(cx, tvr.addr()); + return !JSVAL_IS_NULL(tvr.value()); +} + +JS_PUBLIC_API(JSBool) +JS_ValueToInt32(JSContext *cx, jsval v, int32 *ip) +{ + CHECK_REQUEST(cx); + + JSAutoTempValueRooter tvr(cx, v); + *ip = js_ValueToInt32(cx, tvr.addr()); + return !JSVAL_IS_NULL(tvr.value()); +} + +JS_PUBLIC_API(JSBool) +JS_ValueToUint16(JSContext *cx, jsval v, uint16 *ip) +{ + CHECK_REQUEST(cx); + + JSAutoTempValueRooter tvr(cx, v); + *ip = js_ValueToUint16(cx, tvr.addr()); + return !JSVAL_IS_NULL(tvr.value()); +} + +JS_PUBLIC_API(JSBool) +JS_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp) +{ + CHECK_REQUEST(cx); + *bp = js_ValueToBoolean(v); + return JS_TRUE; +} + +JS_PUBLIC_API(JSType) +JS_TypeOfValue(JSContext *cx, jsval v) +{ + JSType type; + JSObject *obj; + const JSObjectOps *ops; + JSClass *clasp; + + CHECK_REQUEST(cx); + if (JSVAL_IS_OBJECT(v)) { + type = JSTYPE_OBJECT; /* XXXbe JSTYPE_NULL for JS2 */ + obj = JSVAL_TO_OBJECT(v); + if (obj) { + obj = js_GetWrappedObject(cx, obj); + + ops = obj->map->ops; +#if JS_HAS_XML_SUPPORT + if (ops == &js_XMLObjectOps) { + type = JSTYPE_XML; + } else +#endif + { + /* + * ECMA 262, 11.4.3 says that any native object that implements + * [[Call]] should be of type "function". However, RegExp is of + * type "object", not "function", for Web compatibility. + */ + clasp = OBJ_GET_CLASS(cx, obj); + if ((ops == &js_ObjectOps) + ? (clasp->call + ? clasp == &js_ScriptClass + : clasp == &js_FunctionClass) + : ops->call != NULL) { + type = JSTYPE_FUNCTION; + } else { +#ifdef NARCISSUS + JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED); + + if (!obj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.__call__Atom), + &v)) { + JS_ClearPendingException(cx); + } else if (VALUE_IS_FUNCTION(cx, v)) { + type = JSTYPE_FUNCTION; + } +#endif + } + } + } + } else if (JSVAL_IS_NUMBER(v)) { + type = JSTYPE_NUMBER; + } else if (JSVAL_IS_STRING(v)) { + type = JSTYPE_STRING; + } else if (JSVAL_IS_BOOLEAN(v)) { + type = JSTYPE_BOOLEAN; + } else { + type = JSTYPE_VOID; + } + return type; +} + +JS_PUBLIC_API(const char *) +JS_GetTypeName(JSContext *cx, JSType type) +{ + if ((uintN)type >= (uintN)JSTYPE_LIMIT) + return NULL; + return JS_TYPE_STR(type); +} + +JS_PUBLIC_API(JSBool) +JS_StrictlyEqual(JSContext *cx, jsval v1, jsval v2) +{ + return js_StrictlyEqual(cx, v1, v2); +} + +JS_PUBLIC_API(JSBool) +JS_SameValue(JSContext *cx, jsval v1, jsval v2) +{ + return js_SameValue(v1, v2, cx); +} + +/************************************************************************/ + +/* + * Has a new runtime ever been created? This flag is used to detect unsafe + * changes to js_CStringsAreUTF8 after a runtime has been created, and to + * ensure that "first checks" on runtime creation are run only once. + */ +#ifdef DEBUG +static JSBool js_NewRuntimeWasCalled = JS_FALSE; +#endif + +JSRuntime::JSRuntime() +{ + /* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */ + JS_INIT_CLIST(&contextList); + JS_INIT_CLIST(&trapList); + JS_INIT_CLIST(&watchPointList); +} + +bool +JSRuntime::init(uint32 maxbytes) +{ + if (!js_InitDtoa() || + !js_InitGC(this, maxbytes) || + !js_InitAtomState(this) || + !js_InitDeflatedStringCache(this)) { + return false; + } +#ifdef JS_THREADSAFE + gcLock = JS_NEW_LOCK(); + if (!gcLock) + return false; + gcDone = JS_NEW_CONDVAR(gcLock); + if (!gcDone) + return false; + requestDone = JS_NEW_CONDVAR(gcLock); + if (!requestDone) + return false; + /* this is asymmetric with JS_ShutDown: */ + if (!js_SetupLocks(8, 16)) + return false; + rtLock = JS_NEW_LOCK(); + if (!rtLock) + return false; + stateChange = JS_NEW_CONDVAR(gcLock); + if (!stateChange) + return false; + titleSharingDone = JS_NEW_CONDVAR(gcLock); + if (!titleSharingDone) + return false; + titleSharingTodo = NO_TITLE_SHARING_TODO; + debuggerLock = JS_NEW_LOCK(); + if (!debuggerLock) + return false; + deallocatorThread = new JSBackgroundThread(); + if (!deallocatorThread || !deallocatorThread->init()) + return false; +#endif + return js_InitPropertyTree(this) && js_InitThreads(this); +} + +JSRuntime::~JSRuntime() +{ +#ifdef DEBUG + /* Don't hurt everyone in leaky ol' Mozilla with a fatal JS_ASSERT! */ + if (!JS_CLIST_IS_EMPTY(&contextList)) { + JSContext *cx, *iter = NULL; + uintN cxcount = 0; + while ((cx = js_ContextIterator(this, JS_TRUE, &iter)) != NULL) { + fprintf(stderr, +"JS API usage error: found live context at %p\n", + (void *) cx); + cxcount++; + } + fprintf(stderr, +"JS API usage error: %u context%s left in runtime upon JS_DestroyRuntime.\n", + cxcount, (cxcount == 1) ? "" : "s"); + } +#endif + + js_FinishThreads(this); + js_FreeRuntimeScriptState(this); + js_FinishAtomState(this); + + /* + * Finish the deflated string cache after the last GC and after + * calling js_FinishAtomState, which finalizes strings. + */ + js_FinishDeflatedStringCache(this); + js_FinishGC(this); +#ifdef JS_THREADSAFE + if (gcLock) + JS_DESTROY_LOCK(gcLock); + if (gcDone) + JS_DESTROY_CONDVAR(gcDone); + if (requestDone) + JS_DESTROY_CONDVAR(requestDone); + if (rtLock) + JS_DESTROY_LOCK(rtLock); + if (stateChange) + JS_DESTROY_CONDVAR(stateChange); + if (titleSharingDone) + JS_DESTROY_CONDVAR(titleSharingDone); + if (debuggerLock) + JS_DESTROY_LOCK(debuggerLock); + if (deallocatorThread) { + deallocatorThread->cancel(); + delete deallocatorThread; + } +#endif + js_FinishPropertyTree(this); +} + + +JS_PUBLIC_API(JSRuntime *) +JS_NewRuntime(uint32 maxbytes) +{ +#ifdef DEBUG + if (!js_NewRuntimeWasCalled) { + /* + * This code asserts that the numbers associated with the error names + * in jsmsg.def are monotonically increasing. It uses values for the + * error names enumerated in jscntxt.c. It's not a compile-time check + * but it's better than nothing. + */ + int errorNumber = 0; +#define MSG_DEF(name, number, count, exception, format) \ + JS_ASSERT(name == errorNumber++); +#include "js.msg" +#undef MSG_DEF + +#define MSG_DEF(name, number, count, exception, format) \ + JS_BEGIN_MACRO \ + uintN numfmtspecs = 0; \ + const char *fmt; \ + for (fmt = format; *fmt != '\0'; fmt++) { \ + if (*fmt == '{' && isdigit(fmt[1])) \ + ++numfmtspecs; \ + } \ + JS_ASSERT(count == numfmtspecs); \ + JS_END_MACRO; +#include "js.msg" +#undef MSG_DEF + + /* + * If it were possible for pure inline function calls with constant + * arguments to be computed at compile time, these would be static + * assertions, but since it isn't, this is the best we can do. + */ + JS_ASSERT(JSVAL_NULL == OBJECT_TO_JSVAL(NULL)); + JS_ASSERT(JSVAL_ZERO == INT_TO_JSVAL(0)); + JS_ASSERT(JSVAL_ONE == INT_TO_JSVAL(1)); + JS_ASSERT(JSVAL_FALSE == BOOLEAN_TO_JSVAL(JS_FALSE)); + JS_ASSERT(JSVAL_TRUE == BOOLEAN_TO_JSVAL(JS_TRUE)); + + JS_ASSERT(JSVAL_TO_SPECIAL(JSVAL_VOID) == 2); + JS_ASSERT(JSVAL_TO_SPECIAL(JSVAL_HOLE) == (2 | (JSVAL_HOLE_FLAG >> JSVAL_TAGBITS))); + JS_ASSERT(JSVAL_TO_SPECIAL(JSVAL_ARETURN) == 8); + + js_NewRuntimeWasCalled = JS_TRUE; + } +#endif /* DEBUG */ + + void *mem = js_calloc(sizeof(JSRuntime)); + if (!mem) + return NULL; + + JSRuntime *rt = new (mem) JSRuntime(); + if (!rt->init(maxbytes)) { + JS_DestroyRuntime(rt); + return NULL; + } + + return rt; +} + +JS_PUBLIC_API(void) +JS_CommenceRuntimeShutDown(JSRuntime *rt) +{ + rt->gcFlushCodeCaches = true; +} + +JS_PUBLIC_API(void) +JS_DestroyRuntime(JSRuntime *rt) +{ + rt->~JSRuntime(); + + js_free(rt); +} + +JS_PUBLIC_API(void) +JS_ShutDown(void) +{ +#ifdef MOZ_TRACEVIS + JS_StopTraceVis(); +#endif + +#ifdef JS_OPMETER + extern void js_DumpOpMeters(); + + js_DumpOpMeters(); +#endif + + js_FinishDtoa(); +#ifdef JS_THREADSAFE + js_CleanupLocks(); +#endif + PRMJ_NowShutdown(); +} + +JS_PUBLIC_API(void *) +JS_GetRuntimePrivate(JSRuntime *rt) +{ + return rt->data; +} + +JS_PUBLIC_API(void) +JS_SetRuntimePrivate(JSRuntime *rt, void *data) +{ + rt->data = data; +} + +JS_PUBLIC_API(void) +JS_BeginRequest(JSContext *cx) +{ +#ifdef JS_THREADSAFE + JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread)); + if (!cx->requestDepth) { + JSRuntime *rt = cx->runtime; + JS_LOCK_GC(rt); + + /* Wait until the GC is finished. */ + if (rt->gcThread != cx->thread) { + while (rt->gcLevel > 0) + JS_AWAIT_GC_DONE(rt); + } + + /* Indicate that a request is running. */ + rt->requestCount++; + cx->requestDepth = 1; + cx->outstandingRequests++; + JS_UNLOCK_GC(rt); + return; + } + cx->requestDepth++; + cx->outstandingRequests++; +#endif +} + +JS_PUBLIC_API(void) +JS_EndRequest(JSContext *cx) +{ +#ifdef JS_THREADSAFE + JSRuntime *rt; + + CHECK_REQUEST(cx); + JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread)); + JS_ASSERT(cx->requestDepth > 0); + JS_ASSERT(cx->outstandingRequests > 0); + if (cx->requestDepth == 1) { + js_LeaveTrace(cx); /* for GC safety */ + + /* Lock before clearing to interlock with ClaimScope, in jslock.c. */ + rt = cx->runtime; + JS_LOCK_GC(rt); + cx->requestDepth = 0; + cx->outstandingRequests--; + + js_ShareWaitingTitles(cx); + + /* Give the GC a chance to run if this was the last request running. */ + JS_ASSERT(rt->requestCount > 0); + rt->requestCount--; + if (rt->requestCount == 0) + JS_NOTIFY_REQUEST_DONE(rt); + + JS_UNLOCK_GC(rt); + return; + } + + cx->requestDepth--; + cx->outstandingRequests--; +#endif +} + +/* Yield to pending GC operations, regardless of request depth */ +JS_PUBLIC_API(void) +JS_YieldRequest(JSContext *cx) +{ +#ifdef JS_THREADSAFE + JS_ASSERT(cx->thread); + CHECK_REQUEST(cx); + JS_ResumeRequest(cx, JS_SuspendRequest(cx)); +#endif +} + +JS_PUBLIC_API(jsrefcount) +JS_SuspendRequest(JSContext *cx) +{ +#ifdef JS_THREADSAFE + jsrefcount saveDepth = cx->requestDepth; + + while (cx->requestDepth) { + cx->outstandingRequests++; /* compensate for JS_EndRequest */ + JS_EndRequest(cx); + } + return saveDepth; +#else + return 0; +#endif +} + +JS_PUBLIC_API(void) +JS_ResumeRequest(JSContext *cx, jsrefcount saveDepth) +{ +#ifdef JS_THREADSAFE + JS_ASSERT(!cx->requestDepth); + while (--saveDepth >= 0) { + JS_BeginRequest(cx); + cx->outstandingRequests--; /* compensate for JS_BeginRequest */ + } +#endif +} + +JS_PUBLIC_API(void) +JS_Lock(JSRuntime *rt) +{ + JS_LOCK_RUNTIME(rt); +} + +JS_PUBLIC_API(void) +JS_Unlock(JSRuntime *rt) +{ + JS_UNLOCK_RUNTIME(rt); +} + +JS_PUBLIC_API(JSContextCallback) +JS_SetContextCallback(JSRuntime *rt, JSContextCallback cxCallback) +{ + JSContextCallback old; + + old = rt->cxCallback; + rt->cxCallback = cxCallback; + return old; +} + +JS_PUBLIC_API(JSContext *) +JS_NewContext(JSRuntime *rt, size_t stackChunkSize) +{ + return js_NewContext(rt, stackChunkSize); +} + +JS_PUBLIC_API(void) +JS_DestroyContext(JSContext *cx) +{ + js_DestroyContext(cx, JSDCM_FORCE_GC); +} + +JS_PUBLIC_API(void) +JS_DestroyContextNoGC(JSContext *cx) +{ + js_DestroyContext(cx, JSDCM_NO_GC); +} + +JS_PUBLIC_API(void) +JS_DestroyContextMaybeGC(JSContext *cx) +{ + js_DestroyContext(cx, JSDCM_MAYBE_GC); +} + +JS_PUBLIC_API(void *) +JS_GetContextPrivate(JSContext *cx) +{ + return cx->data; +} + +JS_PUBLIC_API(void) +JS_SetContextPrivate(JSContext *cx, void *data) +{ + cx->data = data; +} + +JS_PUBLIC_API(JSRuntime *) +JS_GetRuntime(JSContext *cx) +{ + return cx->runtime; +} + +JS_PUBLIC_API(JSContext *) +JS_ContextIterator(JSRuntime *rt, JSContext **iterp) +{ + return js_ContextIterator(rt, JS_TRUE, iterp); +} + +JS_PUBLIC_API(JSVersion) +JS_GetVersion(JSContext *cx) +{ + return JSVERSION_NUMBER(cx); +} + +JS_PUBLIC_API(JSVersion) +JS_SetVersion(JSContext *cx, JSVersion version) +{ + JSVersion oldVersion; + + JS_ASSERT(version != JSVERSION_UNKNOWN); + JS_ASSERT((version & ~JSVERSION_MASK) == 0); + + oldVersion = JSVERSION_NUMBER(cx); + if (version == oldVersion) + return oldVersion; + + /* We no longer support 1.4 or below. */ + if (version != JSVERSION_DEFAULT && version <= JSVERSION_1_4) + return oldVersion; + + cx->version = (cx->version & ~JSVERSION_MASK) | version; + js_OnVersionChange(cx); + return oldVersion; +} + +static struct v2smap { + JSVersion version; + const char *string; +} v2smap[] = { + {JSVERSION_1_0, "1.0"}, + {JSVERSION_1_1, "1.1"}, + {JSVERSION_1_2, "1.2"}, + {JSVERSION_1_3, "1.3"}, + {JSVERSION_1_4, "1.4"}, + {JSVERSION_ECMA_3, "ECMAv3"}, + {JSVERSION_1_5, "1.5"}, + {JSVERSION_1_6, "1.6"}, + {JSVERSION_1_7, "1.7"}, + {JSVERSION_1_8, "1.8"}, + {JSVERSION_ECMA_5, "ECMAv5"}, + {JSVERSION_DEFAULT, js_default_str}, + {JSVERSION_UNKNOWN, NULL}, /* must be last, NULL is sentinel */ +}; + +JS_PUBLIC_API(const char *) +JS_VersionToString(JSVersion version) +{ + int i; + + for (i = 0; v2smap[i].string; i++) + if (v2smap[i].version == version) + return v2smap[i].string; + return "unknown"; +} + +JS_PUBLIC_API(JSVersion) +JS_StringToVersion(const char *string) +{ + int i; + + for (i = 0; v2smap[i].string; i++) + if (strcmp(v2smap[i].string, string) == 0) + return v2smap[i].version; + return JSVERSION_UNKNOWN; +} + +JS_PUBLIC_API(uint32) +JS_GetOptions(JSContext *cx) +{ + return cx->options; +} + +JS_PUBLIC_API(uint32) +JS_SetOptions(JSContext *cx, uint32 options) +{ + JS_LOCK_GC(cx->runtime); + uint32 oldopts = cx->options; + cx->options = options; + js_SyncOptionsToVersion(cx); + cx->updateJITEnabled(); + JS_UNLOCK_GC(cx->runtime); + return oldopts; +} + +JS_PUBLIC_API(uint32) +JS_ToggleOptions(JSContext *cx, uint32 options) +{ + JS_LOCK_GC(cx->runtime); + uint32 oldopts = cx->options; + cx->options ^= options; + js_SyncOptionsToVersion(cx); + cx->updateJITEnabled(); + JS_UNLOCK_GC(cx->runtime); + return oldopts; +} + +JS_PUBLIC_API(const char *) +JS_GetImplementationVersion(void) +{ + return "JavaScript-C 1.8.0 pre-release 1 2007-10-03"; +} + + +JS_PUBLIC_API(JSObject *) +JS_GetGlobalObject(JSContext *cx) +{ + return cx->globalObject; +} + +JS_PUBLIC_API(void) +JS_SetGlobalObject(JSContext *cx, JSObject *obj) +{ + cx->globalObject = obj; + +#if JS_HAS_XML_SUPPORT + cx->xmlSettingFlags = 0; +#endif +} + +JS_BEGIN_EXTERN_C + +JSObject * +js_InitFunctionAndObjectClasses(JSContext *cx, JSObject *obj) +{ + JSDHashTable *table; + JSBool resolving; + JSRuntime *rt; + JSResolvingKey key; + JSResolvingEntry *entry; + JSObject *fun_proto, *obj_proto; + + /* If cx has no global object, use obj so prototypes can be found. */ + if (!cx->globalObject) + JS_SetGlobalObject(cx, obj); + + /* Record Function and Object in cx->resolvingTable, if we are resolving. */ + table = cx->resolvingTable; + resolving = (table && table->entryCount); + rt = cx->runtime; + key.obj = obj; + if (resolving) { + key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Function]); + entry = (JSResolvingEntry *) + JS_DHashTableOperate(table, &key, JS_DHASH_ADD); + if (entry && entry->key.obj && (entry->flags & JSRESFLAG_LOOKUP)) { + /* Already resolving Function, record Object too. */ + JS_ASSERT(entry->key.obj == obj); + key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Object]); + entry = (JSResolvingEntry *) + JS_DHashTableOperate(table, &key, JS_DHASH_ADD); + } + if (!entry) { + JS_ReportOutOfMemory(cx); + return NULL; + } + JS_ASSERT(!entry->key.obj && entry->flags == 0); + entry->key = key; + entry->flags = JSRESFLAG_LOOKUP; + } else { + key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Object]); + if (!js_StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry)) + return NULL; + + key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Function]); + if (!js_StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry)) { + key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Object]); + JS_DHashTableOperate(table, &key, JS_DHASH_REMOVE); + return NULL; + } + + table = cx->resolvingTable; + } + + /* Initialize the function class first so constructors can be made. */ + if (!js_GetClassPrototype(cx, obj, INT_TO_JSID(JSProto_Function), + &fun_proto)) { + fun_proto = NULL; + goto out; + } + if (!fun_proto) { + fun_proto = js_InitFunctionClass(cx, obj); + if (!fun_proto) + goto out; + } else { + JSObject *ctor; + + ctor = JS_GetConstructor(cx, fun_proto); + if (!ctor) { + fun_proto = NULL; + goto out; + } + obj->defineProperty(cx, ATOM_TO_JSID(CLASS_ATOM(cx, Function)), + OBJECT_TO_JSVAL(ctor), 0, 0, 0); + } + + /* Initialize the object class next so Object.prototype works. */ + if (!js_GetClassPrototype(cx, obj, INT_TO_JSID(JSProto_Object), + &obj_proto)) { + fun_proto = NULL; + goto out; + } + if (!obj_proto) + obj_proto = js_InitObjectClass(cx, obj); + if (!obj_proto) { + fun_proto = NULL; + goto out; + } + + /* Function.prototype and the global object delegate to Object.prototype. */ + OBJ_SET_PROTO(cx, fun_proto, obj_proto); + if (!OBJ_GET_PROTO(cx, obj)) + OBJ_SET_PROTO(cx, obj, obj_proto); + +out: + /* If resolving, remove the other entry (Object or Function) from table. */ + JS_DHashTableOperate(table, &key, JS_DHASH_REMOVE); + if (!resolving) { + /* If not resolving, remove the first entry added above, for Object. */ + JS_ASSERT(key.id == \ + ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Function])); + key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Object]); + JS_DHashTableOperate(table, &key, JS_DHASH_REMOVE); + } + return fun_proto; +} + +JS_END_EXTERN_C + +JS_PUBLIC_API(JSBool) +JS_InitStandardClasses(JSContext *cx, JSObject *obj) +{ + JSAtom *atom; + + CHECK_REQUEST(cx); + + /* Define a top-level property 'undefined' with the undefined value. */ + atom = cx->runtime->atomState.typeAtoms[JSTYPE_VOID]; + if (!obj->defineProperty(cx, ATOM_TO_JSID(atom), JSVAL_VOID, + JS_PropertyStub, JS_PropertyStub, JSPROP_PERMANENT)) { + return JS_FALSE; + } + + /* Function and Object require cooperative bootstrapping magic. */ + if (!js_InitFunctionAndObjectClasses(cx, obj)) + return JS_FALSE; + + /* Initialize the rest of the standard objects and functions. */ + return js_InitArrayClass(cx, obj) && + js_InitBooleanClass(cx, obj) && + js_InitExceptionClasses(cx, obj) && + js_InitMathClass(cx, obj) && + js_InitNumberClass(cx, obj) && + js_InitJSONClass(cx, obj) && + js_InitRegExpClass(cx, obj) && + js_InitStringClass(cx, obj) && + js_InitEval(cx, obj) && +#if JS_HAS_SCRIPT_OBJECT + js_InitScriptClass(cx, obj) && +#endif +#if JS_HAS_XML_SUPPORT + js_InitXMLClasses(cx, obj) && +#endif +#if JS_HAS_FILE_OBJECT + js_InitFileClass(cx, obj) && +#endif +#if JS_HAS_GENERATORS + js_InitIteratorClasses(cx, obj) && +#endif + js_InitDateClass(cx, obj); +} + +#define CLASP(name) (&js_##name##Class) +#define XCLASP(name) (&js_##name##Class.base) +#define EAGER_ATOM(name) ATOM_OFFSET(name), NULL +#define EAGER_CLASS_ATOM(name) CLASS_ATOM_OFFSET(name), NULL +#define EAGER_ATOM_AND_CLASP(name) EAGER_CLASS_ATOM(name), CLASP(name) +#define EAGER_ATOM_AND_XCLASP(name) EAGER_CLASS_ATOM(name), XCLASP(name) +#define LAZY_ATOM(name) ATOM_OFFSET(lazy.name), js_##name##_str + +typedef struct JSStdName { + JSObjectOp init; + size_t atomOffset; /* offset of atom pointer in JSAtomState */ + const char *name; /* null if atom is pre-pinned, else name */ + JSClass *clasp; +} JSStdName; + +static JSAtom * +StdNameToAtom(JSContext *cx, JSStdName *stdn) +{ + size_t offset; + JSAtom *atom; + const char *name; + + offset = stdn->atomOffset; + atom = OFFSET_TO_ATOM(cx->runtime, offset); + if (!atom) { + name = stdn->name; + if (name) { + atom = js_Atomize(cx, name, strlen(name), ATOM_PINNED); + OFFSET_TO_ATOM(cx->runtime, offset) = atom; + } + } + return atom; +} + +/* + * Table of class initializers and their atom offsets in rt->atomState. + * If you add a "standard" class, remember to update this table. + */ +static JSStdName standard_class_atoms[] = { + {js_InitFunctionAndObjectClasses, EAGER_ATOM_AND_CLASP(Function)}, + {js_InitFunctionAndObjectClasses, EAGER_ATOM_AND_CLASP(Object)}, + {js_InitArrayClass, EAGER_ATOM_AND_CLASP(Array)}, + {js_InitBooleanClass, EAGER_ATOM_AND_CLASP(Boolean)}, + {js_InitDateClass, EAGER_ATOM_AND_CLASP(Date)}, + {js_InitMathClass, EAGER_ATOM_AND_CLASP(Math)}, + {js_InitNumberClass, EAGER_ATOM_AND_CLASP(Number)}, + {js_InitStringClass, EAGER_ATOM_AND_CLASP(String)}, + {js_InitExceptionClasses, EAGER_ATOM_AND_CLASP(Error)}, + {js_InitRegExpClass, EAGER_ATOM_AND_CLASP(RegExp)}, +#if JS_HAS_SCRIPT_OBJECT + {js_InitScriptClass, EAGER_ATOM_AND_CLASP(Script)}, +#endif +#if JS_HAS_XML_SUPPORT + {js_InitXMLClass, EAGER_ATOM_AND_CLASP(XML)}, + {js_InitNamespaceClass, EAGER_ATOM_AND_XCLASP(Namespace)}, + {js_InitQNameClass, EAGER_ATOM_AND_XCLASP(QName)}, +#endif +#if JS_HAS_FILE_OBJECT + {js_InitFileClass, EAGER_ATOM_AND_CLASP(File)}, +#endif +#if JS_HAS_GENERATORS + {js_InitIteratorClasses, EAGER_ATOM_AND_CLASP(StopIteration)}, +#endif + {js_InitJSONClass, EAGER_ATOM_AND_CLASP(JSON)}, + {NULL, 0, NULL, NULL} +}; + +/* + * Table of top-level function and constant names and their init functions. + * If you add a "standard" global function or property, remember to update + * this table. + */ +static JSStdName standard_class_names[] = { + /* ECMA requires that eval be a direct property of the global object. */ + {js_InitEval, EAGER_ATOM(eval), NULL}, + + /* Global properties and functions defined by the Number class. */ + {js_InitNumberClass, LAZY_ATOM(NaN), NULL}, + {js_InitNumberClass, LAZY_ATOM(Infinity), NULL}, + {js_InitNumberClass, LAZY_ATOM(isNaN), NULL}, + {js_InitNumberClass, LAZY_ATOM(isFinite), NULL}, + {js_InitNumberClass, LAZY_ATOM(parseFloat), NULL}, + {js_InitNumberClass, LAZY_ATOM(parseInt), NULL}, + + /* String global functions. */ + {js_InitStringClass, LAZY_ATOM(escape), NULL}, + {js_InitStringClass, LAZY_ATOM(unescape), NULL}, + {js_InitStringClass, LAZY_ATOM(decodeURI), NULL}, + {js_InitStringClass, LAZY_ATOM(encodeURI), NULL}, + {js_InitStringClass, LAZY_ATOM(decodeURIComponent), NULL}, + {js_InitStringClass, LAZY_ATOM(encodeURIComponent), NULL}, +#if JS_HAS_UNEVAL + {js_InitStringClass, LAZY_ATOM(uneval), NULL}, +#endif + + /* Exception constructors. */ + {js_InitExceptionClasses, EAGER_CLASS_ATOM(Error), CLASP(Error)}, + {js_InitExceptionClasses, EAGER_CLASS_ATOM(InternalError), CLASP(Error)}, + {js_InitExceptionClasses, EAGER_CLASS_ATOM(EvalError), CLASP(Error)}, + {js_InitExceptionClasses, EAGER_CLASS_ATOM(RangeError), CLASP(Error)}, + {js_InitExceptionClasses, EAGER_CLASS_ATOM(ReferenceError), CLASP(Error)}, + {js_InitExceptionClasses, EAGER_CLASS_ATOM(SyntaxError), CLASP(Error)}, + {js_InitExceptionClasses, EAGER_CLASS_ATOM(TypeError), CLASP(Error)}, + {js_InitExceptionClasses, EAGER_CLASS_ATOM(URIError), CLASP(Error)}, + +#if JS_HAS_XML_SUPPORT + {js_InitAnyNameClass, EAGER_ATOM_AND_CLASP(AnyName)}, + {js_InitAttributeNameClass, EAGER_ATOM_AND_CLASP(AttributeName)}, + {js_InitXMLClass, LAZY_ATOM(XMLList), &js_XMLClass}, + {js_InitXMLClass, LAZY_ATOM(isXMLName), NULL}, +#endif + +#if JS_HAS_GENERATORS + {js_InitIteratorClasses, EAGER_ATOM_AND_CLASP(Iterator)}, + {js_InitIteratorClasses, EAGER_ATOM_AND_CLASP(Generator)}, +#endif + + {NULL, 0, NULL, NULL} +}; + +static JSStdName object_prototype_names[] = { + /* Object.prototype properties (global delegates to Object.prototype). */ + {js_InitObjectClass, EAGER_ATOM(proto), NULL}, + {js_InitObjectClass, EAGER_ATOM(parent), NULL}, + {js_InitObjectClass, EAGER_ATOM(count), NULL}, +#if JS_HAS_TOSOURCE + {js_InitObjectClass, EAGER_ATOM(toSource), NULL}, +#endif + {js_InitObjectClass, EAGER_ATOM(toString), NULL}, + {js_InitObjectClass, EAGER_ATOM(toLocaleString), NULL}, + {js_InitObjectClass, EAGER_ATOM(valueOf), NULL}, +#if JS_HAS_OBJ_WATCHPOINT + {js_InitObjectClass, LAZY_ATOM(watch), NULL}, + {js_InitObjectClass, LAZY_ATOM(unwatch), NULL}, +#endif + {js_InitObjectClass, LAZY_ATOM(hasOwnProperty), NULL}, + {js_InitObjectClass, LAZY_ATOM(isPrototypeOf), NULL}, + {js_InitObjectClass, LAZY_ATOM(propertyIsEnumerable), NULL}, +#if JS_HAS_GETTER_SETTER + {js_InitObjectClass, LAZY_ATOM(defineGetter), NULL}, + {js_InitObjectClass, LAZY_ATOM(defineSetter), NULL}, + {js_InitObjectClass, LAZY_ATOM(lookupGetter), NULL}, + {js_InitObjectClass, LAZY_ATOM(lookupSetter), NULL}, +#endif + + {NULL, 0, NULL, NULL} +}; + +JS_PUBLIC_API(JSBool) +JS_ResolveStandardClass(JSContext *cx, JSObject *obj, jsval id, + JSBool *resolved) +{ + JSString *idstr; + JSRuntime *rt; + JSAtom *atom; + JSStdName *stdnm; + uintN i; + + CHECK_REQUEST(cx); + *resolved = JS_FALSE; + + rt = cx->runtime; + JS_ASSERT(rt->state != JSRTS_DOWN); + if (rt->state == JSRTS_LANDING || !JSVAL_IS_STRING(id)) + return JS_TRUE; + + idstr = JSVAL_TO_STRING(id); + + /* Check whether we're resolving 'undefined', and define it if so. */ + atom = rt->atomState.typeAtoms[JSTYPE_VOID]; + if (idstr == ATOM_TO_STRING(atom)) { + *resolved = JS_TRUE; + return obj->defineProperty(cx, ATOM_TO_JSID(atom), JSVAL_VOID, + JS_PropertyStub, JS_PropertyStub, + JSPROP_PERMANENT); + } + + /* Try for class constructors/prototypes named by well-known atoms. */ + stdnm = NULL; + for (i = 0; standard_class_atoms[i].init; i++) { + atom = OFFSET_TO_ATOM(rt, standard_class_atoms[i].atomOffset); + if (idstr == ATOM_TO_STRING(atom)) { + stdnm = &standard_class_atoms[i]; + break; + } + } + + if (!stdnm) { + /* Try less frequently used top-level functions and constants. */ + for (i = 0; standard_class_names[i].init; i++) { + atom = StdNameToAtom(cx, &standard_class_names[i]); + if (!atom) + return JS_FALSE; + if (idstr == ATOM_TO_STRING(atom)) { + stdnm = &standard_class_names[i]; + break; + } + } + + if (!stdnm && !OBJ_GET_PROTO(cx, obj)) { + /* + * Try even less frequently used names delegated from the global + * object to Object.prototype, but only if the Object class hasn't + * yet been initialized. + */ + for (i = 0; object_prototype_names[i].init; i++) { + atom = StdNameToAtom(cx, &object_prototype_names[i]); + if (!atom) + return JS_FALSE; + if (idstr == ATOM_TO_STRING(atom)) { + stdnm = &standard_class_names[i]; + break; + } + } + } + } + + if (stdnm) { + /* + * If this standard class is anonymous and obj advertises itself as a + * global object (in order to reserve slots for standard class object + * pointers), then we don't want to resolve by name. + * + * If inversely, either id does not name a class, or id does not name + * an anonymous class, or the global does not reserve slots for class + * objects, then we must call the init hook here. + */ + if (stdnm->clasp && + (stdnm->clasp->flags & JSCLASS_IS_ANONYMOUS) && + (OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL)) { + return JS_TRUE; + } + + if (!stdnm->init(cx, obj)) + return JS_FALSE; + *resolved = JS_TRUE; + } + return JS_TRUE; +} + +static JSBool +AlreadyHasOwnProperty(JSContext *cx, JSObject *obj, JSAtom *atom) +{ + JS_LOCK_OBJ(cx, obj); + JSScope *scope = OBJ_SCOPE(obj); + bool found = scope->hasProperty(ATOM_TO_JSID(atom)); + JS_UNLOCK_SCOPE(cx, scope); + return found; +} + +JS_PUBLIC_API(JSBool) +JS_EnumerateStandardClasses(JSContext *cx, JSObject *obj) +{ + JSRuntime *rt; + JSAtom *atom; + uintN i; + + CHECK_REQUEST(cx); + rt = cx->runtime; + + /* Check whether we need to bind 'undefined' and define it if so. */ + atom = rt->atomState.typeAtoms[JSTYPE_VOID]; + if (!AlreadyHasOwnProperty(cx, obj, atom) && + !obj->defineProperty(cx, ATOM_TO_JSID(atom), JSVAL_VOID, + JS_PropertyStub, JS_PropertyStub, JSPROP_PERMANENT)) { + return JS_FALSE; + } + + /* Initialize any classes that have not been resolved yet. */ + for (i = 0; standard_class_atoms[i].init; i++) { + atom = OFFSET_TO_ATOM(rt, standard_class_atoms[i].atomOffset); + if (!AlreadyHasOwnProperty(cx, obj, atom) && + !standard_class_atoms[i].init(cx, obj)) { + return JS_FALSE; + } + } + + return JS_TRUE; +} + +static JSIdArray * +NewIdArray(JSContext *cx, jsint length) +{ + JSIdArray *ida; + + ida = (JSIdArray *) + cx->malloc(offsetof(JSIdArray, vector) + length * sizeof(jsval)); + if (ida) + ida->length = length; + return ida; +} + +/* + * Unlike realloc(3), this function frees ida on failure. + */ +static JSIdArray * +SetIdArrayLength(JSContext *cx, JSIdArray *ida, jsint length) +{ + JSIdArray *rida; + + rida = (JSIdArray *) + JS_realloc(cx, ida, + offsetof(JSIdArray, vector) + length * sizeof(jsval)); + if (!rida) + JS_DestroyIdArray(cx, ida); + else + rida->length = length; + return rida; +} + +static JSIdArray * +AddAtomToArray(JSContext *cx, JSAtom *atom, JSIdArray *ida, jsint *ip) +{ + jsint i, length; + + i = *ip; + length = ida->length; + if (i >= length) { + ida = SetIdArrayLength(cx, ida, JS_MAX(length * 2, 8)); + if (!ida) + return NULL; + JS_ASSERT(i < ida->length); + } + ida->vector[i] = ATOM_TO_JSID(atom); + *ip = i + 1; + return ida; +} + +static JSIdArray * +EnumerateIfResolved(JSContext *cx, JSObject *obj, JSAtom *atom, JSIdArray *ida, + jsint *ip, JSBool *foundp) +{ + *foundp = AlreadyHasOwnProperty(cx, obj, atom); + if (*foundp) + ida = AddAtomToArray(cx, atom, ida, ip); + return ida; +} + +JS_PUBLIC_API(JSIdArray *) +JS_EnumerateResolvedStandardClasses(JSContext *cx, JSObject *obj, + JSIdArray *ida) +{ + JSRuntime *rt; + jsint i, j, k; + JSAtom *atom; + JSBool found; + JSObjectOp init; + + CHECK_REQUEST(cx); + rt = cx->runtime; + if (ida) { + i = ida->length; + } else { + ida = NewIdArray(cx, 8); + if (!ida) + return NULL; + i = 0; + } + + /* Check whether 'undefined' has been resolved and enumerate it if so. */ + atom = rt->atomState.typeAtoms[JSTYPE_VOID]; + ida = EnumerateIfResolved(cx, obj, atom, ida, &i, &found); + if (!ida) + return NULL; + + /* Enumerate only classes that *have* been resolved. */ + for (j = 0; standard_class_atoms[j].init; j++) { + atom = OFFSET_TO_ATOM(rt, standard_class_atoms[j].atomOffset); + ida = EnumerateIfResolved(cx, obj, atom, ida, &i, &found); + if (!ida) + return NULL; + + if (found) { + init = standard_class_atoms[j].init; + + for (k = 0; standard_class_names[k].init; k++) { + if (standard_class_names[k].init == init) { + atom = StdNameToAtom(cx, &standard_class_names[k]); + ida = AddAtomToArray(cx, atom, ida, &i); + if (!ida) + return NULL; + } + } + + if (init == js_InitObjectClass) { + for (k = 0; object_prototype_names[k].init; k++) { + atom = StdNameToAtom(cx, &object_prototype_names[k]); + ida = AddAtomToArray(cx, atom, ida, &i); + if (!ida) + return NULL; + } + } + } + } + + /* Trim to exact length. */ + return SetIdArrayLength(cx, ida, i); +} + +#undef CLASP +#undef EAGER_ATOM +#undef EAGER_CLASS_ATOM +#undef EAGER_ATOM_CLASP +#undef LAZY_ATOM + +JS_PUBLIC_API(JSBool) +JS_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, + JSObject **objp) +{ + CHECK_REQUEST(cx); + return js_GetClassObject(cx, obj, key, objp); +} + +JS_PUBLIC_API(JSObject *) +JS_GetScopeChain(JSContext *cx) +{ + JSStackFrame *fp; + + CHECK_REQUEST(cx); + fp = js_GetTopStackFrame(cx); + if (!fp) { + /* + * There is no code active on this context. In place of an actual + * scope chain, use the context's global object, which is set in + * js_InitFunctionAndObjectClasses, and which represents the default + * scope chain for the embedding. See also js_FindClassObject. + * + * For embeddings that use the inner and outer object hooks, the inner + * object represents the ultimate global object, with the outer object + * acting as a stand-in. + */ + JSObject *obj = cx->globalObject; + if (!obj) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE); + return NULL; + } + + OBJ_TO_INNER_OBJECT(cx, obj); + return obj; + } + return js_GetScopeChain(cx, fp); +} + +JS_PUBLIC_API(JSObject *) +JS_GetGlobalForObject(JSContext *cx, JSObject *obj) +{ + JSObject *parent; + + while ((parent = OBJ_GET_PARENT(cx, obj)) != NULL) + obj = parent; + return obj; +} + +JS_PUBLIC_API(jsval) +JS_ComputeThis(JSContext *cx, jsval *vp) +{ + if (!js_ComputeThis(cx, JS_FALSE, vp + 2)) + return JSVAL_NULL; + return vp[1]; +} + +JS_PUBLIC_API(void *) +JS_malloc(JSContext *cx, size_t nbytes) +{ + return cx->malloc(nbytes); +} + +JS_PUBLIC_API(void *) +JS_realloc(JSContext *cx, void *p, size_t nbytes) +{ + return cx->realloc(p, nbytes); +} + +JS_PUBLIC_API(void) +JS_free(JSContext *cx, void *p) +{ + return cx->free(p); +} + +JS_PUBLIC_API(void) +JS_updateMallocCounter(JSContext *cx, size_t nbytes) +{ + return cx->updateMallocCounter(nbytes); +} + +JS_PUBLIC_API(char *) +JS_strdup(JSContext *cx, const char *s) +{ + size_t n; + void *p; + + n = strlen(s) + 1; + p = cx->malloc(n); + if (!p) + return NULL; + return (char *)memcpy(p, s, n); +} + +JS_PUBLIC_API(jsdouble *) +JS_NewDouble(JSContext *cx, jsdouble d) +{ + CHECK_REQUEST(cx); + return js_NewWeaklyRootedDouble(cx, d); +} + +JS_PUBLIC_API(JSBool) +JS_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval) +{ + jsdouble *dp; + + CHECK_REQUEST(cx); + dp = js_NewWeaklyRootedDouble(cx, d); + if (!dp) + return JS_FALSE; + *rval = DOUBLE_TO_JSVAL(dp); + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval) +{ + CHECK_REQUEST(cx); + return js_NewWeaklyRootedNumber(cx, d, rval); +} + +#undef JS_AddRoot +JS_PUBLIC_API(JSBool) +JS_AddRoot(JSContext *cx, void *rp) +{ + CHECK_REQUEST(cx); + return js_AddRoot(cx, rp, NULL); +} + +JS_PUBLIC_API(JSBool) +JS_AddNamedRootRT(JSRuntime *rt, void *rp, const char *name) +{ + return js_AddRootRT(rt, rp, name); +} + +JS_PUBLIC_API(JSBool) +JS_RemoveRoot(JSContext *cx, void *rp) +{ + CHECK_REQUEST(cx); + return js_RemoveRoot(cx->runtime, rp); +} + +JS_PUBLIC_API(JSBool) +JS_RemoveRootRT(JSRuntime *rt, void *rp) +{ + return js_RemoveRoot(rt, rp); +} + +JS_PUBLIC_API(JSBool) +JS_AddNamedRoot(JSContext *cx, void *rp, const char *name) +{ + CHECK_REQUEST(cx); + return js_AddRoot(cx, rp, name); +} + +JS_PUBLIC_API(void) +JS_ClearNewbornRoots(JSContext *cx) +{ + JS_CLEAR_WEAK_ROOTS(&cx->weakRoots); +} + +JS_PUBLIC_API(JSBool) +JS_EnterLocalRootScope(JSContext *cx) +{ + CHECK_REQUEST(cx); + return js_EnterLocalRootScope(cx); +} + +JS_PUBLIC_API(void) +JS_LeaveLocalRootScope(JSContext *cx) +{ + CHECK_REQUEST(cx); + js_LeaveLocalRootScope(cx); +} + +JS_PUBLIC_API(void) +JS_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval) +{ + CHECK_REQUEST(cx); + js_LeaveLocalRootScopeWithResult(cx, rval); +} + +JS_PUBLIC_API(void) +JS_ForgetLocalRoot(JSContext *cx, void *thing) +{ + CHECK_REQUEST(cx); + js_ForgetLocalRoot(cx, (jsval) thing); +} + +#ifdef DEBUG + +JS_PUBLIC_API(void) +JS_DumpNamedRoots(JSRuntime *rt, + void (*dump)(const char *name, void *rp, void *data), + void *data) +{ + js_DumpNamedRoots(rt, dump, data); +} + +#endif /* DEBUG */ + +JS_PUBLIC_API(uint32) +JS_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data) +{ + return js_MapGCRoots(rt, map, data); +} + +JS_PUBLIC_API(JSBool) +JS_LockGCThing(JSContext *cx, void *thing) +{ + JSBool ok; + + CHECK_REQUEST(cx); + ok = js_LockGCThingRT(cx->runtime, thing); + if (!ok) + JS_ReportOutOfMemory(cx); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_LockGCThingRT(JSRuntime *rt, void *thing) +{ + return js_LockGCThingRT(rt, thing); +} + +JS_PUBLIC_API(JSBool) +JS_UnlockGCThing(JSContext *cx, void *thing) +{ + JSBool ok; + + CHECK_REQUEST(cx); + ok = js_UnlockGCThingRT(cx->runtime, thing); + if (!ok) + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_UNLOCK); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_UnlockGCThingRT(JSRuntime *rt, void *thing) +{ + return js_UnlockGCThingRT(rt, thing); +} + +JS_PUBLIC_API(void) +JS_SetExtraGCRoots(JSRuntime *rt, JSTraceDataOp traceOp, void *data) +{ + rt->gcExtraRootsTraceOp = traceOp; + rt->gcExtraRootsData = data; +} + +JS_PUBLIC_API(void) +JS_TraceRuntime(JSTracer *trc) +{ + JSBool allAtoms = trc->context->runtime->gcKeepAtoms != 0; + + js_LeaveTrace(trc->context); + js_TraceRuntime(trc, allAtoms); +} + +#ifdef DEBUG + +#ifdef HAVE_XPCONNECT +#include "dump_xpc.h" +#endif + +JS_PUBLIC_API(void) +JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, + void *thing, uint32 kind, JSBool details) +{ + const char *name; + size_t n; + + if (bufsize == 0) + return; + + switch (kind) { + case JSTRACE_OBJECT: + { + JSObject *obj = (JSObject *)thing; + JSClass *clasp = STOBJ_GET_CLASS(obj); + + name = clasp->name; +#ifdef HAVE_XPCONNECT + if (clasp->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) { + void *privateThing = obj->getPrivate(); + if (privateThing) { + const char *xpcClassName = GetXPCObjectClassName(privateThing); + if (xpcClassName) + name = xpcClassName; + } + } +#endif + break; + } + + case JSTRACE_STRING: + name = ((JSString *)thing)->isDependent() + ? "substring" + : "string"; + break; + + case JSTRACE_DOUBLE: + name = "double"; + break; + +#if JS_HAS_XML_SUPPORT + case JSTRACE_XML: + name = "xml"; + break; +#endif + default: + JS_ASSERT(0); + return; + break; + } + + n = strlen(name); + if (n > bufsize - 1) + n = bufsize - 1; + memcpy(buf, name, n + 1); + buf += n; + bufsize -= n; + + if (details && bufsize > 2) { + *buf++ = ' '; + bufsize--; + + switch (kind) { + case JSTRACE_OBJECT: + { + JSObject *obj = (JSObject *)thing; + JSClass *clasp = STOBJ_GET_CLASS(obj); + if (clasp == &js_FunctionClass) { + JSFunction *fun = GET_FUNCTION_PRIVATE(trc->context, obj); + if (!fun) { + JS_snprintf(buf, bufsize, ""); + } else if (FUN_OBJECT(fun) != obj) { + JS_snprintf(buf, bufsize, "%p", fun); + } else { + if (fun->atom && ATOM_IS_STRING(fun->atom)) + js_PutEscapedString(buf, bufsize, + ATOM_TO_STRING(fun->atom), 0); + } + } else if (clasp->flags & JSCLASS_HAS_PRIVATE) { + JS_snprintf(buf, bufsize, "%p", obj->getPrivate()); + } else { + JS_snprintf(buf, bufsize, ""); + } + break; + } + + case JSTRACE_STRING: + js_PutEscapedString(buf, bufsize, (JSString *)thing, 0); + break; + + case JSTRACE_DOUBLE: + JS_snprintf(buf, bufsize, "%g", *(jsdouble *)thing); + break; + +#if JS_HAS_XML_SUPPORT + case JSTRACE_XML: + { + extern const char *js_xml_class_str[]; + JSXML *xml = (JSXML *)thing; + + JS_snprintf(buf, bufsize, "%s", js_xml_class_str[xml->xml_class]); + break; + } +#endif + default: + JS_ASSERT(0); + break; + } + } + buf[bufsize - 1] = '\0'; +} + +typedef struct JSHeapDumpNode JSHeapDumpNode; + +struct JSHeapDumpNode { + void *thing; + uint32 kind; + JSHeapDumpNode *next; /* next sibling */ + JSHeapDumpNode *parent; /* node with the thing that refer to thing + from this node */ + char edgeName[1]; /* name of the edge from parent->thing + into thing */ +}; + +typedef struct JSDumpingTracer { + JSTracer base; + JSDHashTable visited; + JSBool ok; + void *startThing; + void *thingToFind; + void *thingToIgnore; + JSHeapDumpNode *parentNode; + JSHeapDumpNode **lastNodep; + char buffer[200]; +} JSDumpingTracer; + +static void +DumpNotify(JSTracer *trc, void *thing, uint32 kind) +{ + JSDumpingTracer *dtrc; + JSContext *cx; + JSDHashEntryStub *entry; + JSHeapDumpNode *node; + const char *edgeName; + size_t edgeNameSize; + + JS_ASSERT(trc->callback == DumpNotify); + dtrc = (JSDumpingTracer *)trc; + + if (!dtrc->ok || thing == dtrc->thingToIgnore) + return; + + cx = trc->context; + + /* + * Check if we have already seen thing unless it is thingToFind to include + * it to the graph each time we reach it and print all live things that + * refer to thingToFind. + * + * This does not print all possible paths leading to thingToFind since + * when a thing A refers directly or indirectly to thingToFind and A is + * present several times in the graph, we will print only the first path + * leading to A and thingToFind, other ways to reach A will be ignored. + */ + if (dtrc->thingToFind != thing) { + /* + * The startThing check allows to avoid putting startThing into the + * hash table before tracing startThing in JS_DumpHeap. + */ + if (thing == dtrc->startThing) + return; + entry = (JSDHashEntryStub *) + JS_DHashTableOperate(&dtrc->visited, thing, JS_DHASH_ADD); + if (!entry) { + JS_ReportOutOfMemory(cx); + dtrc->ok = JS_FALSE; + return; + } + if (entry->key) + return; + entry->key = thing; + } + + if (dtrc->base.debugPrinter) { + dtrc->base.debugPrinter(trc, dtrc->buffer, sizeof(dtrc->buffer)); + edgeName = dtrc->buffer; + } else if (dtrc->base.debugPrintIndex != (size_t)-1) { + JS_snprintf(dtrc->buffer, sizeof(dtrc->buffer), "%s[%lu]", + (const char *)dtrc->base.debugPrintArg, + dtrc->base.debugPrintIndex); + edgeName = dtrc->buffer; + } else { + edgeName = (const char*)dtrc->base.debugPrintArg; + } + + edgeNameSize = strlen(edgeName) + 1; + node = (JSHeapDumpNode *) + cx->malloc(offsetof(JSHeapDumpNode, edgeName) + edgeNameSize); + if (!node) { + dtrc->ok = JS_FALSE; + return; + } + + node->thing = thing; + node->kind = kind; + node->next = NULL; + node->parent = dtrc->parentNode; + memcpy(node->edgeName, edgeName, edgeNameSize); + + JS_ASSERT(!*dtrc->lastNodep); + *dtrc->lastNodep = node; + dtrc->lastNodep = &node->next; +} + +/* Dump node and the chain that leads to thing it contains. */ +static JSBool +DumpNode(JSDumpingTracer *dtrc, FILE* fp, JSHeapDumpNode *node) +{ + JSHeapDumpNode *prev, *following; + size_t chainLimit; + JSBool ok; + enum { MAX_PARENTS_TO_PRINT = 10 }; + + JS_PrintTraceThingInfo(dtrc->buffer, sizeof dtrc->buffer, + &dtrc->base, node->thing, node->kind, JS_TRUE); + if (fprintf(fp, "%p %-22s via ", node->thing, dtrc->buffer) < 0) + return JS_FALSE; + + /* + * We need to print the parent chain in the reverse order. To do it in + * O(N) time where N is the chain length we first reverse the chain while + * searching for the top and then print each node while restoring the + * chain order. + */ + chainLimit = MAX_PARENTS_TO_PRINT; + prev = NULL; + for (;;) { + following = node->parent; + node->parent = prev; + prev = node; + node = following; + if (!node) + break; + if (chainLimit == 0) { + if (fputs("...", fp) < 0) + return JS_FALSE; + break; + } + --chainLimit; + } + + node = prev; + prev = following; + ok = JS_TRUE; + do { + /* Loop must continue even when !ok to restore the parent chain. */ + if (ok) { + if (!prev) { + /* Print edge from some runtime root or startThing. */ + if (fputs(node->edgeName, fp) < 0) + ok = JS_FALSE; + } else { + JS_PrintTraceThingInfo(dtrc->buffer, sizeof dtrc->buffer, + &dtrc->base, prev->thing, prev->kind, + JS_FALSE); + if (fprintf(fp, "(%p %s).%s", + prev->thing, dtrc->buffer, node->edgeName) < 0) { + ok = JS_FALSE; + } + } + } + following = node->parent; + node->parent = prev; + prev = node; + node = following; + } while (node); + + return ok && putc('\n', fp) >= 0; +} + +JS_PUBLIC_API(JSBool) +JS_DumpHeap(JSContext *cx, FILE *fp, void* startThing, uint32 startKind, + void *thingToFind, size_t maxDepth, void *thingToIgnore) +{ + JSDumpingTracer dtrc; + JSHeapDumpNode *node, *children, *next, *parent; + size_t depth; + JSBool thingToFindWasTraced; + + if (maxDepth == 0) + return JS_TRUE; + + JS_TRACER_INIT(&dtrc.base, cx, DumpNotify); + if (!JS_DHashTableInit(&dtrc.visited, JS_DHashGetStubOps(), + NULL, sizeof(JSDHashEntryStub), + JS_DHASH_DEFAULT_CAPACITY(100))) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + dtrc.ok = JS_TRUE; + dtrc.startThing = startThing; + dtrc.thingToFind = thingToFind; + dtrc.thingToIgnore = thingToIgnore; + dtrc.parentNode = NULL; + node = NULL; + dtrc.lastNodep = &node; + if (!startThing) { + JS_ASSERT(startKind == 0); + JS_TraceRuntime(&dtrc.base); + } else { + JS_TraceChildren(&dtrc.base, startThing, startKind); + } + + depth = 1; + if (!node) + goto dump_out; + + thingToFindWasTraced = thingToFind && thingToFind == startThing; + for (;;) { + /* + * Loop must continue even when !dtrc.ok to free all nodes allocated + * so far. + */ + if (dtrc.ok) { + if (thingToFind == NULL || thingToFind == node->thing) + dtrc.ok = DumpNode(&dtrc, fp, node); + + /* Descend into children. */ + if (dtrc.ok && + depth < maxDepth && + (thingToFind != node->thing || !thingToFindWasTraced)) { + dtrc.parentNode = node; + children = NULL; + dtrc.lastNodep = &children; + JS_TraceChildren(&dtrc.base, node->thing, node->kind); + if (thingToFind == node->thing) + thingToFindWasTraced = JS_TRUE; + if (children != NULL) { + ++depth; + node = children; + continue; + } + } + } + + /* Move to next or parents next and free the node. */ + for (;;) { + next = node->next; + parent = node->parent; + cx->free(node); + node = next; + if (node) + break; + if (!parent) + goto dump_out; + JS_ASSERT(depth > 1); + --depth; + node = parent; + } + } + + dump_out: + JS_ASSERT(depth == 1); + JS_DHashTableFinish(&dtrc.visited); + return dtrc.ok; +} + +#endif /* DEBUG */ + +JS_PUBLIC_API(void) +JS_MarkGCThing(JSContext *cx, void *thing, const char *name, void *arg) +{ + JSTracer *trc; + + trc = (JSTracer *)arg; + if (!trc) + trc = cx->runtime->gcMarkingTracer; + else + JS_ASSERT(trc == cx->runtime->gcMarkingTracer); + +#ifdef JS_THREADSAFE + JS_ASSERT(cx->runtime->gcThread == trc->context->thread); +#endif + JS_SET_TRACING_NAME(trc, name ? name : "unknown"); + js_CallValueTracerIfGCThing(trc, (jsval)thing); +} + +extern JS_PUBLIC_API(JSBool) +JS_IsGCMarkingTracer(JSTracer *trc) +{ + return IS_GC_MARKING_TRACER(trc); +} + +JS_PUBLIC_API(void) +JS_GC(JSContext *cx) +{ + js_LeaveTrace(cx); + + /* Don't nuke active arenas if executing or compiling. */ + if (cx->stackPool.current == &cx->stackPool.first) + JS_FinishArenaPool(&cx->stackPool); + if (cx->tempPool.current == &cx->tempPool.first) + JS_FinishArenaPool(&cx->tempPool); + js_GC(cx, GC_NORMAL); +} + +JS_PUBLIC_API(void) +JS_MaybeGC(JSContext *cx) +{ + JSRuntime *rt; + uint32 bytes, lastBytes; + + rt = cx->runtime; + +#ifdef JS_GC_ZEAL + if (rt->gcZeal > 0) { + JS_GC(cx); + return; + } +#endif + + bytes = rt->gcBytes; + lastBytes = rt->gcLastBytes; + + /* + * We run the GC if we used all available free GC cells and had to + * allocate extra 1/3 of GC arenas since the last run of GC, or if + * we have malloc'd more bytes through JS_malloc than we were told + * to allocate by JS_NewRuntime. + * + * The reason for + * bytes > 4/3 lastBytes + * condition is the following. Bug 312238 changed bytes and lastBytes + * to mean the total amount of memory that the GC uses now and right + * after the last GC. + * + * Before the bug the variables meant the size of allocated GC things + * now and right after the last GC. That size did not include the + * memory taken by free GC cells and the condition was + * bytes > 3/2 lastBytes. + * That is, we run the GC if we have half again as many bytes of + * GC-things as the last time we GC'd. To be compatible we need to + * express that condition through the new meaning of bytes and + * lastBytes. + * + * We write the original condition as + * B*(1-F) > 3/2 Bl*(1-Fl) + * where B is the total memory size allocated by GC and F is the free + * cell density currently and Sl and Fl are the size and the density + * right after GC. The density by definition is memory taken by free + * cells divided by total amount of memory. In other words, B and Bl + * are bytes and lastBytes with the new meaning and B*(1-F) and + * Bl*(1-Fl) are bytes and lastBytes with the original meaning. + * + * Our task is to exclude F and Fl from the last statement. According + * to the stats from bug 331966 comment 23, Fl is about 10-25% for a + * typical run of the browser. It means that the original condition + * implied that we did not run GC unless we exhausted the pool of + * free cells. Indeed if we still have free cells, then B == Bl since + * we did not yet allocated any new arenas and the condition means + * 1 - F > 3/2 (1-Fl) or 3/2Fl > 1/2 + F + * That implies 3/2 Fl > 1/2 or Fl > 1/3. That cannot be fulfilled + * for the state described by the stats. So we can write the original + * condition as: + * F == 0 && B > 3/2 Bl(1-Fl) + * Again using the stats we see that Fl is about 11% when the browser + * starts up and when we are far from hitting rt->gcMaxBytes. With + * this F we have + * F == 0 && B > 3/2 Bl(1-0.11) + * or approximately F == 0 && B > 4/3 Bl. + */ + if ((bytes > 8192 && bytes > lastBytes + lastBytes / 3) || + rt->isGCMallocLimitReached()) { + JS_GC(cx); + } +} + +JS_PUBLIC_API(JSGCCallback) +JS_SetGCCallback(JSContext *cx, JSGCCallback cb) +{ + CHECK_REQUEST(cx); + return JS_SetGCCallbackRT(cx->runtime, cb); +} + +JS_PUBLIC_API(JSGCCallback) +JS_SetGCCallbackRT(JSRuntime *rt, JSGCCallback cb) +{ + JSGCCallback oldcb; + + oldcb = rt->gcCallback; + rt->gcCallback = cb; + return oldcb; +} + +JS_PUBLIC_API(JSBool) +JS_IsAboutToBeFinalized(JSContext *cx, void *thing) +{ + JS_ASSERT(thing); + return js_IsAboutToBeFinalized(cx, thing); +} + +JS_PUBLIC_API(void) +JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32 value) +{ + switch (key) { + case JSGC_MAX_BYTES: + rt->gcMaxBytes = value; + break; + case JSGC_MAX_MALLOC_BYTES: + rt->setGCMaxMallocBytes(value); + break; + case JSGC_STACKPOOL_LIFESPAN: + rt->gcEmptyArenaPoolLifespan = value; + break; + default: + JS_ASSERT(key == JSGC_TRIGGER_FACTOR); + JS_ASSERT(value >= 100); + rt->setGCTriggerFactor(value); + return; + } +} + +JS_PUBLIC_API(uint32) +JS_GetGCParameter(JSRuntime *rt, JSGCParamKey key) +{ + switch (key) { + case JSGC_MAX_BYTES: + return rt->gcMaxBytes; + case JSGC_MAX_MALLOC_BYTES: + return rt->gcMaxMallocBytes; + case JSGC_STACKPOOL_LIFESPAN: + return rt->gcEmptyArenaPoolLifespan; + case JSGC_TRIGGER_FACTOR: + return rt->gcTriggerFactor; + case JSGC_BYTES: + return rt->gcBytes; + default: + JS_ASSERT(key == JSGC_NUMBER); + return rt->gcNumber; + } +} + +JS_PUBLIC_API(void) +JS_SetGCParameterForThread(JSContext *cx, JSGCParamKey key, uint32 value) +{ + JS_ASSERT(key == JSGC_MAX_CODE_CACHE_BYTES); +#ifdef JS_TRACER + js_SetMaxCodeCacheBytes(cx, value); +#endif +} + +JS_PUBLIC_API(uint32) +JS_GetGCParameterForThread(JSContext *cx, JSGCParamKey key) +{ + JS_ASSERT(key == JSGC_MAX_CODE_CACHE_BYTES); +#ifdef JS_TRACER + return JS_THREAD_DATA(cx)->traceMonitor.maxCodeCacheBytes; +#else + return 0; +#endif +} + +JS_PUBLIC_API(intN) +JS_AddExternalStringFinalizer(JSStringFinalizeOp finalizer) +{ + return js_ChangeExternalStringFinalizer(NULL, finalizer); +} + +JS_PUBLIC_API(intN) +JS_RemoveExternalStringFinalizer(JSStringFinalizeOp finalizer) +{ + return js_ChangeExternalStringFinalizer(finalizer, NULL); +} + +JS_PUBLIC_API(JSString *) +JS_NewExternalString(JSContext *cx, jschar *chars, size_t length, intN type) +{ + CHECK_REQUEST(cx); + JS_ASSERT(uintN(type) < JS_EXTERNAL_STRING_LIMIT); + + JSString *str = js_NewGCExternalString(cx, uintN(type)); + if (!str) + return NULL; + str->initFlat(chars, length); + cx->updateMallocCounter((length + 1) * sizeof(jschar)); + return str; +} + +JS_PUBLIC_API(intN) +JS_GetExternalStringGCType(JSRuntime *rt, JSString *str) +{ + /* + * No need to test this in js_GetExternalStringGCType, which asserts its + * inverse instead of wasting cycles on testing a condition we can ensure + * by auditing in-VM calls to the js_... helper. + */ + if (JSString::isStatic(str)) + return -1; + + return js_GetExternalStringGCType(str); +} + +JS_PUBLIC_API(void) +JS_SetThreadStackLimit(JSContext *cx, jsuword limitAddr) +{ +#if JS_STACK_GROWTH_DIRECTION > 0 + if (limitAddr == 0) + limitAddr = (jsuword)-1; +#endif + cx->stackLimit = limitAddr; +} + +JS_PUBLIC_API(void) +JS_SetScriptStackQuota(JSContext *cx, size_t quota) +{ + cx->scriptStackQuota = quota; +} + +/************************************************************************/ + +JS_PUBLIC_API(void) +JS_DestroyIdArray(JSContext *cx, JSIdArray *ida) +{ + cx->free(ida); +} + +JS_PUBLIC_API(JSBool) +JS_ValueToId(JSContext *cx, jsval v, jsid *idp) +{ + CHECK_REQUEST(cx); + if (JSVAL_IS_INT(v)) { + *idp = INT_JSVAL_TO_JSID(v); + return JS_TRUE; + } + +#if JS_HAS_XML_SUPPORT + if (!JSVAL_IS_PRIMITIVE(v)) { + JSClass *clasp = JSVAL_TO_OBJECT(v)->getClass(); + if (JS_UNLIKELY(clasp == &js_QNameClass.base || + clasp == &js_AttributeNameClass || + clasp == &js_AnyNameClass)) { + *idp = OBJECT_JSVAL_TO_JSID(v); + return JS_TRUE; + } + } +#endif + + return js_ValueToStringId(cx, v, idp); +} + +JS_PUBLIC_API(JSBool) +JS_IdToValue(JSContext *cx, jsid id, jsval *vp) +{ + CHECK_REQUEST(cx); + *vp = ID_TO_VALUE(id); + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_PropertyStub(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_EnumerateStub(JSContext *cx, JSObject *obj) +{ + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_ResolveStub(JSContext *cx, JSObject *obj, jsval id) +{ + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_ConvertStub(JSContext *cx, JSObject *obj, JSType type, jsval *vp) +{ + return js_TryValueOf(cx, obj, type, vp); +} + +JS_PUBLIC_API(void) +JS_FinalizeStub(JSContext *cx, JSObject *obj) +{ +} + +JS_PUBLIC_API(JSObject *) +JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, + JSClass *clasp, JSNative constructor, uintN nargs, + JSPropertySpec *ps, JSFunctionSpec *fs, + JSPropertySpec *static_ps, JSFunctionSpec *static_fs) +{ + CHECK_REQUEST(cx); + return js_InitClass(cx, obj, parent_proto, clasp, constructor, nargs, + ps, fs, static_ps, static_fs); +} + +#ifdef JS_THREADSAFE +JS_PUBLIC_API(JSClass *) +JS_GetClass(JSContext *cx, JSObject *obj) +{ + return obj->getClass(); +} +#else +JS_PUBLIC_API(JSClass *) +JS_GetClass(JSObject *obj) +{ + return obj->getClass(); +} +#endif + +JS_PUBLIC_API(JSBool) +JS_InstanceOf(JSContext *cx, JSObject *obj, JSClass *clasp, jsval *argv) +{ + JSFunction *fun; + + CHECK_REQUEST(cx); + if (obj && OBJ_GET_CLASS(cx, obj) == clasp) + return JS_TRUE; + if (argv) { + fun = js_ValueToFunction(cx, &argv[-2], 0); + if (fun) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_INCOMPATIBLE_PROTO, + clasp->name, JS_GetFunctionName(fun), + obj + ? OBJ_GET_CLASS(cx, obj)->name + : js_null_str); + } + } + return JS_FALSE; +} + +JS_PUBLIC_API(JSBool) +JS_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) +{ + return js_HasInstance(cx, obj, v, bp); +} + +JS_PUBLIC_API(void *) +JS_GetPrivate(JSContext *cx, JSObject *obj) +{ + return obj->getPrivate(); +} + +JS_PUBLIC_API(JSBool) +JS_SetPrivate(JSContext *cx, JSObject *obj, void *data) +{ + obj->setPrivate(data); + return true; +} + +JS_PUBLIC_API(void *) +JS_GetInstancePrivate(JSContext *cx, JSObject *obj, JSClass *clasp, + jsval *argv) +{ + if (!JS_InstanceOf(cx, obj, clasp, argv)) + return NULL; + return obj->getPrivate(); +} + +JS_PUBLIC_API(JSObject *) +JS_GetPrototype(JSContext *cx, JSObject *obj) +{ + JSObject *proto; + + CHECK_REQUEST(cx); + proto = OBJ_GET_PROTO(cx, obj); + + /* Beware ref to dead object (we may be called from obj's finalizer). */ + return proto && proto->map ? proto : NULL; +} + +JS_PUBLIC_API(JSBool) +JS_SetPrototype(JSContext *cx, JSObject *obj, JSObject *proto) +{ + CHECK_REQUEST(cx); + return js_SetProtoOrParent(cx, obj, JSSLOT_PROTO, proto, JS_FALSE); +} + +JS_PUBLIC_API(JSObject *) +JS_GetParent(JSContext *cx, JSObject *obj) +{ + JSObject *parent; + + parent = OBJ_GET_PARENT(cx, obj); + + /* Beware ref to dead object (we may be called from obj's finalizer). */ + return parent && parent->map ? parent : NULL; +} + +JS_PUBLIC_API(JSBool) +JS_SetParent(JSContext *cx, JSObject *obj, JSObject *parent) +{ + CHECK_REQUEST(cx); + return js_SetProtoOrParent(cx, obj, JSSLOT_PARENT, parent, JS_FALSE); +} + +JS_PUBLIC_API(JSObject *) +JS_GetConstructor(JSContext *cx, JSObject *proto) +{ + jsval cval; + + CHECK_REQUEST(cx); + { + JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED); + + if (!proto->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.constructorAtom), &cval)) + return NULL; + } + if (!VALUE_IS_FUNCTION(cx, cval)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_CONSTRUCTOR, + OBJ_GET_CLASS(cx, proto)->name); + return NULL; + } + return JSVAL_TO_OBJECT(cval); +} + +JS_PUBLIC_API(JSBool) +JS_GetObjectId(JSContext *cx, JSObject *obj, jsid *idp) +{ + JS_ASSERT(JSID_IS_OBJECT(obj)); + *idp = OBJECT_TO_JSID(obj); + return JS_TRUE; +} + +JS_PUBLIC_API(JSObject *) +JS_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent) +{ + CHECK_REQUEST(cx); + if (!clasp) + clasp = &js_ObjectClass; /* default class is Object */ + return js_NewObject(cx, clasp, proto, parent); +} + +JS_PUBLIC_API(JSObject *) +JS_NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto, + JSObject *parent) +{ + CHECK_REQUEST(cx); + if (!clasp) + clasp = &js_ObjectClass; /* default class is Object */ + return js_NewObjectWithGivenProto(cx, clasp, proto, parent); +} + +JS_PUBLIC_API(JSBool) +JS_SealObject(JSContext *cx, JSObject *obj, JSBool deep) +{ + JSScope *scope; + JSIdArray *ida; + uint32 nslots, i; + jsval v; + + if (OBJ_IS_DENSE_ARRAY(cx, obj) && !js_MakeArraySlow(cx, obj)) + return JS_FALSE; + + if (!OBJ_IS_NATIVE(obj)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_CANT_SEAL_OBJECT, + OBJ_GET_CLASS(cx, obj)->name); + return JS_FALSE; + } + + scope = OBJ_SCOPE(obj); + +#if defined JS_THREADSAFE && defined DEBUG + /* Insist on scope being used exclusively by cx's thread. */ + if (scope->title.ownercx != cx) { + JS_LOCK_OBJ(cx, obj); + JS_ASSERT(OBJ_SCOPE(obj) == scope); + JS_ASSERT(scope->title.ownercx == cx); + JS_UNLOCK_SCOPE(cx, scope); + } +#endif + + /* Nothing to do if obj's scope is already sealed. */ + if (scope->sealed()) + return JS_TRUE; + + /* XXX Enumerate lazy properties now, as they can't be added later. */ + ida = JS_Enumerate(cx, obj); + if (!ida) + return JS_FALSE; + JS_DestroyIdArray(cx, ida); + + /* Ensure that obj has its own, mutable scope, and seal that scope. */ + JS_LOCK_OBJ(cx, obj); + scope = js_GetMutableScope(cx, obj); + if (scope) { + scope->sealingShapeChange(cx); + scope->setSealed(); + } + JS_UNLOCK_OBJ(cx, obj); + if (!scope) + return JS_FALSE; + + /* If we are not sealing an entire object graph, we're done. */ + if (!deep) + return JS_TRUE; + + /* Walk slots in obj and if any value is a non-null object, seal it. */ + nslots = scope->freeslot; + for (i = 0; i != nslots; ++i) { + v = STOBJ_GET_SLOT(obj, i); + if (JSVAL_IS_PRIMITIVE(v)) + continue; + if (!JS_SealObject(cx, JSVAL_TO_OBJECT(v), deep)) + return JS_FALSE; + } + return JS_TRUE; +} + +JS_PUBLIC_API(JSObject *) +JS_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, + JSObject *parent) +{ + CHECK_REQUEST(cx); + if (!clasp) + clasp = &js_ObjectClass; /* default class is Object */ + return js_ConstructObject(cx, clasp, proto, parent, 0, NULL); +} + +JS_PUBLIC_API(JSObject *) +JS_ConstructObjectWithArguments(JSContext *cx, JSClass *clasp, JSObject *proto, + JSObject *parent, uintN argc, jsval *argv) +{ + CHECK_REQUEST(cx); + if (!clasp) + clasp = &js_ObjectClass; /* default class is Object */ + return js_ConstructObject(cx, clasp, proto, parent, argc, argv); +} + +static JSBool +DefinePropertyById(JSContext *cx, JSObject *obj, jsid id, jsval value, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs, + uintN flags, intN tinyid) +{ + if (flags != 0 && OBJ_IS_NATIVE(obj)) { + JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_DECLARING); + return !!js_DefineNativeProperty(cx, obj, id, value, getter, setter, + attrs, flags, tinyid, NULL); + } + return obj->defineProperty(cx, id, value, getter, setter, attrs); +} + +static JSBool +DefineProperty(JSContext *cx, JSObject *obj, const char *name, jsval value, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs, + uintN flags, intN tinyid) +{ + jsid id; + JSAtom *atom; + + if (attrs & JSPROP_INDEX) { + id = INT_TO_JSID(JS_PTR_TO_INT32(name)); + atom = NULL; + attrs &= ~JSPROP_INDEX; + } else { + atom = js_Atomize(cx, name, strlen(name), 0); + if (!atom) + return JS_FALSE; + id = ATOM_TO_JSID(atom); + } + return DefinePropertyById(cx, obj, id, value, getter, setter, attrs, + flags, tinyid); +} + +#define AUTO_NAMELEN(s,n) (((n) == (size_t)-1) ? js_strlen(s) : (n)) + +static JSBool +DefineUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, jsval value, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs, + uintN flags, intN tinyid) +{ + JSAtom *atom; + + atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); + if (!atom) + return JS_FALSE; + if (flags != 0 && OBJ_IS_NATIVE(obj)) { + JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_DECLARING); + return !!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), value, + getter, setter, attrs, flags, tinyid, + NULL); + } + return obj->defineProperty(cx, ATOM_TO_JSID(atom), value, getter, setter, attrs); +} + +JS_PUBLIC_API(JSObject *) +JS_DefineObject(JSContext *cx, JSObject *obj, const char *name, JSClass *clasp, + JSObject *proto, uintN attrs) +{ + JSObject *nobj; + + CHECK_REQUEST(cx); + if (!clasp) + clasp = &js_ObjectClass; /* default class is Object */ + nobj = js_NewObject(cx, clasp, proto, obj); + if (!nobj) + return NULL; + if (!DefineProperty(cx, obj, name, OBJECT_TO_JSVAL(nobj), NULL, NULL, attrs, + 0, 0)) { + return NULL; + } + return nobj; +} + +JS_PUBLIC_API(JSBool) +JS_DefineConstDoubles(JSContext *cx, JSObject *obj, JSConstDoubleSpec *cds) +{ + JSBool ok; + jsval value; + uintN attrs; + + CHECK_REQUEST(cx); + for (ok = JS_TRUE; cds->name; cds++) { + ok = js_NewNumberInRootedValue(cx, cds->dval, &value); + if (!ok) + break; + attrs = cds->flags; + if (!attrs) + attrs = JSPROP_READONLY | JSPROP_PERMANENT; + ok = DefineProperty(cx, obj, cds->name, value, NULL, NULL, attrs, 0, 0); + if (!ok) + break; + } + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_DefineProperties(JSContext *cx, JSObject *obj, JSPropertySpec *ps) +{ + JSBool ok; + + CHECK_REQUEST(cx); + for (ok = JS_TRUE; ps->name; ps++) { + ok = DefineProperty(cx, obj, ps->name, JSVAL_VOID, + ps->getter, ps->setter, ps->flags, + SPROP_HAS_SHORTID, ps->tinyid); + if (!ok) + break; + } + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_DefineProperty(JSContext *cx, JSObject *obj, const char *name, jsval value, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs) +{ + CHECK_REQUEST(cx); + return DefineProperty(cx, obj, name, value, getter, setter, attrs, 0, 0); +} + +JS_PUBLIC_API(JSBool) +JS_DefinePropertyById(JSContext *cx, JSObject *obj, jsid id, jsval value, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs) +{ + CHECK_REQUEST(cx); + return DefinePropertyById(cx, obj, id, value, getter, setter, attrs, 0, 0); +} + +JS_PUBLIC_API(JSBool) +JS_DefinePropertyWithTinyId(JSContext *cx, JSObject *obj, const char *name, + int8 tinyid, jsval value, + JSPropertyOp getter, JSPropertyOp setter, + uintN attrs) +{ + CHECK_REQUEST(cx); + return DefineProperty(cx, obj, name, value, getter, setter, attrs, + SPROP_HAS_SHORTID, tinyid); +} + +static JSBool +LookupPropertyById(JSContext *cx, JSObject *obj, jsid id, uintN flags, + JSObject **objp, JSProperty **propp) +{ + JSAutoResolveFlags rf(cx, flags); + id = js_CheckForStringIndex(id); + return obj->lookupProperty(cx, id, objp, propp); +} + +static JSBool +LookupProperty(JSContext *cx, JSObject *obj, const char *name, uintN flags, + JSObject **objp, JSProperty **propp) +{ + JSAtom *atom; + + atom = js_Atomize(cx, name, strlen(name), 0); + if (!atom) + return JS_FALSE; + return LookupPropertyById(cx, obj, ATOM_TO_JSID(atom), flags, objp, propp); +} + +static JSBool +LookupUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, uintN flags, + JSObject **objp, JSProperty **propp) +{ + JSAtom *atom; + + atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); + if (!atom) + return JS_FALSE; + return LookupPropertyById(cx, obj, ATOM_TO_JSID(atom), flags, objp, propp); +} + +JS_PUBLIC_API(JSBool) +JS_AliasProperty(JSContext *cx, JSObject *obj, const char *name, + const char *alias) +{ + JSObject *obj2; + JSProperty *prop; + JSAtom *atom; + JSBool ok; + JSScopeProperty *sprop; + + CHECK_REQUEST(cx); + if (!LookupProperty(cx, obj, name, JSRESOLVE_QUALIFIED, &obj2, &prop)) + return JS_FALSE; + if (!prop) { + js_ReportIsNotDefined(cx, name); + return JS_FALSE; + } + if (obj2 != obj || !OBJ_IS_NATIVE(obj)) { + obj2->dropProperty(cx, prop); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_ALIAS, + alias, name, OBJ_GET_CLASS(cx, obj2)->name); + return JS_FALSE; + } + atom = js_Atomize(cx, alias, strlen(alias), 0); + if (!atom) { + ok = JS_FALSE; + } else { + sprop = (JSScopeProperty *)prop; + ok = (js_AddNativeProperty(cx, obj, ATOM_TO_JSID(atom), + sprop->getter, sprop->setter, sprop->slot, + sprop->attrs, sprop->flags | SPROP_IS_ALIAS, + sprop->shortid) + != NULL); + } + obj->dropProperty(cx, prop); + return ok; +} + +static JSBool +LookupResult(JSContext *cx, JSObject *obj, JSObject *obj2, JSProperty *prop, + jsval *vp) +{ + if (!prop) { + /* XXX bad API: no way to tell "not defined" from "void value" */ + *vp = JSVAL_VOID; + return JS_TRUE; + } + + JSBool ok = JS_TRUE; + if (OBJ_IS_NATIVE(obj2)) { + JSScopeProperty *sprop = (JSScopeProperty *) prop; + + if (sprop->isMethod()) { + JSAutoTempValueRooter root(cx, sprop); + JS_UNLOCK_OBJ(cx, obj2); + *vp = sprop->methodValue(); + return OBJ_SCOPE(obj2)->methodReadBarrier(cx, sprop, vp); + } + + /* Peek at the native property's slot value, without doing a Get. */ + *vp = SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj2)) + ? LOCKED_OBJ_GET_SLOT(obj2, sprop->slot) + : JSVAL_TRUE; + } else if (OBJ_IS_DENSE_ARRAY(cx, obj2)) { + ok = js_GetDenseArrayElementValue(cx, obj2, prop, vp); + } else { + /* XXX bad API: no way to return "defined but value unknown" */ + *vp = JSVAL_TRUE; + } + obj2->dropProperty(cx, prop); + return ok; +} + +static JSBool +GetPropertyAttributesById(JSContext *cx, JSObject *obj, jsid id, uintN flags, + JSBool own, JSPropertyDescriptor *desc) +{ + JSObject *obj2; + JSProperty *prop; + JSBool ok; + + if (!LookupPropertyById(cx, obj, id, flags, &obj2, &prop)) + return JS_FALSE; + + if (!prop || (own && obj != obj2)) { + desc->obj = NULL; + desc->attrs = 0; + desc->getter = NULL; + desc->setter = NULL; + desc->value = JSVAL_VOID; + if (prop) + obj2->dropProperty(cx, prop); + return JS_TRUE; + } + + desc->obj = obj2; + + ok = obj2->getAttributes(cx, id, prop, &desc->attrs); + if (ok) { + if (OBJ_IS_NATIVE(obj2)) { + JSScopeProperty *sprop = (JSScopeProperty *) prop; + + desc->getter = sprop->getter; + desc->setter = sprop->setter; + desc->value = SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj2)) + ? LOCKED_OBJ_GET_SLOT(obj2, sprop->slot) + : JSVAL_VOID; + } else { + desc->getter = NULL; + desc->setter = NULL; + desc->value = JSVAL_VOID; + } + } + obj2->dropProperty(cx, prop); + return ok; +} + +static JSBool +GetPropertyAttributes(JSContext *cx, JSObject *obj, JSAtom *atom, + uintN *attrsp, JSBool *foundp, + JSPropertyOp *getterp, JSPropertyOp *setterp) + +{ + if (!atom) + return JS_FALSE; + + JSPropertyDescriptor desc; + if (!GetPropertyAttributesById(cx, obj, ATOM_TO_JSID(atom), + JSRESOLVE_QUALIFIED, JS_FALSE, &desc)) { + return JS_FALSE; + } + + *attrsp = desc.attrs; + *foundp = (desc.obj != NULL); + if (getterp) + *getterp = desc.getter; + if (setterp) + *setterp = desc.setter; + return JS_TRUE; +} + +static JSBool +SetPropertyAttributes(JSContext *cx, JSObject *obj, JSAtom *atom, + uintN attrs, JSBool *foundp) +{ + JSObject *obj2; + JSProperty *prop; + JSBool ok; + + if (!atom) + return JS_FALSE; + if (!LookupPropertyById(cx, obj, ATOM_TO_JSID(atom), JSRESOLVE_QUALIFIED, + &obj2, &prop)) { + return JS_FALSE; + } + if (!prop || obj != obj2) { + *foundp = JS_FALSE; + if (prop) + obj2->dropProperty(cx, prop); + return JS_TRUE; + } + + *foundp = JS_TRUE; + ok = obj->setAttributes(cx, ATOM_TO_JSID(atom), prop, &attrs); + obj->dropProperty(cx, prop); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_GetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name, + uintN *attrsp, JSBool *foundp) +{ + CHECK_REQUEST(cx); + return GetPropertyAttributes(cx, obj, + js_Atomize(cx, name, strlen(name), 0), + attrsp, foundp, NULL, NULL); +} + +JS_PUBLIC_API(JSBool) +JS_GetPropertyAttrsGetterAndSetter(JSContext *cx, JSObject *obj, + const char *name, + uintN *attrsp, JSBool *foundp, + JSPropertyOp *getterp, + JSPropertyOp *setterp) +{ + CHECK_REQUEST(cx); + return GetPropertyAttributes(cx, obj, + js_Atomize(cx, name, strlen(name), 0), + attrsp, foundp, getterp, setterp); +} + +JS_PUBLIC_API(JSBool) +JS_GetPropertyAttrsGetterAndSetterById(JSContext *cx, JSObject *obj, + jsid id, + uintN *attrsp, JSBool *foundp, + JSPropertyOp *getterp, + JSPropertyOp *setterp) +{ + CHECK_REQUEST(cx); + + JSPropertyDescriptor desc; + if (!GetPropertyAttributesById(cx, obj, id, JSRESOLVE_QUALIFIED, JS_FALSE, &desc)) + return JS_FALSE; + + *attrsp = desc.attrs; + *foundp = (desc.obj != NULL); + if (getterp) + *getterp = desc.getter; + if (setterp) + *setterp = desc.setter; + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_SetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name, + uintN attrs, JSBool *foundp) +{ + CHECK_REQUEST(cx); + return SetPropertyAttributes(cx, obj, + js_Atomize(cx, name, strlen(name), 0), + attrs, foundp); +} + +static JSBool +AlreadyHasOwnPropertyHelper(JSContext *cx, JSObject *obj, jsid id, + JSBool *foundp) +{ + JSScope *scope; + + if (!OBJ_IS_NATIVE(obj)) { + JSObject *obj2; + JSProperty *prop; + + if (!LookupPropertyById(cx, obj, id, + JSRESOLVE_QUALIFIED | JSRESOLVE_DETECTING, + &obj2, &prop)) { + return JS_FALSE; + } + *foundp = (obj == obj2); + if (prop) + obj2->dropProperty(cx, prop); + return JS_TRUE; + } + + JS_LOCK_OBJ(cx, obj); + scope = OBJ_SCOPE(obj); + *foundp = scope->hasProperty(id); + JS_UNLOCK_SCOPE(cx, scope); + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_AlreadyHasOwnProperty(JSContext *cx, JSObject *obj, const char *name, + JSBool *foundp) +{ + JSAtom *atom; + + CHECK_REQUEST(cx); + atom = js_Atomize(cx, name, strlen(name), 0); + if (!atom) + return JS_FALSE; + return AlreadyHasOwnPropertyHelper(cx, obj, ATOM_TO_JSID(atom), foundp); +} + +JS_PUBLIC_API(JSBool) +JS_AlreadyHasOwnPropertyById(JSContext *cx, JSObject *obj, jsid id, + JSBool *foundp) +{ + CHECK_REQUEST(cx); + return AlreadyHasOwnPropertyHelper(cx, obj, id, foundp); +} + +JS_PUBLIC_API(JSBool) +JS_HasProperty(JSContext *cx, JSObject *obj, const char *name, JSBool *foundp) +{ + JSBool ok; + JSObject *obj2; + JSProperty *prop; + + CHECK_REQUEST(cx); + ok = LookupProperty(cx, obj, name, + JSRESOLVE_QUALIFIED | JSRESOLVE_DETECTING, + &obj2, &prop); + if (ok) { + *foundp = (prop != NULL); + if (prop) + obj2->dropProperty(cx, prop); + } + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_HasPropertyById(JSContext *cx, JSObject *obj, jsid id, JSBool *foundp) +{ + JSBool ok; + JSObject *obj2; + JSProperty *prop; + + CHECK_REQUEST(cx); + ok = LookupPropertyById(cx, obj, id, + JSRESOLVE_QUALIFIED | JSRESOLVE_DETECTING, + &obj2, &prop); + if (ok) { + *foundp = (prop != NULL); + if (prop) + obj2->dropProperty(cx, prop); + } + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_LookupProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp) +{ + JSObject *obj2; + JSProperty *prop; + + CHECK_REQUEST(cx); + return LookupProperty(cx, obj, name, JSRESOLVE_QUALIFIED, &obj2, &prop) && + LookupResult(cx, obj, obj2, prop, vp); +} + +JS_PUBLIC_API(JSBool) +JS_LookupPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + JSObject *obj2; + JSProperty *prop; + + CHECK_REQUEST(cx); + return LookupPropertyById(cx, obj, id, JSRESOLVE_QUALIFIED, &obj2, &prop) && + LookupResult(cx, obj, obj2, prop, vp); +} + +JS_PUBLIC_API(JSBool) +JS_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, const char *name, + uintN flags, jsval *vp) +{ + JSAtom *atom; + JSObject *obj2; + + atom = js_Atomize(cx, name, strlen(name), 0); + return atom && + JS_LookupPropertyWithFlagsById(cx, obj, ATOM_TO_JSID(atom), flags, + &obj2, vp); +} + +JS_PUBLIC_API(JSBool) +JS_LookupPropertyWithFlagsById(JSContext *cx, JSObject *obj, jsid id, + uintN flags, JSObject **objp, jsval *vp) +{ + JSBool ok; + JSProperty *prop; + + CHECK_REQUEST(cx); + ok = OBJ_IS_NATIVE(obj) + ? js_LookupPropertyWithFlags(cx, obj, id, flags, objp, &prop) >= 0 + : obj->lookupProperty(cx, id, objp, &prop); + if (ok) + ok = LookupResult(cx, obj, *objp, prop, vp); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_GetPropertyDescriptorById(JSContext *cx, JSObject *obj, jsid id, uintN flags, + JSPropertyDescriptor *desc) +{ + CHECK_REQUEST(cx); + + return GetPropertyAttributesById(cx, obj, id, flags, JS_FALSE, desc); +} + +JS_PUBLIC_API(JSBool) +JS_GetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp) +{ + JSAtom *atom; + + CHECK_REQUEST(cx); + atom = js_Atomize(cx, name, strlen(name), 0); + if (!atom) + return JS_FALSE; + + JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED); + return obj->getProperty(cx, ATOM_TO_JSID(atom), vp); +} + +JS_PUBLIC_API(JSBool) +JS_GetPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + CHECK_REQUEST(cx); + JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED); + return obj->getProperty(cx, id, vp); +} + +JS_PUBLIC_API(JSBool) +JS_GetMethodById(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, + jsval *vp) +{ + CHECK_REQUEST(cx); + if (!js_GetMethod(cx, obj, id, JSGET_METHOD_BARRIER, vp)) + return JS_FALSE; + if (objp) + *objp = obj; + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_GetMethod(JSContext *cx, JSObject *obj, const char *name, JSObject **objp, + jsval *vp) +{ + JSAtom *atom; + + atom = js_Atomize(cx, name, strlen(name), 0); + if (!atom) + return JS_FALSE; + return JS_GetMethodById(cx, obj, ATOM_TO_JSID(atom), objp, vp); +} + +JS_PUBLIC_API(JSBool) +JS_SetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp) +{ + JSAtom *atom; + + CHECK_REQUEST(cx); + atom = js_Atomize(cx, name, strlen(name), 0); + if (!atom) + return JS_FALSE; + + JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING); + return obj->setProperty(cx, ATOM_TO_JSID(atom), vp); +} + +JS_PUBLIC_API(JSBool) +JS_SetPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + CHECK_REQUEST(cx); + JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING); + return obj->setProperty(cx, id, vp); +} + +JS_PUBLIC_API(JSBool) +JS_DeleteProperty(JSContext *cx, JSObject *obj, const char *name) +{ + jsval junk; + + return JS_DeleteProperty2(cx, obj, name, &junk); +} + +JS_PUBLIC_API(JSBool) +JS_DeleteProperty2(JSContext *cx, JSObject *obj, const char *name, + jsval *rval) +{ + JSAtom *atom; + + CHECK_REQUEST(cx); + atom = js_Atomize(cx, name, strlen(name), 0); + if (!atom) + return JS_FALSE; + + JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED); + return obj->deleteProperty(cx, ATOM_TO_JSID(atom), rval); +} + +JS_PUBLIC_API(JSBool) +JS_DeletePropertyById(JSContext *cx, JSObject *obj, jsid id) +{ + jsval junk; + + return JS_DeletePropertyById2(cx, obj, id, &junk); +} + +JS_PUBLIC_API(JSBool) +JS_DeletePropertyById2(JSContext *cx, JSObject *obj, jsid id, jsval *rval) +{ + CHECK_REQUEST(cx); + JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED); + return obj->deleteProperty(cx, id, rval); +} + +JS_PUBLIC_API(JSBool) +JS_DefineUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, jsval value, + JSPropertyOp getter, JSPropertyOp setter, + uintN attrs) +{ + CHECK_REQUEST(cx); + return DefineUCProperty(cx, obj, name, namelen, value, getter, setter, + attrs, 0, 0); +} + +JS_PUBLIC_API(JSBool) +JS_GetUCPropertyAttributes(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + uintN *attrsp, JSBool *foundp) +{ + CHECK_REQUEST(cx); + return GetPropertyAttributes(cx, obj, + js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0), + attrsp, foundp, NULL, NULL); +} + +JS_PUBLIC_API(JSBool) +JS_GetUCPropertyAttrsGetterAndSetter(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + uintN *attrsp, JSBool *foundp, + JSPropertyOp *getterp, + JSPropertyOp *setterp) +{ + CHECK_REQUEST(cx); + return GetPropertyAttributes(cx, obj, + js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0), + attrsp, foundp, getterp, setterp); +} + +JS_PUBLIC_API(JSBool) +JS_SetUCPropertyAttributes(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + uintN attrs, JSBool *foundp) +{ + CHECK_REQUEST(cx); + return SetPropertyAttributes(cx, obj, + js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0), + attrs, foundp); +} + +JS_PUBLIC_API(JSBool) +JS_DefineUCPropertyWithTinyId(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + int8 tinyid, jsval value, + JSPropertyOp getter, JSPropertyOp setter, + uintN attrs) +{ + CHECK_REQUEST(cx); + return DefineUCProperty(cx, obj, name, namelen, value, getter, setter, + attrs, SPROP_HAS_SHORTID, tinyid); +} + +JS_PUBLIC_API(JSBool) +JS_AlreadyHasOwnUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + JSBool *foundp) +{ + JSAtom *atom; + + CHECK_REQUEST(cx); + atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); + if (!atom) + return JS_FALSE; + return AlreadyHasOwnPropertyHelper(cx, obj, ATOM_TO_JSID(atom), foundp); +} + +JS_PUBLIC_API(JSBool) +JS_HasUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + JSBool *vp) +{ + JSBool ok; + JSObject *obj2; + JSProperty *prop; + + CHECK_REQUEST(cx); + ok = LookupUCProperty(cx, obj, name, namelen, + JSRESOLVE_QUALIFIED | JSRESOLVE_DETECTING, + &obj2, &prop); + if (ok) { + *vp = (prop != NULL); + if (prop) + obj2->dropProperty(cx, prop); + } + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_LookupUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + jsval *vp) +{ + JSObject *obj2; + JSProperty *prop; + + CHECK_REQUEST(cx); + return LookupUCProperty(cx, obj, name, namelen, JSRESOLVE_QUALIFIED, + &obj2, &prop) && + LookupResult(cx, obj, obj2, prop, vp); +} + +JS_PUBLIC_API(JSBool) +JS_GetUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + jsval *vp) +{ + JSAtom *atom; + + CHECK_REQUEST(cx); + atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); + if (!atom) + return JS_FALSE; + + JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED); + return obj->getProperty(cx, ATOM_TO_JSID(atom), vp); +} + +JS_PUBLIC_API(JSBool) +JS_SetUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + jsval *vp) +{ + JSAtom *atom; + + CHECK_REQUEST(cx); + atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); + if (!atom) + return JS_FALSE; + + JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING); + return obj->setProperty(cx, ATOM_TO_JSID(atom), vp); +} + +JS_PUBLIC_API(JSBool) +JS_DeleteUCProperty2(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + jsval *rval) +{ + JSAtom *atom; + + CHECK_REQUEST(cx); + atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); + if (!atom) + return JS_FALSE; + + JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED); + return obj->deleteProperty(cx, ATOM_TO_JSID(atom), rval); +} + +JS_PUBLIC_API(JSObject *) +JS_NewArrayObject(JSContext *cx, jsint length, jsval *vector) +{ + CHECK_REQUEST(cx); + /* NB: jsuint cast does ToUint32. */ + return js_NewArrayObject(cx, (jsuint)length, vector); +} + +JS_PUBLIC_API(JSBool) +JS_IsArrayObject(JSContext *cx, JSObject *obj) +{ + return OBJ_IS_ARRAY(cx, js_GetWrappedObject(cx, obj)); +} + +JS_PUBLIC_API(JSBool) +JS_GetArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp) +{ + CHECK_REQUEST(cx); + return js_GetLengthProperty(cx, obj, lengthp); +} + +JS_PUBLIC_API(JSBool) +JS_SetArrayLength(JSContext *cx, JSObject *obj, jsuint length) +{ + CHECK_REQUEST(cx); + return js_SetLengthProperty(cx, obj, length); +} + +JS_PUBLIC_API(JSBool) +JS_HasArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp) +{ + CHECK_REQUEST(cx); + return js_HasLengthProperty(cx, obj, lengthp); +} + +JS_PUBLIC_API(JSBool) +JS_DefineElement(JSContext *cx, JSObject *obj, jsint index, jsval value, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs) +{ + JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_DECLARING); + + CHECK_REQUEST(cx); + return obj->defineProperty(cx, INT_TO_JSID(index), value, getter, setter, attrs); +} + +JS_PUBLIC_API(JSBool) +JS_AliasElement(JSContext *cx, JSObject *obj, const char *name, jsint alias) +{ + JSObject *obj2; + JSProperty *prop; + JSScopeProperty *sprop; + JSBool ok; + + CHECK_REQUEST(cx); + if (!LookupProperty(cx, obj, name, JSRESOLVE_QUALIFIED, &obj2, &prop)) + return JS_FALSE; + if (!prop) { + js_ReportIsNotDefined(cx, name); + return JS_FALSE; + } + if (obj2 != obj || !OBJ_IS_NATIVE(obj)) { + char numBuf[12]; + obj2->dropProperty(cx, prop); + JS_snprintf(numBuf, sizeof numBuf, "%ld", (long)alias); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_ALIAS, + numBuf, name, OBJ_GET_CLASS(cx, obj2)->name); + return JS_FALSE; + } + sprop = (JSScopeProperty *)prop; + ok = (js_AddNativeProperty(cx, obj, INT_TO_JSID(alias), + sprop->getter, sprop->setter, sprop->slot, + sprop->attrs, sprop->flags | SPROP_IS_ALIAS, + sprop->shortid) + != NULL); + obj->dropProperty(cx, prop); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_AlreadyHasOwnElement(JSContext *cx, JSObject *obj, jsint index, + JSBool *foundp) +{ + return AlreadyHasOwnPropertyHelper(cx, obj, INT_TO_JSID(index), foundp); +} + +JS_PUBLIC_API(JSBool) +JS_HasElement(JSContext *cx, JSObject *obj, jsint index, JSBool *foundp) +{ + JSBool ok; + JSObject *obj2; + JSProperty *prop; + + CHECK_REQUEST(cx); + ok = LookupPropertyById(cx, obj, INT_TO_JSID(index), + JSRESOLVE_QUALIFIED | JSRESOLVE_DETECTING, + &obj2, &prop); + if (ok) { + *foundp = (prop != NULL); + if (prop) + obj2->dropProperty(cx, prop); + } + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_LookupElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp) +{ + JSObject *obj2; + JSProperty *prop; + + CHECK_REQUEST(cx); + return LookupPropertyById(cx, obj, INT_TO_JSID(index), JSRESOLVE_QUALIFIED, + &obj2, &prop) && + LookupResult(cx, obj, obj2, prop, vp); +} + +JS_PUBLIC_API(JSBool) +JS_GetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp) +{ + JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED); + + CHECK_REQUEST(cx); + return obj->getProperty(cx, INT_TO_JSID(index), vp); +} + +JS_PUBLIC_API(JSBool) +JS_SetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp) +{ + JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING); + + CHECK_REQUEST(cx); + return obj->setProperty(cx, INT_TO_JSID(index), vp); +} + +JS_PUBLIC_API(JSBool) +JS_DeleteElement(JSContext *cx, JSObject *obj, jsint index) +{ + jsval junk; + + return JS_DeleteElement2(cx, obj, index, &junk); +} + +JS_PUBLIC_API(JSBool) +JS_DeleteElement2(JSContext *cx, JSObject *obj, jsint index, jsval *rval) +{ + JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED); + + CHECK_REQUEST(cx); + return obj->deleteProperty(cx, INT_TO_JSID(index), rval); +} + +JS_PUBLIC_API(void) +JS_ClearScope(JSContext *cx, JSObject *obj) +{ + CHECK_REQUEST(cx); + + if (obj->map->ops->clear) + obj->map->ops->clear(cx, obj); + + /* Clear cached class objects on the global object. */ + if (OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL) { + int key; + + for (key = JSProto_Null; key < JSProto_LIMIT; key++) + JS_SetReservedSlot(cx, obj, key, JSVAL_VOID); + } +} + +JS_PUBLIC_API(JSIdArray *) +JS_Enumerate(JSContext *cx, JSObject *obj) +{ + jsint i, n; + jsval iter_state, num_properties; + jsid id; + JSIdArray *ida; + jsval *vector; + + CHECK_REQUEST(cx); + + ida = NULL; + iter_state = JSVAL_NULL; + JSAutoEnumStateRooter tvr(cx, obj, &iter_state); + + /* Get the number of properties to enumerate. */ + if (!obj->enumerate(cx, JSENUMERATE_INIT, &iter_state, &num_properties)) + goto error; + if (!JSVAL_IS_INT(num_properties)) { + JS_ASSERT(0); + goto error; + } + + /* Grow as needed if we don't know the exact amount ahead of time. */ + n = JSVAL_TO_INT(num_properties); + if (n <= 0) + n = 8; + + /* Create an array of jsids large enough to hold all the properties */ + ida = NewIdArray(cx, n); + if (!ida) + goto error; + + i = 0; + vector = &ida->vector[0]; + for (;;) { + if (!obj->enumerate(cx, JSENUMERATE_NEXT, &iter_state, &id)) + goto error; + + /* No more jsid's to enumerate ? */ + if (iter_state == JSVAL_NULL) + break; + + if (i == ida->length) { + ida = SetIdArrayLength(cx, ida, ida->length * 2); + if (!ida) + goto error; + vector = &ida->vector[0]; + } + vector[i++] = id; + } + return SetIdArrayLength(cx, ida, i); + +error: + if (!JSVAL_IS_NULL(iter_state)) + obj->enumerate(cx, JSENUMERATE_DESTROY, &iter_state, 0); + if (ida) + JS_DestroyIdArray(cx, ida); + return NULL; +} + +/* + * XXX reverse iterator for properties, unreverse and meld with jsinterp.c's + * prop_iterator_class somehow... + * + preserve the obj->enumerate API while optimizing the native object case + * + native case here uses a JSScopeProperty *, but that iterates in reverse! + * + so we make non-native match, by reverse-iterating after JS_Enumerating + */ +const uint32 JSSLOT_ITER_INDEX = JSSLOT_PRIVATE + 1; +JS_STATIC_ASSERT(JSSLOT_ITER_INDEX < JS_INITIAL_NSLOTS); + +static void +prop_iter_finalize(JSContext *cx, JSObject *obj) +{ + void *pdata = obj->getPrivate(); + if (!pdata) + return; + + if (JSVAL_TO_INT(obj->fslots[JSSLOT_ITER_INDEX]) >= 0) { + /* Non-native case: destroy the ida enumerated when obj was created. */ + JSIdArray *ida = (JSIdArray *) pdata; + JS_DestroyIdArray(cx, ida); + } +} + +static void +prop_iter_trace(JSTracer *trc, JSObject *obj) +{ + void *pdata = obj->getPrivate(); + if (!pdata) + return; + + if (JSVAL_TO_INT(obj->fslots[JSSLOT_ITER_INDEX]) < 0) { + /* Native case: just mark the next property to visit. */ + ((JSScopeProperty *) pdata)->trace(trc); + } else { + /* Non-native case: mark each id in the JSIdArray private. */ + JSIdArray *ida = (JSIdArray *) pdata; + for (jsint i = 0, n = ida->length; i < n; i++) + js_TraceId(trc, ida->vector[i]); + } +} + +static JSClass prop_iter_class = { + "PropertyIterator", + JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | + JSCLASS_MARK_IS_TRACE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, prop_iter_finalize, + NULL, NULL, NULL, NULL, + NULL, NULL, JS_CLASS_TRACE(prop_iter_trace), NULL +}; + +JS_PUBLIC_API(JSObject *) +JS_NewPropertyIterator(JSContext *cx, JSObject *obj) +{ + JSObject *iterobj; + JSScope *scope; + void *pdata; + jsint index; + JSIdArray *ida; + + CHECK_REQUEST(cx); + iterobj = js_NewObject(cx, &prop_iter_class, NULL, obj); + if (!iterobj) + return NULL; + + if (OBJ_IS_NATIVE(obj)) { + /* Native case: start with the last property in obj's own scope. */ + scope = OBJ_SCOPE(obj); + pdata = scope->lastProperty(); + index = -1; + } else { + /* + * Non-native case: enumerate a JSIdArray and keep it via private. + * + * Note: we have to make sure that we root obj around the call to + * JS_Enumerate to protect against multiple allocations under it. + */ + JSAutoTempValueRooter tvr(cx, iterobj); + ida = JS_Enumerate(cx, obj); + if (!ida) + return NULL; + pdata = ida; + index = ida->length; + } + + /* iterobj cannot escape to other threads here. */ + iterobj->setPrivate(pdata); + iterobj->fslots[JSSLOT_ITER_INDEX] = INT_TO_JSVAL(index); + return iterobj; +} + +JS_PUBLIC_API(JSBool) +JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp) +{ + jsint i; + JSObject *obj; + JSScope *scope; + JSScopeProperty *sprop; + JSIdArray *ida; + + CHECK_REQUEST(cx); + i = JSVAL_TO_INT(iterobj->fslots[JSSLOT_ITER_INDEX]); + if (i < 0) { + /* Native case: private data is a property tree node pointer. */ + obj = OBJ_GET_PARENT(cx, iterobj); + JS_ASSERT(OBJ_IS_NATIVE(obj)); + scope = OBJ_SCOPE(obj); + sprop = (JSScopeProperty *) iterobj->getPrivate(); + + /* + * If the next property mapped by scope in the property tree ancestor + * line is not enumerable, or it's an alias, skip it and keep on trying + * to find an enumerable property that is still in scope. + */ + while (sprop && + (!(sprop->attrs & JSPROP_ENUMERATE) || + (sprop->flags & SPROP_IS_ALIAS))) { + sprop = sprop->parent; + } + + if (!sprop) { + *idp = JSVAL_VOID; + } else { + iterobj->setPrivate(sprop->parent); + *idp = sprop->id; + } + } else { + /* Non-native case: use the ida enumerated when iterobj was created. */ + ida = (JSIdArray *) iterobj->getPrivate(); + JS_ASSERT(i <= ida->length); + if (i == 0) { + *idp = JSVAL_VOID; + } else { + *idp = ida->vector[--i]; + STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_INDEX, INT_TO_JSVAL(i)); + } + } + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, + jsval *vp, uintN *attrsp) +{ + CHECK_REQUEST(cx); + return obj->checkAccess(cx, id, mode, vp, attrsp); +} + +JS_PUBLIC_API(JSBool) +JS_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval *vp) +{ + CHECK_REQUEST(cx); + return js_GetReservedSlot(cx, obj, index, vp); +} + +JS_PUBLIC_API(JSBool) +JS_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval v) +{ + CHECK_REQUEST(cx); + return js_SetReservedSlot(cx, obj, index, v); +} + +#ifdef JS_THREADSAFE +JS_PUBLIC_API(jsrefcount) +JS_HoldPrincipals(JSContext *cx, JSPrincipals *principals) +{ + return JS_ATOMIC_INCREMENT(&principals->refcount); +} + +JS_PUBLIC_API(jsrefcount) +JS_DropPrincipals(JSContext *cx, JSPrincipals *principals) +{ + jsrefcount rc = JS_ATOMIC_DECREMENT(&principals->refcount); + if (rc == 0) + principals->destroy(cx, principals); + return rc; +} +#endif + +JS_PUBLIC_API(JSSecurityCallbacks *) +JS_SetRuntimeSecurityCallbacks(JSRuntime *rt, JSSecurityCallbacks *callbacks) +{ + JSSecurityCallbacks *oldcallbacks; + + oldcallbacks = rt->securityCallbacks; + rt->securityCallbacks = callbacks; + return oldcallbacks; +} + +JS_PUBLIC_API(JSSecurityCallbacks *) +JS_GetRuntimeSecurityCallbacks(JSRuntime *rt) +{ + return rt->securityCallbacks; +} + +JS_PUBLIC_API(JSSecurityCallbacks *) +JS_SetContextSecurityCallbacks(JSContext *cx, JSSecurityCallbacks *callbacks) +{ + JSSecurityCallbacks *oldcallbacks; + + oldcallbacks = cx->securityCallbacks; + cx->securityCallbacks = callbacks; + return oldcallbacks; +} + +JS_PUBLIC_API(JSSecurityCallbacks *) +JS_GetSecurityCallbacks(JSContext *cx) +{ + return cx->securityCallbacks + ? cx->securityCallbacks + : cx->runtime->securityCallbacks; +} + +JS_PUBLIC_API(JSFunction *) +JS_NewFunction(JSContext *cx, JSNative native, uintN nargs, uintN flags, + JSObject *parent, const char *name) +{ + JSAtom *atom; + + CHECK_REQUEST(cx); + + if (!name) { + atom = NULL; + } else { + atom = js_Atomize(cx, name, strlen(name), 0); + if (!atom) + return NULL; + } + return js_NewFunction(cx, NULL, native, nargs, flags, parent, atom); +} + +JS_PUBLIC_API(JSObject *) +JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent) +{ + CHECK_REQUEST(cx); + if (OBJ_GET_CLASS(cx, funobj) != &js_FunctionClass) { + /* + * We cannot clone this object, so fail (we used to return funobj, bad + * idea, but we changed incompatibly to teach any abusers a lesson!). + */ + jsval v = OBJECT_TO_JSVAL(funobj); + js_ReportIsNotFunction(cx, &v, 0); + return NULL; + } + + JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj); + JSObject *clone = js_CloneFunctionObject(cx, fun, parent); + if (!clone) + return NULL; + + /* + * A flat closure carries its own environment, so why clone it? In case + * someone wants to mutate its fixed slots or add ad-hoc properties. API + * compatibility suggests we not return funobj and let callers mutate the + * returned object at will. + * + * But it's worse than that: API compatibility according to the test for + * bug 300079 requires we get "upvars" from parent and its ancestors! So + * we do that (grudgingly!). The scope chain ancestors are searched as if + * they were activations, respecting the skip field in each upvar's cookie + * but looking up the property by name instead of frame slot. + */ + if (FUN_FLAT_CLOSURE(fun)) { + JS_ASSERT(funobj->dslots); + if (!js_EnsureReservedSlots(cx, clone, + fun->countInterpretedReservedSlots())) { + return NULL; + } + + JSUpvarArray *uva = fun->u.i.script->upvars(); + JS_ASSERT(uva->length <= size_t(clone->dslots[-1])); + + void *mark = JS_ARENA_MARK(&cx->tempPool); + jsuword *names = js_GetLocalNameArray(cx, fun, &cx->tempPool); + if (!names) + return NULL; + + uint32 i = 0, n = uva->length; + for (; i < n; i++) { + JSObject *obj = parent; + int skip = UPVAR_FRAME_SKIP(uva->vector[i]); + while (--skip > 0) { + if (!obj) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_CLONE_FUNOBJ_SCOPE); + goto break2; + } + obj = OBJ_GET_PARENT(cx, obj); + } + + JSAtom *atom = JS_LOCAL_NAME_TO_ATOM(names[i]); + if (!obj->getProperty(cx, ATOM_TO_JSID(atom), &clone->dslots[i])) + break; + } + + break2: + JS_ARENA_RELEASE(&cx->tempPool, mark); + if (i < n) + return NULL; + } + + return clone; +} + +JS_PUBLIC_API(JSObject *) +JS_GetFunctionObject(JSFunction *fun) +{ + return FUN_OBJECT(fun); +} + +JS_PUBLIC_API(const char *) +JS_GetFunctionName(JSFunction *fun) +{ + return fun->atom + ? JS_GetStringBytes(ATOM_TO_STRING(fun->atom)) + : js_anonymous_str; +} + +JS_PUBLIC_API(JSString *) +JS_GetFunctionId(JSFunction *fun) +{ + return fun->atom ? ATOM_TO_STRING(fun->atom) : NULL; +} + +JS_PUBLIC_API(uintN) +JS_GetFunctionFlags(JSFunction *fun) +{ + return fun->flags; +} + +JS_PUBLIC_API(uint16) +JS_GetFunctionArity(JSFunction *fun) +{ + return fun->nargs; +} + +JS_PUBLIC_API(JSBool) +JS_ObjectIsFunction(JSContext *cx, JSObject *obj) +{ + return OBJ_GET_CLASS(cx, obj) == &js_FunctionClass; +} + +JS_BEGIN_EXTERN_C +static JSBool +js_generic_fast_native_method_dispatcher(JSContext *cx, uintN argc, jsval *vp) +{ + jsval fsv; + JSFunctionSpec *fs; + JSObject *tmp; + JSFastNative native; + + if (!JS_GetReservedSlot(cx, JSVAL_TO_OBJECT(*vp), 0, &fsv)) + return JS_FALSE; + fs = (JSFunctionSpec *) JSVAL_TO_PRIVATE(fsv); + JS_ASSERT((~fs->flags & (JSFUN_FAST_NATIVE | JSFUN_GENERIC_NATIVE)) == 0); + + /* + * We know that vp[2] is valid because JS_DefineFunctions, which is our + * only (indirect) referrer, defined us as requiring at least one argument + * (notice how it passes fs->nargs + 1 as the next-to-last argument to + * JS_DefineFunction). + */ + if (JSVAL_IS_PRIMITIVE(vp[2])) { + /* + * Make sure that this is an object or null, as required by the generic + * functions. + */ + if (!js_ValueToObject(cx, vp[2], &tmp)) + return JS_FALSE; + vp[2] = OBJECT_TO_JSVAL(tmp); + } + + /* + * Copy all actual (argc) arguments down over our |this| parameter, vp[1], + * which is almost always the class constructor object, e.g. Array. Then + * call the corresponding prototype native method with our first argument + * passed as |this|. + */ + memmove(vp + 1, vp + 2, argc * sizeof(jsval)); + + /* + * Follow Function.prototype.apply and .call by using the global object as + * the 'this' param if no args. + */ + if (!js_ComputeThis(cx, JS_FALSE, vp + 2)) + return JS_FALSE; + /* + * Protect against argc underflowing. By calling js_ComputeThis, we made + * it as if the static was called with one parameter, the explicit |this| + * object. + */ + if (argc != 0) { + /* Clear the last parameter in case too few arguments were passed. */ + vp[2 + --argc] = JSVAL_VOID; + } + + native = +#ifdef JS_TRACER + (fs->flags & JSFUN_TRCINFO) + ? JS_FUNC_TO_DATA_PTR(JSNativeTraceInfo *, fs->call)->native + : +#endif + (JSFastNative) fs->call; + return native(cx, argc, vp); +} + +static JSBool +js_generic_native_method_dispatcher(JSContext *cx, JSObject *obj, + uintN argc, jsval *argv, jsval *rval) +{ + jsval fsv; + JSFunctionSpec *fs; + JSObject *tmp; + + if (!JS_GetReservedSlot(cx, JSVAL_TO_OBJECT(argv[-2]), 0, &fsv)) + return JS_FALSE; + fs = (JSFunctionSpec *) JSVAL_TO_PRIVATE(fsv); + JS_ASSERT((fs->flags & (JSFUN_FAST_NATIVE | JSFUN_GENERIC_NATIVE)) == + JSFUN_GENERIC_NATIVE); + + /* + * We know that argv[0] is valid because JS_DefineFunctions, which is our + * only (indirect) referrer, defined us as requiring at least one argument + * (notice how it passes fs->nargs + 1 as the next-to-last argument to + * JS_DefineFunction). + */ + if (JSVAL_IS_PRIMITIVE(argv[0])) { + /* + * Make sure that this is an object or null, as required by the generic + * functions. + */ + if (!js_ValueToObject(cx, argv[0], &tmp)) + return JS_FALSE; + argv[0] = OBJECT_TO_JSVAL(tmp); + } + + /* + * Copy all actual (argc) arguments down over our |this| parameter, + * argv[-1], which is almost always the class constructor object, e.g. + * Array. Then call the corresponding prototype native method with our + * first argument passed as |this|. + */ + memmove(argv - 1, argv, argc * sizeof(jsval)); + + /* + * Follow Function.prototype.apply and .call by using the global object as + * the 'this' param if no args. + */ + if (!js_ComputeThis(cx, JS_TRUE, argv)) + return JS_FALSE; + js_GetTopStackFrame(cx)->thisv = argv[-1]; + JS_ASSERT(cx->fp->argv == argv); + + /* + * Protect against argc underflowing. By calling js_ComputeThis, we made + * it as if the static was called with one parameter, the explicit |this| + * object. + */ + if (argc != 0) { + /* Clear the last parameter in case too few arguments were passed. */ + argv[--argc] = JSVAL_VOID; + } + + return fs->call(cx, JSVAL_TO_OBJECT(argv[-1]), argc, argv, rval); +} +JS_END_EXTERN_C + +JS_PUBLIC_API(JSBool) +JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs) +{ + uintN flags; + JSObject *ctor; + JSFunction *fun; + + CHECK_REQUEST(cx); + ctor = NULL; + for (; fs->name; fs++) { + flags = fs->flags; + + /* + * Define a generic arity N+1 static method for the arity N prototype + * method if flags contains JSFUN_GENERIC_NATIVE. + */ + if (flags & JSFUN_GENERIC_NATIVE) { + if (!ctor) { + ctor = JS_GetConstructor(cx, obj); + if (!ctor) + return JS_FALSE; + } + + flags &= ~JSFUN_GENERIC_NATIVE; + fun = JS_DefineFunction(cx, ctor, fs->name, + (flags & JSFUN_FAST_NATIVE) + ? (JSNative) + js_generic_fast_native_method_dispatcher + : js_generic_native_method_dispatcher, + fs->nargs + 1, + flags & ~JSFUN_TRCINFO); + if (!fun) + return JS_FALSE; + fun->u.n.extra = (uint16)fs->extra; + + /* + * As jsapi.h notes, fs must point to storage that lives as long + * as fun->object lives. + */ + if (!JS_SetReservedSlot(cx, FUN_OBJECT(fun), 0, PRIVATE_TO_JSVAL(fs))) + return JS_FALSE; + } + + JS_ASSERT(!(flags & JSFUN_FAST_NATIVE) || + (uint16)(fs->extra >> 16) <= fs->nargs); + fun = JS_DefineFunction(cx, obj, fs->name, fs->call, fs->nargs, flags); + if (!fun) + return JS_FALSE; + fun->u.n.extra = (uint16)fs->extra; + } + return JS_TRUE; +} + +JS_PUBLIC_API(JSFunction *) +JS_DefineFunction(JSContext *cx, JSObject *obj, const char *name, JSNative call, + uintN nargs, uintN attrs) +{ + JSAtom *atom; + + CHECK_REQUEST(cx); + atom = js_Atomize(cx, name, strlen(name), 0); + if (!atom) + return NULL; + return js_DefineFunction(cx, obj, atom, call, nargs, attrs); +} + +JS_PUBLIC_API(JSFunction *) +JS_DefineUCFunction(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, JSNative call, + uintN nargs, uintN attrs) +{ + JSAtom *atom; + + atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); + if (!atom) + return NULL; + return js_DefineFunction(cx, obj, atom, call, nargs, attrs); +} + +JS_PUBLIC_API(JSScript *) +JS_CompileScript(JSContext *cx, JSObject *obj, + const char *bytes, size_t length, + const char *filename, uintN lineno) +{ + jschar *chars; + JSScript *script; + + CHECK_REQUEST(cx); + chars = js_InflateString(cx, bytes, &length); + if (!chars) + return NULL; + script = JS_CompileUCScript(cx, obj, chars, length, filename, lineno); + cx->free(chars); + return script; +} + +JS_PUBLIC_API(JSScript *) +JS_CompileScriptForPrincipals(JSContext *cx, JSObject *obj, + JSPrincipals *principals, + const char *bytes, size_t length, + const char *filename, uintN lineno) +{ + jschar *chars; + JSScript *script; + + CHECK_REQUEST(cx); + chars = js_InflateString(cx, bytes, &length); + if (!chars) + return NULL; + script = JS_CompileUCScriptForPrincipals(cx, obj, principals, + chars, length, filename, lineno); + cx->free(chars); + return script; +} + +JS_PUBLIC_API(JSScript *) +JS_CompileUCScript(JSContext *cx, JSObject *obj, + const jschar *chars, size_t length, + const char *filename, uintN lineno) +{ + CHECK_REQUEST(cx); + return JS_CompileUCScriptForPrincipals(cx, obj, NULL, chars, length, + filename, lineno); +} + +#define LAST_FRAME_EXCEPTION_CHECK(cx,result) \ + JS_BEGIN_MACRO \ + if (!(result) && !((cx)->options & JSOPTION_DONT_REPORT_UNCAUGHT)) \ + js_ReportUncaughtException(cx); \ + JS_END_MACRO + +#define LAST_FRAME_CHECKS(cx,result) \ + JS_BEGIN_MACRO \ + if (!JS_IsRunning(cx)) { \ + (cx)->weakRoots.lastInternalResult = JSVAL_NULL; \ + LAST_FRAME_EXCEPTION_CHECK(cx, result); \ + } \ + JS_END_MACRO + +#define JS_OPTIONS_TO_TCFLAGS(cx) \ + ((((cx)->options & JSOPTION_COMPILE_N_GO) ? TCF_COMPILE_N_GO : 0) | \ + (((cx)->options & JSOPTION_NO_SCRIPT_RVAL) ? TCF_NO_SCRIPT_RVAL : 0)) + +JS_PUBLIC_API(JSScript *) +JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *obj, + JSPrincipals *principals, + const jschar *chars, size_t length, + const char *filename, uintN lineno) +{ + uint32 tcflags; + JSScript *script; + + CHECK_REQUEST(cx); + tcflags = JS_OPTIONS_TO_TCFLAGS(cx) | TCF_NEED_MUTABLE_SCRIPT; + script = JSCompiler::compileScript(cx, obj, NULL, principals, tcflags, + chars, length, NULL, filename, lineno); + LAST_FRAME_CHECKS(cx, script); + return script; +} + +JS_PUBLIC_API(JSBool) +JS_BufferIsCompilableUnit(JSContext *cx, JSObject *obj, + const char *bytes, size_t length) +{ + jschar *chars; + JSBool result; + JSExceptionState *exnState; + JSErrorReporter older; + + CHECK_REQUEST(cx); + chars = js_InflateString(cx, bytes, &length); + if (!chars) + return JS_TRUE; + + /* + * Return true on any out-of-memory error, so our caller doesn't try to + * collect more buffered source. + */ + result = JS_TRUE; + exnState = JS_SaveExceptionState(cx); + { + JSCompiler jsc(cx); + if (jsc.init(chars, length, NULL, NULL, 1)) { + older = JS_SetErrorReporter(cx, NULL); + if (!jsc.parse(obj) && + (jsc.tokenStream.flags & TSF_UNEXPECTED_EOF)) { + /* + * We ran into an error. If it was because we ran out of + * source, we return false so our caller knows to try to + * collect more buffered source. + */ + result = JS_FALSE; + } + JS_SetErrorReporter(cx, older); + } + } + cx->free(chars); + JS_RestoreExceptionState(cx, exnState); + return result; +} + +JS_PUBLIC_API(JSScript *) +JS_CompileFile(JSContext *cx, JSObject *obj, const char *filename) +{ + FILE *fp; + uint32 tcflags; + JSScript *script; + + CHECK_REQUEST(cx); + if (!filename || strcmp(filename, "-") == 0) { + fp = stdin; + } else { + fp = fopen(filename, "r"); + if (!fp) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_OPEN, + filename, "No such file or directory"); + return NULL; + } + } + + tcflags = JS_OPTIONS_TO_TCFLAGS(cx); + script = JSCompiler::compileScript(cx, obj, NULL, NULL, tcflags, + NULL, 0, fp, filename, 1); + if (fp != stdin) + fclose(fp); + LAST_FRAME_CHECKS(cx, script); + return script; +} + +JS_PUBLIC_API(JSScript *) +JS_CompileFileHandle(JSContext *cx, JSObject *obj, const char *filename, + FILE *file) +{ + return JS_CompileFileHandleForPrincipals(cx, obj, filename, file, NULL); +} + +JS_PUBLIC_API(JSScript *) +JS_CompileFileHandleForPrincipals(JSContext *cx, JSObject *obj, + const char *filename, FILE *file, + JSPrincipals *principals) +{ + uint32 tcflags; + JSScript *script; + + CHECK_REQUEST(cx); + tcflags = JS_OPTIONS_TO_TCFLAGS(cx); + script = JSCompiler::compileScript(cx, obj, NULL, principals, tcflags, + NULL, 0, file, filename, 1); + LAST_FRAME_CHECKS(cx, script); + return script; +} + +JS_PUBLIC_API(JSObject *) +JS_NewScriptObject(JSContext *cx, JSScript *script) +{ + JSTempValueRooter tvr; + JSObject *obj; + + CHECK_REQUEST(cx); + if (!script) + return js_NewObject(cx, &js_ScriptClass, NULL, NULL); + + JS_ASSERT(!script->u.object); + + JS_PUSH_TEMP_ROOT_SCRIPT(cx, script, &tvr); + obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL); + if (obj) { + obj->setPrivate(script); + script->u.object = obj; +#ifdef CHECK_SCRIPT_OWNER + script->owner = NULL; +#endif + } + JS_POP_TEMP_ROOT(cx, &tvr); + return obj; +} + +JS_PUBLIC_API(JSObject *) +JS_GetScriptObject(JSScript *script) +{ + return script->u.object; +} + +JS_PUBLIC_API(void) +JS_DestroyScript(JSContext *cx, JSScript *script) +{ + CHECK_REQUEST(cx); + js_DestroyScript(cx, script); +} + +JS_PUBLIC_API(JSFunction *) +JS_CompileFunction(JSContext *cx, JSObject *obj, const char *name, + uintN nargs, const char **argnames, + const char *bytes, size_t length, + const char *filename, uintN lineno) +{ + jschar *chars; + JSFunction *fun; + + CHECK_REQUEST(cx); + chars = js_InflateString(cx, bytes, &length); + if (!chars) + return NULL; + fun = JS_CompileUCFunction(cx, obj, name, nargs, argnames, chars, length, + filename, lineno); + cx->free(chars); + return fun; +} + +JS_PUBLIC_API(JSFunction *) +JS_CompileFunctionForPrincipals(JSContext *cx, JSObject *obj, + JSPrincipals *principals, const char *name, + uintN nargs, const char **argnames, + const char *bytes, size_t length, + const char *filename, uintN lineno) +{ + jschar *chars; + JSFunction *fun; + + CHECK_REQUEST(cx); + chars = js_InflateString(cx, bytes, &length); + if (!chars) + return NULL; + fun = JS_CompileUCFunctionForPrincipals(cx, obj, principals, name, + nargs, argnames, chars, length, + filename, lineno); + cx->free(chars); + return fun; +} + +JS_PUBLIC_API(JSFunction *) +JS_CompileUCFunction(JSContext *cx, JSObject *obj, const char *name, + uintN nargs, const char **argnames, + const jschar *chars, size_t length, + const char *filename, uintN lineno) +{ + CHECK_REQUEST(cx); + return JS_CompileUCFunctionForPrincipals(cx, obj, NULL, name, + nargs, argnames, + chars, length, + filename, lineno); +} + +JS_PUBLIC_API(JSFunction *) +JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj, + JSPrincipals *principals, const char *name, + uintN nargs, const char **argnames, + const jschar *chars, size_t length, + const char *filename, uintN lineno) +{ + JSFunction *fun; + JSTempValueRooter tvr; + JSAtom *funAtom, *argAtom; + uintN i; + + CHECK_REQUEST(cx); + if (!name) { + funAtom = NULL; + } else { + funAtom = js_Atomize(cx, name, strlen(name), 0); + if (!funAtom) { + fun = NULL; + goto out2; + } + } + fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, obj, funAtom); + if (!fun) + goto out2; + + MUST_FLOW_THROUGH("out"); + JS_PUSH_TEMP_ROOT_OBJECT(cx, FUN_OBJECT(fun), &tvr); + for (i = 0; i < nargs; i++) { + argAtom = js_Atomize(cx, argnames[i], strlen(argnames[i]), 0); + if (!argAtom) { + fun = NULL; + goto out; + } + if (!js_AddLocal(cx, fun, argAtom, JSLOCAL_ARG)) { + fun = NULL; + goto out; + } + } + + if (!JSCompiler::compileFunctionBody(cx, fun, principals, + chars, length, filename, lineno)) { + fun = NULL; + goto out; + } + + if (obj && + funAtom && + !obj->defineProperty(cx, ATOM_TO_JSID(funAtom), OBJECT_TO_JSVAL(FUN_OBJECT(fun)), + NULL, NULL, JSPROP_ENUMERATE)) { + fun = NULL; + } + +#ifdef JS_SCOPE_DEPTH_METER + if (fun && obj) { + JSObject *pobj = obj; + uintN depth = 1; + + while ((pobj = OBJ_GET_PARENT(cx, pobj)) != NULL) + ++depth; + JS_BASIC_STATS_ACCUM(&cx->runtime->hostenvScopeDepthStats, depth); + } +#endif + + out: + cx->weakRoots.finalizableNewborns[FINALIZE_FUNCTION] = fun; + JS_POP_TEMP_ROOT(cx, &tvr); + + out2: + LAST_FRAME_CHECKS(cx, fun); + return fun; +} + +JS_PUBLIC_API(JSString *) +JS_DecompileScript(JSContext *cx, JSScript *script, const char *name, + uintN indent) +{ + JSPrinter *jp; + JSString *str; + + CHECK_REQUEST(cx); + jp = js_NewPrinter(cx, name, NULL, + indent & ~JS_DONT_PRETTY_PRINT, + !(indent & JS_DONT_PRETTY_PRINT), + false, false); + if (!jp) + return NULL; + if (js_DecompileScript(jp, script)) + str = js_GetPrinterOutput(jp); + else + str = NULL; + js_DestroyPrinter(jp); + return str; +} + +JS_PUBLIC_API(JSString *) +JS_DecompileFunction(JSContext *cx, JSFunction *fun, uintN indent) +{ + CHECK_REQUEST(cx); + return js_DecompileToString(cx, "JS_DecompileFunction", fun, + indent & ~JS_DONT_PRETTY_PRINT, + !(indent & JS_DONT_PRETTY_PRINT), + false, false, js_DecompileFunction); +} + +JS_PUBLIC_API(JSString *) +JS_DecompileFunctionBody(JSContext *cx, JSFunction *fun, uintN indent) +{ + CHECK_REQUEST(cx); + return js_DecompileToString(cx, "JS_DecompileFunctionBody", fun, + indent & ~JS_DONT_PRETTY_PRINT, + !(indent & JS_DONT_PRETTY_PRINT), + false, false, js_DecompileFunctionBody); +} + +JS_PUBLIC_API(JSBool) +JS_ExecuteScript(JSContext *cx, JSObject *obj, JSScript *script, jsval *rval) +{ + JSBool ok; + + CHECK_REQUEST(cx); + ok = js_Execute(cx, obj, script, NULL, 0, rval); + LAST_FRAME_CHECKS(cx, ok); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_ExecuteScriptPart(JSContext *cx, JSObject *obj, JSScript *script, + JSExecPart part, jsval *rval) +{ + JSScript tmp; + JSBool ok; + + /* Make a temporary copy of the JSScript structure and farble it a bit. */ + tmp = *script; + if (part == JSEXEC_PROLOG) { + tmp.length = tmp.main - tmp.code; + } else { + tmp.length -= tmp.main - tmp.code; + tmp.code = tmp.main; + } + + /* Tell the debugger about our temporary copy of the script structure. */ + const JSDebugHooks *hooks = cx->debugHooks; + if (hooks->newScriptHook) { + hooks->newScriptHook(cx, tmp.filename, tmp.lineno, &tmp, NULL, + hooks->newScriptHookData); + } + + /* Execute the farbled struct and tell the debugger to forget about it. */ + ok = JS_ExecuteScript(cx, obj, &tmp, rval); + if (hooks->destroyScriptHook) + hooks->destroyScriptHook(cx, &tmp, hooks->destroyScriptHookData); + return ok; +} + +/* Ancient uintN nbytes is part of API/ABI, so use size_t length local. */ +JS_PUBLIC_API(JSBool) +JS_EvaluateScript(JSContext *cx, JSObject *obj, + const char *bytes, uintN nbytes, + const char *filename, uintN lineno, + jsval *rval) +{ + size_t length = nbytes; + jschar *chars; + JSBool ok; + + CHECK_REQUEST(cx); + chars = js_InflateString(cx, bytes, &length); + if (!chars) + return JS_FALSE; + ok = JS_EvaluateUCScript(cx, obj, chars, length, filename, lineno, rval); + cx->free(chars); + return ok; +} + +/* Ancient uintN nbytes is part of API/ABI, so use size_t length local. */ +JS_PUBLIC_API(JSBool) +JS_EvaluateScriptForPrincipals(JSContext *cx, JSObject *obj, + JSPrincipals *principals, + const char *bytes, uintN nbytes, + const char *filename, uintN lineno, + jsval *rval) +{ + size_t length = nbytes; + jschar *chars; + JSBool ok; + + CHECK_REQUEST(cx); + chars = js_InflateString(cx, bytes, &length); + if (!chars) + return JS_FALSE; + ok = JS_EvaluateUCScriptForPrincipals(cx, obj, principals, chars, length, + filename, lineno, rval); + cx->free(chars); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_EvaluateUCScript(JSContext *cx, JSObject *obj, + const jschar *chars, uintN length, + const char *filename, uintN lineno, + jsval *rval) +{ + CHECK_REQUEST(cx); + return JS_EvaluateUCScriptForPrincipals(cx, obj, NULL, chars, length, + filename, lineno, rval); +} + +JS_PUBLIC_API(JSBool) +JS_EvaluateUCScriptForPrincipals(JSContext *cx, JSObject *obj, + JSPrincipals *principals, + const jschar *chars, uintN length, + const char *filename, uintN lineno, + jsval *rval) +{ + JSScript *script; + JSBool ok; + + CHECK_REQUEST(cx); + script = JSCompiler::compileScript(cx, obj, NULL, principals, + !rval + ? TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL + : TCF_COMPILE_N_GO, + chars, length, NULL, filename, lineno); + if (!script) { + LAST_FRAME_CHECKS(cx, script); + return JS_FALSE; + } + ok = js_Execute(cx, obj, script, NULL, 0, rval); + LAST_FRAME_CHECKS(cx, ok); + JS_DestroyScript(cx, script); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_CallFunction(JSContext *cx, JSObject *obj, JSFunction *fun, uintN argc, + jsval *argv, jsval *rval) +{ + JSBool ok; + + CHECK_REQUEST(cx); + ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(FUN_OBJECT(fun)), argc, argv, + rval); + LAST_FRAME_CHECKS(cx, ok); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_CallFunctionName(JSContext *cx, JSObject *obj, const char *name, uintN argc, + jsval *argv, jsval *rval) +{ + CHECK_REQUEST(cx); + + JSAutoTempValueRooter tvr(cx); + JSAtom *atom = js_Atomize(cx, name, strlen(name), 0); + JSBool ok = atom && + js_GetMethod(cx, obj, ATOM_TO_JSID(atom), + JSGET_NO_METHOD_BARRIER, tvr.addr()) && + js_InternalCall(cx, obj, tvr.value(), argc, argv, rval); + LAST_FRAME_CHECKS(cx, ok); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, uintN argc, + jsval *argv, jsval *rval) +{ + JSBool ok; + + CHECK_REQUEST(cx); + ok = js_InternalCall(cx, obj, fval, argc, argv, rval); + LAST_FRAME_CHECKS(cx, ok); + return ok; +} + +JS_PUBLIC_API(JSOperationCallback) +JS_SetOperationCallback(JSContext *cx, JSOperationCallback callback) +{ +#ifdef JS_THREADSAFE + JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread)); +#endif + JSOperationCallback old = cx->operationCallback; + cx->operationCallback = callback; + return old; +} + +JS_PUBLIC_API(JSOperationCallback) +JS_GetOperationCallback(JSContext *cx) +{ + return cx->operationCallback; +} + +JS_PUBLIC_API(void) +JS_TriggerOperationCallback(JSContext *cx) +{ + /* + * Use JS_ATOMIC_SET in the hope that it will make sure the write + * will become immediately visible to other processors polling + * cx->operationCallbackFlag. Note that we only care about + * visibility here, not read/write ordering. + */ + JS_ATOMIC_SET(&cx->operationCallbackFlag, 1); +} + +JS_PUBLIC_API(void) +JS_TriggerAllOperationCallbacks(JSRuntime *rt) +{ + js_TriggerAllOperationCallbacks(rt, JS_FALSE); +} + +JS_PUBLIC_API(JSBool) +JS_IsRunning(JSContext *cx) +{ + /* + * The use of cx->fp below is safe. Rationale: Here we don't care if the + * interpreter state is stale. We just want to know if there *is* any + * interpreter state. + */ + VOUCH_DOES_NOT_REQUIRE_STACK(); + +#ifdef JS_TRACER + JS_ASSERT_IF(JS_TRACE_MONITOR(cx).tracecx == cx, cx->fp); +#endif + return cx->fp != NULL; +} + +JS_PUBLIC_API(JSBool) +JS_IsConstructing(JSContext *cx) +{ +#ifdef JS_TRACER + if (JS_ON_TRACE(cx)) { + JS_ASSERT(cx->bailExit); + return *cx->bailExit->pc == JSOP_NEW; + } +#endif + + JSStackFrame *fp = js_GetTopStackFrame(cx); + return fp && (fp->flags & JSFRAME_CONSTRUCTING); +} + +JS_FRIEND_API(JSBool) +JS_IsAssigning(JSContext *cx) +{ + JSStackFrame *fp; + + fp = js_GetScriptedCaller(cx, NULL); + if (!fp || !fp->regs) + return JS_FALSE; + return (js_CodeSpec[*fp->regs->pc].format & JOF_ASSIGNING) != 0; +} + +JS_PUBLIC_API(JSStackFrame *) +JS_SaveFrameChain(JSContext *cx) +{ + JSStackFrame *fp; + + fp = js_GetTopStackFrame(cx); + if (!fp) + return fp; + + JS_ASSERT(!fp->dormantNext); + fp->dormantNext = cx->dormantFrameChain; + cx->dormantFrameChain = fp; + cx->fp = NULL; + return fp; +} + +JS_PUBLIC_API(void) +JS_RestoreFrameChain(JSContext *cx, JSStackFrame *fp) +{ + JS_ASSERT_NOT_ON_TRACE(cx); + JS_ASSERT(!cx->fp); + if (!fp) + return; + + JS_ASSERT(fp == cx->dormantFrameChain); + cx->fp = fp; + cx->dormantFrameChain = fp->dormantNext; + fp->dormantNext = NULL; +} + +/************************************************************************/ + +JS_PUBLIC_API(JSString *) +JS_NewString(JSContext *cx, char *bytes, size_t nbytes) +{ + size_t length = nbytes; + jschar *chars; + JSString *str; + + CHECK_REQUEST(cx); + + /* Make a UTF-16 vector from the 8-bit char codes in bytes. */ + chars = js_InflateString(cx, bytes, &length); + if (!chars) + return NULL; + + /* Free chars (but not bytes, which caller frees on error) if we fail. */ + str = js_NewString(cx, chars, length); + if (!str) { + cx->free(chars); + return NULL; + } + + /* Hand off bytes to the deflated string cache, if possible. */ + if (!js_SetStringBytes(cx, str, bytes, nbytes)) + cx->free(bytes); + return str; +} + +JS_PUBLIC_API(JSString *) +JS_NewStringCopyN(JSContext *cx, const char *s, size_t n) +{ + jschar *js; + JSString *str; + + CHECK_REQUEST(cx); + js = js_InflateString(cx, s, &n); + if (!js) + return NULL; + str = js_NewString(cx, js, n); + if (!str) + cx->free(js); + return str; +} + +JS_PUBLIC_API(JSString *) +JS_NewStringCopyZ(JSContext *cx, const char *s) +{ + size_t n; + jschar *js; + JSString *str; + + CHECK_REQUEST(cx); + if (!s) + return cx->runtime->emptyString; + n = strlen(s); + js = js_InflateString(cx, s, &n); + if (!js) + return NULL; + str = js_NewString(cx, js, n); + if (!str) + cx->free(js); + return str; +} + +JS_PUBLIC_API(JSString *) +JS_InternString(JSContext *cx, const char *s) +{ + JSAtom *atom; + + CHECK_REQUEST(cx); + atom = js_Atomize(cx, s, strlen(s), ATOM_INTERNED); + if (!atom) + return NULL; + return ATOM_TO_STRING(atom); +} + +JS_PUBLIC_API(JSString *) +JS_NewUCString(JSContext *cx, jschar *chars, size_t length) +{ + CHECK_REQUEST(cx); + return js_NewString(cx, chars, length); +} + +JS_PUBLIC_API(JSString *) +JS_NewUCStringCopyN(JSContext *cx, const jschar *s, size_t n) +{ + CHECK_REQUEST(cx); + return js_NewStringCopyN(cx, s, n); +} + +JS_PUBLIC_API(JSString *) +JS_NewUCStringCopyZ(JSContext *cx, const jschar *s) +{ + CHECK_REQUEST(cx); + if (!s) + return cx->runtime->emptyString; + return js_NewStringCopyZ(cx, s); +} + +JS_PUBLIC_API(JSString *) +JS_InternUCStringN(JSContext *cx, const jschar *s, size_t length) +{ + JSAtom *atom; + + CHECK_REQUEST(cx); + atom = js_AtomizeChars(cx, s, length, ATOM_INTERNED); + if (!atom) + return NULL; + return ATOM_TO_STRING(atom); +} + +JS_PUBLIC_API(JSString *) +JS_InternUCString(JSContext *cx, const jschar *s) +{ + return JS_InternUCStringN(cx, s, js_strlen(s)); +} + +JS_PUBLIC_API(char *) +JS_GetStringBytes(JSString *str) +{ + const char *bytes; + + bytes = js_GetStringBytes(NULL, str); + return (char *)(bytes ? bytes : ""); +} + +JS_PUBLIC_API(jschar *) +JS_GetStringChars(JSString *str) +{ + size_t n, size; + jschar *s; + + /* + * API botch (again, shades of JS_GetStringBytes): we have no cx to report + * out-of-memory when undepending strings, so we replace js_UndependString + * with explicit malloc call and ignore its errors. + * + * If we fail to convert a dependent string into an independent one, our + * caller will not be guaranteed a \u0000 terminator as a backstop. This + * may break some clients who already misbehave on embedded NULs. + * + * The gain of dependent strings, which cure quadratic and cubic growth + * rate bugs in string concatenation, is worth this slight loss in API + * compatibility. + */ + if (str->isDependent()) { + n = str->dependentLength(); + size = (n + 1) * sizeof(jschar); + s = (jschar *) js_malloc(size); + if (s) { + memcpy(s, str->dependentChars(), n * sizeof *s); + s[n] = 0; + str->reinitFlat(s, n); + } else { + s = str->dependentChars(); + } + } else { + str->flatClearMutable(); + s = str->flatChars(); + } + return s; +} + +JS_PUBLIC_API(size_t) +JS_GetStringLength(JSString *str) +{ + return str->length(); +} + +JS_PUBLIC_API(const char *) +JS_GetStringBytesZ(JSContext *cx, JSString *str) +{ + return js_GetStringBytes(cx, str); +} + +JS_PUBLIC_API(const jschar *) +JS_GetStringCharsZ(JSContext *cx, JSString *str) +{ + return js_UndependString(cx, str); +} + +JS_PUBLIC_API(intN) +JS_CompareStrings(JSString *str1, JSString *str2) +{ + return js_CompareStrings(str1, str2); +} + +JS_PUBLIC_API(JSString *) +JS_NewGrowableString(JSContext *cx, jschar *chars, size_t length) +{ + JSString *str; + + CHECK_REQUEST(cx); + str = js_NewString(cx, chars, length); + if (!str) + return str; + str->flatSetMutable(); + return str; +} + +JS_PUBLIC_API(JSString *) +JS_NewDependentString(JSContext *cx, JSString *str, size_t start, + size_t length) +{ + CHECK_REQUEST(cx); + return js_NewDependentString(cx, str, start, length); +} + +JS_PUBLIC_API(JSString *) +JS_ConcatStrings(JSContext *cx, JSString *left, JSString *right) +{ + CHECK_REQUEST(cx); + return js_ConcatStrings(cx, left, right); +} + +JS_PUBLIC_API(const jschar *) +JS_UndependString(JSContext *cx, JSString *str) +{ + CHECK_REQUEST(cx); + return js_UndependString(cx, str); +} + +JS_PUBLIC_API(JSBool) +JS_MakeStringImmutable(JSContext *cx, JSString *str) +{ + CHECK_REQUEST(cx); + return js_MakeStringImmutable(cx, str); +} + +JS_PUBLIC_API(JSBool) +JS_EncodeCharacters(JSContext *cx, const jschar *src, size_t srclen, char *dst, + size_t *dstlenp) +{ + size_t n; + + if (!dst) { + n = js_GetDeflatedStringLength(cx, src, srclen); + if (n == (size_t)-1) { + *dstlenp = 0; + return JS_FALSE; + } + *dstlenp = n; + return JS_TRUE; + } + + return js_DeflateStringToBuffer(cx, src, srclen, dst, dstlenp); +} + +JS_PUBLIC_API(JSBool) +JS_DecodeBytes(JSContext *cx, const char *src, size_t srclen, jschar *dst, + size_t *dstlenp) +{ + return js_InflateStringToBuffer(cx, src, srclen, dst, dstlenp); +} + +JS_PUBLIC_API(char *) +JS_EncodeString(JSContext *cx, JSString *str) +{ + return js_DeflateString(cx, str->chars(), str->length()); +} + +JS_PUBLIC_API(JSBool) +JS_Stringify(JSContext *cx, jsval *vp, JSObject *replacer, jsval space, + JSONWriteCallback callback, void *data) +{ + CHECK_REQUEST(cx); + JSCharBuffer cb(cx); + if (!js_Stringify(cx, vp, replacer, space, cb)) + return false; + return callback(cb.begin(), cb.length(), data); +} + +JS_PUBLIC_API(JSBool) +JS_TryJSON(JSContext *cx, jsval *vp) +{ + CHECK_REQUEST(cx); + return js_TryJSON(cx, vp); +} + +JS_PUBLIC_API(JSONParser *) +JS_BeginJSONParse(JSContext *cx, jsval *vp) +{ + CHECK_REQUEST(cx); + return js_BeginJSONParse(cx, vp); +} + +JS_PUBLIC_API(JSBool) +JS_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len) +{ + CHECK_REQUEST(cx); + return js_ConsumeJSONText(cx, jp, data, len); +} + +JS_PUBLIC_API(JSBool) +JS_FinishJSONParse(JSContext *cx, JSONParser *jp, jsval reviver) +{ + CHECK_REQUEST(cx); + return js_FinishJSONParse(cx, jp, reviver); +} + +/* + * The following determines whether C Strings are to be treated as UTF-8 + * or ISO-8859-1. For correct operation, it must be set prior to the + * first call to JS_NewRuntime. + */ +#ifndef JS_C_STRINGS_ARE_UTF8 +JSBool js_CStringsAreUTF8 = JS_FALSE; +#endif + +JS_PUBLIC_API(JSBool) +JS_CStringsAreUTF8() +{ + return js_CStringsAreUTF8; +} + +JS_PUBLIC_API(void) +JS_SetCStringsAreUTF8() +{ + JS_ASSERT(!js_NewRuntimeWasCalled); + +#ifndef JS_C_STRINGS_ARE_UTF8 + js_CStringsAreUTF8 = JS_TRUE; +#endif +} + +/************************************************************************/ + +JS_PUBLIC_API(void) +JS_ReportError(JSContext *cx, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + js_ReportErrorVA(cx, JSREPORT_ERROR, format, ap); + va_end(ap); +} + +JS_PUBLIC_API(void) +JS_ReportErrorNumber(JSContext *cx, JSErrorCallback errorCallback, + void *userRef, const uintN errorNumber, ...) +{ + va_list ap; + + va_start(ap, errorNumber); + js_ReportErrorNumberVA(cx, JSREPORT_ERROR, errorCallback, userRef, + errorNumber, JS_TRUE, ap); + va_end(ap); +} + +JS_PUBLIC_API(void) +JS_ReportErrorNumberUC(JSContext *cx, JSErrorCallback errorCallback, + void *userRef, const uintN errorNumber, ...) +{ + va_list ap; + + va_start(ap, errorNumber); + js_ReportErrorNumberVA(cx, JSREPORT_ERROR, errorCallback, userRef, + errorNumber, JS_FALSE, ap); + va_end(ap); +} + +JS_PUBLIC_API(JSBool) +JS_ReportWarning(JSContext *cx, const char *format, ...) +{ + va_list ap; + JSBool ok; + + va_start(ap, format); + ok = js_ReportErrorVA(cx, JSREPORT_WARNING, format, ap); + va_end(ap); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_ReportErrorFlagsAndNumber(JSContext *cx, uintN flags, + JSErrorCallback errorCallback, void *userRef, + const uintN errorNumber, ...) +{ + va_list ap; + JSBool ok; + + va_start(ap, errorNumber); + ok = js_ReportErrorNumberVA(cx, flags, errorCallback, userRef, + errorNumber, JS_TRUE, ap); + va_end(ap); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_ReportErrorFlagsAndNumberUC(JSContext *cx, uintN flags, + JSErrorCallback errorCallback, void *userRef, + const uintN errorNumber, ...) +{ + va_list ap; + JSBool ok; + + va_start(ap, errorNumber); + ok = js_ReportErrorNumberVA(cx, flags, errorCallback, userRef, + errorNumber, JS_FALSE, ap); + va_end(ap); + return ok; +} + +JS_PUBLIC_API(void) +JS_ReportOutOfMemory(JSContext *cx) +{ + js_ReportOutOfMemory(cx); +} + +JS_PUBLIC_API(void) +JS_ReportAllocationOverflow(JSContext *cx) +{ + js_ReportAllocationOverflow(cx); +} + +JS_PUBLIC_API(JSErrorReporter) +JS_SetErrorReporter(JSContext *cx, JSErrorReporter er) +{ + JSErrorReporter older; + + older = cx->errorReporter; + cx->errorReporter = er; + return older; +} + +/************************************************************************/ + +/* + * Regular Expressions. + */ +JS_PUBLIC_API(JSObject *) +JS_NewRegExpObject(JSContext *cx, char *bytes, size_t length, uintN flags) +{ + jschar *chars; + JSObject *obj; + + CHECK_REQUEST(cx); + chars = js_InflateString(cx, bytes, &length); + if (!chars) + return NULL; + obj = js_NewRegExpObject(cx, NULL, chars, length, flags); + cx->free(chars); + return obj; +} + +JS_PUBLIC_API(JSObject *) +JS_NewUCRegExpObject(JSContext *cx, jschar *chars, size_t length, uintN flags) +{ + CHECK_REQUEST(cx); + return js_NewRegExpObject(cx, NULL, chars, length, flags); +} + +JS_PUBLIC_API(void) +JS_SetRegExpInput(JSContext *cx, JSString *input, JSBool multiline) +{ + JSRegExpStatics *res; + + CHECK_REQUEST(cx); + /* No locking required, cx is thread-private and input must be live. */ + res = &cx->regExpStatics; + res->input = input; + res->multiline = multiline; + cx->runtime->gcPoke = JS_TRUE; +} + +JS_PUBLIC_API(void) +JS_ClearRegExpStatics(JSContext *cx) +{ + JSRegExpStatics *res; + + /* No locking required, cx is thread-private and input must be live. */ + res = &cx->regExpStatics; + res->input = NULL; + res->multiline = JS_FALSE; + res->parenCount = 0; + res->lastMatch = res->lastParen = js_EmptySubString; + res->leftContext = res->rightContext = js_EmptySubString; + if (res->moreParens) { + cx->free(res->moreParens); + res->moreParens = NULL; + } + cx->runtime->gcPoke = JS_TRUE; +} + +JS_PUBLIC_API(void) +JS_ClearRegExpRoots(JSContext *cx) +{ + JSRegExpStatics *res; + + /* No locking required, cx is thread-private and input must be live. */ + res = &cx->regExpStatics; + res->input = NULL; + cx->runtime->gcPoke = JS_TRUE; +} + +/* TODO: compile, execute, get/set other statics... */ + +/************************************************************************/ + +JS_PUBLIC_API(void) +JS_SetLocaleCallbacks(JSContext *cx, JSLocaleCallbacks *callbacks) +{ + cx->localeCallbacks = callbacks; +} + +JS_PUBLIC_API(JSLocaleCallbacks *) +JS_GetLocaleCallbacks(JSContext *cx) +{ + return cx->localeCallbacks; +} + +/************************************************************************/ + +JS_PUBLIC_API(JSBool) +JS_IsExceptionPending(JSContext *cx) +{ + return (JSBool) cx->throwing; +} + +JS_PUBLIC_API(JSBool) +JS_GetPendingException(JSContext *cx, jsval *vp) +{ + CHECK_REQUEST(cx); + if (!cx->throwing) + return JS_FALSE; + *vp = cx->exception; + return JS_TRUE; +} + +JS_PUBLIC_API(void) +JS_SetPendingException(JSContext *cx, jsval v) +{ + CHECK_REQUEST(cx); + cx->throwing = JS_TRUE; + cx->exception = v; +} + +JS_PUBLIC_API(void) +JS_ClearPendingException(JSContext *cx) +{ + cx->throwing = JS_FALSE; + cx->exception = JSVAL_VOID; +} + +JS_PUBLIC_API(JSBool) +JS_ReportPendingException(JSContext *cx) +{ + JSBool save, ok; + + CHECK_REQUEST(cx); + + /* + * Set cx->generatingError to suppress the standard error-to-exception + * conversion done by all {js,JS}_Report* functions except for OOM. The + * cx->generatingError flag was added to suppress recursive divergence + * under js_ErrorToException, but it serves for our purposes here too. + */ + save = cx->generatingError; + cx->generatingError = JS_TRUE; + ok = js_ReportUncaughtException(cx); + cx->generatingError = save; + return ok; +} + +struct JSExceptionState { + JSBool throwing; + jsval exception; +}; + +JS_PUBLIC_API(JSExceptionState *) +JS_SaveExceptionState(JSContext *cx) +{ + JSExceptionState *state; + + CHECK_REQUEST(cx); + state = (JSExceptionState *) cx->malloc(sizeof(JSExceptionState)); + if (state) { + state->throwing = JS_GetPendingException(cx, &state->exception); + if (state->throwing && JSVAL_IS_GCTHING(state->exception)) + js_AddRoot(cx, &state->exception, "JSExceptionState.exception"); + } + return state; +} + +JS_PUBLIC_API(void) +JS_RestoreExceptionState(JSContext *cx, JSExceptionState *state) +{ + CHECK_REQUEST(cx); + if (state) { + if (state->throwing) + JS_SetPendingException(cx, state->exception); + else + JS_ClearPendingException(cx); + JS_DropExceptionState(cx, state); + } +} + +JS_PUBLIC_API(void) +JS_DropExceptionState(JSContext *cx, JSExceptionState *state) +{ + CHECK_REQUEST(cx); + if (state) { + if (state->throwing && JSVAL_IS_GCTHING(state->exception)) + JS_RemoveRoot(cx, &state->exception); + cx->free(state); + } +} + +JS_PUBLIC_API(JSErrorReport *) +JS_ErrorFromException(JSContext *cx, jsval v) +{ + CHECK_REQUEST(cx); + return js_ErrorFromException(cx, v); +} + +JS_PUBLIC_API(JSBool) +JS_ThrowReportedError(JSContext *cx, const char *message, + JSErrorReport *reportp) +{ + return JS_IsRunning(cx) && js_ErrorToException(cx, message, reportp); +} + +JS_PUBLIC_API(JSBool) +JS_ThrowStopIteration(JSContext *cx) +{ + return js_ThrowStopIteration(cx); +} + +/* + * Get the owning thread id of a context. Returns 0 if the context is not + * owned by any thread. + */ +JS_PUBLIC_API(jsword) +JS_GetContextThread(JSContext *cx) +{ +#ifdef JS_THREADSAFE + return JS_THREAD_ID(cx); +#else + return 0; +#endif +} + +/* + * Set the current thread as the owning thread of a context. Returns the + * old owning thread id, or -1 if the operation failed. + */ +JS_PUBLIC_API(jsword) +JS_SetContextThread(JSContext *cx) +{ +#ifdef JS_THREADSAFE + JS_ASSERT(cx->requestDepth == 0); + if (cx->thread) { + JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread)); + return cx->thread->id; + } + + if (!js_InitContextThread(cx)) { + js_ReportOutOfMemory(cx); + return -1; + } + + /* Here the GC lock is still held after js_InitContextThread took it. */ + JS_UNLOCK_GC(cx->runtime); +#endif + return 0; +} + +JS_PUBLIC_API(jsword) +JS_ClearContextThread(JSContext *cx) +{ +#ifdef JS_THREADSAFE + /* + * This must be called outside a request and, if cx is associated with a + * thread, this must be called only from that thread. If not, this is a + * harmless no-op. + */ + JS_ASSERT(cx->requestDepth == 0); + if (!cx->thread) + return 0; + JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread)); + jsword old = cx->thread->id; + + /* + * We must not race with a GC that accesses cx->thread for all threads, + * see bug 476934. + */ + JSRuntime *rt = cx->runtime; + JS_LOCK_GC(rt); + js_WaitForGC(rt); + js_ClearContextThread(cx); + JS_UNLOCK_GC(rt); + return old; +#else + return 0; +#endif +} + +#ifdef JS_GC_ZEAL +JS_PUBLIC_API(void) +JS_SetGCZeal(JSContext *cx, uint8 zeal) +{ + cx->runtime->gcZeal = zeal; +} +#endif + +/************************************************************************/ + +#if !defined(STATIC_JS_API) && defined(XP_WIN) && !defined (WINCE) + +#include + +/* + * Initialization routine for the JS DLL. + */ +BOOL WINAPI DllMain (HINSTANCE hDLL, DWORD dwReason, LPVOID lpReserved) +{ + return TRUE; +} + +#endif diff --git a/ape-server/deps/js/src/jsapi.h b/ape-server/deps/js/src/jsapi.h new file mode 100755 index 0000000..7e15b36 --- /dev/null +++ b/ape-server/deps/js/src/jsapi.h @@ -0,0 +1,2833 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsapi_h___ +#define jsapi_h___ +/* + * JavaScript API. + */ +#include +#include +#include "js-config.h" +#include "jspubtd.h" +#include "jsutil.h" + +JS_BEGIN_EXTERN_C + +/* + * Type tags stored in the low bits of a jsval. + */ +typedef enum jsvaltag { + JSVAL_OBJECT = 0x0, /* untagged reference to object */ + JSVAL_INT = 0x1, /* tagged 31-bit integer value */ + JSVAL_DOUBLE = 0x2, /* tagged reference to double */ + JSVAL_STRING = 0x4, /* tagged reference to string */ + JSVAL_SPECIAL = 0x6 /* tagged boolean or private value */ +} jsvaltag; + +/* Type tag bitfield length and derived macros. */ +#define JSVAL_TAGBITS 3 +#define JSVAL_TAGMASK ((jsval) JS_BITMASK(JSVAL_TAGBITS)) +#define JSVAL_ALIGN JS_BIT(JSVAL_TAGBITS) + +/* Not a function, because we have static asserts that use it */ +#define JSVAL_TAG(v) ((jsvaltag)((v) & JSVAL_TAGMASK)) + +/* Not a function, because we have static asserts that use it */ +#define JSVAL_SETTAG(v, t) ((v) | (t)) + +static JS_ALWAYS_INLINE jsval +JSVAL_CLRTAG(jsval v) +{ + return v & ~(jsval)JSVAL_TAGMASK; +} + +/* + * Well-known JS values. The extern'd variables are initialized when the + * first JSContext is created by JS_NewContext (see below). + */ +#define JSVAL_NULL ((jsval) 0) +#define JSVAL_ZERO INT_TO_JSVAL(0) +#define JSVAL_ONE INT_TO_JSVAL(1) +#define JSVAL_FALSE SPECIAL_TO_JSVAL(JS_FALSE) +#define JSVAL_TRUE SPECIAL_TO_JSVAL(JS_TRUE) +#define JSVAL_VOID SPECIAL_TO_JSVAL(2) + +/* + * A "special" value is a 29-bit (for 32-bit jsval) or 61-bit (for 64-bit jsval) + * value whose tag is JSVAL_SPECIAL. These values include the booleans 0 and 1. + * + * JSVAL_VOID is a non-boolean special value, but embedders MUST NOT rely on + * this. All other possible special values are implementation-reserved + * and MUST NOT be constructed by any embedding of SpiderMonkey. + */ +#define JSVAL_TO_SPECIAL(v) ((JSBool) ((v) >> JSVAL_TAGBITS)) +#define SPECIAL_TO_JSVAL(b) \ + JSVAL_SETTAG((jsval) (b) << JSVAL_TAGBITS, JSVAL_SPECIAL) + +/* Predicates for type testing. */ +static JS_ALWAYS_INLINE JSBool +JSVAL_IS_OBJECT(jsval v) +{ + return JSVAL_TAG(v) == JSVAL_OBJECT; +} + +static JS_ALWAYS_INLINE JSBool +JSVAL_IS_INT(jsval v) +{ + return v & JSVAL_INT; +} + +static JS_ALWAYS_INLINE JSBool +JSVAL_IS_DOUBLE(jsval v) +{ + return JSVAL_TAG(v) == JSVAL_DOUBLE; +} + +static JS_ALWAYS_INLINE JSBool +JSVAL_IS_NUMBER(jsval v) +{ + return JSVAL_IS_INT(v) || JSVAL_IS_DOUBLE(v); +} + +static JS_ALWAYS_INLINE JSBool +JSVAL_IS_STRING(jsval v) +{ + return JSVAL_TAG(v) == JSVAL_STRING; +} + +static JS_ALWAYS_INLINE JSBool +JSVAL_IS_SPECIAL(jsval v) +{ + return JSVAL_TAG(v) == JSVAL_SPECIAL; +} + +static JS_ALWAYS_INLINE JSBool +JSVAL_IS_BOOLEAN(jsval v) +{ + return (v & ~((jsval)1 << JSVAL_TAGBITS)) == JSVAL_SPECIAL; +} + +static JS_ALWAYS_INLINE JSBool +JSVAL_IS_NULL(jsval v) +{ + return v == JSVAL_NULL; +} + +static JS_ALWAYS_INLINE JSBool +JSVAL_IS_VOID(jsval v) +{ + return v == JSVAL_VOID; +} + +static JS_ALWAYS_INLINE JSBool +JSVAL_IS_PRIMITIVE(jsval v) +{ + return !JSVAL_IS_OBJECT(v) || JSVAL_IS_NULL(v); +} + +/* Objects, strings, and doubles are GC'ed. */ +static JS_ALWAYS_INLINE JSBool +JSVAL_IS_GCTHING(jsval v) +{ + return !(v & JSVAL_INT) && JSVAL_TAG(v) != JSVAL_SPECIAL; +} + +static JS_ALWAYS_INLINE void * +JSVAL_TO_GCTHING(jsval v) +{ + JS_ASSERT(JSVAL_IS_GCTHING(v)); + return (void *) JSVAL_CLRTAG(v); +} + +static JS_ALWAYS_INLINE JSObject * +JSVAL_TO_OBJECT(jsval v) +{ + JS_ASSERT(JSVAL_IS_OBJECT(v)); + return (JSObject *) JSVAL_TO_GCTHING(v); +} + +static JS_ALWAYS_INLINE jsdouble * +JSVAL_TO_DOUBLE(jsval v) +{ + JS_ASSERT(JSVAL_IS_DOUBLE(v)); + return (jsdouble *) JSVAL_TO_GCTHING(v); +} + +static JS_ALWAYS_INLINE JSString * +JSVAL_TO_STRING(jsval v) +{ + JS_ASSERT(JSVAL_IS_STRING(v)); + return (JSString *) JSVAL_TO_GCTHING(v); +} + +static JS_ALWAYS_INLINE jsval +OBJECT_TO_JSVAL(JSObject *obj) +{ + JS_ASSERT(((jsval) obj & JSVAL_TAGMASK) == JSVAL_OBJECT); + return (jsval) obj; +} + +static JS_ALWAYS_INLINE jsval +DOUBLE_TO_JSVAL(jsdouble *dp) +{ + JS_ASSERT(((jsword) dp & JSVAL_TAGMASK) == 0); + return JSVAL_SETTAG((jsval) dp, JSVAL_DOUBLE); +} + +static JS_ALWAYS_INLINE jsval +STRING_TO_JSVAL(JSString *str) +{ + return JSVAL_SETTAG((jsval) str, JSVAL_STRING); +} + +/* Lock and unlock the GC thing held by a jsval. */ +#define JSVAL_LOCK(cx,v) (JSVAL_IS_GCTHING(v) \ + ? JS_LockGCThing(cx, JSVAL_TO_GCTHING(v)) \ + : JS_TRUE) +#define JSVAL_UNLOCK(cx,v) (JSVAL_IS_GCTHING(v) \ + ? JS_UnlockGCThing(cx, JSVAL_TO_GCTHING(v)) \ + : JS_TRUE) + +/* Domain limits for the jsval int type. */ +#define JSVAL_INT_BITS 31 +#define JSVAL_INT_POW2(n) ((jsval)1 << (n)) +#define JSVAL_INT_MIN (-JSVAL_INT_POW2(30)) +#define JSVAL_INT_MAX (JSVAL_INT_POW2(30) - 1) + +/* Not a function, because we have static asserts that use it */ +#define INT_FITS_IN_JSVAL(i) ((jsuint)(i) - (jsuint)JSVAL_INT_MIN <= \ + (jsuint)(JSVAL_INT_MAX - JSVAL_INT_MIN)) + +static JS_ALWAYS_INLINE jsint +JSVAL_TO_INT(jsval v) +{ + JS_ASSERT(JSVAL_IS_INT(v)); + return (jsint) v >> 1; +} + +/* Not a function, because we have static asserts that use it */ +#define INT_TO_JSVAL_CONSTEXPR(i) (((jsval)(i) << 1) | JSVAL_INT) + +static JS_ALWAYS_INLINE jsval +INT_TO_JSVAL(jsint i) +{ + JS_ASSERT(INT_FITS_IN_JSVAL(i)); + return INT_TO_JSVAL_CONSTEXPR(i); +} + +/* Convert between boolean and jsval, asserting that inputs are valid. */ +static JS_ALWAYS_INLINE JSBool +JSVAL_TO_BOOLEAN(jsval v) +{ + JS_ASSERT(v == JSVAL_TRUE || v == JSVAL_FALSE); + return JSVAL_TO_SPECIAL(v); +} + +static JS_ALWAYS_INLINE jsval +BOOLEAN_TO_JSVAL(JSBool b) +{ + JS_ASSERT(b == JS_TRUE || b == JS_FALSE); + return SPECIAL_TO_JSVAL(b); +} + +/* A private data pointer (2-byte-aligned) can be stored as an int jsval. */ +#define JSVAL_TO_PRIVATE(v) ((void *)((v) & ~JSVAL_INT)) +#define PRIVATE_TO_JSVAL(p) ((jsval)(p) | JSVAL_INT) + +/* Property attributes, set in JSPropertySpec and passed to API functions. */ +#define JSPROP_ENUMERATE 0x01 /* property is visible to for/in loop */ +#define JSPROP_READONLY 0x02 /* not settable: assignment is no-op */ +#define JSPROP_PERMANENT 0x04 /* property cannot be deleted */ +#define JSPROP_GETTER 0x10 /* property holds getter function */ +#define JSPROP_SETTER 0x20 /* property holds setter function */ +#define JSPROP_SHARED 0x40 /* don't allocate a value slot for this + property; don't copy the property on + set of the same-named property in an + object that delegates to a prototype + containing this property */ +#define JSPROP_INDEX 0x80 /* name is actually (jsint) index */ + +/* Function flags, set in JSFunctionSpec and passed to JS_NewFunction etc. */ +#define JSFUN_LAMBDA 0x08 /* expressed, not declared, function */ +#define JSFUN_GETTER JSPROP_GETTER +#define JSFUN_SETTER JSPROP_SETTER +#define JSFUN_BOUND_METHOD 0x40 /* bind this to fun->object's parent */ +#define JSFUN_HEAVYWEIGHT 0x80 /* activation requires a Call object */ + +#define JSFUN_DISJOINT_FLAGS(f) ((f) & 0x0f) +#define JSFUN_GSFLAGS(f) ((f) & (JSFUN_GETTER | JSFUN_SETTER)) + +#define JSFUN_GETTER_TEST(f) ((f) & JSFUN_GETTER) +#define JSFUN_SETTER_TEST(f) ((f) & JSFUN_SETTER) +#define JSFUN_BOUND_METHOD_TEST(f) ((f) & JSFUN_BOUND_METHOD) +#define JSFUN_HEAVYWEIGHT_TEST(f) ((f) & JSFUN_HEAVYWEIGHT) + +#define JSFUN_GSFLAG2ATTR(f) JSFUN_GSFLAGS(f) + +#define JSFUN_THISP_FLAGS(f) (f) +#define JSFUN_THISP_TEST(f,t) ((f) & t) + +#define JSFUN_THISP_STRING 0x0100 /* |this| may be a primitive string */ +#define JSFUN_THISP_NUMBER 0x0200 /* |this| may be a primitive number */ +#define JSFUN_THISP_BOOLEAN 0x0400 /* |this| may be a primitive boolean */ +#define JSFUN_THISP_PRIMITIVE 0x0700 /* |this| may be any primitive value */ + +#define JSFUN_FAST_NATIVE 0x0800 /* JSFastNative needs no JSStackFrame */ + +#define JSFUN_FLAGS_MASK 0x0ff8 /* overlay JSFUN_* attributes -- + bits 12-15 are used internally to + flag interpreted functions */ + +#define JSFUN_STUB_GSOPS 0x1000 /* use JS_PropertyStub getter/setter + instead of defaulting to class gsops + for property holding function */ + +/* + * Re-use JSFUN_LAMBDA, which applies only to scripted functions, for use in + * JSFunctionSpec arrays that specify generic native prototype methods, i.e., + * methods of a class prototype that are exposed as static methods taking an + * extra leading argument: the generic |this| parameter. + * + * If you set this flag in a JSFunctionSpec struct's flags initializer, then + * that struct must live at least as long as the native static method object + * created due to this flag by JS_DefineFunctions or JS_InitClass. Typically + * JSFunctionSpec structs are allocated in static arrays. + */ +#define JSFUN_GENERIC_NATIVE JSFUN_LAMBDA + +/* + * Microseconds since the epoch, midnight, January 1, 1970 UTC. See the + * comment in jstypes.h regarding safe int64 usage. + */ +extern JS_PUBLIC_API(int64) +JS_Now(void); + +/* Don't want to export data, so provide accessors for non-inline jsvals. */ +extern JS_PUBLIC_API(jsval) +JS_GetNaNValue(JSContext *cx); + +extern JS_PUBLIC_API(jsval) +JS_GetNegativeInfinityValue(JSContext *cx); + +extern JS_PUBLIC_API(jsval) +JS_GetPositiveInfinityValue(JSContext *cx); + +extern JS_PUBLIC_API(jsval) +JS_GetEmptyStringValue(JSContext *cx); + +/* + * Format is a string of the following characters (spaces are insignificant), + * specifying the tabulated type conversions: + * + * b JSBool Boolean + * c uint16/jschar ECMA uint16, Unicode char + * i int32 ECMA int32 + * u uint32 ECMA uint32 + * j int32 Rounded int32 (coordinate) + * d jsdouble IEEE double + * I jsdouble Integral IEEE double + * s char * C string + * S JSString * Unicode string, accessed by a JSString pointer + * W jschar * Unicode character vector, 0-terminated (W for wide) + * o JSObject * Object reference + * f JSFunction * Function private + * v jsval Argument value (no conversion) + * * N/A Skip this argument (no vararg) + * / N/A End of required arguments + * + * The variable argument list after format must consist of &b, &c, &s, e.g., + * where those variables have the types given above. For the pointer types + * char *, JSString *, and JSObject *, the pointed-at memory returned belongs + * to the JS runtime, not to the calling native code. The runtime promises + * to keep this memory valid so long as argv refers to allocated stack space + * (so long as the native function is active). + * + * Fewer arguments than format specifies may be passed only if there is a / + * in format after the last required argument specifier and argc is at least + * the number of required arguments. More arguments than format specifies + * may be passed without error; it is up to the caller to deal with trailing + * unconverted arguments. + */ +extern JS_PUBLIC_API(JSBool) +JS_ConvertArguments(JSContext *cx, uintN argc, jsval *argv, const char *format, + ...); + +#ifdef va_start +extern JS_PUBLIC_API(JSBool) +JS_ConvertArgumentsVA(JSContext *cx, uintN argc, jsval *argv, + const char *format, va_list ap); +#endif + +/* + * Inverse of JS_ConvertArguments: scan format and convert trailing arguments + * into jsvals, GC-rooted if necessary by the JS stack. Return null on error, + * and a pointer to the new argument vector on success. Also return a stack + * mark on success via *markp, in which case the caller must eventually clean + * up by calling JS_PopArguments. + * + * Note that the number of actual arguments supplied is specified exclusively + * by format, so there is no argc parameter. + */ +extern JS_PUBLIC_API(jsval *) +JS_PushArguments(JSContext *cx, void **markp, const char *format, ...); + +#ifdef va_start +extern JS_PUBLIC_API(jsval *) +JS_PushArgumentsVA(JSContext *cx, void **markp, const char *format, va_list ap); +#endif + +extern JS_PUBLIC_API(void) +JS_PopArguments(JSContext *cx, void *mark); + +#ifdef JS_ARGUMENT_FORMATTER_DEFINED + +/* + * Add and remove a format string handler for JS_{Convert,Push}Arguments{,VA}. + * The handler function has this signature (see jspubtd.h): + * + * JSBool MyArgumentFormatter(JSContext *cx, const char *format, + * JSBool fromJS, jsval **vpp, va_list *app); + * + * It should return true on success, and return false after reporting an error + * or detecting an already-reported error. + * + * For a given format string, for example "AA", the formatter is called from + * JS_ConvertArgumentsVA like so: + * + * formatter(cx, "AA...", JS_TRUE, &sp, &ap); + * + * sp points into the arguments array on the JS stack, while ap points into + * the stdarg.h va_list on the C stack. The JS_TRUE passed for fromJS tells + * the formatter to convert zero or more jsvals at sp to zero or more C values + * accessed via pointers-to-values at ap, updating both sp (via *vpp) and ap + * (via *app) to point past the converted arguments and their result pointers + * on the C stack. + * + * When called from JS_PushArgumentsVA, the formatter is invoked thus: + * + * formatter(cx, "AA...", JS_FALSE, &sp, &ap); + * + * where JS_FALSE for fromJS means to wrap the C values at ap according to the + * format specifier and store them at sp, updating ap and sp appropriately. + * + * The "..." after "AA" is the rest of the format string that was passed into + * JS_{Convert,Push}Arguments{,VA}. The actual format trailing substring used + * in each Convert or PushArguments call is passed to the formatter, so that + * one such function may implement several formats, in order to share code. + * + * Remove just forgets about any handler associated with format. Add does not + * copy format, it points at the string storage allocated by the caller, which + * is typically a string constant. If format is in dynamic storage, it is up + * to the caller to keep the string alive until Remove is called. + */ +extern JS_PUBLIC_API(JSBool) +JS_AddArgumentFormatter(JSContext *cx, const char *format, + JSArgumentFormatter formatter); + +extern JS_PUBLIC_API(void) +JS_RemoveArgumentFormatter(JSContext *cx, const char *format); + +#endif /* JS_ARGUMENT_FORMATTER_DEFINED */ + +extern JS_PUBLIC_API(JSBool) +JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_ValueToObject(JSContext *cx, jsval v, JSObject **objp); + +extern JS_PUBLIC_API(JSFunction *) +JS_ValueToFunction(JSContext *cx, jsval v); + +extern JS_PUBLIC_API(JSFunction *) +JS_ValueToConstructor(JSContext *cx, jsval v); + +extern JS_PUBLIC_API(JSString *) +JS_ValueToString(JSContext *cx, jsval v); + +extern JS_PUBLIC_API(JSString *) +JS_ValueToSource(JSContext *cx, jsval v); + +extern JS_PUBLIC_API(JSBool) +JS_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp); + +/* + * Convert a value to a number, then to an int32, according to the ECMA rules + * for ToInt32. + */ +extern JS_PUBLIC_API(JSBool) +JS_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip); + +/* + * Convert a value to a number, then to a uint32, according to the ECMA rules + * for ToUint32. + */ +extern JS_PUBLIC_API(JSBool) +JS_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip); + +/* + * Convert a value to a number, then to an int32 if it fits by rounding to + * nearest; but failing with an error report if the double is out of range + * or unordered. + */ +extern JS_PUBLIC_API(JSBool) +JS_ValueToInt32(JSContext *cx, jsval v, int32 *ip); + +/* + * ECMA ToUint16, for mapping a jsval to a Unicode point. + */ +extern JS_PUBLIC_API(JSBool) +JS_ValueToUint16(JSContext *cx, jsval v, uint16 *ip); + +extern JS_PUBLIC_API(JSBool) +JS_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp); + +extern JS_PUBLIC_API(JSType) +JS_TypeOfValue(JSContext *cx, jsval v); + +extern JS_PUBLIC_API(const char *) +JS_GetTypeName(JSContext *cx, JSType type); + +extern JS_PUBLIC_API(JSBool) +JS_StrictlyEqual(JSContext *cx, jsval v1, jsval v2); + +extern JS_PUBLIC_API(JSBool) +JS_SameValue(JSContext *cx, jsval v1, jsval v2); + +/************************************************************************/ + +/* + * Initialization, locking, contexts, and memory allocation. + * + * It is important that the first runtime and first context be created in a + * single-threaded fashion, otherwise the behavior of the library is undefined. + * See: http://developer.mozilla.org/en/docs/Category:JSAPI_Reference + */ +#define JS_NewRuntime JS_Init +#define JS_DestroyRuntime JS_Finish +#define JS_LockRuntime JS_Lock +#define JS_UnlockRuntime JS_Unlock + +extern JS_PUBLIC_API(JSRuntime *) +JS_NewRuntime(uint32 maxbytes); + +extern JS_PUBLIC_API(void) +JS_CommenceRuntimeShutDown(JSRuntime *rt); + +extern JS_PUBLIC_API(void) +JS_DestroyRuntime(JSRuntime *rt); + +extern JS_PUBLIC_API(void) +JS_ShutDown(void); + +JS_PUBLIC_API(void *) +JS_GetRuntimePrivate(JSRuntime *rt); + +JS_PUBLIC_API(void) +JS_SetRuntimePrivate(JSRuntime *rt, void *data); + +extern JS_PUBLIC_API(void) +JS_BeginRequest(JSContext *cx); + +extern JS_PUBLIC_API(void) +JS_EndRequest(JSContext *cx); + +/* Yield to pending GC operations, regardless of request depth */ +extern JS_PUBLIC_API(void) +JS_YieldRequest(JSContext *cx); + +extern JS_PUBLIC_API(jsrefcount) +JS_SuspendRequest(JSContext *cx); + +extern JS_PUBLIC_API(void) +JS_ResumeRequest(JSContext *cx, jsrefcount saveDepth); + +#ifdef __cplusplus +JS_END_EXTERN_C + +class JSAutoRequest { + public: + JSAutoRequest(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM) + : mContext(cx), mSaveDepth(0) { + JS_GUARD_OBJECT_NOTIFIER_INIT; + JS_BeginRequest(mContext); + } + ~JSAutoRequest() { + JS_EndRequest(mContext); + } + + void suspend() { + mSaveDepth = JS_SuspendRequest(mContext); + } + void resume() { + JS_ResumeRequest(mContext, mSaveDepth); + } + + protected: + JSContext *mContext; + jsrefcount mSaveDepth; + JS_DECL_USE_GUARD_OBJECT_NOTIFIER + +#if 0 + private: + static void *operator new(size_t) CPP_THROW_NEW { return 0; }; + static void operator delete(void *, size_t) { }; +#endif +}; + +class JSAutoSuspendRequest { + public: + JSAutoSuspendRequest(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM) + : mContext(cx), mSaveDepth(0) { + JS_GUARD_OBJECT_NOTIFIER_INIT; + if (mContext) { + mSaveDepth = JS_SuspendRequest(mContext); + } + } + ~JSAutoSuspendRequest() { + resume(); + } + + void resume() { + if (mContext) { + JS_ResumeRequest(mContext, mSaveDepth); + mContext = 0; + } + } + + protected: + JSContext *mContext; + jsrefcount mSaveDepth; + JS_DECL_USE_GUARD_OBJECT_NOTIFIER + +#if 0 + private: + static void *operator new(size_t) CPP_THROW_NEW { return 0; }; + static void operator delete(void *, size_t) { }; +#endif +}; + +JS_BEGIN_EXTERN_C +#endif + +extern JS_PUBLIC_API(void) +JS_Lock(JSRuntime *rt); + +extern JS_PUBLIC_API(void) +JS_Unlock(JSRuntime *rt); + +extern JS_PUBLIC_API(JSContextCallback) +JS_SetContextCallback(JSRuntime *rt, JSContextCallback cxCallback); + +extern JS_PUBLIC_API(JSContext *) +JS_NewContext(JSRuntime *rt, size_t stackChunkSize); + +extern JS_PUBLIC_API(void) +JS_DestroyContext(JSContext *cx); + +extern JS_PUBLIC_API(void) +JS_DestroyContextNoGC(JSContext *cx); + +extern JS_PUBLIC_API(void) +JS_DestroyContextMaybeGC(JSContext *cx); + +extern JS_PUBLIC_API(void *) +JS_GetContextPrivate(JSContext *cx); + +extern JS_PUBLIC_API(void) +JS_SetContextPrivate(JSContext *cx, void *data); + +extern JS_PUBLIC_API(JSRuntime *) +JS_GetRuntime(JSContext *cx); + +extern JS_PUBLIC_API(JSContext *) +JS_ContextIterator(JSRuntime *rt, JSContext **iterp); + +extern JS_PUBLIC_API(JSVersion) +JS_GetVersion(JSContext *cx); + +extern JS_PUBLIC_API(JSVersion) +JS_SetVersion(JSContext *cx, JSVersion version); + +extern JS_PUBLIC_API(const char *) +JS_VersionToString(JSVersion version); + +extern JS_PUBLIC_API(JSVersion) +JS_StringToVersion(const char *string); + +/* + * JS options are orthogonal to version, and may be freely composed with one + * another as well as with version. + * + * JSOPTION_VAROBJFIX is recommended -- see the comments associated with the + * prototypes for JS_ExecuteScript, JS_EvaluateScript, etc. + */ +#define JSOPTION_STRICT JS_BIT(0) /* warn on dubious practice */ +#define JSOPTION_WERROR JS_BIT(1) /* convert warning to error */ +#define JSOPTION_VAROBJFIX JS_BIT(2) /* make JS_EvaluateScript use + the last object on its 'obj' + param's scope chain as the + ECMA 'variables object' */ +#define JSOPTION_PRIVATE_IS_NSISUPPORTS \ + JS_BIT(3) /* context private data points + to an nsISupports subclass */ +#define JSOPTION_COMPILE_N_GO JS_BIT(4) /* caller of JS_Compile*Script + promises to execute compiled + script once only; enables + compile-time scope chain + resolution of consts. */ +#define JSOPTION_ATLINE JS_BIT(5) /* //@line number ["filename"] + option supported for the + XUL preprocessor and kindred + beasts. */ +#define JSOPTION_XML JS_BIT(6) /* EMCAScript for XML support: + parse as a token, + not backward compatible with + the comment-hiding hack used + in HTML script tags. */ +#define JSOPTION_DONT_REPORT_UNCAUGHT \ + JS_BIT(8) /* When returning from the + outermost API call, prevent + uncaught exceptions from + being converted to error + reports */ + +#define JSOPTION_RELIMIT JS_BIT(9) /* Throw exception on any + regular expression which + backtracks more than n^3 + times, where n is length + of the input string */ +#define JSOPTION_ANONFUNFIX JS_BIT(10) /* Disallow function () {} in + statement context per + ECMA-262 Edition 3. */ + +#define JSOPTION_JIT JS_BIT(11) /* Enable JIT compilation. */ + +#define JSOPTION_NO_SCRIPT_RVAL JS_BIT(12) /* A promise to the compiler + that a null rval out-param + will be passed to each call + to JS_ExecuteScript. */ +#define JSOPTION_UNROOTED_GLOBAL JS_BIT(13) /* The GC will not root the + contexts' global objects + (see JS_GetGlobalObject), + leaving that up to the + embedding. */ + +extern JS_PUBLIC_API(uint32) +JS_GetOptions(JSContext *cx); + +extern JS_PUBLIC_API(uint32) +JS_SetOptions(JSContext *cx, uint32 options); + +extern JS_PUBLIC_API(uint32) +JS_ToggleOptions(JSContext *cx, uint32 options); + +extern JS_PUBLIC_API(const char *) +JS_GetImplementationVersion(void); + +extern JS_PUBLIC_API(JSObject *) +JS_GetGlobalObject(JSContext *cx); + +extern JS_PUBLIC_API(void) +JS_SetGlobalObject(JSContext *cx, JSObject *obj); + +/* + * Initialize standard JS class constructors, prototypes, and any top-level + * functions and constants associated with the standard classes (e.g. isNaN + * for Number). + * + * NB: This sets cx's global object to obj if it was null. + */ +extern JS_PUBLIC_API(JSBool) +JS_InitStandardClasses(JSContext *cx, JSObject *obj); + +/* + * Resolve id, which must contain either a string or an int, to a standard + * class name in obj if possible, defining the class's constructor and/or + * prototype and storing true in *resolved. If id does not name a standard + * class or a top-level property induced by initializing a standard class, + * store false in *resolved and just return true. Return false on error, + * as usual for JSBool result-typed API entry points. + * + * This API can be called directly from a global object class's resolve op, + * to define standard classes lazily. The class's enumerate op should call + * JS_EnumerateStandardClasses(cx, obj), to define eagerly during for..in + * loops any classes not yet resolved lazily. + */ +extern JS_PUBLIC_API(JSBool) +JS_ResolveStandardClass(JSContext *cx, JSObject *obj, jsval id, + JSBool *resolved); + +extern JS_PUBLIC_API(JSBool) +JS_EnumerateStandardClasses(JSContext *cx, JSObject *obj); + +/* + * Enumerate any already-resolved standard class ids into ida, or into a new + * JSIdArray if ida is null. Return the augmented array on success, null on + * failure with ida (if it was non-null on entry) destroyed. + */ +extern JS_PUBLIC_API(JSIdArray *) +JS_EnumerateResolvedStandardClasses(JSContext *cx, JSObject *obj, + JSIdArray *ida); + +extern JS_PUBLIC_API(JSBool) +JS_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, + JSObject **objp); + +extern JS_PUBLIC_API(JSObject *) +JS_GetScopeChain(JSContext *cx); + +extern JS_PUBLIC_API(JSObject *) +JS_GetGlobalForObject(JSContext *cx, JSObject *obj); + +/* + * Macros to hide interpreter stack layout details from a JSFastNative using + * its jsval *vp parameter. The stack layout underlying invocation can't change + * without breaking source and binary compatibility (argv[-2] is well-known to + * be the callee jsval, and argv[-1] is as well known to be |this|). + * + * Note well: However, argv[-1] may be JSVAL_NULL where with slow natives it + * is the global object, so embeddings implementing fast natives *must* call + * JS_THIS or JS_THIS_OBJECT and test for failure indicated by a null return, + * which should propagate as a false return from native functions and hooks. + * + * To reduce boilerplace checks, JS_InstanceOf and JS_GetInstancePrivate now + * handle a null obj parameter by returning false (throwing a TypeError if + * given non-null argv), so most native functions that type-check their |this| + * parameter need not add null checking. + * + * NB: there is an anti-dependency between JS_CALLEE and JS_SET_RVAL: native + * methods that may inspect their callee must defer setting their return value + * until after any such possible inspection. Otherwise the return value will be + * inspected instead of the callee function object. + * + * WARNING: These are not (yet) mandatory macros, but new code outside of the + * engine should use them. In the Mozilla 2.0 milestone their definitions may + * change incompatibly. + */ +#define JS_CALLEE(cx,vp) ((vp)[0]) +#define JS_ARGV_CALLEE(argv) ((argv)[-2]) +#define JS_THIS(cx,vp) JS_ComputeThis(cx, vp) +#define JS_THIS_OBJECT(cx,vp) ((JSObject *) JS_THIS(cx,vp)) +#define JS_ARGV(cx,vp) ((vp) + 2) +#define JS_RVAL(cx,vp) (*(vp)) +#define JS_SET_RVAL(cx,vp,v) (*(vp) = (v)) + +extern JS_PUBLIC_API(jsval) +JS_ComputeThis(JSContext *cx, jsval *vp); + +extern JS_PUBLIC_API(void *) +JS_malloc(JSContext *cx, size_t nbytes); + +extern JS_PUBLIC_API(void *) +JS_realloc(JSContext *cx, void *p, size_t nbytes); + +extern JS_PUBLIC_API(void) +JS_free(JSContext *cx, void *p); + +extern JS_PUBLIC_API(void) +JS_updateMallocCounter(JSContext *cx, size_t nbytes); + +extern JS_PUBLIC_API(char *) +JS_strdup(JSContext *cx, const char *s); + +extern JS_PUBLIC_API(jsdouble *) +JS_NewDouble(JSContext *cx, jsdouble d); + +extern JS_PUBLIC_API(JSBool) +JS_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval); + +extern JS_PUBLIC_API(JSBool) +JS_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval); + +/* + * A JS GC root is a pointer to a JSObject *, JSString *, or jsdouble * that + * itself points into the GC heap (more recently, we support this extension: + * a root may be a pointer to a jsval v for which JSVAL_IS_GCTHING(v) is true). + * + * Therefore, you never pass JSObject *obj to JS_AddRoot(cx, obj). You always + * call JS_AddRoot(cx, &obj), passing obj by reference. And later, before obj + * or the structure it is embedded within goes out of scope or is freed, you + * must call JS_RemoveRoot(cx, &obj). + * + * Also, use JS_AddNamedRoot(cx, &structPtr->memberObj, "structPtr->memberObj") + * in preference to JS_AddRoot(cx, &structPtr->memberObj), in order to identify + * roots by their source callsites. This way, you can find the callsite while + * debugging if you should fail to do JS_RemoveRoot(cx, &structPtr->memberObj) + * before freeing structPtr's memory. + */ +extern JS_PUBLIC_API(JSBool) +JS_AddRoot(JSContext *cx, void *rp); + +#ifdef NAME_ALL_GC_ROOTS +#define JS_DEFINE_TO_TOKEN(def) #def +#define JS_DEFINE_TO_STRING(def) JS_DEFINE_TO_TOKEN(def) +#define JS_AddRoot(cx,rp) JS_AddNamedRoot((cx), (rp), (__FILE__ ":" JS_TOKEN_TO_STRING(__LINE__)) +#endif + +extern JS_PUBLIC_API(JSBool) +JS_AddNamedRoot(JSContext *cx, void *rp, const char *name); + +extern JS_PUBLIC_API(JSBool) +JS_AddNamedRootRT(JSRuntime *rt, void *rp, const char *name); + +extern JS_PUBLIC_API(JSBool) +JS_RemoveRoot(JSContext *cx, void *rp); + +extern JS_PUBLIC_API(JSBool) +JS_RemoveRootRT(JSRuntime *rt, void *rp); + +/* + * The last GC thing of each type (object, string, double, external string + * types) created on a given context is kept alive until another thing of the + * same type is created, using a newborn root in the context. These newborn + * roots help native code protect newly-created GC-things from GC invocations + * activated before those things can be rooted using local or global roots. + * + * However, the newborn roots can also entrain great gobs of garbage, so the + * JS_GC entry point clears them for the context on which GC is being forced. + * Embeddings may need to do likewise for all contexts. + * + * See the scoped local root API immediately below for a better way to manage + * newborns in cases where native hooks (functions, getters, setters, etc.) + * create many GC-things, potentially without connecting them to predefined + * local roots such as *rval or argv[i] in an active native function. Using + * JS_EnterLocalRootScope disables updating of the context's per-gc-thing-type + * newborn roots, until control flow unwinds and leaves the outermost nesting + * local root scope. + */ +extern JS_PUBLIC_API(void) +JS_ClearNewbornRoots(JSContext *cx); + +/* + * Scoped local root management allows native functions, getter/setters, etc. + * to avoid worrying about the newborn root pigeon-holes, overloading local + * roots allocated in argv and *rval, or ending up having to call JS_Add*Root + * and JS_RemoveRoot to manage global roots temporarily. + * + * Instead, calling JS_EnterLocalRootScope and JS_LeaveLocalRootScope around + * the body of the native hook causes the engine to allocate a local root for + * each newborn created in between the two API calls, using a local root stack + * associated with cx. For example: + * + * JSBool + * my_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) + * { + * JSBool ok; + * + * if (!JS_EnterLocalRootScope(cx)) + * return JS_FALSE; + * ok = my_GetPropertyBody(cx, obj, id, vp); + * JS_LeaveLocalRootScope(cx); + * return ok; + * } + * + * NB: JS_LeaveLocalRootScope must be called once for every prior successful + * call to JS_EnterLocalRootScope. If JS_EnterLocalRootScope fails, you must + * not make the matching JS_LeaveLocalRootScope call. + * + * JS_LeaveLocalRootScopeWithResult(cx, rval) is an alternative way to leave + * a local root scope that protects a result or return value, by effectively + * pushing it in the caller's local root scope. + * + * In case a native hook allocates many objects or other GC-things, but the + * native protects some of those GC-things by storing them as property values + * in an object that is itself protected, the hook can call JS_ForgetLocalRoot + * to free the local root automatically pushed for the now-protected GC-thing. + * + * JS_ForgetLocalRoot works on any GC-thing allocated in the current local + * root scope, but it's more time-efficient when called on references to more + * recently created GC-things. Calling it successively on other than the most + * recently allocated GC-thing will tend to average the time inefficiency, and + * may risk O(n^2) growth rate, but in any event, you shouldn't allocate too + * many local roots if you can root as you go (build a tree of objects from + * the top down, forgetting each latest-allocated GC-thing immediately upon + * linking it to its parent). + */ +extern JS_PUBLIC_API(JSBool) +JS_EnterLocalRootScope(JSContext *cx); + +extern JS_PUBLIC_API(void) +JS_LeaveLocalRootScope(JSContext *cx); + +extern JS_PUBLIC_API(void) +JS_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval); + +extern JS_PUBLIC_API(void) +JS_ForgetLocalRoot(JSContext *cx, void *thing); + +#ifdef __cplusplus +JS_END_EXTERN_C + +class JSAutoLocalRootScope { + public: + JSAutoLocalRootScope(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM) + : mContext(cx) { + JS_GUARD_OBJECT_NOTIFIER_INIT; + JS_EnterLocalRootScope(mContext); + } + ~JSAutoLocalRootScope() { + JS_LeaveLocalRootScope(mContext); + } + + void forget(void *thing) { + JS_ForgetLocalRoot(mContext, thing); + } + + protected: + JSContext *mContext; + JS_DECL_USE_GUARD_OBJECT_NOTIFIER + +#if 0 + private: + static void *operator new(size_t) CPP_THROW_NEW { return 0; }; + static void operator delete(void *, size_t) { }; +#endif +}; + +JS_BEGIN_EXTERN_C +#endif + +#ifdef DEBUG +extern JS_PUBLIC_API(void) +JS_DumpNamedRoots(JSRuntime *rt, + void (*dump)(const char *name, void *rp, void *data), + void *data); +#endif + +/* + * Call JS_MapGCRoots to map the GC's roots table using map(rp, name, data). + * The root is pointed at by rp; if the root is unnamed, name is null; data is + * supplied from the third parameter to JS_MapGCRoots. + * + * The map function should return JS_MAP_GCROOT_REMOVE to cause the currently + * enumerated root to be removed. To stop enumeration, set JS_MAP_GCROOT_STOP + * in the return value. To keep on mapping, return JS_MAP_GCROOT_NEXT. These + * constants are flags; you can OR them together. + * + * This function acquires and releases rt's GC lock around the mapping of the + * roots table, so the map function should run to completion in as few cycles + * as possible. Of course, map cannot call JS_GC, JS_MaybeGC, JS_BeginRequest, + * or any JS API entry point that acquires locks, without double-tripping or + * deadlocking on the GC lock. + * + * JS_MapGCRoots returns the count of roots that were successfully mapped. + */ +#define JS_MAP_GCROOT_NEXT 0 /* continue mapping entries */ +#define JS_MAP_GCROOT_STOP 1 /* stop mapping entries */ +#define JS_MAP_GCROOT_REMOVE 2 /* remove and free the current entry */ + +typedef intN +(* JSGCRootMapFun)(void *rp, const char *name, void *data); + +extern JS_PUBLIC_API(uint32) +JS_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data); + +extern JS_PUBLIC_API(JSBool) +JS_LockGCThing(JSContext *cx, void *thing); + +extern JS_PUBLIC_API(JSBool) +JS_LockGCThingRT(JSRuntime *rt, void *thing); + +extern JS_PUBLIC_API(JSBool) +JS_UnlockGCThing(JSContext *cx, void *thing); + +extern JS_PUBLIC_API(JSBool) +JS_UnlockGCThingRT(JSRuntime *rt, void *thing); + +/* + * Register externally maintained GC roots. + * + * traceOp: the trace operation. For each root the implementation should call + * JS_CallTracer whenever the root contains a traceable thing. + * data: the data argument to pass to each invocation of traceOp. + */ +extern JS_PUBLIC_API(void) +JS_SetExtraGCRoots(JSRuntime *rt, JSTraceDataOp traceOp, void *data); + +/* + * For implementors of JSMarkOp. All new code should implement JSTraceOp + * instead. + */ +extern JS_PUBLIC_API(void) +JS_MarkGCThing(JSContext *cx, void *thing, const char *name, void *arg); + +/* + * JS_CallTracer API and related macros for implementors of JSTraceOp, to + * enumerate all references to traceable things reachable via a property or + * other strong ref identified for debugging purposes by name or index or + * a naming callback. + * + * By definition references to traceable things include non-null pointers + * to JSObject, JSString and jsdouble and corresponding jsvals. + * + * See the JSTraceOp typedef in jspubtd.h. + */ + +/* Trace kinds to pass to JS_Tracing. */ +#define JSTRACE_OBJECT 0 +#define JSTRACE_DOUBLE 1 +#define JSTRACE_STRING 2 + +/* + * Use the following macros to check if a particular jsval is a traceable + * thing and to extract the thing and its kind to pass to JS_CallTracer. + */ +#define JSVAL_IS_TRACEABLE(v) (JSVAL_IS_GCTHING(v) && !JSVAL_IS_NULL(v)) +#define JSVAL_TO_TRACEABLE(v) (JSVAL_TO_GCTHING(v)) +#define JSVAL_TRACE_KIND(v) (JSVAL_TAG(v) >> 1) + +struct JSTracer { + JSContext *context; + JSTraceCallback callback; + JSTraceNamePrinter debugPrinter; + const void *debugPrintArg; + size_t debugPrintIndex; +}; + +/* + * The method to call on each reference to a traceable thing stored in a + * particular JSObject or other runtime structure. With DEBUG defined the + * caller before calling JS_CallTracer must initialize JSTracer fields + * describing the reference using the macros below. + */ +extern JS_PUBLIC_API(void) +JS_CallTracer(JSTracer *trc, void *thing, uint32 kind); + +/* + * Set debugging information about a reference to a traceable thing to prepare + * for the following call to JS_CallTracer. + * + * When printer is null, arg must be const char * or char * C string naming + * the reference and index must be either (size_t)-1 indicating that the name + * alone describes the reference or it must be an index into some array vector + * that stores the reference. + * + * When printer callback is not null, the arg and index arguments are + * available to the callback as debugPrinterArg and debugPrintIndex fields + * of JSTracer. + * + * The storage for name or callback's arguments needs to live only until + * the following call to JS_CallTracer returns. + */ +#ifdef DEBUG +# define JS_SET_TRACING_DETAILS(trc, printer, arg, index) \ + JS_BEGIN_MACRO \ + (trc)->debugPrinter = (printer); \ + (trc)->debugPrintArg = (arg); \ + (trc)->debugPrintIndex = (index); \ + JS_END_MACRO +#else +# define JS_SET_TRACING_DETAILS(trc, printer, arg, index) \ + JS_BEGIN_MACRO \ + JS_END_MACRO +#endif + +/* + * Convenience macro to describe the argument of JS_CallTracer using C string + * and index. + */ +# define JS_SET_TRACING_INDEX(trc, name, index) \ + JS_SET_TRACING_DETAILS(trc, NULL, name, index) + +/* + * Convenience macro to describe the argument of JS_CallTracer using C string. + */ +# define JS_SET_TRACING_NAME(trc, name) \ + JS_SET_TRACING_DETAILS(trc, NULL, name, (size_t)-1) + +/* + * Convenience macro to invoke JS_CallTracer using C string as the name for + * the reference to a traceable thing. + */ +# define JS_CALL_TRACER(trc, thing, kind, name) \ + JS_BEGIN_MACRO \ + JS_SET_TRACING_NAME(trc, name); \ + JS_CallTracer((trc), (thing), (kind)); \ + JS_END_MACRO + +/* + * Convenience macros to invoke JS_CallTracer when jsval represents a + * reference to a traceable thing. + */ +#define JS_CALL_VALUE_TRACER(trc, val, name) \ + JS_BEGIN_MACRO \ + if (JSVAL_IS_TRACEABLE(val)) { \ + JS_CALL_TRACER((trc), JSVAL_TO_GCTHING(val), \ + JSVAL_TRACE_KIND(val), name); \ + } \ + JS_END_MACRO + +#define JS_CALL_OBJECT_TRACER(trc, object, name) \ + JS_BEGIN_MACRO \ + JSObject *obj_ = (object); \ + JS_ASSERT(obj_); \ + JS_CALL_TRACER((trc), obj_, JSTRACE_OBJECT, name); \ + JS_END_MACRO + +#define JS_CALL_STRING_TRACER(trc, string, name) \ + JS_BEGIN_MACRO \ + JSString *str_ = (string); \ + JS_ASSERT(str_); \ + JS_CALL_TRACER((trc), str_, JSTRACE_STRING, name); \ + JS_END_MACRO + +#define JS_CALL_DOUBLE_TRACER(trc, number, name) \ + JS_BEGIN_MACRO \ + jsdouble *num_ = (number); \ + JS_ASSERT(num_); \ + JS_CALL_TRACER((trc), num_, JSTRACE_DOUBLE, name); \ + JS_END_MACRO + +/* + * API for JSTraceCallback implementations. + */ +# define JS_TRACER_INIT(trc, cx_, callback_) \ + JS_BEGIN_MACRO \ + (trc)->context = (cx_); \ + (trc)->callback = (callback_); \ + (trc)->debugPrinter = NULL; \ + (trc)->debugPrintArg = NULL; \ + (trc)->debugPrintIndex = (size_t)-1; \ + JS_END_MACRO + +extern JS_PUBLIC_API(void) +JS_TraceChildren(JSTracer *trc, void *thing, uint32 kind); + +extern JS_PUBLIC_API(void) +JS_TraceRuntime(JSTracer *trc); + +#ifdef DEBUG + +extern JS_PUBLIC_API(void) +JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, + void *thing, uint32 kind, JSBool includeDetails); + +/* + * DEBUG-only method to dump the object graph of heap-allocated things. + * + * fp: file for the dump output. + * start: when non-null, dump only things reachable from start + * thing. Otherwise dump all things reachable from the + * runtime roots. + * startKind: trace kind of start if start is not null. Must be 0 when + * start is null. + * thingToFind: dump only paths in the object graph leading to thingToFind + * when non-null. + * maxDepth: the upper bound on the number of edges to descend from the + * graph roots. + * thingToIgnore: thing to ignore during the graph traversal when non-null. + */ +extern JS_PUBLIC_API(JSBool) +JS_DumpHeap(JSContext *cx, FILE *fp, void* startThing, uint32 startKind, + void *thingToFind, size_t maxDepth, void *thingToIgnore); + +#endif + +/* + * Garbage collector API. + */ +extern JS_PUBLIC_API(void) +JS_GC(JSContext *cx); + +extern JS_PUBLIC_API(void) +JS_MaybeGC(JSContext *cx); + +extern JS_PUBLIC_API(JSGCCallback) +JS_SetGCCallback(JSContext *cx, JSGCCallback cb); + +extern JS_PUBLIC_API(JSGCCallback) +JS_SetGCCallbackRT(JSRuntime *rt, JSGCCallback cb); + +extern JS_PUBLIC_API(JSBool) +JS_IsGCMarkingTracer(JSTracer *trc); + +extern JS_PUBLIC_API(JSBool) +JS_IsAboutToBeFinalized(JSContext *cx, void *thing); + +typedef enum JSGCParamKey { + /* Maximum nominal heap before last ditch GC. */ + JSGC_MAX_BYTES = 0, + + /* Number of JS_malloc bytes before last ditch GC. */ + JSGC_MAX_MALLOC_BYTES = 1, + + /* Hoard stackPools for this long, in ms, default is 30 seconds. */ + JSGC_STACKPOOL_LIFESPAN = 2, + + /* + * The factor that defines when the GC is invoked. The factor is a + * percent of the memory allocated by the GC after the last run of + * the GC. When the current memory allocated by the GC is more than + * this percent then the GC is invoked. The factor cannot be less + * than 100 since the current memory allocated by the GC cannot be less + * than the memory allocated after the last run of the GC. + */ + JSGC_TRIGGER_FACTOR = 3, + + /* Amount of bytes allocated by the GC. */ + JSGC_BYTES = 4, + + /* Number of times when GC was invoked. */ + JSGC_NUMBER = 5, + + /* Max size of the code cache in bytes. */ + JSGC_MAX_CODE_CACHE_BYTES = 6 +} JSGCParamKey; + +extern JS_PUBLIC_API(void) +JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32 value); + +extern JS_PUBLIC_API(uint32) +JS_GetGCParameter(JSRuntime *rt, JSGCParamKey key); + +extern JS_PUBLIC_API(void) +JS_SetGCParameterForThread(JSContext *cx, JSGCParamKey key, uint32 value); + +extern JS_PUBLIC_API(uint32) +JS_GetGCParameterForThread(JSContext *cx, JSGCParamKey key); + +/* + * Add a finalizer for external strings created by JS_NewExternalString (see + * below) using a type-code returned from this function, and that understands + * how to free or release the memory pointed at by JS_GetStringChars(str). + * + * Return a nonnegative type index if there is room for finalizer in the + * global GC finalizers table, else return -1. If the engine is compiled + * JS_THREADSAFE and used in a multi-threaded environment, this function must + * be invoked on the primordial thread only, at startup -- or else the entire + * program must single-thread itself while loading a module that calls this + * function. + */ +extern JS_PUBLIC_API(intN) +JS_AddExternalStringFinalizer(JSStringFinalizeOp finalizer); + +/* + * Remove finalizer from the global GC finalizers table, returning its type + * code if found, -1 if not found. + * + * As with JS_AddExternalStringFinalizer, there is a threading restriction + * if you compile the engine JS_THREADSAFE: this function may be called for a + * given finalizer pointer on only one thread; different threads may call to + * remove distinct finalizers safely. + * + * You must ensure that all strings with finalizer's type have been collected + * before calling this function. Otherwise, string data will be leaked by the + * GC, for want of a finalizer to call. + */ +extern JS_PUBLIC_API(intN) +JS_RemoveExternalStringFinalizer(JSStringFinalizeOp finalizer); + +/* + * Create a new JSString whose chars member refers to external memory, i.e., + * memory requiring special, type-specific finalization. The type code must + * be a nonnegative return value from JS_AddExternalStringFinalizer. + */ +extern JS_PUBLIC_API(JSString *) +JS_NewExternalString(JSContext *cx, jschar *chars, size_t length, intN type); + +/* + * Returns the external-string finalizer index for this string, or -1 if it is + * an "internal" (native to JS engine) string. + */ +extern JS_PUBLIC_API(intN) +JS_GetExternalStringGCType(JSRuntime *rt, JSString *str); + +/* + * Sets maximum (if stack grows upward) or minimum (downward) legal stack byte + * address in limitAddr for the thread or process stack used by cx. To disable + * stack size checking, pass 0 for limitAddr. + */ +extern JS_PUBLIC_API(void) +JS_SetThreadStackLimit(JSContext *cx, jsuword limitAddr); + +/* + * Set the quota on the number of bytes that stack-like data structures can + * use when the runtime compiles and executes scripts. These structures + * consume heap space, so JS_SetThreadStackLimit does not bound their size. + * The default quota is 32MB which is quite generous. + * + * The function must be called before any script compilation or execution API + * calls, i.e. either immediately after JS_NewContext or from JSCONTEXT_NEW + * context callback. + */ +extern JS_PUBLIC_API(void) +JS_SetScriptStackQuota(JSContext *cx, size_t quota); + +#define JS_DEFAULT_SCRIPT_STACK_QUOTA ((size_t) 0x2000000) + +/************************************************************************/ + +/* + * Classes, objects, and properties. + */ + +/* For detailed comments on the function pointer types, see jspubtd.h. */ +struct JSClass { + const char *name; + uint32 flags; + + /* Mandatory non-null function pointer members. */ + JSPropertyOp addProperty; + JSPropertyOp delProperty; + JSPropertyOp getProperty; + JSPropertyOp setProperty; + JSEnumerateOp enumerate; + JSResolveOp resolve; + JSConvertOp convert; + JSFinalizeOp finalize; + + /* Optionally non-null members start here. */ + JSGetObjectOps getObjectOps; + JSCheckAccessOp checkAccess; + JSNative call; + JSNative construct; + JSXDRObjectOp xdrObject; + JSHasInstanceOp hasInstance; + JSMarkOp mark; + JSReserveSlotsOp reserveSlots; +}; + +struct JSExtendedClass { + JSClass base; + JSEqualityOp equality; + JSObjectOp outerObject; + JSObjectOp innerObject; + JSIteratorOp iteratorObject; + JSObjectOp wrappedObject; /* NB: infallible, null + returns are treated as + the original object */ + void (*reserved0)(void); + void (*reserved1)(void); + void (*reserved2)(void); +}; + +#define JSCLASS_HAS_PRIVATE (1<<0) /* objects have private slot */ +#define JSCLASS_NEW_ENUMERATE (1<<1) /* has JSNewEnumerateOp hook */ +#define JSCLASS_NEW_RESOLVE (1<<2) /* has JSNewResolveOp hook */ +#define JSCLASS_PRIVATE_IS_NSISUPPORTS (1<<3) /* private is (nsISupports *) */ +#define JSCLASS_SHARE_ALL_PROPERTIES (1<<4) /* all properties are SHARED */ +#define JSCLASS_NEW_RESOLVE_GETS_START (1<<5) /* JSNewResolveOp gets starting + object in prototype chain + passed in via *objp in/out + parameter */ +#define JSCLASS_CONSTRUCT_PROTOTYPE (1<<6) /* call constructor on class + prototype */ +#define JSCLASS_DOCUMENT_OBSERVER (1<<7) /* DOM document observer */ + +/* + * To reserve slots fetched and stored via JS_Get/SetReservedSlot, bitwise-or + * JSCLASS_HAS_RESERVED_SLOTS(n) into the initializer for JSClass.flags, where + * n is a constant in [1, 255]. Reserved slots are indexed from 0 to n-1. + */ +#define JSCLASS_RESERVED_SLOTS_SHIFT 8 /* room for 8 flags below */ +#define JSCLASS_RESERVED_SLOTS_WIDTH 8 /* and 16 above this field */ +#define JSCLASS_RESERVED_SLOTS_MASK JS_BITMASK(JSCLASS_RESERVED_SLOTS_WIDTH) +#define JSCLASS_HAS_RESERVED_SLOTS(n) (((n) & JSCLASS_RESERVED_SLOTS_MASK) \ + << JSCLASS_RESERVED_SLOTS_SHIFT) +#define JSCLASS_RESERVED_SLOTS(clasp) (((clasp)->flags \ + >> JSCLASS_RESERVED_SLOTS_SHIFT) \ + & JSCLASS_RESERVED_SLOTS_MASK) + +#define JSCLASS_HIGH_FLAGS_SHIFT (JSCLASS_RESERVED_SLOTS_SHIFT + \ + JSCLASS_RESERVED_SLOTS_WIDTH) + +/* True if JSClass is really a JSExtendedClass. */ +#define JSCLASS_IS_EXTENDED (1<<(JSCLASS_HIGH_FLAGS_SHIFT+0)) +#define JSCLASS_IS_ANONYMOUS (1<<(JSCLASS_HIGH_FLAGS_SHIFT+1)) +#define JSCLASS_IS_GLOBAL (1<<(JSCLASS_HIGH_FLAGS_SHIFT+2)) + +/* Indicates that JSClass.mark is a tracer with JSTraceOp type. */ +#define JSCLASS_MARK_IS_TRACE (1<<(JSCLASS_HIGH_FLAGS_SHIFT+3)) + +/* + * ECMA-262 requires that most constructors used internally create objects + * with "the original Foo.prototype value" as their [[Prototype]] (__proto__) + * member initial value. The "original ... value" verbiage is there because + * in ECMA-262, global properties naming class objects are read/write and + * deleteable, for the most part. + * + * Implementing this efficiently requires that global objects have classes + * with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS won't break + * anything except the ECMA-262 "original prototype value" behavior, which was + * broken for years in SpiderMonkey. In other words, without these flags you + * get backward compatibility. + */ +#define JSCLASS_GLOBAL_FLAGS \ + (JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSProto_LIMIT)) + +/* Fast access to the original value of each standard class's prototype. */ +#define JSCLASS_CACHED_PROTO_SHIFT (JSCLASS_HIGH_FLAGS_SHIFT + 8) +#define JSCLASS_CACHED_PROTO_WIDTH 8 +#define JSCLASS_CACHED_PROTO_MASK JS_BITMASK(JSCLASS_CACHED_PROTO_WIDTH) +#define JSCLASS_HAS_CACHED_PROTO(key) ((key) << JSCLASS_CACHED_PROTO_SHIFT) +#define JSCLASS_CACHED_PROTO_KEY(clasp) ((JSProtoKey) \ + (((clasp)->flags \ + >> JSCLASS_CACHED_PROTO_SHIFT) \ + & JSCLASS_CACHED_PROTO_MASK)) + +/* Initializer for unused members of statically initialized JSClass structs. */ +#define JSCLASS_NO_OPTIONAL_MEMBERS 0,0,0,0,0,0,0,0 +#define JSCLASS_NO_RESERVED_MEMBERS 0,0,0 + +struct JSIdArray { + jsint length; + jsid vector[1]; /* actually, length jsid words */ +}; + +extern JS_PUBLIC_API(void) +JS_DestroyIdArray(JSContext *cx, JSIdArray *ida); + +extern JS_PUBLIC_API(JSBool) +JS_ValueToId(JSContext *cx, jsval v, jsid *idp); + +extern JS_PUBLIC_API(JSBool) +JS_IdToValue(JSContext *cx, jsid id, jsval *vp); + +/* + * The magic XML namespace id is int-tagged, but not a valid integer jsval. + * Global object classes in embeddings that enable JS_HAS_XML_SUPPORT (E4X) + * should handle this id specially before converting id via JSVAL_TO_INT. + */ +#define JS_DEFAULT_XML_NAMESPACE_ID ((jsid) JSVAL_VOID) + +/* + * JSNewResolveOp flag bits. + */ +#define JSRESOLVE_QUALIFIED 0x01 /* resolve a qualified property id */ +#define JSRESOLVE_ASSIGNING 0x02 /* resolve on the left of assignment */ +#define JSRESOLVE_DETECTING 0x04 /* 'if (o.p)...' or '(o.p) ?...:...' */ +#define JSRESOLVE_DECLARING 0x08 /* var, const, or function prolog op */ +#define JSRESOLVE_CLASSNAME 0x10 /* class name used when constructing */ +#define JSRESOLVE_WITH 0x20 /* resolve inside a with statement */ + +extern JS_PUBLIC_API(JSBool) +JS_PropertyStub(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_EnumerateStub(JSContext *cx, JSObject *obj); + +extern JS_PUBLIC_API(JSBool) +JS_ResolveStub(JSContext *cx, JSObject *obj, jsval id); + +extern JS_PUBLIC_API(JSBool) +JS_ConvertStub(JSContext *cx, JSObject *obj, JSType type, jsval *vp); + +extern JS_PUBLIC_API(void) +JS_FinalizeStub(JSContext *cx, JSObject *obj); + +struct JSConstDoubleSpec { + jsdouble dval; + const char *name; + uint8 flags; + uint8 spare[3]; +}; + +/* + * To define an array element rather than a named property member, cast the + * element's index to (const char *) and initialize name with it, and set the + * JSPROP_INDEX bit in flags. + */ +struct JSPropertySpec { + const char *name; + int8 tinyid; + uint8 flags; + JSPropertyOp getter; + JSPropertyOp setter; +}; + +struct JSFunctionSpec { + const char *name; + JSNative call; + uint16 nargs; + uint16 flags; + + /* + * extra & 0xFFFF: Number of extra argument slots for local GC roots. + * If fast native, must be zero. + * extra >> 16: Reserved for future use (must be 0). + */ + uint32 extra; +}; + +/* + * Terminating sentinel initializer to put at the end of a JSFunctionSpec array + * that's passed to JS_DefineFunctions or JS_InitClass. + */ +#define JS_FS_END JS_FS(NULL,NULL,0,0,0) + +/* + * Initializer macro for a JSFunctionSpec array element. This is the original + * kind of native function specifier initializer. Use JS_FN ("fast native", see + * JSFastNative in jspubtd.h) for all functions that do not need a stack frame + * when activated. + */ +#define JS_FS(name,call,nargs,flags,extra) \ + {name, call, nargs, flags, extra} + +/* + * "Fast native" initializer macro for a JSFunctionSpec array element. Use this + * in preference to JS_FS if the native in question does not need its own stack + * frame when activated. + */ +#define JS_FN(name,fastcall,nargs,flags) \ + JS_FS(name, (JSNative)(fastcall), nargs, \ + (flags) | JSFUN_FAST_NATIVE | JSFUN_STUB_GSOPS, 0) + +extern JS_PUBLIC_API(JSObject *) +JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, + JSClass *clasp, JSNative constructor, uintN nargs, + JSPropertySpec *ps, JSFunctionSpec *fs, + JSPropertySpec *static_ps, JSFunctionSpec *static_fs); + +#ifdef JS_THREADSAFE +extern JS_PUBLIC_API(JSClass *) +JS_GetClass(JSContext *cx, JSObject *obj); + +#define JS_GET_CLASS(cx,obj) JS_GetClass(cx, obj) +#else +extern JS_PUBLIC_API(JSClass *) +JS_GetClass(JSObject *obj); + +#define JS_GET_CLASS(cx,obj) JS_GetClass(obj) +#endif + +extern JS_PUBLIC_API(JSBool) +JS_InstanceOf(JSContext *cx, JSObject *obj, JSClass *clasp, jsval *argv); + +extern JS_PUBLIC_API(JSBool) +JS_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp); + +extern JS_PUBLIC_API(void *) +JS_GetPrivate(JSContext *cx, JSObject *obj); + +extern JS_PUBLIC_API(JSBool) +JS_SetPrivate(JSContext *cx, JSObject *obj, void *data); + +extern JS_PUBLIC_API(void *) +JS_GetInstancePrivate(JSContext *cx, JSObject *obj, JSClass *clasp, + jsval *argv); + +extern JS_PUBLIC_API(JSObject *) +JS_GetPrototype(JSContext *cx, JSObject *obj); + +extern JS_PUBLIC_API(JSBool) +JS_SetPrototype(JSContext *cx, JSObject *obj, JSObject *proto); + +extern JS_PUBLIC_API(JSObject *) +JS_GetParent(JSContext *cx, JSObject *obj); + +extern JS_PUBLIC_API(JSBool) +JS_SetParent(JSContext *cx, JSObject *obj, JSObject *parent); + +extern JS_PUBLIC_API(JSObject *) +JS_GetConstructor(JSContext *cx, JSObject *proto); + +/* + * Get a unique identifier for obj, good for the lifetime of obj (even if it + * is moved by a copying GC). Return false on failure (likely out of memory), + * and true with *idp containing the unique id on success. + */ +extern JS_PUBLIC_API(JSBool) +JS_GetObjectId(JSContext *cx, JSObject *obj, jsid *idp); + +extern JS_PUBLIC_API(JSObject *) +JS_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent); + +/* + * Unlike JS_NewObject, JS_NewObjectWithGivenProto does not compute a default + * proto if proto's actual parameter value is null. + */ +extern JS_PUBLIC_API(JSObject *) +JS_NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto, + JSObject *parent); + +extern JS_PUBLIC_API(JSBool) +JS_SealObject(JSContext *cx, JSObject *obj, JSBool deep); + +extern JS_PUBLIC_API(JSObject *) +JS_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, + JSObject *parent); + +extern JS_PUBLIC_API(JSObject *) +JS_ConstructObjectWithArguments(JSContext *cx, JSClass *clasp, JSObject *proto, + JSObject *parent, uintN argc, jsval *argv); + +extern JS_PUBLIC_API(JSObject *) +JS_DefineObject(JSContext *cx, JSObject *obj, const char *name, JSClass *clasp, + JSObject *proto, uintN attrs); + +extern JS_PUBLIC_API(JSBool) +JS_DefineConstDoubles(JSContext *cx, JSObject *obj, JSConstDoubleSpec *cds); + +extern JS_PUBLIC_API(JSBool) +JS_DefineProperties(JSContext *cx, JSObject *obj, JSPropertySpec *ps); + +extern JS_PUBLIC_API(JSBool) +JS_DefineProperty(JSContext *cx, JSObject *obj, const char *name, jsval value, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs); + +extern JS_PUBLIC_API(JSBool) +JS_DefinePropertyById(JSContext *cx, JSObject *obj, jsid id, jsval value, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs); + +/* + * Determine the attributes (JSPROP_* flags) of a property on a given object. + * + * If the object does not have a property by that name, *foundp will be + * JS_FALSE and the value of *attrsp is undefined. + */ +extern JS_PUBLIC_API(JSBool) +JS_GetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name, + uintN *attrsp, JSBool *foundp); + +/* + * The same, but if the property is native, return its getter and setter via + * *getterp and *setterp, respectively (and only if the out parameter pointer + * is not null). + */ +extern JS_PUBLIC_API(JSBool) +JS_GetPropertyAttrsGetterAndSetter(JSContext *cx, JSObject *obj, + const char *name, + uintN *attrsp, JSBool *foundp, + JSPropertyOp *getterp, + JSPropertyOp *setterp); + +extern JS_PUBLIC_API(JSBool) +JS_GetPropertyAttrsGetterAndSetterById(JSContext *cx, JSObject *obj, + jsid id, + uintN *attrsp, JSBool *foundp, + JSPropertyOp *getterp, + JSPropertyOp *setterp); + +/* + * Set the attributes of a property on a given object. + * + * If the object does not have a property by that name, *foundp will be + * JS_FALSE and nothing will be altered. + */ +extern JS_PUBLIC_API(JSBool) +JS_SetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name, + uintN attrs, JSBool *foundp); + +extern JS_PUBLIC_API(JSBool) +JS_DefinePropertyWithTinyId(JSContext *cx, JSObject *obj, const char *name, + int8 tinyid, jsval value, + JSPropertyOp getter, JSPropertyOp setter, + uintN attrs); + +extern JS_PUBLIC_API(JSBool) +JS_AliasProperty(JSContext *cx, JSObject *obj, const char *name, + const char *alias); + +extern JS_PUBLIC_API(JSBool) +JS_AlreadyHasOwnProperty(JSContext *cx, JSObject *obj, const char *name, + JSBool *foundp); + +extern JS_PUBLIC_API(JSBool) +JS_AlreadyHasOwnPropertyById(JSContext *cx, JSObject *obj, jsid id, + JSBool *foundp); + +extern JS_PUBLIC_API(JSBool) +JS_HasProperty(JSContext *cx, JSObject *obj, const char *name, JSBool *foundp); + +extern JS_PUBLIC_API(JSBool) +JS_HasPropertyById(JSContext *cx, JSObject *obj, jsid id, JSBool *foundp); + +extern JS_PUBLIC_API(JSBool) +JS_LookupProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_LookupPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, const char *name, + uintN flags, jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_LookupPropertyWithFlagsById(JSContext *cx, JSObject *obj, jsid id, + uintN flags, JSObject **objp, jsval *vp); + +struct JSPropertyDescriptor { + JSObject *obj; + uintN attrs; + JSPropertyOp getter; + JSPropertyOp setter; + jsval value; +}; + +/* + * Like JS_GetPropertyAttrsGetterAndSetterById but will return a property on + * an object on the prototype chain (returned in objp). If data->obj is null, + * then this property was not found on the prototype chain. + */ +extern JS_PUBLIC_API(JSBool) +JS_GetPropertyDescriptorById(JSContext *cx, JSObject *obj, jsid id, uintN flags, + JSPropertyDescriptor *desc); + +extern JS_PUBLIC_API(JSBool) +JS_GetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_GetPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_GetMethodById(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, + jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_GetMethod(JSContext *cx, JSObject *obj, const char *name, JSObject **objp, + jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_SetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_SetPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_DeleteProperty(JSContext *cx, JSObject *obj, const char *name); + +extern JS_PUBLIC_API(JSBool) +JS_DeleteProperty2(JSContext *cx, JSObject *obj, const char *name, + jsval *rval); + +extern JS_PUBLIC_API(JSBool) +JS_DeletePropertyById(JSContext *cx, JSObject *obj, jsid id); + +extern JS_PUBLIC_API(JSBool) +JS_DeletePropertyById2(JSContext *cx, JSObject *obj, jsid id, jsval *rval); + +extern JS_PUBLIC_API(JSBool) +JS_DefineUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, jsval value, + JSPropertyOp getter, JSPropertyOp setter, + uintN attrs); + +/* + * Determine the attributes (JSPROP_* flags) of a property on a given object. + * + * If the object does not have a property by that name, *foundp will be + * JS_FALSE and the value of *attrsp is undefined. + */ +extern JS_PUBLIC_API(JSBool) +JS_GetUCPropertyAttributes(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + uintN *attrsp, JSBool *foundp); + +/* + * The same, but if the property is native, return its getter and setter via + * *getterp and *setterp, respectively (and only if the out parameter pointer + * is not null). + */ +extern JS_PUBLIC_API(JSBool) +JS_GetUCPropertyAttrsGetterAndSetter(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + uintN *attrsp, JSBool *foundp, + JSPropertyOp *getterp, + JSPropertyOp *setterp); + +/* + * Set the attributes of a property on a given object. + * + * If the object does not have a property by that name, *foundp will be + * JS_FALSE and nothing will be altered. + */ +extern JS_PUBLIC_API(JSBool) +JS_SetUCPropertyAttributes(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + uintN attrs, JSBool *foundp); + + +extern JS_PUBLIC_API(JSBool) +JS_DefineUCPropertyWithTinyId(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + int8 tinyid, jsval value, + JSPropertyOp getter, JSPropertyOp setter, + uintN attrs); + +extern JS_PUBLIC_API(JSBool) +JS_AlreadyHasOwnUCProperty(JSContext *cx, JSObject *obj, const jschar *name, + size_t namelen, JSBool *foundp); + +extern JS_PUBLIC_API(JSBool) +JS_HasUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + JSBool *vp); + +extern JS_PUBLIC_API(JSBool) +JS_LookupUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_GetUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_SetUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_DeleteUCProperty2(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + jsval *rval); + +extern JS_PUBLIC_API(JSObject *) +JS_NewArrayObject(JSContext *cx, jsint length, jsval *vector); + +extern JS_PUBLIC_API(JSBool) +JS_IsArrayObject(JSContext *cx, JSObject *obj); + +extern JS_PUBLIC_API(JSBool) +JS_GetArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp); + +extern JS_PUBLIC_API(JSBool) +JS_SetArrayLength(JSContext *cx, JSObject *obj, jsuint length); + +extern JS_PUBLIC_API(JSBool) +JS_HasArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp); + +extern JS_PUBLIC_API(JSBool) +JS_DefineElement(JSContext *cx, JSObject *obj, jsint index, jsval value, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs); + +extern JS_PUBLIC_API(JSBool) +JS_AliasElement(JSContext *cx, JSObject *obj, const char *name, jsint alias); + +extern JS_PUBLIC_API(JSBool) +JS_AlreadyHasOwnElement(JSContext *cx, JSObject *obj, jsint index, + JSBool *foundp); + +extern JS_PUBLIC_API(JSBool) +JS_HasElement(JSContext *cx, JSObject *obj, jsint index, JSBool *foundp); + +extern JS_PUBLIC_API(JSBool) +JS_LookupElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_GetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_SetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_DeleteElement(JSContext *cx, JSObject *obj, jsint index); + +extern JS_PUBLIC_API(JSBool) +JS_DeleteElement2(JSContext *cx, JSObject *obj, jsint index, jsval *rval); + +extern JS_PUBLIC_API(void) +JS_ClearScope(JSContext *cx, JSObject *obj); + +extern JS_PUBLIC_API(JSIdArray *) +JS_Enumerate(JSContext *cx, JSObject *obj); + +/* + * Create an object to iterate over enumerable properties of obj, in arbitrary + * property definition order. NB: This differs from longstanding for..in loop + * order, which uses order of property definition in obj. + */ +extern JS_PUBLIC_API(JSObject *) +JS_NewPropertyIterator(JSContext *cx, JSObject *obj); + +/* + * Return true on success with *idp containing the id of the next enumerable + * property to visit using iterobj, or JSVAL_VOID if there is no such property + * left to visit. Return false on error. + */ +extern JS_PUBLIC_API(JSBool) +JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp); + +extern JS_PUBLIC_API(JSBool) +JS_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, + jsval *vp, uintN *attrsp); + +extern JS_PUBLIC_API(JSBool) +JS_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval v); + +/************************************************************************/ + +/* + * Security protocol. + */ +struct JSPrincipals { + char *codebase; + + /* XXX unspecified and unused by Mozilla code -- can we remove these? */ + void * (* getPrincipalArray)(JSContext *cx, JSPrincipals *); + JSBool (* globalPrivilegesEnabled)(JSContext *cx, JSPrincipals *); + + /* Don't call "destroy"; use reference counting macros below. */ + jsrefcount refcount; + + void (* destroy)(JSContext *cx, JSPrincipals *); + JSBool (* subsume)(JSPrincipals *, JSPrincipals *); +}; + +#ifdef JS_THREADSAFE +#define JSPRINCIPALS_HOLD(cx, principals) JS_HoldPrincipals(cx,principals) +#define JSPRINCIPALS_DROP(cx, principals) JS_DropPrincipals(cx,principals) + +extern JS_PUBLIC_API(jsrefcount) +JS_HoldPrincipals(JSContext *cx, JSPrincipals *principals); + +extern JS_PUBLIC_API(jsrefcount) +JS_DropPrincipals(JSContext *cx, JSPrincipals *principals); + +#else +#define JSPRINCIPALS_HOLD(cx, principals) (++(principals)->refcount) +#define JSPRINCIPALS_DROP(cx, principals) \ + ((--(principals)->refcount == 0) \ + ? ((*(principals)->destroy)((cx), (principals)), 0) \ + : (principals)->refcount) +#endif + + +struct JSSecurityCallbacks { + JSCheckAccessOp checkObjectAccess; + JSPrincipalsTranscoder principalsTranscoder; + JSObjectPrincipalsFinder findObjectPrincipals; +}; + +extern JS_PUBLIC_API(JSSecurityCallbacks *) +JS_SetRuntimeSecurityCallbacks(JSRuntime *rt, JSSecurityCallbacks *callbacks); + +extern JS_PUBLIC_API(JSSecurityCallbacks *) +JS_GetRuntimeSecurityCallbacks(JSRuntime *rt); + +extern JS_PUBLIC_API(JSSecurityCallbacks *) +JS_SetContextSecurityCallbacks(JSContext *cx, JSSecurityCallbacks *callbacks); + +extern JS_PUBLIC_API(JSSecurityCallbacks *) +JS_GetSecurityCallbacks(JSContext *cx); + +/************************************************************************/ + +/* + * Functions and scripts. + */ +extern JS_PUBLIC_API(JSFunction *) +JS_NewFunction(JSContext *cx, JSNative call, uintN nargs, uintN flags, + JSObject *parent, const char *name); + +extern JS_PUBLIC_API(JSObject *) +JS_GetFunctionObject(JSFunction *fun); + +/* + * Deprecated, useful only for diagnostics. Use JS_GetFunctionId instead for + * anonymous vs. "anonymous" disambiguation and Unicode fidelity. + */ +extern JS_PUBLIC_API(const char *) +JS_GetFunctionName(JSFunction *fun); + +/* + * Return the function's identifier as a JSString, or null if fun is unnamed. + * The returned string lives as long as fun, so you don't need to root a saved + * reference to it if fun is well-connected or rooted, and provided you bound + * the use of the saved reference by fun's lifetime. + * + * Prefer JS_GetFunctionId over JS_GetFunctionName because it returns null for + * truly anonymous functions, and because it doesn't chop to ISO-Latin-1 chars + * from UTF-16-ish jschars. + */ +extern JS_PUBLIC_API(JSString *) +JS_GetFunctionId(JSFunction *fun); + +/* + * Return JSFUN_* flags for fun. + */ +extern JS_PUBLIC_API(uintN) +JS_GetFunctionFlags(JSFunction *fun); + +/* + * Return the arity (length) of fun. + */ +extern JS_PUBLIC_API(uint16) +JS_GetFunctionArity(JSFunction *fun); + +/* + * Infallible predicate to test whether obj is a function object (faster than + * comparing obj's class name to "Function", but equivalent unless someone has + * overwritten the "Function" identifier with a different constructor and then + * created instances using that constructor that might be passed in as obj). + */ +extern JS_PUBLIC_API(JSBool) +JS_ObjectIsFunction(JSContext *cx, JSObject *obj); + +extern JS_PUBLIC_API(JSBool) +JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs); + +extern JS_PUBLIC_API(JSFunction *) +JS_DefineFunction(JSContext *cx, JSObject *obj, const char *name, JSNative call, + uintN nargs, uintN attrs); + +extern JS_PUBLIC_API(JSFunction *) +JS_DefineUCFunction(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, JSNative call, + uintN nargs, uintN attrs); + +extern JS_PUBLIC_API(JSObject *) +JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent); + +/* + * Given a buffer, return JS_FALSE if the buffer might become a valid + * javascript statement with the addition of more lines. Otherwise return + * JS_TRUE. The intent is to support interactive compilation - accumulate + * lines in a buffer until JS_BufferIsCompilableUnit is true, then pass it to + * the compiler. + */ +extern JS_PUBLIC_API(JSBool) +JS_BufferIsCompilableUnit(JSContext *cx, JSObject *obj, + const char *bytes, size_t length); + +/* + * The JSScript objects returned by the following functions refer to string and + * other kinds of literals, including doubles and RegExp objects. These + * literals are vulnerable to garbage collection; to root script objects and + * prevent literals from being collected, create a rootable object using + * JS_NewScriptObject, and root the resulting object using JS_Add[Named]Root. + */ +extern JS_PUBLIC_API(JSScript *) +JS_CompileScript(JSContext *cx, JSObject *obj, + const char *bytes, size_t length, + const char *filename, uintN lineno); + +extern JS_PUBLIC_API(JSScript *) +JS_CompileScriptForPrincipals(JSContext *cx, JSObject *obj, + JSPrincipals *principals, + const char *bytes, size_t length, + const char *filename, uintN lineno); + +extern JS_PUBLIC_API(JSScript *) +JS_CompileUCScript(JSContext *cx, JSObject *obj, + const jschar *chars, size_t length, + const char *filename, uintN lineno); + +extern JS_PUBLIC_API(JSScript *) +JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *obj, + JSPrincipals *principals, + const jschar *chars, size_t length, + const char *filename, uintN lineno); + +extern JS_PUBLIC_API(JSScript *) +JS_CompileFile(JSContext *cx, JSObject *obj, const char *filename); + +extern JS_PUBLIC_API(JSScript *) +JS_CompileFileHandle(JSContext *cx, JSObject *obj, const char *filename, + FILE *fh); + +extern JS_PUBLIC_API(JSScript *) +JS_CompileFileHandleForPrincipals(JSContext *cx, JSObject *obj, + const char *filename, FILE *fh, + JSPrincipals *principals); + +/* + * NB: you must use JS_NewScriptObject and root a pointer to its return value + * in order to keep a JSScript and its atoms safe from garbage collection after + * creating the script via JS_Compile* and before a JS_ExecuteScript* call. + * E.g., and without error checks: + * + * JSScript *script = JS_CompileFile(cx, global, filename); + * JSObject *scrobj = JS_NewScriptObject(cx, script); + * JS_AddNamedRoot(cx, &scrobj, "scrobj"); + * do { + * jsval result; + * JS_ExecuteScript(cx, global, script, &result); + * JS_GC(); + * } while (!JSVAL_IS_BOOLEAN(result) || JSVAL_TO_BOOLEAN(result)); + * JS_RemoveRoot(cx, &scrobj); + */ +extern JS_PUBLIC_API(JSObject *) +JS_NewScriptObject(JSContext *cx, JSScript *script); + +/* + * Infallible getter for a script's object. If JS_NewScriptObject has not been + * called on script yet, the return value will be null. + */ +extern JS_PUBLIC_API(JSObject *) +JS_GetScriptObject(JSScript *script); + +extern JS_PUBLIC_API(void) +JS_DestroyScript(JSContext *cx, JSScript *script); + +extern JS_PUBLIC_API(JSFunction *) +JS_CompileFunction(JSContext *cx, JSObject *obj, const char *name, + uintN nargs, const char **argnames, + const char *bytes, size_t length, + const char *filename, uintN lineno); + +extern JS_PUBLIC_API(JSFunction *) +JS_CompileFunctionForPrincipals(JSContext *cx, JSObject *obj, + JSPrincipals *principals, const char *name, + uintN nargs, const char **argnames, + const char *bytes, size_t length, + const char *filename, uintN lineno); + +extern JS_PUBLIC_API(JSFunction *) +JS_CompileUCFunction(JSContext *cx, JSObject *obj, const char *name, + uintN nargs, const char **argnames, + const jschar *chars, size_t length, + const char *filename, uintN lineno); + +extern JS_PUBLIC_API(JSFunction *) +JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj, + JSPrincipals *principals, const char *name, + uintN nargs, const char **argnames, + const jschar *chars, size_t length, + const char *filename, uintN lineno); + +extern JS_PUBLIC_API(JSString *) +JS_DecompileScript(JSContext *cx, JSScript *script, const char *name, + uintN indent); + +/* + * API extension: OR this into indent to avoid pretty-printing the decompiled + * source resulting from JS_DecompileFunction{,Body}. + */ +#define JS_DONT_PRETTY_PRINT ((uintN)0x8000) + +extern JS_PUBLIC_API(JSString *) +JS_DecompileFunction(JSContext *cx, JSFunction *fun, uintN indent); + +extern JS_PUBLIC_API(JSString *) +JS_DecompileFunctionBody(JSContext *cx, JSFunction *fun, uintN indent); + +/* + * NB: JS_ExecuteScript, JS_ExecuteScriptPart, and the JS_Evaluate*Script* + * quadruplets all use the obj parameter as the initial scope chain header, + * the 'this' keyword value, and the variables object (ECMA parlance for where + * 'var' and 'function' bind names) of the execution context for script. + * + * Using obj as the variables object is problematic if obj's parent (which is + * the scope chain link; see JS_SetParent and JS_NewObject) is not null: in + * this case, variables created by 'var x = 0', e.g., go in obj, but variables + * created by assignment to an unbound id, 'x = 0', go in the last object on + * the scope chain linked by parent. + * + * ECMA calls that last scoping object the "global object", but note that many + * embeddings have several such objects. ECMA requires that "global code" be + * executed with the variables object equal to this global object. But these + * JS API entry points provide freedom to execute code against a "sub-global", + * i.e., a parented or scoped object, in which case the variables object will + * differ from the last object on the scope chain, resulting in confusing and + * non-ECMA explicit vs. implicit variable creation. + * + * Caveat embedders: unless you already depend on this buggy variables object + * binding behavior, you should call JS_SetOptions(cx, JSOPTION_VAROBJFIX) or + * JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_VAROBJFIX) -- the latter if + * someone may have set other options on cx already -- for each context in the + * application, if you pass parented objects as the obj parameter, or may ever + * pass such objects in the future. + * + * Why a runtime option? The alternative is to add six or so new API entry + * points with signatures matching the following six, and that doesn't seem + * worth the code bloat cost. Such new entry points would probably have less + * obvious names, too, so would not tend to be used. The JS_SetOption call, + * OTOH, can be more easily hacked into existing code that does not depend on + * the bug; such code can continue to use the familiar JS_EvaluateScript, + * etc., entry points. + */ +extern JS_PUBLIC_API(JSBool) +JS_ExecuteScript(JSContext *cx, JSObject *obj, JSScript *script, jsval *rval); + +/* + * Execute either the function-defining prolog of a script, or the script's + * main body, but not both. + */ +typedef enum JSExecPart { JSEXEC_PROLOG, JSEXEC_MAIN } JSExecPart; + +extern JS_PUBLIC_API(JSBool) +JS_ExecuteScriptPart(JSContext *cx, JSObject *obj, JSScript *script, + JSExecPart part, jsval *rval); + +extern JS_PUBLIC_API(JSBool) +JS_EvaluateScript(JSContext *cx, JSObject *obj, + const char *bytes, uintN length, + const char *filename, uintN lineno, + jsval *rval); + +extern JS_PUBLIC_API(JSBool) +JS_EvaluateScriptForPrincipals(JSContext *cx, JSObject *obj, + JSPrincipals *principals, + const char *bytes, uintN length, + const char *filename, uintN lineno, + jsval *rval); + +extern JS_PUBLIC_API(JSBool) +JS_EvaluateUCScript(JSContext *cx, JSObject *obj, + const jschar *chars, uintN length, + const char *filename, uintN lineno, + jsval *rval); + +extern JS_PUBLIC_API(JSBool) +JS_EvaluateUCScriptForPrincipals(JSContext *cx, JSObject *obj, + JSPrincipals *principals, + const jschar *chars, uintN length, + const char *filename, uintN lineno, + jsval *rval); + +extern JS_PUBLIC_API(JSBool) +JS_CallFunction(JSContext *cx, JSObject *obj, JSFunction *fun, uintN argc, + jsval *argv, jsval *rval); + +extern JS_PUBLIC_API(JSBool) +JS_CallFunctionName(JSContext *cx, JSObject *obj, const char *name, uintN argc, + jsval *argv, jsval *rval); + +extern JS_PUBLIC_API(JSBool) +JS_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, uintN argc, + jsval *argv, jsval *rval); + +/* + * These functions allow setting an operation callback that will be called + * from the thread the context is associated with some time after any thread + * triggered the callback using JS_TriggerOperationCallback(cx). + * + * In a threadsafe build the engine internally triggers operation callbacks + * under certain circumstances (i.e. GC and title transfer) to force the + * context to yield its current request, which the engine always + * automatically does immediately prior to calling the callback function. + * The embedding should thus not rely on callbacks being triggered through + * the external API only. + * + * Important note: Additional callbacks can occur inside the callback handler + * if it re-enters the JS engine. The embedding must ensure that the callback + * is disconnected before attempting such re-entry. + */ + +extern JS_PUBLIC_API(JSOperationCallback) +JS_SetOperationCallback(JSContext *cx, JSOperationCallback callback); + +extern JS_PUBLIC_API(JSOperationCallback) +JS_GetOperationCallback(JSContext *cx); + +extern JS_PUBLIC_API(void) +JS_TriggerOperationCallback(JSContext *cx); + +extern JS_PUBLIC_API(void) +JS_TriggerAllOperationCallbacks(JSRuntime *rt); + +extern JS_PUBLIC_API(JSBool) +JS_IsRunning(JSContext *cx); + +extern JS_PUBLIC_API(JSBool) +JS_IsConstructing(JSContext *cx); + +/* + * Returns true if a script is executing and its current bytecode is a set + * (assignment) operation, even if there are native (no script) stack frames + * between the script and the caller to JS_IsAssigning. + */ +extern JS_FRIEND_API(JSBool) +JS_IsAssigning(JSContext *cx); + +/* + * Saving and restoring frame chains. + * + * These two functions are used to set aside cx's call stack while that stack + * is inactive. After a call to JS_SaveFrameChain, it looks as if there is no + * code running on cx. Before calling JS_RestoreFrameChain, cx's call stack + * must be balanced and all nested calls to JS_SaveFrameChain must have had + * matching JS_RestoreFrameChain calls. + * + * JS_SaveFrameChain deals with cx not having any code running on it. A null + * return does not signify an error, and JS_RestoreFrameChain handles a null + * frame pointer argument safely. + */ +extern JS_PUBLIC_API(JSStackFrame *) +JS_SaveFrameChain(JSContext *cx); + +extern JS_PUBLIC_API(void) +JS_RestoreFrameChain(JSContext *cx, JSStackFrame *fp); + +/************************************************************************/ + +/* + * Strings. + * + * NB: JS_NewString takes ownership of bytes on success, avoiding a copy; but + * on error (signified by null return), it leaves bytes owned by the caller. + * So the caller must free bytes in the error case, if it has no use for them. + * In contrast, all the JS_New*StringCopy* functions do not take ownership of + * the character memory passed to them -- they copy it. + */ +extern JS_PUBLIC_API(JSString *) +JS_NewString(JSContext *cx, char *bytes, size_t length); + +extern JS_PUBLIC_API(JSString *) +JS_NewStringCopyN(JSContext *cx, const char *s, size_t n); + +extern JS_PUBLIC_API(JSString *) +JS_NewStringCopyZ(JSContext *cx, const char *s); + +extern JS_PUBLIC_API(JSString *) +JS_InternString(JSContext *cx, const char *s); + +extern JS_PUBLIC_API(JSString *) +JS_NewUCString(JSContext *cx, jschar *chars, size_t length); + +extern JS_PUBLIC_API(JSString *) +JS_NewUCStringCopyN(JSContext *cx, const jschar *s, size_t n); + +extern JS_PUBLIC_API(JSString *) +JS_NewUCStringCopyZ(JSContext *cx, const jschar *s); + +extern JS_PUBLIC_API(JSString *) +JS_InternUCStringN(JSContext *cx, const jschar *s, size_t length); + +extern JS_PUBLIC_API(JSString *) +JS_InternUCString(JSContext *cx, const jschar *s); + +extern JS_PUBLIC_API(char *) +JS_GetStringBytes(JSString *str); + +extern JS_PUBLIC_API(jschar *) +JS_GetStringChars(JSString *str); + +extern JS_PUBLIC_API(size_t) +JS_GetStringLength(JSString *str); + +extern JS_PUBLIC_API(const char *) +JS_GetStringBytesZ(JSContext *cx, JSString *str); + +extern JS_PUBLIC_API(const jschar *) +JS_GetStringCharsZ(JSContext *cx, JSString *str); + +extern JS_PUBLIC_API(intN) +JS_CompareStrings(JSString *str1, JSString *str2); + +/* + * Mutable string support. A string's characters are never mutable in this JS + * implementation, but a growable string has a buffer that can be reallocated, + * and a dependent string is a substring of another (growable, dependent, or + * immutable) string. The direct data members of the (opaque to API clients) + * JSString struct may be changed in a single-threaded way for growable and + * dependent strings. + * + * Therefore mutable strings cannot be used by more than one thread at a time. + * You may call JS_MakeStringImmutable to convert the string from a mutable + * (growable or dependent) string to an immutable (and therefore thread-safe) + * string. The engine takes care of converting growable and dependent strings + * to immutable for you if you store strings in multi-threaded objects using + * JS_SetProperty or kindred API entry points. + * + * If you store a JSString pointer in a native data structure that is (safely) + * accessible to multiple threads, you must call JS_MakeStringImmutable before + * retiring the store. + */ +extern JS_PUBLIC_API(JSString *) +JS_NewGrowableString(JSContext *cx, jschar *chars, size_t length); + +/* + * Create a dependent string, i.e., a string that owns no character storage, + * but that refers to a slice of another string's chars. Dependent strings + * are mutable by definition, so the thread safety comments above apply. + */ +extern JS_PUBLIC_API(JSString *) +JS_NewDependentString(JSContext *cx, JSString *str, size_t start, + size_t length); + +/* + * Concatenate two strings, resulting in a new growable string. If you create + * the left string and pass it to JS_ConcatStrings on a single thread, try to + * use JS_NewGrowableString to create the left string -- doing so helps Concat + * avoid allocating a new buffer for the result and copying left's chars into + * the new buffer. See above for thread safety comments. + */ +extern JS_PUBLIC_API(JSString *) +JS_ConcatStrings(JSContext *cx, JSString *left, JSString *right); + +/* + * Convert a dependent string into an independent one. This function does not + * change the string's mutability, so the thread safety comments above apply. + */ +extern JS_PUBLIC_API(const jschar *) +JS_UndependString(JSContext *cx, JSString *str); + +/* + * Convert a mutable string (either growable or dependent) into an immutable, + * thread-safe one. + */ +extern JS_PUBLIC_API(JSBool) +JS_MakeStringImmutable(JSContext *cx, JSString *str); + +/* + * Return JS_TRUE if C (char []) strings passed via the API and internally + * are UTF-8. + */ +JS_PUBLIC_API(JSBool) +JS_CStringsAreUTF8(void); + +/* + * Update the value to be returned by JS_CStringsAreUTF8(). Once set, it + * can never be changed. This API must be called before the first call to + * JS_NewRuntime. + */ +JS_PUBLIC_API(void) +JS_SetCStringsAreUTF8(void); + +/* + * Character encoding support. + * + * For both JS_EncodeCharacters and JS_DecodeBytes, set *dstlenp to the size + * of the destination buffer before the call; on return, *dstlenp contains the + * number of bytes (JS_EncodeCharacters) or jschars (JS_DecodeBytes) actually + * stored. To determine the necessary destination buffer size, make a sizing + * call that passes NULL for dst. + * + * On errors, the functions report the error. In that case, *dstlenp contains + * the number of characters or bytes transferred so far. If cx is NULL, no + * error is reported on failure, and the functions simply return JS_FALSE. + * + * NB: Neither function stores an additional zero byte or jschar after the + * transcoded string. + * + * If JS_CStringsAreUTF8() is true then JS_EncodeCharacters encodes to + * UTF-8, and JS_DecodeBytes decodes from UTF-8, which may create additional + * errors if the character sequence is malformed. If UTF-8 support is + * disabled, the functions deflate and inflate, respectively. + */ +JS_PUBLIC_API(JSBool) +JS_EncodeCharacters(JSContext *cx, const jschar *src, size_t srclen, char *dst, + size_t *dstlenp); + +JS_PUBLIC_API(JSBool) +JS_DecodeBytes(JSContext *cx, const char *src, size_t srclen, jschar *dst, + size_t *dstlenp); + +/* + * A variation on JS_EncodeCharacters where a null terminated string is + * returned that you are expected to call JS_free on when done. + */ +JS_PUBLIC_API(char *) +JS_EncodeString(JSContext *cx, JSString *str); + +/************************************************************************/ +/* + * JSON functions + */ +typedef JSBool (* JSONWriteCallback)(const jschar *buf, uint32 len, void *data); + +/* + * JSON.stringify as specified by ES3.1 (draft) + */ +JS_PUBLIC_API(JSBool) +JS_Stringify(JSContext *cx, jsval *vp, JSObject *replacer, jsval space, + JSONWriteCallback callback, void *data); + +/* + * Retrieve a toJSON function. If found, set vp to its result. + */ +JS_PUBLIC_API(JSBool) +JS_TryJSON(JSContext *cx, jsval *vp); + +/* + * JSON.parse as specified by ES3.1 (draft) + */ +JS_PUBLIC_API(JSONParser *) +JS_BeginJSONParse(JSContext *cx, jsval *vp); + +JS_PUBLIC_API(JSBool) +JS_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len); + +JS_PUBLIC_API(JSBool) +JS_FinishJSONParse(JSContext *cx, JSONParser *jp, jsval reviver); + +/************************************************************************/ + +/* + * Locale specific string conversion and error message callbacks. + */ +struct JSLocaleCallbacks { + JSLocaleToUpperCase localeToUpperCase; + JSLocaleToLowerCase localeToLowerCase; + JSLocaleCompare localeCompare; + JSLocaleToUnicode localeToUnicode; + JSErrorCallback localeGetErrorMessage; +}; + +/* + * Establish locale callbacks. The pointer must persist as long as the + * JSContext. Passing NULL restores the default behaviour. + */ +extern JS_PUBLIC_API(void) +JS_SetLocaleCallbacks(JSContext *cx, JSLocaleCallbacks *callbacks); + +/* + * Return the address of the current locale callbacks struct, which may + * be NULL. + */ +extern JS_PUBLIC_API(JSLocaleCallbacks *) +JS_GetLocaleCallbacks(JSContext *cx); + +/************************************************************************/ + +/* + * Error reporting. + */ + +/* + * Report an exception represented by the sprintf-like conversion of format + * and its arguments. This exception message string is passed to a pre-set + * JSErrorReporter function (set by JS_SetErrorReporter; see jspubtd.h for + * the JSErrorReporter typedef). + */ +extern JS_PUBLIC_API(void) +JS_ReportError(JSContext *cx, const char *format, ...); + +/* + * Use an errorNumber to retrieve the format string, args are char * + */ +extern JS_PUBLIC_API(void) +JS_ReportErrorNumber(JSContext *cx, JSErrorCallback errorCallback, + void *userRef, const uintN errorNumber, ...); + +/* + * Use an errorNumber to retrieve the format string, args are jschar * + */ +extern JS_PUBLIC_API(void) +JS_ReportErrorNumberUC(JSContext *cx, JSErrorCallback errorCallback, + void *userRef, const uintN errorNumber, ...); + +/* + * As above, but report a warning instead (JSREPORT_IS_WARNING(report.flags)). + * Return true if there was no error trying to issue the warning, and if the + * warning was not converted into an error due to the JSOPTION_WERROR option + * being set, false otherwise. + */ +extern JS_PUBLIC_API(JSBool) +JS_ReportWarning(JSContext *cx, const char *format, ...); + +extern JS_PUBLIC_API(JSBool) +JS_ReportErrorFlagsAndNumber(JSContext *cx, uintN flags, + JSErrorCallback errorCallback, void *userRef, + const uintN errorNumber, ...); + +extern JS_PUBLIC_API(JSBool) +JS_ReportErrorFlagsAndNumberUC(JSContext *cx, uintN flags, + JSErrorCallback errorCallback, void *userRef, + const uintN errorNumber, ...); + +/* + * Complain when out of memory. + */ +extern JS_PUBLIC_API(void) +JS_ReportOutOfMemory(JSContext *cx); + +/* + * Complain when an allocation size overflows the maximum supported limit. + */ +extern JS_PUBLIC_API(void) +JS_ReportAllocationOverflow(JSContext *cx); + +struct JSErrorReport { + const char *filename; /* source file name, URL, etc., or null */ + uintN lineno; /* source line number */ + const char *linebuf; /* offending source line without final \n */ + const char *tokenptr; /* pointer to error token in linebuf */ + const jschar *uclinebuf; /* unicode (original) line buffer */ + const jschar *uctokenptr; /* unicode (original) token pointer */ + uintN flags; /* error/warning, etc. */ + uintN errorNumber; /* the error number, e.g. see js.msg */ + const jschar *ucmessage; /* the (default) error message */ + const jschar **messageArgs; /* arguments for the error message */ +}; + +/* + * JSErrorReport flag values. These may be freely composed. + */ +#define JSREPORT_ERROR 0x0 /* pseudo-flag for default case */ +#define JSREPORT_WARNING 0x1 /* reported via JS_ReportWarning */ +#define JSREPORT_EXCEPTION 0x2 /* exception was thrown */ +#define JSREPORT_STRICT 0x4 /* error or warning due to strict option */ + +/* + * This condition is an error in strict mode code, a warning if + * JS_HAS_STRICT_OPTION(cx), and otherwise should not be reported at + * all. We check the strictness of the context's top frame's script; + * where that isn't appropriate, the caller should do the right checks + * itself instead of using this flag. + */ +#define JSREPORT_STRICT_MODE_ERROR 0x8 + +/* + * If JSREPORT_EXCEPTION is set, then a JavaScript-catchable exception + * has been thrown for this runtime error, and the host should ignore it. + * Exception-aware hosts should also check for JS_IsExceptionPending if + * JS_ExecuteScript returns failure, and signal or propagate the exception, as + * appropriate. + */ +#define JSREPORT_IS_WARNING(flags) (((flags) & JSREPORT_WARNING) != 0) +#define JSREPORT_IS_EXCEPTION(flags) (((flags) & JSREPORT_EXCEPTION) != 0) +#define JSREPORT_IS_STRICT(flags) (((flags) & JSREPORT_STRICT) != 0) +#define JSREPORT_IS_STRICT_MODE_ERROR(flags) (((flags) & \ + JSREPORT_STRICT_MODE_ERROR) != 0) + +extern JS_PUBLIC_API(JSErrorReporter) +JS_SetErrorReporter(JSContext *cx, JSErrorReporter er); + +/************************************************************************/ + +/* + * Regular Expressions. + */ +#define JSREG_FOLD 0x01 /* fold uppercase to lowercase */ +#define JSREG_GLOB 0x02 /* global exec, creates array of matches */ +#define JSREG_MULTILINE 0x04 /* treat ^ and $ as begin and end of line */ +#define JSREG_STICKY 0x08 /* only match starting at lastIndex */ +#define JSREG_FLAT 0x10 /* parse as a flat regexp */ +#define JSREG_NOCOMPILE 0x20 /* do not try to compile to native code */ + +extern JS_PUBLIC_API(JSObject *) +JS_NewRegExpObject(JSContext *cx, char *bytes, size_t length, uintN flags); + +extern JS_PUBLIC_API(JSObject *) +JS_NewUCRegExpObject(JSContext *cx, jschar *chars, size_t length, uintN flags); + +extern JS_PUBLIC_API(void) +JS_SetRegExpInput(JSContext *cx, JSString *input, JSBool multiline); + +extern JS_PUBLIC_API(void) +JS_ClearRegExpStatics(JSContext *cx); + +extern JS_PUBLIC_API(void) +JS_ClearRegExpRoots(JSContext *cx); + +/* TODO: compile, exec, get/set other statics... */ + +/************************************************************************/ + +extern JS_PUBLIC_API(JSBool) +JS_IsExceptionPending(JSContext *cx); + +extern JS_PUBLIC_API(JSBool) +JS_GetPendingException(JSContext *cx, jsval *vp); + +extern JS_PUBLIC_API(void) +JS_SetPendingException(JSContext *cx, jsval v); + +extern JS_PUBLIC_API(void) +JS_ClearPendingException(JSContext *cx); + +extern JS_PUBLIC_API(JSBool) +JS_ReportPendingException(JSContext *cx); + +/* + * Save the current exception state. This takes a snapshot of cx's current + * exception state without making any change to that state. + * + * The returned state pointer MUST be passed later to JS_RestoreExceptionState + * (to restore that saved state, overriding any more recent state) or else to + * JS_DropExceptionState (to free the state struct in case it is not correct + * or desirable to restore it). Both Restore and Drop free the state struct, + * so callers must stop using the pointer returned from Save after calling the + * Release or Drop API. + */ +extern JS_PUBLIC_API(JSExceptionState *) +JS_SaveExceptionState(JSContext *cx); + +extern JS_PUBLIC_API(void) +JS_RestoreExceptionState(JSContext *cx, JSExceptionState *state); + +extern JS_PUBLIC_API(void) +JS_DropExceptionState(JSContext *cx, JSExceptionState *state); + +/* + * If the given value is an exception object that originated from an error, + * the exception will contain an error report struct, and this API will return + * the address of that struct. Otherwise, it returns NULL. The lifetime of + * the error report struct that might be returned is the same as the lifetime + * of the exception object. + */ +extern JS_PUBLIC_API(JSErrorReport *) +JS_ErrorFromException(JSContext *cx, jsval v); + +/* + * Given a reported error's message and JSErrorReport struct pointer, throw + * the corresponding exception on cx. + */ +extern JS_PUBLIC_API(JSBool) +JS_ThrowReportedError(JSContext *cx, const char *message, + JSErrorReport *reportp); + +/* + * Throws a StopIteration exception on cx. + */ +extern JS_PUBLIC_API(JSBool) +JS_ThrowStopIteration(JSContext *cx); + +/* + * Associate the current thread with the given context. This is done + * implicitly by JS_NewContext. + * + * Returns the old thread id for this context, which should be treated as + * an opaque value. This value is provided for comparison to 0, which + * indicates that ClearContextThread has been called on this context + * since the last SetContextThread, or non-0, which indicates the opposite. + */ +extern JS_PUBLIC_API(jsword) +JS_GetContextThread(JSContext *cx); + +extern JS_PUBLIC_API(jsword) +JS_SetContextThread(JSContext *cx); + +extern JS_PUBLIC_API(jsword) +JS_ClearContextThread(JSContext *cx); + +/************************************************************************/ + +#ifdef DEBUG +#define JS_GC_ZEAL 1 +#endif + +#ifdef JS_GC_ZEAL +extern JS_PUBLIC_API(void) +JS_SetGCZeal(JSContext *cx, uint8 zeal); +#endif + +JS_END_EXTERN_C + +#endif /* jsapi_h___ */ diff --git a/ape-server/deps/js/src/jsarena.cpp b/ape-server/deps/js/src/jsarena.cpp new file mode 100755 index 0000000..b6f661c --- /dev/null +++ b/ape-server/deps/js/src/jsarena.cpp @@ -0,0 +1,450 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Lifetime-based fast allocation, inspired by much prior art, including + * "Fast Allocation and Deallocation of Memory Based on Object Lifetimes" + * David R. Hanson, Software -- Practice and Experience, Vol. 20(1). + */ +#include +#include +#include "jstypes.h" +#include "jsstdint.h" +#include "jsbit.h" +#include "jsarena.h" /* Added by JSIFY */ +#include "jsutil.h" /* Added by JSIFY */ + +#ifdef JS_ARENAMETER +static JSArenaStats *arena_stats_list; + +#define COUNT(pool,what) (pool)->stats.what++ +#else +#define COUNT(pool,what) /* nothing */ +#endif + +#define JS_ARENA_DEFAULT_ALIGN sizeof(double) + +JS_PUBLIC_API(void) +JS_InitArenaPool(JSArenaPool *pool, const char *name, size_t size, + size_t align, size_t *quotap) +{ + if (align == 0) + align = JS_ARENA_DEFAULT_ALIGN; + pool->mask = JS_BITMASK(JS_CeilingLog2(align)); + pool->first.next = NULL; + pool->first.base = pool->first.avail = pool->first.limit = + JS_ARENA_ALIGN(pool, &pool->first + 1); + pool->current = &pool->first; + pool->arenasize = size; + pool->quotap = quotap; +#ifdef JS_ARENAMETER + memset(&pool->stats, 0, sizeof pool->stats); + pool->stats.name = strdup(name); + pool->stats.next = arena_stats_list; + arena_stats_list = &pool->stats; +#endif +} + +/* + * An allocation that consumes more than pool->arenasize also has a header + * pointing back to its previous arena's next member. This header is not + * included in [a->base, a->limit), so its space can't be wrongly claimed. + * + * As the header is a pointer, it must be well-aligned. If pool->mask is + * greater than or equal to POINTER_MASK, the header just preceding a->base + * for an oversized arena a is well-aligned, because a->base is well-aligned. + * However, we may need to add more space to pad the JSArena ** back-pointer + * so that it lies just behind a->base, because a might not be aligned such + * that (jsuword)(a + 1) is on a pointer boundary. + * + * By how much must we pad? Let M be the alignment modulus for pool and P + * the modulus for a pointer. Given M >= P, the base of an oversized arena + * that satisfies M is well-aligned for P. + * + * On the other hand, if M < P, we must include enough space in the header + * size to align the back-pointer on a P boundary so that it can be found by + * subtracting P from a->base. This means a->base must be on a P boundary, + * even though subsequent allocations from a may be aligned on a lesser (M) + * boundary. Given powers of two M and P as above, the extra space needed + * when M < P is P-M or POINTER_MASK - pool->mask. + * + * The size of a header including padding is given by the HEADER_SIZE macro, + * below, for any pool (for any value of M). + * + * The mask to align a->base for any pool is (pool->mask | POINTER_MASK), or + * HEADER_BASE_MASK(pool). + * + * PTR_TO_HEADER computes the address of the back-pointer, given an oversized + * allocation at p. By definition, p must be a->base for the arena a that + * contains p. GET_HEADER and SET_HEADER operate on an oversized arena a, in + * the case of SET_HEADER with back-pointer ap. + */ +#define POINTER_MASK ((jsuword)(JS_ALIGN_OF_POINTER - 1)) +#define HEADER_SIZE(pool) (sizeof(JSArena **) \ + + (((pool)->mask < POINTER_MASK) \ + ? POINTER_MASK - (pool)->mask \ + : 0)) +#define HEADER_BASE_MASK(pool) ((pool)->mask | POINTER_MASK) +#define PTR_TO_HEADER(pool,p) (JS_ASSERT(((jsuword)(p) \ + & HEADER_BASE_MASK(pool)) \ + == 0), \ + (JSArena ***)(p) - 1) +#define GET_HEADER(pool,a) (*PTR_TO_HEADER(pool, (a)->base)) +#define SET_HEADER(pool,a,ap) (*PTR_TO_HEADER(pool, (a)->base) = (ap)) + +JS_PUBLIC_API(void *) +JS_ArenaAllocate(JSArenaPool *pool, size_t nb) +{ + JSArena **ap, *a, *b; + jsuword extra, hdrsz, gross; + void *p; + + /* + * Search pool from current forward till we find or make enough space. + * + * NB: subtract nb from a->limit in the loop condition, instead of adding + * nb to a->avail, to avoid overflowing a 32-bit address space (possible + * when running a 32-bit program on a 64-bit system where the kernel maps + * the heap up against the top of the 32-bit address space). + * + * Thanks to Juergen Kreileder , who brought this up in + * https://bugzilla.mozilla.org/show_bug.cgi?id=279273. + */ + JS_ASSERT((nb & pool->mask) == 0); + for (a = pool->current; nb > a->limit || a->avail > a->limit - nb; + pool->current = a) { + ap = &a->next; + if (!*ap) { + /* Not enough space in pool, so we must malloc. */ + extra = (nb > pool->arenasize) ? HEADER_SIZE(pool) : 0; + hdrsz = sizeof *a + extra + pool->mask; + gross = hdrsz + JS_MAX(nb, pool->arenasize); + if (gross < nb) + return NULL; + if (pool->quotap) { + if (gross > *pool->quotap) + return NULL; + b = (JSArena *) js_malloc(gross); + if (!b) + return NULL; + *pool->quotap -= gross; + } else { + b = (JSArena *) js_malloc(gross); + if (!b) + return NULL; + } + + b->next = NULL; + b->limit = (jsuword)b + gross; + JS_COUNT_ARENA(pool,++); + COUNT(pool, nmallocs); + + /* If oversized, store ap in the header, just before a->base. */ + *ap = a = b; + JS_ASSERT(gross <= JS_UPTRDIFF(a->limit, a)); + if (extra) { + a->base = a->avail = + ((jsuword)a + hdrsz) & ~HEADER_BASE_MASK(pool); + SET_HEADER(pool, a, ap); + } else { + a->base = a->avail = JS_ARENA_ALIGN(pool, a + 1); + } + continue; + } + a = *ap; /* move to next arena */ + } + + p = (void *)a->avail; + a->avail += nb; + JS_ASSERT(a->base <= a->avail && a->avail <= a->limit); + return p; +} + +JS_PUBLIC_API(void *) +JS_ArenaRealloc(JSArenaPool *pool, void *p, size_t size, size_t incr) +{ + JSArena **ap, *a, *b; + jsuword boff, aoff, extra, hdrsz, gross, growth; + + /* + * Use the oversized-single-allocation header to avoid searching for ap. + * See JS_ArenaAllocate, the SET_HEADER call. + */ + if (size > pool->arenasize) { + ap = *PTR_TO_HEADER(pool, p); + a = *ap; + } else { + ap = &pool->first.next; + while ((a = *ap) != pool->current) + ap = &a->next; + } + + JS_ASSERT(a->base == (jsuword)p); + boff = JS_UPTRDIFF(a->base, a); + aoff = JS_ARENA_ALIGN(pool, size + incr); + JS_ASSERT(aoff > pool->arenasize); + extra = HEADER_SIZE(pool); /* oversized header holds ap */ + hdrsz = sizeof *a + extra + pool->mask; /* header and alignment slop */ + gross = hdrsz + aoff; + JS_ASSERT(gross > aoff); + if (pool->quotap) { + growth = gross - (a->limit - (jsuword) a); + if (growth > *pool->quotap) + return NULL; + a = (JSArena *) js_realloc(a, gross); + if (!a) + return NULL; + *pool->quotap -= growth; + } else { + a = (JSArena *) js_realloc(a, gross); + if (!a) + return NULL; + } +#ifdef JS_ARENAMETER + pool->stats.nreallocs++; +#endif + + if (a != *ap) { + /* Oops, realloc moved the allocation: update other pointers to a. */ + if (pool->current == *ap) + pool->current = a; + b = a->next; + if (b && b->avail - b->base > pool->arenasize) { + JS_ASSERT(GET_HEADER(pool, b) == &(*ap)->next); + SET_HEADER(pool, b, &a->next); + } + + /* Now update *ap, the next link of the arena before a. */ + *ap = a; + } + + a->base = ((jsuword)a + hdrsz) & ~HEADER_BASE_MASK(pool); + a->limit = (jsuword)a + gross; + a->avail = a->base + aoff; + JS_ASSERT(a->base <= a->avail && a->avail <= a->limit); + + /* Check whether realloc aligned differently, and copy if necessary. */ + if (boff != JS_UPTRDIFF(a->base, a)) + memmove((void *)a->base, (char *)a + boff, size); + + /* Store ap in the oversized-load arena header. */ + SET_HEADER(pool, a, ap); + return (void *)a->base; +} + +JS_PUBLIC_API(void *) +JS_ArenaGrow(JSArenaPool *pool, void *p, size_t size, size_t incr) +{ + void *newp; + + /* + * If p points to an oversized allocation, it owns an entire arena, so we + * can simply realloc the arena. + */ + if (size > pool->arenasize) + return JS_ArenaRealloc(pool, p, size, incr); + + JS_ARENA_ALLOCATE(newp, pool, size + incr); + if (newp) + memcpy(newp, p, size); + return newp; +} + +/* + * Free tail arenas linked after head, which may not be the true list head. + * Reset pool->current to point to head in case it pointed at a tail arena. + */ +static void +FreeArenaList(JSArenaPool *pool, JSArena *head) +{ + JSArena **ap, *a; + + ap = &head->next; + a = *ap; + if (!a) + return; + +#ifdef DEBUG + do { + JS_ASSERT(a->base <= a->avail && a->avail <= a->limit); + a->avail = a->base; + JS_CLEAR_UNUSED(a); + } while ((a = a->next) != NULL); + a = *ap; +#endif + + do { + *ap = a->next; + if (pool->quotap) + *pool->quotap += a->limit - (jsuword) a; + JS_CLEAR_ARENA(a); + JS_COUNT_ARENA(pool,--); + js_free(a); + } while ((a = *ap) != NULL); + + pool->current = head; +} + +JS_PUBLIC_API(void) +JS_ArenaRelease(JSArenaPool *pool, char *mark) +{ + JSArena *a; + + for (a = &pool->first; a; a = a->next) { + JS_ASSERT(a->base <= a->avail && a->avail <= a->limit); + + if (JS_ARENA_MARK_MATCH(a, mark)) { + a->avail = JS_ARENA_ALIGN(pool, mark); + JS_ASSERT(a->avail <= a->limit); + FreeArenaList(pool, a); + return; + } + } +} + +JS_PUBLIC_API(void) +JS_FreeArenaPool(JSArenaPool *pool) +{ + FreeArenaList(pool, &pool->first); + COUNT(pool, ndeallocs); +} + +JS_PUBLIC_API(void) +JS_FinishArenaPool(JSArenaPool *pool) +{ + FreeArenaList(pool, &pool->first); +#ifdef JS_ARENAMETER + { + JSArenaStats *stats, **statsp; + + if (pool->stats.name) { + js_free(pool->stats.name); + pool->stats.name = NULL; + } + for (statsp = &arena_stats_list; (stats = *statsp) != 0; + statsp = &stats->next) { + if (stats == &pool->stats) { + *statsp = stats->next; + return; + } + } + } +#endif +} + +JS_PUBLIC_API(void) +JS_ArenaFinish() +{ +} + +JS_PUBLIC_API(void) +JS_ArenaShutDown(void) +{ +} + +#ifdef JS_ARENAMETER +JS_PUBLIC_API(void) +JS_ArenaCountAllocation(JSArenaPool *pool, size_t nb) +{ + pool->stats.nallocs++; + pool->stats.nbytes += nb; + if (nb > pool->stats.maxalloc) + pool->stats.maxalloc = nb; + pool->stats.variance += nb * nb; +} + +JS_PUBLIC_API(void) +JS_ArenaCountInplaceGrowth(JSArenaPool *pool, size_t size, size_t incr) +{ + pool->stats.ninplace++; +} + +JS_PUBLIC_API(void) +JS_ArenaCountGrowth(JSArenaPool *pool, size_t size, size_t incr) +{ + pool->stats.ngrows++; + pool->stats.nbytes += incr; + pool->stats.variance -= size * size; + size += incr; + if (size > pool->stats.maxalloc) + pool->stats.maxalloc = size; + pool->stats.variance += size * size; +} + +JS_PUBLIC_API(void) +JS_ArenaCountRelease(JSArenaPool *pool, char *mark) +{ + pool->stats.nreleases++; +} + +JS_PUBLIC_API(void) +JS_ArenaCountRetract(JSArenaPool *pool, char *mark) +{ + pool->stats.nfastrels++; +} + +#include + +JS_PUBLIC_API(void) +JS_DumpArenaStats(FILE *fp) +{ + JSArenaStats *stats; + double mean, sigma; + + for (stats = arena_stats_list; stats; stats = stats->next) { + mean = JS_MeanAndStdDev(stats->nallocs, stats->nbytes, stats->variance, + &sigma); + + fprintf(fp, "\n%s allocation statistics:\n", stats->name); + fprintf(fp, " number of arenas: %u\n", stats->narenas); + fprintf(fp, " number of allocations: %u\n", stats->nallocs); + fprintf(fp, " number of malloc calls: %u\n", stats->nmallocs); + fprintf(fp, " number of deallocations: %u\n", stats->ndeallocs); + fprintf(fp, " number of allocation growths: %u\n", stats->ngrows); + fprintf(fp, " number of in-place growths: %u\n", stats->ninplace); + fprintf(fp, " number of realloc'ing growths: %u\n", stats->nreallocs); + fprintf(fp, "number of released allocations: %u\n", stats->nreleases); + fprintf(fp, " number of fast releases: %u\n", stats->nfastrels); + fprintf(fp, " total bytes allocated: %u\n", stats->nbytes); + fprintf(fp, " mean allocation size: %g\n", mean); + fprintf(fp, " standard deviation: %g\n", sigma); + fprintf(fp, " maximum allocation size: %u\n", stats->maxalloc); + } +} +#endif /* JS_ARENAMETER */ diff --git a/ape-server/deps/js/src/jsarena.h b/ape-server/deps/js/src/jsarena.h new file mode 100755 index 0000000..47207f9 --- /dev/null +++ b/ape-server/deps/js/src/jsarena.h @@ -0,0 +1,289 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsarena_h___ +#define jsarena_h___ +/* + * Lifetime-based fast allocation, inspired by much prior art, including + * "Fast Allocation and Deallocation of Memory Based on Object Lifetimes" + * David R. Hanson, Software -- Practice and Experience, Vol. 20(1). + * + * Also supports LIFO allocation (JS_ARENA_MARK/JS_ARENA_RELEASE). + */ +#include +#include "jstypes.h" +#include "jscompat.h" + +JS_BEGIN_EXTERN_C + +typedef struct JSArena JSArena; +typedef struct JSArenaPool JSArenaPool; + +struct JSArena { + JSArena *next; /* next arena for this lifetime */ + jsuword base; /* aligned base address, follows this header */ + jsuword limit; /* one beyond last byte in arena */ + jsuword avail; /* points to next available byte */ +}; + +#ifdef JS_ARENAMETER +typedef struct JSArenaStats JSArenaStats; + +struct JSArenaStats { + JSArenaStats *next; /* next in arenaStats list */ + char *name; /* name for debugging */ + uint32 narenas; /* number of arenas in pool */ + uint32 nallocs; /* number of JS_ARENA_ALLOCATE() calls */ + uint32 nmallocs; /* number of malloc() calls */ + uint32 ndeallocs; /* number of lifetime deallocations */ + uint32 ngrows; /* number of JS_ARENA_GROW() calls */ + uint32 ninplace; /* number of in-place growths */ + uint32 nreallocs; /* number of arena grow extending reallocs */ + uint32 nreleases; /* number of JS_ARENA_RELEASE() calls */ + uint32 nfastrels; /* number of "fast path" releases */ + size_t nbytes; /* total bytes allocated */ + size_t maxalloc; /* maximum allocation size in bytes */ + double variance; /* size variance accumulator */ +}; +#endif + +struct JSArenaPool { + JSArena first; /* first arena in pool list */ + JSArena *current; /* arena from which to allocate space */ + size_t arenasize; /* net exact size of a new arena */ + jsuword mask; /* alignment mask (power-of-2 - 1) */ + size_t *quotap; /* pointer to the quota on pool allocation + size or null if pool is unlimited */ +#ifdef JS_ARENAMETER + JSArenaStats stats; +#endif +}; + +#define JS_ARENA_ALIGN(pool, n) (((jsuword)(n) + (pool)->mask) & ~(pool)->mask) + +#define JS_ARENA_ALLOCATE(p, pool, nb) \ + JS_ARENA_ALLOCATE_CAST(p, void *, pool, nb) + +#define JS_ARENA_ALLOCATE_TYPE(p, type, pool) \ + JS_ARENA_ALLOCATE_COMMON(p, type *, pool, sizeof(type), 0) + +#define JS_ARENA_ALLOCATE_CAST(p, type, pool, nb) \ + JS_ARENA_ALLOCATE_COMMON(p, type, pool, nb, _nb > _a->limit) + +/* + * NB: In JS_ARENA_ALLOCATE_CAST and JS_ARENA_GROW_CAST, always subtract _nb + * from a->limit rather than adding _nb to _p, to avoid overflowing a 32-bit + * address space (possible when running a 32-bit program on a 64-bit system + * where the kernel maps the heap up against the top of the 32-bit address + * space). + * + * Thanks to Juergen Kreileder , who brought this up in + * https://bugzilla.mozilla.org/show_bug.cgi?id=279273. + */ +#define JS_ARENA_ALLOCATE_COMMON(p, type, pool, nb, guard) \ + JS_BEGIN_MACRO \ + JSArena *_a = (pool)->current; \ + size_t _nb = JS_ARENA_ALIGN(pool, nb); \ + jsuword _p = _a->avail; \ + if ((guard) || _p > _a->limit - _nb) \ + _p = (jsuword)JS_ArenaAllocate(pool, _nb); \ + else \ + _a->avail = _p + _nb; \ + p = (type) _p; \ + JS_ArenaCountAllocation(pool, nb); \ + JS_END_MACRO + +#define JS_ARENA_GROW(p, pool, size, incr) \ + JS_ARENA_GROW_CAST(p, void *, pool, size, incr) + +#define JS_ARENA_GROW_CAST(p, type, pool, size, incr) \ + JS_BEGIN_MACRO \ + JSArena *_a = (pool)->current; \ + if (_a->avail == (jsuword)(p) + JS_ARENA_ALIGN(pool, size)) { \ + size_t _nb = (size) + (incr); \ + _nb = JS_ARENA_ALIGN(pool, _nb); \ + if (_a->limit >= _nb && (jsuword)(p) <= _a->limit - _nb) { \ + _a->avail = (jsuword)(p) + _nb; \ + JS_ArenaCountInplaceGrowth(pool, size, incr); \ + } else if ((jsuword)(p) == _a->base) { \ + p = (type) JS_ArenaRealloc(pool, p, size, incr); \ + } else { \ + p = (type) JS_ArenaGrow(pool, p, size, incr); \ + } \ + } else { \ + p = (type) JS_ArenaGrow(pool, p, size, incr); \ + } \ + JS_ArenaCountGrowth(pool, size, incr); \ + JS_END_MACRO + +#define JS_ARENA_MARK(pool) ((void *) (pool)->current->avail) +#define JS_UPTRDIFF(p,q) ((jsuword)(p) - (jsuword)(q)) + +/* + * Check if the mark is inside arena's allocated area. + */ +#define JS_ARENA_MARK_MATCH(a, mark) \ + (JS_UPTRDIFF(mark, (a)->base) <= JS_UPTRDIFF((a)->avail, (a)->base)) + +#ifdef DEBUG +#define JS_FREE_PATTERN 0xDA +#define JS_CLEAR_UNUSED(a) (JS_ASSERT((a)->avail <= (a)->limit), \ + memset((void*)(a)->avail, JS_FREE_PATTERN, \ + (a)->limit - (a)->avail)) +#define JS_CLEAR_ARENA(a) memset((void*)(a), JS_FREE_PATTERN, \ + (a)->limit - (jsuword)(a)) +#else +#define JS_CLEAR_UNUSED(a) /* nothing */ +#define JS_CLEAR_ARENA(a) /* nothing */ +#endif + +#define JS_ARENA_RELEASE(pool, mark) \ + JS_BEGIN_MACRO \ + char *_m = (char *)(mark); \ + JSArena *_a = (pool)->current; \ + if (_a != &(pool)->first && JS_ARENA_MARK_MATCH(_a, _m)) { \ + _a->avail = (jsuword)JS_ARENA_ALIGN(pool, _m); \ + JS_ASSERT(_a->avail <= _a->limit); \ + JS_CLEAR_UNUSED(_a); \ + JS_ArenaCountRetract(pool, _m); \ + } else { \ + JS_ArenaRelease(pool, _m); \ + } \ + JS_ArenaCountRelease(pool, _m); \ + JS_END_MACRO + +#ifdef JS_ARENAMETER +#define JS_COUNT_ARENA(pool,op) ((pool)->stats.narenas op) +#else +#define JS_COUNT_ARENA(pool,op) +#endif + +#define JS_ARENA_DESTROY(pool, a, pnext) \ + JS_BEGIN_MACRO \ + JS_COUNT_ARENA(pool,--); \ + if ((pool)->current == (a)) (pool)->current = &(pool)->first; \ + *(pnext) = (a)->next; \ + JS_CLEAR_ARENA(a); \ + free(a); \ + (a) = NULL; \ + JS_END_MACRO + +/* + * Initialize an arena pool with a minimum size per arena of size bytes. + */ +extern JS_PUBLIC_API(void) +JS_InitArenaPool(JSArenaPool *pool, const char *name, size_t size, + size_t align, size_t *quotap); + +/* + * Free the arenas in pool. The user may continue to allocate from pool + * after calling this function. There is no need to call JS_InitArenaPool() + * again unless JS_FinishArenaPool(pool) has been called. + */ +extern JS_PUBLIC_API(void) +JS_FreeArenaPool(JSArenaPool *pool); + +/* + * Free the arenas in pool and finish using it altogether. + */ +extern JS_PUBLIC_API(void) +JS_FinishArenaPool(JSArenaPool *pool); + +/* + * Deprecated do-nothing function. + */ +extern JS_PUBLIC_API(void) +JS_ArenaFinish(void); + +/* + * Deprecated do-nothing function. + */ +extern JS_PUBLIC_API(void) +JS_ArenaShutDown(void); + +/* + * Friend functions used by the JS_ARENA_*() macros. + */ +extern JS_PUBLIC_API(void *) +JS_ArenaAllocate(JSArenaPool *pool, size_t nb); + +extern JS_PUBLIC_API(void *) +JS_ArenaRealloc(JSArenaPool *pool, void *p, size_t size, size_t incr); + +extern JS_PUBLIC_API(void *) +JS_ArenaGrow(JSArenaPool *pool, void *p, size_t size, size_t incr); + +extern JS_PUBLIC_API(void) +JS_ArenaRelease(JSArenaPool *pool, char *mark); + +#ifdef JS_ARENAMETER + +#include + +extern JS_PUBLIC_API(void) +JS_ArenaCountAllocation(JSArenaPool *pool, size_t nb); + +extern JS_PUBLIC_API(void) +JS_ArenaCountInplaceGrowth(JSArenaPool *pool, size_t size, size_t incr); + +extern JS_PUBLIC_API(void) +JS_ArenaCountGrowth(JSArenaPool *pool, size_t size, size_t incr); + +extern JS_PUBLIC_API(void) +JS_ArenaCountRelease(JSArenaPool *pool, char *mark); + +extern JS_PUBLIC_API(void) +JS_ArenaCountRetract(JSArenaPool *pool, char *mark); + +extern JS_PUBLIC_API(void) +JS_DumpArenaStats(FILE *fp); + +#else /* !JS_ARENAMETER */ + +#define JS_ArenaCountAllocation(ap, nb) /* nothing */ +#define JS_ArenaCountInplaceGrowth(ap, size, incr) /* nothing */ +#define JS_ArenaCountGrowth(ap, size, incr) /* nothing */ +#define JS_ArenaCountRelease(ap, mark) /* nothing */ +#define JS_ArenaCountRetract(ap, mark) /* nothing */ + +#endif /* !JS_ARENAMETER */ + +JS_END_EXTERN_C + +#endif /* jsarena_h___ */ diff --git a/ape-server/deps/js/src/jsarray.cpp b/ape-server/deps/js/src/jsarray.cpp new file mode 100755 index 0000000..9354238 --- /dev/null +++ b/ape-server/deps/js/src/jsarray.cpp @@ -0,0 +1,3627 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set sw=4 ts=8 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * JS array class. + * + * Array objects begin as "dense" arrays, optimized for index-only property + * access over a vector of slots (obj->dslots) with high load factor. Array + * methods optimize for denseness by testing that the object's class is + * &js_ArrayClass, and can then directly manipulate the slots for efficiency. + * + * We track these pieces of metadata for arrays in dense mode: + * - the array's length property as a uint32, in JSSLOT_ARRAY_LENGTH, + * - the number of indices that are filled (non-holes), in JSSLOT_ARRAY_COUNT, + * - the net number of slots starting at dslots (capacity), in dslots[-1] if + * dslots is non-NULL. + * + * In dense mode, holes in the array are represented by JSVAL_HOLE. The final + * slot in fslots is unused. + * + * NB: the capacity and length of a dense array are entirely unrelated! The + * length may be greater than, less than, or equal to the capacity. See + * array_length_setter for an explanation of how the first, most surprising + * case may occur. + * + * Arrays are converted to use js_SlowArrayClass when any of these conditions + * are met: + * - the load factor (COUNT / capacity) is less than 0.25, and there are + * more than MIN_SPARSE_INDEX slots total + * - a property is set that is not indexed (and not "length"); or + * - a property is defined that has non-default property attributes. + * + * Dense arrays do not track property creation order, so unlike other native + * objects and slow arrays, enumerating an array does not necessarily visit the + * properties in the order they were created. We could instead maintain the + * scope to track property enumeration order, but still use the fast slot + * access. That would have the same memory cost as just using a + * js_SlowArrayClass, but have the same performance characteristics as a dense + * array for slot accesses, at some cost in code complexity. + */ +#include +#include +#include "jstypes.h" +#include "jsstdint.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jsapi.h" +#include "jsarray.h" +#include "jsatom.h" +#include "jsbit.h" +#include "jsbool.h" +#include "jstracer.h" +#include "jsbuiltins.h" +#include "jscntxt.h" +#include "jsversion.h" +#include "jsdbgapi.h" /* for js_TraceWatchPoints */ +#include "jsdtoa.h" +#include "jsfun.h" +#include "jsgc.h" +#include "jsinterp.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsobj.h" +#include "jsscope.h" +#include "jsstr.h" +#include "jsstaticcheck.h" +#include "jsvector.h" + +#include "jsatominlines.h" + +/* 2^32 - 1 as a number and a string */ +#define MAXINDEX 4294967295u +#define MAXSTR "4294967295" + +/* Small arrays are dense, no matter what. */ +#define MIN_SPARSE_INDEX 256 + +static inline bool +INDEX_TOO_BIG(jsuint index) +{ + return index > JS_BIT(29) - 1; +} + +#define INDEX_TOO_SPARSE(array, index) \ + (INDEX_TOO_BIG(index) || \ + ((index) > js_DenseArrayCapacity(array) && (index) >= MIN_SPARSE_INDEX && \ + (index) > (uint32)((array)->fslots[JSSLOT_ARRAY_COUNT] + 1) * 4)) + +JS_STATIC_ASSERT(sizeof(JSScopeProperty) > 4 * sizeof(jsval)); + +#define ENSURE_SLOW_ARRAY(cx, obj) \ + (OBJ_GET_CLASS(cx, obj) == &js_SlowArrayClass || js_MakeArraySlow(cx, obj)) + +/* + * Determine if the id represents an array index or an XML property index. + * + * An id is an array index according to ECMA by (15.4): + * + * "Array objects give special treatment to a certain class of property names. + * A property name P (in the form of a string value) is an array index if and + * only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal + * to 2^32-1." + * + * In our implementation, it would be sufficient to check for JSVAL_IS_INT(id) + * except that by using signed 32-bit integers we miss the top half of the + * valid range. This function checks the string representation itself; note + * that calling a standard conversion routine might allow strings such as + * "08" or "4.0" as array indices, which they are not. + */ +JSBool +js_IdIsIndex(jsval id, jsuint *indexp) +{ + JSString *str; + jschar *cp; + + if (JSVAL_IS_INT(id)) { + jsint i; + i = JSVAL_TO_INT(id); + if (i < 0) + return JS_FALSE; + *indexp = (jsuint)i; + return JS_TRUE; + } + + /* NB: id should be a string, but jsxml.c may call us with an object id. */ + if (!JSVAL_IS_STRING(id)) + return JS_FALSE; + + str = JSVAL_TO_STRING(id); + cp = str->chars(); + if (JS7_ISDEC(*cp) && str->length() < sizeof(MAXSTR)) { + jsuint index = JS7_UNDEC(*cp++); + jsuint oldIndex = 0; + jsuint c = 0; + if (index != 0) { + while (JS7_ISDEC(*cp)) { + oldIndex = index; + c = JS7_UNDEC(*cp); + index = 10*index + c; + cp++; + } + } + + /* Ensure that all characters were consumed and we didn't overflow. */ + if (*cp == 0 && + (oldIndex < (MAXINDEX / 10) || + (oldIndex == (MAXINDEX / 10) && c < (MAXINDEX % 10)))) + { + *indexp = index; + return JS_TRUE; + } + } + return JS_FALSE; +} + +static jsuint +ValueIsLength(JSContext *cx, jsval* vp) +{ + jsint i; + jsdouble d; + jsuint length; + + if (JSVAL_IS_INT(*vp)) { + i = JSVAL_TO_INT(*vp); + if (i < 0) + goto error; + return (jsuint) i; + } + + d = js_ValueToNumber(cx, vp); + if (JSVAL_IS_NULL(*vp)) + goto error; + + if (JSDOUBLE_IS_NaN(d)) + goto error; + length = (jsuint) d; + if (d != (jsdouble) length) + goto error; + return length; + + error: + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_ARRAY_LENGTH); + *vp = JSVAL_NULL; + return 0; +} + +JSBool +js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp) +{ + if (OBJ_IS_ARRAY(cx, obj)) { + *lengthp = obj->fslots[JSSLOT_ARRAY_LENGTH]; + return JS_TRUE; + } + + JSAutoTempValueRooter tvr(cx, JSVAL_NULL); + if (!obj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), tvr.addr())) + return JS_FALSE; + + if (JSVAL_IS_INT(tvr.value())) { + *lengthp = jsuint(jsint(JSVAL_TO_INT(tvr.value()))); /* jsuint cast does ToUint32 */ + return JS_TRUE; + } + + *lengthp = js_ValueToECMAUint32(cx, tvr.addr()); + return !JSVAL_IS_NULL(tvr.value()); +} + +static JSBool +IndexToValue(JSContext *cx, jsdouble index, jsval *vp) +{ + return js_NewWeaklyRootedNumber(cx, index, vp); +} + +JSBool JS_FASTCALL +js_IndexToId(JSContext *cx, jsuint index, jsid *idp) +{ + JSString *str; + + if (index <= JSVAL_INT_MAX) { + *idp = INT_TO_JSID(index); + return JS_TRUE; + } + str = js_NumberToString(cx, index); + if (!str) + return JS_FALSE; + return js_ValueToStringId(cx, STRING_TO_JSVAL(str), idp); +} + +static JSBool +BigIndexToId(JSContext *cx, JSObject *obj, jsuint index, JSBool createAtom, + jsid *idp) +{ + jschar buf[10], *start; + JSClass *clasp; + JSAtom *atom; + JS_STATIC_ASSERT((jsuint)-1 == 4294967295U); + + JS_ASSERT(index > JSVAL_INT_MAX); + + start = JS_ARRAY_END(buf); + do { + --start; + *start = (jschar)('0' + index % 10); + index /= 10; + } while (index != 0); + + /* + * Skip the atomization if the class is known to store atoms corresponding + * to big indexes together with elements. In such case we know that the + * array does not have an element at the given index if its atom does not + * exist. Fast arrays (clasp == &js_ArrayClass) don't use atoms for + * any indexes, though it would be rare to see them have a big index + * in any case. + */ + if (!createAtom && + ((clasp = OBJ_GET_CLASS(cx, obj)) == &js_SlowArrayClass || + clasp == &js_ArgumentsClass || + clasp == &js_ObjectClass)) { + atom = js_GetExistingStringAtom(cx, start, JS_ARRAY_END(buf) - start); + if (!atom) { + *idp = JSVAL_VOID; + return JS_TRUE; + } + } else { + atom = js_AtomizeChars(cx, start, JS_ARRAY_END(buf) - start, 0); + if (!atom) + return JS_FALSE; + } + + *idp = ATOM_TO_JSID(atom); + return JS_TRUE; +} + +static JSBool +ResizeSlots(JSContext *cx, JSObject *obj, uint32 oldlen, uint32 newlen, + bool initializeAllSlots = true) +{ + jsval *slots, *newslots; + + if (newlen == 0) { + if (obj->dslots) { + cx->free(obj->dslots - 1); + obj->dslots = NULL; + } + return JS_TRUE; + } + + if (newlen > MAX_DSLOTS_LENGTH) { + js_ReportAllocationOverflow(cx); + return JS_FALSE; + } + + slots = obj->dslots ? obj->dslots - 1 : NULL; + newslots = (jsval *) cx->realloc(slots, (size_t(newlen) + 1) * sizeof(jsval)); + if (!newslots) + return JS_FALSE; + + obj->dslots = newslots + 1; + js_SetDenseArrayCapacity(obj, newlen); + + if (initializeAllSlots) { + for (slots = obj->dslots + oldlen; slots < obj->dslots + newlen; slots++) + *slots = JSVAL_HOLE; + } + + return JS_TRUE; +} + +/* + * When a dense array with CAPACITY_DOUBLING_MAX or fewer slots needs to grow, + * double its capacity, to push() N elements in amortized O(N) time. + * + * Above this limit, grow by 12.5% each time. Speed is still amortized O(N), + * with a higher constant factor, and we waste less space. + */ +#define CAPACITY_DOUBLING_MAX (1024 * 1024) + +/* + * Round up all large allocations to a multiple of this (1MB), so as not to + * waste space if malloc gives us 1MB-sized chunks (as jemalloc does). + */ +#define CAPACITY_CHUNK (1024 * 1024 / sizeof(jsval)) + +static JSBool +EnsureCapacity(JSContext *cx, JSObject *obj, uint32 newcap, + bool initializeAllSlots = true) +{ + uint32 oldcap = js_DenseArrayCapacity(obj); + + if (newcap > oldcap) { + /* + * If this overflows uint32, newcap is very large. nextsize will end + * up being less than newcap, the code below will thus disregard it, + * and ResizeSlots will fail. + * + * The way we use dslots[-1] forces a few +1s and -1s here. For + * example, (oldcap * 2 + 1) produces the sequence 7, 15, 31, 63, ... + * which makes the total allocation size (with dslots[-1]) a power + * of two. + */ + uint32 nextsize = (oldcap <= CAPACITY_DOUBLING_MAX) + ? oldcap * 2 + 1 + : oldcap + (oldcap >> 3); + + uint32 actualCapacity = JS_MAX(newcap, nextsize); + if (actualCapacity >= CAPACITY_CHUNK) + actualCapacity = JS_ROUNDUP(actualCapacity + 1, CAPACITY_CHUNK) - 1; /* -1 for dslots[-1] */ + else if (actualCapacity < ARRAY_CAPACITY_MIN) + actualCapacity = ARRAY_CAPACITY_MIN; + if (!ResizeSlots(cx, obj, oldcap, actualCapacity, initializeAllSlots)) + return JS_FALSE; + if (!initializeAllSlots) { + /* + * Initialize the slots caller didn't actually ask for. + */ + for (jsval *slots = obj->dslots + newcap; + slots < obj->dslots + actualCapacity; + slots++) { + *slots = JSVAL_HOLE; + } + } + } + return JS_TRUE; +} + +static bool +ReallyBigIndexToId(JSContext* cx, jsdouble index, jsid* idp) +{ + JSAutoTempValueRooter dval(cx); + if (!js_NewDoubleInRootedValue(cx, index, dval.addr()) || + !js_ValueToStringId(cx, dval.value(), idp)) { + return JS_FALSE; + } + return JS_TRUE; +} + +static bool +IndexToId(JSContext* cx, JSObject* obj, jsdouble index, JSBool* hole, jsid* idp, + JSBool createAtom = JS_FALSE) +{ + if (index <= JSVAL_INT_MAX) { + *idp = INT_TO_JSID(int(index)); + return JS_TRUE; + } + + if (index <= jsuint(-1)) { + if (!BigIndexToId(cx, obj, jsuint(index), createAtom, idp)) + return JS_FALSE; + if (hole && JSVAL_IS_VOID(*idp)) + *hole = JS_TRUE; + return JS_TRUE; + } + + return ReallyBigIndexToId(cx, index, idp); +} + +/* + * If the property at the given index exists, get its value into location + * pointed by vp and set *hole to false. Otherwise set *hole to true and *vp + * to JSVAL_VOID. This function assumes that the location pointed by vp is + * properly rooted and can be used as GC-protected storage for temporaries. + */ +static JSBool +GetArrayElement(JSContext *cx, JSObject *obj, jsdouble index, JSBool *hole, + jsval *vp) +{ + JS_ASSERT(index >= 0); + if (OBJ_IS_DENSE_ARRAY(cx, obj) && index < js_DenseArrayCapacity(obj) && + (*vp = obj->dslots[jsuint(index)]) != JSVAL_HOLE) { + *hole = JS_FALSE; + return JS_TRUE; + } + + JSAutoTempIdRooter idr(cx); + + *hole = JS_FALSE; + if (!IndexToId(cx, obj, index, hole, idr.addr())) + return JS_FALSE; + if (*hole) { + *vp = JSVAL_VOID; + return JS_TRUE; + } + + JSObject *obj2; + JSProperty *prop; + if (!obj->lookupProperty(cx, idr.id(), &obj2, &prop)) + return JS_FALSE; + if (!prop) { + *hole = JS_TRUE; + *vp = JSVAL_VOID; + } else { + obj2->dropProperty(cx, prop); + if (!obj->getProperty(cx, idr.id(), vp)) + return JS_FALSE; + *hole = JS_FALSE; + } + return JS_TRUE; +} + +/* + * Set the value of the property at the given index to v assuming v is rooted. + */ +static JSBool +SetArrayElement(JSContext *cx, JSObject *obj, jsdouble index, jsval v) +{ + JS_ASSERT(index >= 0); + + if (OBJ_IS_DENSE_ARRAY(cx, obj)) { + /* Predicted/prefetched code should favor the remains-dense case. */ + if (index <= jsuint(-1)) { + jsuint idx = jsuint(index); + if (!INDEX_TOO_SPARSE(obj, idx)) { + JS_ASSERT(idx + 1 > idx); + if (!EnsureCapacity(cx, obj, idx + 1)) + return JS_FALSE; + if (idx >= uint32(obj->fslots[JSSLOT_ARRAY_LENGTH])) + obj->fslots[JSSLOT_ARRAY_LENGTH] = idx + 1; + if (obj->dslots[idx] == JSVAL_HOLE) + obj->fslots[JSSLOT_ARRAY_COUNT]++; + obj->dslots[idx] = v; + return JS_TRUE; + } + } + + if (!js_MakeArraySlow(cx, obj)) + return JS_FALSE; + } + + JSAutoTempIdRooter idr(cx); + + if (!IndexToId(cx, obj, index, NULL, idr.addr(), JS_TRUE)) + return JS_FALSE; + JS_ASSERT(!JSVAL_IS_VOID(idr.id())); + + return obj->setProperty(cx, idr.id(), &v); +} + +static JSBool +DeleteArrayElement(JSContext *cx, JSObject *obj, jsdouble index) +{ + JS_ASSERT(index >= 0); + if (OBJ_IS_DENSE_ARRAY(cx, obj)) { + if (index <= jsuint(-1)) { + jsuint idx = jsuint(index); + if (!INDEX_TOO_SPARSE(obj, idx) && idx < js_DenseArrayCapacity(obj)) { + if (obj->dslots[idx] != JSVAL_HOLE) + obj->fslots[JSSLOT_ARRAY_COUNT]--; + obj->dslots[idx] = JSVAL_HOLE; + return JS_TRUE; + } + } + return JS_TRUE; + } + + JSAutoTempIdRooter idr(cx); + + if (!IndexToId(cx, obj, index, NULL, idr.addr())) + return JS_FALSE; + if (JSVAL_IS_VOID(idr.id())) + return JS_TRUE; + + jsval junk; + return obj->deleteProperty(cx, idr.id(), &junk); +} + +/* + * When hole is true, delete the property at the given index. Otherwise set + * its value to v assuming v is rooted. + */ +static JSBool +SetOrDeleteArrayElement(JSContext *cx, JSObject *obj, jsdouble index, + JSBool hole, jsval v) +{ + if (hole) { + JS_ASSERT(JSVAL_IS_VOID(v)); + return DeleteArrayElement(cx, obj, index); + } + return SetArrayElement(cx, obj, index, v); +} + +JSBool +js_SetLengthProperty(JSContext *cx, JSObject *obj, jsdouble length) +{ + jsval v; + jsid id; + + if (!IndexToValue(cx, length, &v)) + return JS_FALSE; + id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); + return obj->setProperty(cx, id, &v); +} + +JSBool +js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp) +{ + JSErrorReporter older = JS_SetErrorReporter(cx, NULL); + JSAutoTempValueRooter tvr(cx, JSVAL_NULL); + jsid id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); + JSBool ok = obj->getProperty(cx, id, tvr.addr()); + JS_SetErrorReporter(cx, older); + if (!ok) + return false; + + *lengthp = ValueIsLength(cx, tvr.addr()); + return !JSVAL_IS_NULL(tvr.value()); +} + +JSBool +js_IsArrayLike(JSContext *cx, JSObject *obj, JSBool *answerp, jsuint *lengthp) +{ + JSClass *clasp; + + clasp = OBJ_GET_CLASS(cx, js_GetWrappedObject(cx, obj)); + *answerp = (clasp == &js_ArgumentsClass || clasp == &js_ArrayClass || + clasp == &js_SlowArrayClass); + if (!*answerp) { + *lengthp = 0; + return JS_TRUE; + } + return js_GetLengthProperty(cx, obj, lengthp); +} + +/* + * The 'length' property of all native Array instances is a shared permanent + * property of Array.prototype, so it appears to be a direct property of each + * array instance delegating to that Array.prototype. It accesses the private + * slot reserved by js_ArrayClass. + * + * Since SpiderMonkey supports cross-class prototype-based delegation, we have + * to be careful about the length getter and setter being called on an object + * not of Array class. For the getter, we search obj's prototype chain for the + * array that caused this getter to be invoked. In the setter case to overcome + * the JSPROP_SHARED attribute, we must define a shadowing length property. + */ +static JSBool +array_length_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + do { + if (OBJ_IS_ARRAY(cx, obj)) + return IndexToValue(cx, obj->fslots[JSSLOT_ARRAY_LENGTH], vp); + } while ((obj = OBJ_GET_PROTO(cx, obj)) != NULL); + return JS_TRUE; +} + +static JSBool +array_length_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + jsuint newlen, oldlen, gap, index; + jsval junk; + JSObject *iter; + JSTempValueRooter tvr; + JSBool ok; + + if (!OBJ_IS_ARRAY(cx, obj)) { + jsid lengthId = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); + + return obj->defineProperty(cx, lengthId, *vp, NULL, NULL, JSPROP_ENUMERATE); + } + + newlen = ValueIsLength(cx, vp); + if (JSVAL_IS_NULL(*vp)) + return JS_FALSE; + oldlen = obj->fslots[JSSLOT_ARRAY_LENGTH]; + + if (oldlen == newlen) + return JS_TRUE; + + if (!IndexToValue(cx, newlen, vp)) + return JS_FALSE; + + if (oldlen < newlen) { + obj->fslots[JSSLOT_ARRAY_LENGTH] = newlen; + return JS_TRUE; + } + + if (OBJ_IS_DENSE_ARRAY(cx, obj)) { + /* Don't reallocate if we're not actually shrinking our slots. */ + jsuint capacity = js_DenseArrayCapacity(obj); + if (capacity > newlen && !ResizeSlots(cx, obj, capacity, newlen)) + return JS_FALSE; + } else if (oldlen - newlen < (1 << 24)) { + do { + --oldlen; + if (!JS_CHECK_OPERATION_LIMIT(cx) || + !DeleteArrayElement(cx, obj, oldlen)) { + return JS_FALSE; + } + } while (oldlen != newlen); + } else { + /* + * We are going to remove a lot of indexes in a presumably sparse + * array. So instead of looping through indexes between newlen and + * oldlen, we iterate through all properties and remove those that + * correspond to indexes in the half-open range [newlen, oldlen). See + * bug 322135. + */ + iter = JS_NewPropertyIterator(cx, obj); + if (!iter) + return JS_FALSE; + + /* Protect iter against GC under JSObject::deleteProperty. */ + JS_PUSH_TEMP_ROOT_OBJECT(cx, iter, &tvr); + gap = oldlen - newlen; + for (;;) { + ok = (JS_CHECK_OPERATION_LIMIT(cx) && + JS_NextProperty(cx, iter, &id)); + if (!ok) + break; + if (JSVAL_IS_VOID(id)) + break; + if (js_IdIsIndex(id, &index) && index - newlen < gap) { + ok = obj->deleteProperty(cx, id, &junk); + if (!ok) + break; + } + } + JS_POP_TEMP_ROOT(cx, &tvr); + if (!ok) + return JS_FALSE; + } + + obj->fslots[JSSLOT_ARRAY_LENGTH] = newlen; + return JS_TRUE; +} + +/* + * We have only indexed properties up to capacity (excepting holes), plus the + * length property. For all else, we delegate to the prototype. + */ +static inline bool +IsDenseArrayId(JSContext *cx, JSObject *obj, jsid id) +{ + JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj)); + + uint32 i; + return id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom) || + (js_IdIsIndex(id, &i) && + obj->fslots[JSSLOT_ARRAY_LENGTH] != 0 && + i < js_DenseArrayCapacity(obj) && + obj->dslots[i] != JSVAL_HOLE); +} + +static JSBool +array_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, + JSProperty **propp) +{ + if (!OBJ_IS_DENSE_ARRAY(cx, obj)) + return js_LookupProperty(cx, obj, id, objp, propp); + + if (IsDenseArrayId(cx, obj, id)) { + *propp = (JSProperty *) id; + *objp = obj; + return JS_TRUE; + } + + JSObject *proto = STOBJ_GET_PROTO(obj); + if (!proto) { + *objp = NULL; + *propp = NULL; + return JS_TRUE; + } + return proto->lookupProperty(cx, id, objp, propp); +} + +static void +array_dropProperty(JSContext *cx, JSObject *obj, JSProperty *prop) +{ + JS_ASSERT(IsDenseArrayId(cx, obj, (jsid) prop)); +} + +JSBool +js_GetDenseArrayElementValue(JSContext *cx, JSObject *obj, JSProperty *prop, + jsval *vp) +{ + jsid id = (jsid) prop; + JS_ASSERT(IsDenseArrayId(cx, obj, id)); + + uint32 i; + if (!js_IdIsIndex(id, &i)) { + JS_ASSERT(id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)); + return IndexToValue(cx, obj->fslots[JSSLOT_ARRAY_LENGTH], vp); + } + *vp = obj->dslots[i]; + return JS_TRUE; +} + +static JSBool +array_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + uint32 i; + + if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) + return IndexToValue(cx, obj->fslots[JSSLOT_ARRAY_LENGTH], vp); + + if (id == ATOM_TO_JSID(cx->runtime->atomState.protoAtom)) { + *vp = OBJECT_TO_JSVAL(obj->getProto()); + return JS_TRUE; + } + + if (!OBJ_IS_DENSE_ARRAY(cx, obj)) + return js_GetProperty(cx, obj, id, vp); + + if (!js_IdIsIndex(ID_TO_VALUE(id), &i) || i >= js_DenseArrayCapacity(obj) || + obj->dslots[i] == JSVAL_HOLE) { + JSObject *obj2; + JSProperty *prop; + JSScopeProperty *sprop; + + JSObject *proto = STOBJ_GET_PROTO(obj); + if (!proto) { + *vp = JSVAL_VOID; + return JS_TRUE; + } + + *vp = JSVAL_VOID; + if (js_LookupPropertyWithFlags(cx, proto, id, cx->resolveFlags, + &obj2, &prop) < 0) + return JS_FALSE; + + if (prop) { + if (OBJ_IS_NATIVE(obj2)) { + sprop = (JSScopeProperty *) prop; + if (!js_NativeGet(cx, obj, obj2, sprop, JSGET_METHOD_BARRIER, vp)) + return JS_FALSE; + } + obj2->dropProperty(cx, prop); + } + return JS_TRUE; + } + + *vp = obj->dslots[i]; + return JS_TRUE; +} + +static JSBool +slowarray_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + jsuint index, length; + + if (!js_IdIsIndex(id, &index)) + return JS_TRUE; + length = obj->fslots[JSSLOT_ARRAY_LENGTH]; + if (index >= length) + obj->fslots[JSSLOT_ARRAY_LENGTH] = index + 1; + return JS_TRUE; +} + +static void +slowarray_trace(JSTracer *trc, JSObject *obj) +{ + uint32 length = obj->fslots[JSSLOT_ARRAY_LENGTH]; + + JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_SlowArrayClass); + + /* + * Move JSSLOT_ARRAY_LENGTH aside to prevent the GC from treating + * untagged integer values as objects or strings. + */ + obj->fslots[JSSLOT_ARRAY_LENGTH] = JSVAL_VOID; + js_TraceObject(trc, obj); + obj->fslots[JSSLOT_ARRAY_LENGTH] = length; +} + +static JSObjectOps js_SlowArrayObjectOps; + +static JSObjectOps * +slowarray_getObjectOps(JSContext *cx, JSClass *clasp) +{ + return &js_SlowArrayObjectOps; +} + +static JSBool +array_setProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + uint32 i; + + if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) + return array_length_setter(cx, obj, id, vp); + + if (!OBJ_IS_DENSE_ARRAY(cx, obj)) + return js_SetProperty(cx, obj, id, vp); + + if (!js_IdIsIndex(id, &i) || INDEX_TOO_SPARSE(obj, i)) { + if (!js_MakeArraySlow(cx, obj)) + return JS_FALSE; + return js_SetProperty(cx, obj, id, vp); + } + + if (!EnsureCapacity(cx, obj, i + 1)) + return JS_FALSE; + + if (i >= (uint32)obj->fslots[JSSLOT_ARRAY_LENGTH]) + obj->fslots[JSSLOT_ARRAY_LENGTH] = i + 1; + if (obj->dslots[i] == JSVAL_HOLE) + obj->fslots[JSSLOT_ARRAY_COUNT]++; + obj->dslots[i] = *vp; + return JS_TRUE; +} + +JSBool +js_PrototypeHasIndexedProperties(JSContext *cx, JSObject *obj) +{ + /* + * Walk up the prototype chain and see if this indexed element already + * exists. If we hit the end of the prototype chain, it's safe to set the + * element on the original object. + */ + while ((obj = obj->getProto()) != NULL) { + /* + * If the prototype is a non-native object (possibly a dense array), or + * a native object (possibly a slow array) that has indexed properties, + * return true. + */ + if (!OBJ_IS_NATIVE(obj)) + return JS_TRUE; + if (OBJ_SCOPE(obj)->hadIndexedProperties()) + return JS_TRUE; + } + return JS_FALSE; +} + +#ifdef JS_TRACER + +static inline JSBool FASTCALL +dense_grow(JSContext* cx, JSObject* obj, jsint i, jsval v) +{ + /* + * Let the interpreter worry about negative array indexes. + */ + JS_ASSERT((MAX_DSLOTS_LENGTH > MAX_DSLOTS_LENGTH32) == (sizeof(jsval) != sizeof(uint32))); + if (MAX_DSLOTS_LENGTH > MAX_DSLOTS_LENGTH32) { + /* + * Have to check for negative values bleeding through on 64-bit machines only, + * since we can't allocate large enough arrays for this on 32-bit machines. + */ + if (i < 0) + return JS_FALSE; + } + + /* + * If needed, grow the array as long it remains dense, otherwise fall off trace. + */ + jsuint u = jsuint(i); + jsuint capacity = js_DenseArrayCapacity(obj); + if ((u >= capacity) && (INDEX_TOO_SPARSE(obj, u) || !EnsureCapacity(cx, obj, u + 1))) + return JS_FALSE; + + if (obj->dslots[u] == JSVAL_HOLE) { + if (js_PrototypeHasIndexedProperties(cx, obj)) + return JS_FALSE; + + if (u >= jsuint(obj->fslots[JSSLOT_ARRAY_LENGTH])) + obj->fslots[JSSLOT_ARRAY_LENGTH] = u + 1; + ++obj->fslots[JSSLOT_ARRAY_COUNT]; + } + + obj->dslots[u] = v; + return JS_TRUE; +} + + +JSBool FASTCALL +js_Array_dense_setelem(JSContext* cx, JSObject* obj, jsint i, jsval v) +{ + JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj)); + return dense_grow(cx, obj, i, v); +} +JS_DEFINE_CALLINFO_4(extern, BOOL, js_Array_dense_setelem, CONTEXT, OBJECT, INT32, JSVAL, 0, 0) + +JSBool FASTCALL +js_Array_dense_setelem_int(JSContext* cx, JSObject* obj, jsint i, int32 j) +{ + JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj)); + + jsval v; + if (JS_LIKELY(INT_FITS_IN_JSVAL(j))) { + v = INT_TO_JSVAL(j); + } else { + jsdouble d = (jsdouble)j; + if (!js_NewDoubleInRootedValue(cx, d, &v)) + return JS_FALSE; + } + + return dense_grow(cx, obj, i, v); +} +JS_DEFINE_CALLINFO_4(extern, BOOL, js_Array_dense_setelem_int, CONTEXT, OBJECT, INT32, INT32, 0, 0) + +JSBool FASTCALL +js_Array_dense_setelem_double(JSContext* cx, JSObject* obj, jsint i, jsdouble d) +{ + JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj)); + + jsval v; + jsint j; + + if (JS_LIKELY(JSDOUBLE_IS_INT(d, j) && INT_FITS_IN_JSVAL(j))) { + v = INT_TO_JSVAL(j); + } else { + if (!js_NewDoubleInRootedValue(cx, d, &v)) + return JS_FALSE; + } + + return dense_grow(cx, obj, i, v); +} +JS_DEFINE_CALLINFO_4(extern, BOOL, js_Array_dense_setelem_double, CONTEXT, OBJECT, INT32, DOUBLE, 0, 0) +#endif + +static JSBool +array_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs) +{ + uint32 i; + JSBool isIndex; + + if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) + return JS_TRUE; + + isIndex = js_IdIsIndex(ID_TO_VALUE(id), &i); + if (!isIndex || attrs != JSPROP_ENUMERATE || !OBJ_IS_DENSE_ARRAY(cx, obj) || INDEX_TOO_SPARSE(obj, i)) { + if (!ENSURE_SLOW_ARRAY(cx, obj)) + return JS_FALSE; + return js_DefineProperty(cx, obj, id, value, getter, setter, attrs); + } + + return array_setProperty(cx, obj, id, &value); +} + +static JSBool +array_getAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, + uintN *attrsp) +{ + *attrsp = id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom) + ? JSPROP_PERMANENT : JSPROP_ENUMERATE; + return JS_TRUE; +} + +static JSBool +array_setAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, + uintN *attrsp) +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_CANT_SET_ARRAY_ATTRS); + return JS_FALSE; +} + +static JSBool +array_deleteProperty(JSContext *cx, JSObject *obj, jsval id, jsval *rval) +{ + uint32 i; + + if (!OBJ_IS_DENSE_ARRAY(cx, obj)) + return js_DeleteProperty(cx, obj, id, rval); + + if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) { + *rval = JSVAL_FALSE; + return JS_TRUE; + } + + if (js_IdIsIndex(id, &i) && i < js_DenseArrayCapacity(obj) && + obj->dslots[i] != JSVAL_HOLE) { + obj->fslots[JSSLOT_ARRAY_COUNT]--; + obj->dslots[i] = JSVAL_HOLE; + } + + *rval = JSVAL_TRUE; + return JS_TRUE; +} + +/* + * JSObjectOps.enumerate implementation. + * + * For a fast array, JSENUMERATE_INIT captures in the enumeration state both + * the length of the array and the bitmap indicating the positions of holes in + * the array. This ensures that adding or deleting array elements does not + * affect the sequence of indexes JSENUMERATE_NEXT returns. + * + * For a common case of an array without holes, to represent the state we pack + * the (nextEnumerationIndex, arrayLength) pair as a pseudo-boolean jsval. + * This is possible when length <= PACKED_UINT_PAIR_BITS. For arrays with + * greater length or holes we allocate the JSIndexIterState structure and + * store it as an int-tagged private pointer jsval. For a slow array we + * delegate the enumeration implementation to js_Enumerate in + * slowarray_enumerate. + * + * Array mutations can turn a fast array into a slow one after the enumeration + * starts. When this happens, slowarray_enumerate receives a state created + * when the array was fast. To distinguish such fast state from a slow state, + * which is an int-tagged pointer that js_Enumerate creates, we set not one + * but two lowest bits when tagging a JSIndexIterState pointer -- see + * INDEX_ITER_TAG usage below. Thus, when slowarray_enumerate receives a state + * tagged with JSVAL_SPECIAL or with two lowest bits set, it knows that this + * is a fast state so it calls array_enumerate to continue enumerating the + * indexes present in the original fast array. + */ + +#define PACKED_UINT_PAIR_BITS 14 +#define PACKED_UINT_PAIR_MASK JS_BITMASK(PACKED_UINT_PAIR_BITS) + +#define UINT_PAIR_TO_SPECIAL_JSVAL(i,j) \ + (JS_ASSERT((uint32) (i) <= PACKED_UINT_PAIR_MASK), \ + JS_ASSERT((uint32) (j) <= PACKED_UINT_PAIR_MASK), \ + ((jsval) (i) << (PACKED_UINT_PAIR_BITS + JSVAL_TAGBITS)) | \ + ((jsval) (j) << (JSVAL_TAGBITS)) | \ + (jsval) JSVAL_SPECIAL) + +#define SPECIAL_JSVAL_TO_UINT_PAIR(v,i,j) \ + (JS_ASSERT(JSVAL_IS_SPECIAL(v)), \ + (i) = (uint32) ((v) >> (PACKED_UINT_PAIR_BITS + JSVAL_TAGBITS)), \ + (j) = (uint32) ((v) >> JSVAL_TAGBITS) & PACKED_UINT_PAIR_MASK, \ + JS_ASSERT((i) <= PACKED_UINT_PAIR_MASK)) + +JS_STATIC_ASSERT(PACKED_UINT_PAIR_BITS * 2 + JSVAL_TAGBITS <= JS_BITS_PER_WORD); + +typedef struct JSIndexIterState { + uint32 index; + uint32 length; + JSBool hasHoles; + + /* + * Variable-length bitmap representing array's holes. It must not be + * accessed when hasHoles is false. + */ + jsbitmap holes[1]; +} JSIndexIterState; + +#define INDEX_ITER_TAG 3 + +JS_STATIC_ASSERT(JSVAL_INT == 1); + +static JSBool +array_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, + jsval *statep, jsid *idp) +{ + uint32 capacity, i; + JSIndexIterState *ii; + + switch (enum_op) { + case JSENUMERATE_INIT: + JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj)); + capacity = js_DenseArrayCapacity(obj); + if (idp) + *idp = INT_TO_JSVAL(obj->fslots[JSSLOT_ARRAY_COUNT]); + ii = NULL; + for (i = 0; i != capacity; ++i) { + if (obj->dslots[i] == JSVAL_HOLE) { + if (!ii) { + ii = (JSIndexIterState *) + cx->malloc(offsetof(JSIndexIterState, holes) + + JS_BITMAP_SIZE(capacity)); + if (!ii) + return JS_FALSE; + ii->hasHoles = JS_TRUE; + memset(ii->holes, 0, JS_BITMAP_SIZE(capacity)); + } + JS_SET_BIT(ii->holes, i); + } + } + if (!ii) { + /* Array has no holes. */ + if (capacity <= PACKED_UINT_PAIR_MASK) { + *statep = UINT_PAIR_TO_SPECIAL_JSVAL(0, capacity); + break; + } + ii = (JSIndexIterState *) + cx->malloc(offsetof(JSIndexIterState, holes)); + if (!ii) + return JS_FALSE; + ii->hasHoles = JS_FALSE; + } + ii->index = 0; + ii->length = capacity; + *statep = (jsval) ii | INDEX_ITER_TAG; + JS_ASSERT(*statep & JSVAL_INT); + break; + + case JSENUMERATE_NEXT: + if (JSVAL_IS_SPECIAL(*statep)) { + SPECIAL_JSVAL_TO_UINT_PAIR(*statep, i, capacity); + if (i != capacity) { + *idp = INT_TO_JSID(i); + *statep = UINT_PAIR_TO_SPECIAL_JSVAL(i + 1, capacity); + break; + } + } else { + JS_ASSERT((*statep & INDEX_ITER_TAG) == INDEX_ITER_TAG); + ii = (JSIndexIterState *) (*statep & ~INDEX_ITER_TAG); + i = ii->index; + if (i != ii->length) { + /* Skip holes if any. */ + if (ii->hasHoles) { + while (JS_TEST_BIT(ii->holes, i) && ++i != ii->length) + continue; + } + if (i != ii->length) { + ii->index = i + 1; + return js_IndexToId(cx, i, idp); + } + } + } + /* FALL THROUGH */ + + case JSENUMERATE_DESTROY: + if (!JSVAL_IS_SPECIAL(*statep)) { + JS_ASSERT((*statep & INDEX_ITER_TAG) == INDEX_ITER_TAG); + ii = (JSIndexIterState *) (*statep & ~INDEX_ITER_TAG); + cx->free(ii); + } + *statep = JSVAL_NULL; + break; + } + return JS_TRUE; +} + +static JSBool +slowarray_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, + jsval *statep, jsid *idp) +{ + JSBool ok; + + /* Are we continuing an enumeration that started when we were dense? */ + if (enum_op != JSENUMERATE_INIT) { + if (JSVAL_IS_SPECIAL(*statep) || + (*statep & INDEX_ITER_TAG) == INDEX_ITER_TAG) { + return array_enumerate(cx, obj, enum_op, statep, idp); + } + JS_ASSERT((*statep & INDEX_ITER_TAG) == JSVAL_INT); + } + ok = js_Enumerate(cx, obj, enum_op, statep, idp); + JS_ASSERT(*statep == JSVAL_NULL || (*statep & INDEX_ITER_TAG) == JSVAL_INT); + return ok; +} + +static void +array_finalize(JSContext *cx, JSObject *obj) +{ + if (obj->dslots) + cx->free(obj->dslots - 1); + obj->dslots = NULL; +} + +static void +array_trace(JSTracer *trc, JSObject *obj) +{ + uint32 capacity; + size_t i; + jsval v; + + JS_ASSERT(js_IsDenseArray(obj)); + obj->traceProtoAndParent(trc); + + capacity = js_DenseArrayCapacity(obj); + for (i = 0; i < capacity; i++) { + v = obj->dslots[i]; + if (JSVAL_IS_TRACEABLE(v)) { + JS_SET_TRACING_INDEX(trc, "array_dslots", i); + JS_CallTracer(trc, JSVAL_TO_TRACEABLE(v), JSVAL_TRACE_KIND(v)); + } + } +} + +extern JSObjectOps js_ArrayObjectOps; + +static const JSObjectMap SharedArrayMap(&js_ArrayObjectOps, JSObjectMap::SHAPELESS); + +JSObjectOps js_ArrayObjectOps = { + &SharedArrayMap, + array_lookupProperty, array_defineProperty, + array_getProperty, array_setProperty, + array_getAttributes, array_setAttributes, + array_deleteProperty, js_DefaultValue, + array_enumerate, js_CheckAccess, + NULL, array_dropProperty, + NULL, NULL, + js_HasInstance, array_trace, + NULL +}; + +static JSObjectOps * +array_getObjectOps(JSContext *cx, JSClass *clasp) +{ + return &js_ArrayObjectOps; +} + +JSClass js_ArrayClass = { + "Array", + JSCLASS_HAS_RESERVED_SLOTS(2) | + JSCLASS_HAS_CACHED_PROTO(JSProto_Array) | + JSCLASS_NEW_ENUMERATE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, js_TryValueOf, array_finalize, + array_getObjectOps, NULL, NULL, NULL, + NULL, NULL, NULL, NULL +}; + +JSClass js_SlowArrayClass = { + "Array", + JSCLASS_HAS_RESERVED_SLOTS(1) | + JSCLASS_HAS_CACHED_PROTO(JSProto_Array), + slowarray_addProperty, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, js_TryValueOf, NULL, + slowarray_getObjectOps, NULL, NULL, NULL, + NULL, NULL, NULL, NULL +}; + +/* + * Convert an array object from fast-and-dense to slow-and-flexible. + */ +JSBool +js_MakeArraySlow(JSContext *cx, JSObject *obj) +{ + JS_ASSERT(obj->getClass() == &js_ArrayClass); + + /* + * Create a native scope. All slow arrays other than Array.prototype get + * the same initial shape. + */ + uint32 emptyShape; + JSObject *arrayProto = obj->getProto(); + if (arrayProto->getClass() == &js_ObjectClass) { + /* obj is Array.prototype. */ + emptyShape = js_GenerateShape(cx, false); + } else { + /* arrayProto is Array.prototype. */ + JS_ASSERT(arrayProto->getClass() == &js_SlowArrayClass); + emptyShape = OBJ_SCOPE(arrayProto)->emptyScope->shape; + } + JSScope *scope = JSScope::create(cx, &js_SlowArrayObjectOps, &js_SlowArrayClass, obj, + emptyShape); + if (!scope) + return JS_FALSE; + + uint32 capacity = js_DenseArrayCapacity(obj); + if (capacity) { + scope->freeslot = STOBJ_NSLOTS(obj) + JS_INITIAL_NSLOTS; + obj->dslots[-1] = JS_INITIAL_NSLOTS + capacity; + } else { + scope->freeslot = STOBJ_NSLOTS(obj); + } + + /* Create new properties pointing to existing values in dslots */ + for (uint32 i = 0; i < capacity; i++) { + jsid id; + JSScopeProperty *sprop; + + if (!JS_ValueToId(cx, INT_TO_JSVAL(i), &id)) + goto out_bad; + + if (obj->dslots[i] == JSVAL_HOLE) { + obj->dslots[i] = JSVAL_VOID; + continue; + } + + sprop = scope->addDataProperty(cx, id, JS_INITIAL_NSLOTS + i, + JSPROP_ENUMERATE); + if (!sprop) + goto out_bad; + } + + /* + * Render our formerly-reserved count property GC-safe. If length fits in + * a jsval, set our slow/sparse COUNT to the current length as a jsval, so + * we can tell when only named properties have been added to a dense array + * to make it slow-but-not-sparse. + */ + { + uint32 length = obj->fslots[JSSLOT_ARRAY_LENGTH]; + obj->fslots[JSSLOT_ARRAY_COUNT] = INT_FITS_IN_JSVAL(length) + ? INT_TO_JSVAL(length) + : JSVAL_VOID; + } + + /* Make sure we preserve any flags borrowing bits in classword. */ + obj->classword ^= (jsuword) &js_ArrayClass; + obj->classword |= (jsuword) &js_SlowArrayClass; + + obj->map = scope; + return JS_TRUE; + + out_bad: + JSScope::destroy(cx, scope); + return JS_FALSE; +} + +/* Transfer ownership of buffer to returned string. */ +static inline JSBool +BufferToString(JSContext *cx, JSCharBuffer &cb, jsval *rval) +{ + JSString *str = js_NewStringFromCharBuffer(cx, cb); + if (!str) + return false; + *rval = STRING_TO_JSVAL(str); + return true; +} + +#if JS_HAS_TOSOURCE +static JSBool +array_toSource(JSContext *cx, uintN argc, jsval *vp) +{ + JS_CHECK_RECURSION(cx, return false); + + JSObject *obj = JS_THIS_OBJECT(cx, vp); + if (!obj || + (OBJ_GET_CLASS(cx, obj) != &js_SlowArrayClass && + !JS_InstanceOf(cx, obj, &js_ArrayClass, vp + 2))) { + return false; + } + + /* Find joins or cycles in the reachable object graph. */ + jschar *sharpchars; + JSHashEntry *he = js_EnterSharpObject(cx, obj, NULL, &sharpchars); + if (!he) + return false; + bool initiallySharp = IS_SHARP(he); + + /* After this point, all paths exit through the 'out' label. */ + MUST_FLOW_THROUGH("out"); + bool ok = false; + + /* + * This object will take responsibility for the jschar buffer until the + * buffer is transferred to the returned JSString. + */ + JSCharBuffer cb(cx); + + /* Cycles/joins are indicated by sharp objects. */ +#if JS_HAS_SHARP_VARS + if (IS_SHARP(he)) { + JS_ASSERT(sharpchars != 0); + cb.replaceRawBuffer(sharpchars, js_strlen(sharpchars)); + goto make_string; + } else if (sharpchars) { + MAKE_SHARP(he); + cb.replaceRawBuffer(sharpchars, js_strlen(sharpchars)); + } +#else + if (IS_SHARP(he)) { + if (!js_AppendLiteral(cb, "[]")) + goto out; + cx->free(sharpchars); + goto make_string; + } +#endif + + if (!cb.append('[')) + goto out; + + jsuint length; + if (!js_GetLengthProperty(cx, obj, &length)) + goto out; + + for (jsuint index = 0; index < length; index++) { + /* Use vp to locally root each element value. */ + JSBool hole; + if (!JS_CHECK_OPERATION_LIMIT(cx) || + !GetArrayElement(cx, obj, index, &hole, vp)) { + goto out; + } + + /* Get element's character string. */ + JSString *str; + if (hole) { + str = cx->runtime->emptyString; + } else { + str = js_ValueToSource(cx, *vp); + if (!str) + goto out; + } + *vp = STRING_TO_JSVAL(str); + const jschar *chars; + size_t charlen; + str->getCharsAndLength(chars, charlen); + + /* Append element to buffer. */ + if (!cb.append(chars, charlen)) + goto out; + if (index + 1 != length) { + if (!js_AppendLiteral(cb, ", ")) + goto out; + } else if (hole) { + if (!cb.append(',')) + goto out; + } + } + + /* Finalize the buffer. */ + if (!cb.append(']')) + goto out; + + make_string: + if (!BufferToString(cx, cb, vp)) + goto out; + + ok = true; + + out: + if (!initiallySharp) + js_LeaveSharpObject(cx, NULL); + return ok; +} +#endif + +static JSHashNumber +js_hash_array(const void *key) +{ + return (JSHashNumber)JS_PTR_TO_UINT32(key) >> JSVAL_TAGBITS; +} + +bool +js_InitContextBusyArrayTable(JSContext *cx) +{ + cx->busyArrayTable = JS_NewHashTable(4, js_hash_array, JS_CompareValues, + JS_CompareValues, NULL, NULL); + return cx->busyArrayTable != NULL; +} + +static JSBool +array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale, + JSString *sepstr, jsval *rval) +{ + JS_CHECK_RECURSION(cx, return false); + + /* + * This hash table is shared between toString invocations and must be empty + * after the root invocation completes. + */ + JSHashTable *table = cx->busyArrayTable; + + /* + * Use HashTable entry as the cycle indicator. On first visit, create the + * entry, and, when leaving, remove the entry. + */ + JSHashNumber hash = js_hash_array(obj); + JSHashEntry **hep = JS_HashTableRawLookup(table, hash, obj); + JSHashEntry *he = *hep; + if (!he) { + /* Not in hash table, so not a cycle. */ + he = JS_HashTableRawAdd(table, hep, hash, obj, NULL); + if (!he) { + JS_ReportOutOfMemory(cx); + return false; + } + } else { + /* Cycle, so return empty string. */ + *rval = ATOM_KEY(cx->runtime->atomState.emptyAtom); + return true; + } + + JSAutoTempValueRooter tvr(cx, obj); + + /* After this point, all paths exit through the 'out' label. */ + MUST_FLOW_THROUGH("out"); + bool ok = false; + + /* Get characters to use for the separator. */ + static const jschar comma = ','; + const jschar *sep; + size_t seplen; + if (sepstr) { + sepstr->getCharsAndLength(sep, seplen); + } else { + sep = , + seplen = 1; + } + + /* + * This object will take responsibility for the jschar buffer until the + * buffer is transferred to the returned JSString. + */ + JSCharBuffer cb(cx); + + jsuint length; + if (!js_GetLengthProperty(cx, obj, &length)) + goto out; + + for (jsuint index = 0; index < length; index++) { + /* Use rval to locally root each element value. */ + JSBool hole; + if (!JS_CHECK_OPERATION_LIMIT(cx) || + !GetArrayElement(cx, obj, index, &hole, rval)) { + goto out; + } + + /* Get element's character string. */ + if (!(hole || JSVAL_IS_VOID(*rval) || JSVAL_IS_NULL(*rval))) { + if (locale) { + /* Work on obj.toLocalString() instead. */ + JSObject *robj; + + if (!js_ValueToObject(cx, *rval, &robj)) + goto out; + *rval = OBJECT_TO_JSVAL(robj); + JSAtom *atom = cx->runtime->atomState.toLocaleStringAtom; + if (!js_TryMethod(cx, robj, atom, 0, NULL, rval)) + goto out; + } + + if (!js_ValueToCharBuffer(cx, *rval, cb)) + goto out; + } + + /* Append the separator. */ + if (index + 1 != length) { + if (!cb.append(sep, seplen)) + goto out; + } + } + + /* Finalize the buffer. */ + if (!BufferToString(cx, cb, rval)) + goto out; + + ok = true; + + out: + /* + * It is possible that 'hep' may have been invalidated by subsequent + * RawAdd/Remove. Hence, 'RawRemove' must not be used. + */ + JS_HashTableRemove(table, obj); + return ok; +} + +static JSBool +array_toString(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *obj; + + obj = JS_THIS_OBJECT(cx, vp); + if (!obj || + (OBJ_GET_CLASS(cx, obj) != &js_SlowArrayClass && + !JS_InstanceOf(cx, obj, &js_ArrayClass, vp + 2))) { + return JS_FALSE; + } + + return array_toString_sub(cx, obj, JS_FALSE, NULL, vp); +} + +static JSBool +array_toLocaleString(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *obj; + + obj = JS_THIS_OBJECT(cx, vp); + if (!obj || + (OBJ_GET_CLASS(cx, obj) != &js_SlowArrayClass && + !JS_InstanceOf(cx, obj, &js_ArrayClass, vp + 2))) { + return JS_FALSE; + } + + /* + * Passing comma here as the separator. Need a way to get a + * locale-specific version. + */ + return array_toString_sub(cx, obj, JS_TRUE, NULL, vp); +} + +enum TargetElementsType { + TargetElementsAllHoles, + TargetElementsMayContainValues +}; + +enum SourceVectorType { + SourceVectorAllValues, + SourceVectorMayContainHoles +}; + +static JSBool +InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint count, jsval *vector, + TargetElementsType targetType, SourceVectorType vectorType) +{ + JS_ASSERT(count < MAXINDEX); + + /* + * Optimize for dense arrays so long as adding the given set of elements + * wouldn't otherwise make the array slow. + */ + if (OBJ_IS_DENSE_ARRAY(cx, obj) && !js_PrototypeHasIndexedProperties(cx, obj) && + start <= MAXINDEX - count && !INDEX_TOO_BIG(start + count)) { + +#ifdef DEBUG_jwalden + { + /* Verify that overwriteType and writeType were accurate. */ + JSAutoTempIdRooter idr(cx, JSVAL_ZERO); + for (jsuint i = 0; i < count; i++) { + JS_ASSERT_IF(vectorType == SourceVectorAllValues, vector[i] != JSVAL_HOLE); + + jsdouble index = jsdouble(start) + i; + if (targetType == TargetElementsAllHoles && index < jsuint(-1)) { + JS_ASSERT(ReallyBigIndexToId(cx, index, idr.addr())); + JSObject* obj2; + JSProperty* prop; + JS_ASSERT(obj->lookupProperty(cx, idr.id(), &obj2, &prop)); + JS_ASSERT(!prop); + } + } + } +#endif + + jsuint newlen = start + count; + JS_ASSERT(jsdouble(start) + count == jsdouble(newlen)); + if (!EnsureCapacity(cx, obj, newlen)) + return JS_FALSE; + + if (newlen > uint32(obj->fslots[JSSLOT_ARRAY_LENGTH])) + obj->fslots[JSSLOT_ARRAY_LENGTH] = newlen; + + JS_ASSERT(count < size_t(-1) / sizeof(jsval)); + if (targetType == TargetElementsMayContainValues) { + jsuint valueCount = 0; + for (jsuint i = 0; i < count; i++) { + if (obj->dslots[start + i] != JSVAL_HOLE) + valueCount++; + } + JS_ASSERT(uint32(obj->fslots[JSSLOT_ARRAY_COUNT]) >= valueCount); + obj->fslots[JSSLOT_ARRAY_COUNT] -= valueCount; + } + memcpy(obj->dslots + start, vector, sizeof(jsval) * count); + if (vectorType == SourceVectorAllValues) { + obj->fslots[JSSLOT_ARRAY_COUNT] += count; + } else { + jsuint valueCount = 0; + for (jsuint i = 0; i < count; i++) { + if (obj->dslots[start + i] != JSVAL_HOLE) + valueCount++; + } + obj->fslots[JSSLOT_ARRAY_COUNT] += valueCount; + } + JS_ASSERT_IF(count != 0, obj->dslots[newlen - 1] != JSVAL_HOLE); + return JS_TRUE; + } + + jsval* end = vector + count; + while (vector != end && start < MAXINDEX) { + if (!JS_CHECK_OPERATION_LIMIT(cx) || + !SetArrayElement(cx, obj, start++, *vector++)) { + return JS_FALSE; + } + } + + if (vector == end) + return JS_TRUE; + + /* Finish out any remaining elements past the max array index. */ + if (OBJ_IS_DENSE_ARRAY(cx, obj) && !ENSURE_SLOW_ARRAY(cx, obj)) + return JS_FALSE; + + JS_ASSERT(start == MAXINDEX); + jsval tmp[2] = {JSVAL_NULL, JSVAL_NULL}; + JSAutoTempValueRooter tvr(cx, JS_ARRAY_LENGTH(tmp), tmp); + if (!js_NewDoubleInRootedValue(cx, MAXINDEX, &tmp[0])) + return JS_FALSE; + jsdouble *dp = JSVAL_TO_DOUBLE(tmp[0]); + JS_ASSERT(*dp == MAXINDEX); + JSAutoTempIdRooter idr(cx); + do { + tmp[1] = *vector++; + if (!js_ValueToStringId(cx, tmp[0], idr.addr()) || + !obj->setProperty(cx, idr.id(), &tmp[1])) { + return JS_FALSE; + } + *dp += 1; + } while (vector != end); + + return JS_TRUE; +} + +static JSBool +InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, jsval *vector, + JSBool holey = JS_FALSE) +{ + JS_ASSERT(OBJ_IS_ARRAY(cx, obj)); + + obj->fslots[JSSLOT_ARRAY_LENGTH] = length; + + if (vector) { + if (!EnsureCapacity(cx, obj, length)) + return JS_FALSE; + + jsuint count = length; + if (!holey) { + memcpy(obj->dslots, vector, length * sizeof (jsval)); + } else { + for (jsuint i = 0; i < length; i++) { + if (vector[i] == JSVAL_HOLE) + --count; + obj->dslots[i] = vector[i]; + } + } + obj->fslots[JSSLOT_ARRAY_COUNT] = count; + } else { + obj->fslots[JSSLOT_ARRAY_COUNT] = 0; + } + return JS_TRUE; +} + +#ifdef JS_TRACER +static JSString* FASTCALL +Array_p_join(JSContext* cx, JSObject* obj, JSString *str) +{ + JSAutoTempValueRooter tvr(cx); + if (!array_toString_sub(cx, obj, JS_FALSE, str, tvr.addr())) { + js_SetBuiltinError(cx); + return NULL; + } + return JSVAL_TO_STRING(tvr.value()); +} + +static JSString* FASTCALL +Array_p_toString(JSContext* cx, JSObject* obj) +{ + JSAutoTempValueRooter tvr(cx); + if (!array_toString_sub(cx, obj, JS_FALSE, NULL, tvr.addr())) { + js_SetBuiltinError(cx); + return NULL; + } + return JSVAL_TO_STRING(tvr.value()); +} +#endif + +/* + * Perl-inspired join, reverse, and sort. + */ +static JSBool +array_join(JSContext *cx, uintN argc, jsval *vp) +{ + JSString *str; + JSObject *obj; + + if (argc == 0 || JSVAL_IS_VOID(vp[2])) { + str = NULL; + } else { + str = js_ValueToString(cx, vp[2]); + if (!str) + return JS_FALSE; + vp[2] = STRING_TO_JSVAL(str); + } + obj = JS_THIS_OBJECT(cx, vp); + return obj && array_toString_sub(cx, obj, JS_FALSE, str, vp); +} + +static JSBool +array_reverse(JSContext *cx, uintN argc, jsval *vp) +{ + jsuint len; + JSObject *obj = JS_THIS_OBJECT(cx, vp); + if (!obj || !js_GetLengthProperty(cx, obj, &len)) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(obj); + + if (OBJ_IS_DENSE_ARRAY(cx, obj) && !js_PrototypeHasIndexedProperties(cx, obj)) { + /* An empty array or an array with no elements is already reversed. */ + if (len == 0 || !obj->dslots) + return JS_TRUE; + + /* + * It's actually surprisingly complicated to reverse an array due to the + * orthogonality of array length and array capacity while handling + * leading and trailing holes correctly. Reversing seems less likely to + * be a common operation than other array mass-mutation methods, so for + * now just take a probably-small memory hit (in the absence of too many + * holes in the array at its start) and ensure that the capacity is + * sufficient to hold all the elements in the array if it were full. + */ + if (!EnsureCapacity(cx, obj, len)) + return JS_FALSE; + + jsval* lo = &obj->dslots[0]; + jsval* hi = &obj->dslots[len - 1]; + for (; lo < hi; lo++, hi--) { + jsval tmp = *lo; + *lo = *hi; + *hi = tmp; + } + + /* + * Per ECMA-262, don't update the length of the array, even if the new + * array has trailing holes (and thus the original array began with + * holes). + */ + return JS_TRUE; + } + + JSAutoTempValueRooter tvr(cx, JSVAL_NULL); + for (jsuint i = 0, half = len / 2; i < half; i++) { + JSBool hole, hole2; + if (!JS_CHECK_OPERATION_LIMIT(cx) || + !GetArrayElement(cx, obj, i, &hole, tvr.addr()) || + !GetArrayElement(cx, obj, len - i - 1, &hole2, vp) || + !SetOrDeleteArrayElement(cx, obj, len - i - 1, hole, tvr.value()) || + !SetOrDeleteArrayElement(cx, obj, i, hole2, *vp)) { + return false; + } + } + *vp = OBJECT_TO_JSVAL(obj); + return true; +} + +typedef struct MSortArgs { + size_t elsize; + JSComparator cmp; + void *arg; + JSBool fastcopy; +} MSortArgs; + +/* Helper function for js_MergeSort. */ +static JSBool +MergeArrays(MSortArgs *msa, void *src, void *dest, size_t run1, size_t run2) +{ + void *arg, *a, *b, *c; + size_t elsize, runtotal; + int cmp_result; + JSComparator cmp; + JSBool fastcopy; + + runtotal = run1 + run2; + + elsize = msa->elsize; + cmp = msa->cmp; + arg = msa->arg; + fastcopy = msa->fastcopy; + +#define CALL_CMP(a, b) \ + if (!cmp(arg, (a), (b), &cmp_result)) return JS_FALSE; + + /* Copy runs already in sorted order. */ + b = (char *)src + run1 * elsize; + a = (char *)b - elsize; + CALL_CMP(a, b); + if (cmp_result <= 0) { + memcpy(dest, src, runtotal * elsize); + return JS_TRUE; + } + +#define COPY_ONE(p,q,n) \ + (fastcopy ? (void)(*(jsval*)(p) = *(jsval*)(q)) : (void)memcpy(p, q, n)) + + a = src; + c = dest; + for (; runtotal != 0; runtotal--) { + JSBool from_a = run2 == 0; + if (!from_a && run1 != 0) { + CALL_CMP(a,b); + from_a = cmp_result <= 0; + } + + if (from_a) { + COPY_ONE(c, a, elsize); + run1--; + a = (char *)a + elsize; + } else { + COPY_ONE(c, b, elsize); + run2--; + b = (char *)b + elsize; + } + c = (char *)c + elsize; + } +#undef COPY_ONE +#undef CALL_CMP + + return JS_TRUE; +} + +/* + * This sort is stable, i.e. sequence of equal elements is preserved. + * See also bug #224128. + */ +JSBool +js_MergeSort(void *src, size_t nel, size_t elsize, + JSComparator cmp, void *arg, void *tmp) +{ + void *swap, *vec1, *vec2; + MSortArgs msa; + size_t i, j, lo, hi, run; + JSBool fastcopy; + int cmp_result; + + /* Avoid memcpy overhead for word-sized and word-aligned elements. */ + fastcopy = (elsize == sizeof(jsval) && + (((jsuword) src | (jsuword) tmp) & JSVAL_ALIGN) == 0); +#define COPY_ONE(p,q,n) \ + (fastcopy ? (void)(*(jsval*)(p) = *(jsval*)(q)) : (void)memcpy(p, q, n)) +#define CALL_CMP(a, b) \ + if (!cmp(arg, (a), (b), &cmp_result)) return JS_FALSE; +#define INS_SORT_INT 4 + + /* + * Apply insertion sort to small chunks to reduce the number of merge + * passes needed. + */ + for (lo = 0; lo < nel; lo += INS_SORT_INT) { + hi = lo + INS_SORT_INT; + if (hi >= nel) + hi = nel; + for (i = lo + 1; i < hi; i++) { + vec1 = (char *)src + i * elsize; + vec2 = (char *)vec1 - elsize; + for (j = i; j > lo; j--) { + CALL_CMP(vec2, vec1); + /* "<=" instead of "<" insures the sort is stable */ + if (cmp_result <= 0) { + break; + } + + /* Swap elements, using "tmp" as tmp storage */ + COPY_ONE(tmp, vec2, elsize); + COPY_ONE(vec2, vec1, elsize); + COPY_ONE(vec1, tmp, elsize); + vec1 = vec2; + vec2 = (char *)vec1 - elsize; + } + } + } +#undef CALL_CMP +#undef COPY_ONE + + msa.elsize = elsize; + msa.cmp = cmp; + msa.arg = arg; + msa.fastcopy = fastcopy; + + vec1 = src; + vec2 = tmp; + for (run = INS_SORT_INT; run < nel; run *= 2) { + for (lo = 0; lo < nel; lo += 2 * run) { + hi = lo + run; + if (hi >= nel) { + memcpy((char *)vec2 + lo * elsize, (char *)vec1 + lo * elsize, + (nel - lo) * elsize); + break; + } + if (!MergeArrays(&msa, (char *)vec1 + lo * elsize, + (char *)vec2 + lo * elsize, run, + hi + run > nel ? nel - hi : run)) { + return JS_FALSE; + } + } + swap = vec1; + vec1 = vec2; + vec2 = swap; + } + if (src != vec1) + memcpy(src, tmp, nel * elsize); + + return JS_TRUE; +} + +typedef struct CompareArgs { + JSContext *context; + jsval fval; + jsval *elemroot; /* stack needed for js_Invoke */ +} CompareArgs; + +static JS_REQUIRES_STACK JSBool +sort_compare(void *arg, const void *a, const void *b, int *result) +{ + jsval av = *(const jsval *)a, bv = *(const jsval *)b; + CompareArgs *ca = (CompareArgs *) arg; + JSContext *cx = ca->context; + jsval *invokevp, *sp; + jsdouble cmp; + + /** + * array_sort deals with holes and undefs on its own and they should not + * come here. + */ + JS_ASSERT(!JSVAL_IS_VOID(av)); + JS_ASSERT(!JSVAL_IS_VOID(bv)); + + if (!JS_CHECK_OPERATION_LIMIT(cx)) + return JS_FALSE; + + invokevp = ca->elemroot; + sp = invokevp; + *sp++ = ca->fval; + *sp++ = JSVAL_NULL; + *sp++ = av; + *sp++ = bv; + + if (!js_Invoke(cx, 2, invokevp, 0)) + return JS_FALSE; + + cmp = js_ValueToNumber(cx, invokevp); + if (JSVAL_IS_NULL(*invokevp)) + return JS_FALSE; + + /* Clamp cmp to -1, 0, 1. */ + *result = 0; + if (!JSDOUBLE_IS_NaN(cmp) && cmp != 0) + *result = cmp > 0 ? 1 : -1; + + /* + * XXX else report some kind of error here? ECMA talks about 'consistent + * compare functions' that don't return NaN, but is silent about what the + * result should be. So we currently ignore it. + */ + + return JS_TRUE; +} + +typedef JSBool (JS_REQUIRES_STACK *JSRedComparator)(void*, const void*, + const void*, int *); + +static inline JS_IGNORE_STACK JSComparator +comparator_stack_cast(JSRedComparator func) +{ + return func; +} + +static int +sort_compare_strings(void *arg, const void *a, const void *b, int *result) +{ + jsval av = *(const jsval *)a, bv = *(const jsval *)b; + + JS_ASSERT(JSVAL_IS_STRING(av)); + JS_ASSERT(JSVAL_IS_STRING(bv)); + if (!JS_CHECK_OPERATION_LIMIT((JSContext *)arg)) + return JS_FALSE; + + *result = (int) js_CompareStrings(JSVAL_TO_STRING(av), JSVAL_TO_STRING(bv)); + return JS_TRUE; +} + +/* + * The array_sort function below assumes JSVAL_NULL is zero in order to + * perform initialization using memset. Other parts of SpiderMonkey likewise + * "know" that JSVAL_NULL is zero; this static assertion covers all cases. + */ +JS_STATIC_ASSERT(JSVAL_NULL == 0); + +static JSBool +array_sort(JSContext *cx, uintN argc, jsval *vp) +{ + jsval *argv, fval, *vec, *mergesort_tmp, v; + JSObject *obj; + CompareArgs ca; + jsuint len, newlen, i, undefs; + JSTempValueRooter tvr; + JSBool hole; + JSBool ok; + size_t elemsize; + JSString *str; + + /* + * Optimize the default compare function case if all of obj's elements + * have values of type string. + */ + JSBool all_strings; + + argv = JS_ARGV(cx, vp); + if (argc > 0) { + if (JSVAL_IS_PRIMITIVE(argv[0])) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_SORT_ARG); + return JS_FALSE; + } + fval = argv[0]; /* non-default compare function */ + } else { + fval = JSVAL_NULL; + } + + obj = JS_THIS_OBJECT(cx, vp); + if (!obj || !js_GetLengthProperty(cx, obj, &len)) + return JS_FALSE; + if (len == 0) { + *vp = OBJECT_TO_JSVAL(obj); + return JS_TRUE; + } + + /* + * We need a temporary array of 2 * len jsvals to hold the array elements + * and the scratch space for merge sort. Check that its size does not + * overflow size_t, which would allow for indexing beyond the end of the + * malloc'd vector. + */ +#if JS_BITS_PER_WORD == 32 + if ((size_t)len > ~(size_t)0 / (2 * sizeof(jsval))) { + js_ReportAllocationOverflow(cx); + return JS_FALSE; + } +#endif + vec = (jsval *) cx->malloc(2 * (size_t) len * sizeof(jsval)); + if (!vec) + return JS_FALSE; + + /* + * Initialize vec as a root. We will clear elements of vec one by + * one while increasing tvr.count when we know that the property at + * the corresponding index exists and its value must be rooted. + * + * In this way when sorting a huge mostly sparse array we will not + * access the tail of vec corresponding to properties that do not + * exist, allowing OS to avoiding committing RAM. See bug 330812. + * + * After this point control must flow through label out: to exit. + */ + JS_PUSH_TEMP_ROOT(cx, 0, vec, &tvr); + + /* + * By ECMA 262, 15.4.4.11, a property that does not exist (which we + * call a "hole") is always greater than an existing property with + * value undefined and that is always greater than any other property. + * Thus to sort holes and undefs we simply count them, sort the rest + * of elements, append undefs after them and then make holes after + * undefs. + */ + undefs = 0; + newlen = 0; + all_strings = JS_TRUE; + for (i = 0; i < len; i++) { + ok = JS_CHECK_OPERATION_LIMIT(cx); + if (!ok) + goto out; + + /* Clear vec[newlen] before including it in the rooted set. */ + vec[newlen] = JSVAL_NULL; + tvr.count = newlen + 1; + ok = GetArrayElement(cx, obj, i, &hole, &vec[newlen]); + if (!ok) + goto out; + + if (hole) + continue; + + if (JSVAL_IS_VOID(vec[newlen])) { + ++undefs; + continue; + } + + /* We know JSVAL_IS_STRING yields 0 or 1, so avoid a branch via &=. */ + all_strings &= JSVAL_IS_STRING(vec[newlen]); + + ++newlen; + } + + if (newlen == 0) { + /* The array has only holes and undefs. */ + ok = JS_TRUE; + goto out; + } + + /* + * The first newlen elements of vec are copied from the array object + * (above). The remaining newlen positions are used as GC-rooted scratch + * space for mergesort. We must clear the space before including it to + * the root set covered by tvr.count. We assume JSVAL_NULL==0 to optimize + * initialization using memset. + */ + mergesort_tmp = vec + newlen; + memset(mergesort_tmp, 0, newlen * sizeof(jsval)); + tvr.count = newlen * 2; + + /* Here len == 2 * (newlen + undefs + number_of_holes). */ + if (fval == JSVAL_NULL) { + /* + * Sort using the default comparator converting all elements to + * strings. + */ + if (all_strings) { + elemsize = sizeof(jsval); + } else { + /* + * To avoid string conversion on each compare we do it only once + * prior to sorting. But we also need the space for the original + * values to recover the sorting result. To reuse + * sort_compare_strings we move the original values to the odd + * indexes in vec, put the string conversion results in the even + * indexes and pass 2 * sizeof(jsval) as an element size to the + * sorting function. In this way sort_compare_strings will only + * see the string values when it casts the compare arguments as + * pointers to jsval. + * + * This requires doubling the temporary storage including the + * scratch space for the merge sort. Since vec already contains + * the rooted scratch space for newlen elements at the tail, we + * can use it to rearrange and convert to strings first and try + * realloc only when we know that we successfully converted all + * the elements. + */ +#if JS_BITS_PER_WORD == 32 + if ((size_t)newlen > ~(size_t)0 / (4 * sizeof(jsval))) { + js_ReportAllocationOverflow(cx); + ok = JS_FALSE; + goto out; + } +#endif + + /* + * Rearrange and string-convert the elements of the vector from + * the tail here and, after sorting, move the results back + * starting from the start to prevent overwrite the existing + * elements. + */ + i = newlen; + do { + --i; + ok = JS_CHECK_OPERATION_LIMIT(cx); + if (!ok) + goto out; + v = vec[i]; + str = js_ValueToString(cx, v); + if (!str) { + ok = JS_FALSE; + goto out; + } + vec[2 * i] = STRING_TO_JSVAL(str); + vec[2 * i + 1] = v; + } while (i != 0); + + JS_ASSERT(tvr.u.array == vec); + vec = (jsval *) cx->realloc(vec, + 4 * (size_t) newlen * sizeof(jsval)); + if (!vec) { + vec = tvr.u.array; + ok = JS_FALSE; + goto out; + } + tvr.u.array = vec; + mergesort_tmp = vec + 2 * newlen; + memset(mergesort_tmp, 0, newlen * 2 * sizeof(jsval)); + tvr.count = newlen * 4; + elemsize = 2 * sizeof(jsval); + } + ok = js_MergeSort(vec, (size_t) newlen, elemsize, + sort_compare_strings, cx, mergesort_tmp); + if (!ok) + goto out; + if (!all_strings) { + /* + * We want to make the following loop fast and to unroot the + * cached results of toString invocations before the operation + * callback has a chance to run the GC. For this reason we do + * not call JS_CHECK_OPERATION_LIMIT in the loop. + */ + i = 0; + do { + vec[i] = vec[2 * i + 1]; + } while (++i != newlen); + } + } else { + void *mark; + + js_LeaveTrace(cx); + + ca.context = cx; + ca.fval = fval; + ca.elemroot = js_AllocStack(cx, 2 + 2, &mark); + if (!ca.elemroot) { + ok = JS_FALSE; + goto out; + } + ok = js_MergeSort(vec, (size_t) newlen, sizeof(jsval), + comparator_stack_cast(sort_compare), + &ca, mergesort_tmp); + js_FreeStack(cx, mark); + if (!ok) + goto out; + } + + /* + * We no longer need to root the scratch space for the merge sort, so + * unroot it now to make the job of a potential GC under InitArrayElements + * easier. + */ + tvr.count = newlen; + ok = InitArrayElements(cx, obj, 0, newlen, vec, TargetElementsMayContainValues, + SourceVectorAllValues); + if (!ok) + goto out; + + out: + JS_POP_TEMP_ROOT(cx, &tvr); + cx->free(vec); + if (!ok) + return JS_FALSE; + + /* Set undefs that sorted after the rest of elements. */ + while (undefs != 0) { + --undefs; + if (!JS_CHECK_OPERATION_LIMIT(cx) || + !SetArrayElement(cx, obj, newlen++, JSVAL_VOID)) { + return JS_FALSE; + } + } + + /* Re-create any holes that sorted to the end of the array. */ + while (len > newlen) { + if (!JS_CHECK_OPERATION_LIMIT(cx) || + !DeleteArrayElement(cx, obj, --len)) { + return JS_FALSE; + } + } + *vp = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +/* + * Perl-inspired push, pop, shift, unshift, and splice methods. + */ +static JSBool +array_push_slowly(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsuint length; + + if (!js_GetLengthProperty(cx, obj, &length)) + return JS_FALSE; + if (!InitArrayElements(cx, obj, length, argc, argv, TargetElementsMayContainValues, + SourceVectorAllValues)) { + return JS_FALSE; + } + + /* Per ECMA-262, return the new array length. */ + jsdouble newlength = length + jsdouble(argc); + if (!IndexToValue(cx, newlength, rval)) + return JS_FALSE; + return js_SetLengthProperty(cx, obj, newlength); +} + +static JSBool +array_push1_dense(JSContext* cx, JSObject* obj, jsval v, jsval *rval) +{ + uint32 length = obj->fslots[JSSLOT_ARRAY_LENGTH]; + if (INDEX_TOO_SPARSE(obj, length)) { + if (!js_MakeArraySlow(cx, obj)) + return JS_FALSE; + return array_push_slowly(cx, obj, 1, &v, rval); + } + + if (!EnsureCapacity(cx, obj, length + 1)) + return JS_FALSE; + obj->fslots[JSSLOT_ARRAY_LENGTH] = length + 1; + + JS_ASSERT(obj->dslots[length] == JSVAL_HOLE); + obj->fslots[JSSLOT_ARRAY_COUNT]++; + obj->dslots[length] = v; + return IndexToValue(cx, obj->fslots[JSSLOT_ARRAY_LENGTH], rval); +} + +JSBool JS_FASTCALL +js_ArrayCompPush(JSContext *cx, JSObject *obj, jsval v) +{ + JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj)); + uint32_t length = (uint32_t) obj->fslots[JSSLOT_ARRAY_LENGTH]; + JS_ASSERT(length <= js_DenseArrayCapacity(obj)); + + if (length == js_DenseArrayCapacity(obj)) { + if (length > JS_ARGS_LENGTH_MAX) { + JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, + JSMSG_ARRAY_INIT_TOO_BIG); + return JS_FALSE; + } + + if (!EnsureCapacity(cx, obj, length + 1)) + return JS_FALSE; + } + obj->fslots[JSSLOT_ARRAY_LENGTH] = length + 1; + obj->fslots[JSSLOT_ARRAY_COUNT]++; + obj->dslots[length] = v; + return JS_TRUE; +} +JS_DEFINE_CALLINFO_3(extern, BOOL, js_ArrayCompPush, CONTEXT, OBJECT, JSVAL, 0, 0) + +#ifdef JS_TRACER +static jsval FASTCALL +Array_p_push1(JSContext* cx, JSObject* obj, jsval v) +{ + JSAutoTempValueRooter tvr(cx, v); + if (OBJ_IS_DENSE_ARRAY(cx, obj) + ? array_push1_dense(cx, obj, v, tvr.addr()) + : array_push_slowly(cx, obj, 1, tvr.addr(), tvr.addr())) { + return tvr.value(); + } + js_SetBuiltinError(cx); + return JSVAL_VOID; +} +#endif + +static JSBool +array_push(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *obj; + + /* Insist on one argument and obj of the expected class. */ + obj = JS_THIS_OBJECT(cx, vp); + if (!obj) + return JS_FALSE; + if (argc != 1 || !OBJ_IS_DENSE_ARRAY(cx, obj)) + return array_push_slowly(cx, obj, argc, vp + 2, vp); + + return array_push1_dense(cx, obj, vp[2], vp); +} + +static JSBool +array_pop_slowly(JSContext *cx, JSObject* obj, jsval *vp) +{ + jsuint index; + JSBool hole; + + if (!js_GetLengthProperty(cx, obj, &index)) + return JS_FALSE; + if (index == 0) { + *vp = JSVAL_VOID; + } else { + index--; + + /* Get the to-be-deleted property's value into vp. */ + if (!GetArrayElement(cx, obj, index, &hole, vp)) + return JS_FALSE; + if (!hole && !DeleteArrayElement(cx, obj, index)) + return JS_FALSE; + } + return js_SetLengthProperty(cx, obj, index); +} + +static JSBool +array_pop_dense(JSContext *cx, JSObject* obj, jsval *vp) +{ + jsuint index; + JSBool hole; + + index = obj->fslots[JSSLOT_ARRAY_LENGTH]; + if (index == 0) { + *vp = JSVAL_VOID; + return JS_TRUE; + } + index--; + if (!GetArrayElement(cx, obj, index, &hole, vp)) + return JS_FALSE; + if (!hole && !DeleteArrayElement(cx, obj, index)) + return JS_FALSE; + obj->fslots[JSSLOT_ARRAY_LENGTH] = index; + return JS_TRUE; +} + +#ifdef JS_TRACER +static jsval FASTCALL +Array_p_pop(JSContext* cx, JSObject* obj) +{ + JSAutoTempValueRooter tvr(cx); + if (OBJ_IS_DENSE_ARRAY(cx, obj) + ? array_pop_dense(cx, obj, tvr.addr()) + : array_pop_slowly(cx, obj, tvr.addr())) { + return tvr.value(); + } + js_SetBuiltinError(cx); + return JSVAL_VOID; +} +#endif + +static JSBool +array_pop(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *obj; + + obj = JS_THIS_OBJECT(cx, vp); + if (!obj) + return JS_FALSE; + if (OBJ_IS_DENSE_ARRAY(cx, obj)) + return array_pop_dense(cx, obj, vp); + return array_pop_slowly(cx, obj, vp); +} + +static JSBool +array_shift(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *obj; + jsuint length, i; + JSBool hole; + + obj = JS_THIS_OBJECT(cx, vp); + if (!obj || !js_GetLengthProperty(cx, obj, &length)) + return JS_FALSE; + if (length == 0) { + *vp = JSVAL_VOID; + } else { + length--; + + if (OBJ_IS_DENSE_ARRAY(cx, obj) && !js_PrototypeHasIndexedProperties(cx, obj) && + length < js_DenseArrayCapacity(obj)) { + if (JS_LIKELY(obj->dslots != NULL)) { + *vp = obj->dslots[0]; + if (*vp == JSVAL_HOLE) + *vp = JSVAL_VOID; + else + obj->fslots[JSSLOT_ARRAY_COUNT]--; + memmove(obj->dslots, obj->dslots + 1, length * sizeof(jsval)); + obj->dslots[length] = JSVAL_HOLE; + } else { + /* + * We don't need to modify the indexed properties of an empty array + * with an explicitly set non-zero length when shift() is called on + * it, but note fallthrough to reduce the length by one. + */ + JS_ASSERT(obj->fslots[JSSLOT_ARRAY_COUNT] == 0); + *vp = JSVAL_VOID; + } + } else { + /* Get the to-be-deleted property's value into vp ASAP. */ + if (!GetArrayElement(cx, obj, 0, &hole, vp)) + return JS_FALSE; + + /* Slide down the array above the first element. */ + JSAutoTempValueRooter tvr(cx, JSVAL_NULL); + for (i = 0; i != length; i++) { + if (!JS_CHECK_OPERATION_LIMIT(cx) || + !GetArrayElement(cx, obj, i + 1, &hole, tvr.addr()) || + !SetOrDeleteArrayElement(cx, obj, i, hole, tvr.value())) { + return JS_FALSE; + } + } + + /* Delete the only or last element when it exists. */ + if (!hole && !DeleteArrayElement(cx, obj, length)) + return JS_FALSE; + } + } + return js_SetLengthProperty(cx, obj, length); +} + +static JSBool +array_unshift(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *obj; + jsval *argv; + jsuint length; + JSBool hole; + jsdouble last, newlen; + + obj = JS_THIS_OBJECT(cx, vp); + if (!obj || !js_GetLengthProperty(cx, obj, &length)) + return JS_FALSE; + newlen = length; + if (argc > 0) { + /* Slide up the array to make room for argc at the bottom. */ + argv = JS_ARGV(cx, vp); + if (length > 0) { + if (OBJ_IS_DENSE_ARRAY(cx, obj) && !js_PrototypeHasIndexedProperties(cx, obj) && + !INDEX_TOO_SPARSE(obj, unsigned(newlen + argc))) { + JS_ASSERT(newlen + argc == length + argc); + if (!EnsureCapacity(cx, obj, length + argc)) + return JS_FALSE; + memmove(obj->dslots + argc, obj->dslots, length * sizeof(jsval)); + for (uint32 i = 0; i < argc; i++) + obj->dslots[i] = JSVAL_HOLE; + } else { + last = length; + jsdouble upperIndex = last + argc; + JSAutoTempValueRooter tvr(cx, JSVAL_NULL); + do { + --last, --upperIndex; + if (!JS_CHECK_OPERATION_LIMIT(cx) || + !GetArrayElement(cx, obj, last, &hole, tvr.addr()) || + !SetOrDeleteArrayElement(cx, obj, upperIndex, hole, tvr.value())) { + return JS_FALSE; + } + } while (last != 0); + } + } + + /* Copy from argv to the bottom of the array. */ + if (!InitArrayElements(cx, obj, 0, argc, argv, TargetElementsAllHoles, SourceVectorAllValues)) + return JS_FALSE; + + newlen += argc; + if (!js_SetLengthProperty(cx, obj, newlen)) + return JS_FALSE; + } + + /* Follow Perl by returning the new array length. */ + return IndexToValue(cx, newlen, vp); +} + +static JSBool +array_splice(JSContext *cx, uintN argc, jsval *vp) +{ + jsval *argv; + JSObject *obj; + jsuint length, begin, end, count, delta, last; + jsdouble d; + JSBool hole; + JSObject *obj2; + + /* + * Create a new array value to return. Our ECMA v2 proposal specs + * that splice always returns an array value, even when given no + * arguments. We think this is best because it eliminates the need + * for callers to do an extra test to handle the empty splice case. + */ + obj2 = js_NewArrayObject(cx, 0, NULL); + if (!obj2) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(obj2); + + /* Nothing to do if no args. Otherwise get length. */ + if (argc == 0) + return JS_TRUE; + argv = JS_ARGV(cx, vp); + obj = JS_THIS_OBJECT(cx, vp); + if (!obj || !js_GetLengthProperty(cx, obj, &length)) + return JS_FALSE; + + /* Convert the first argument into a starting index. */ + d = js_ValueToNumber(cx, argv); + if (JSVAL_IS_NULL(*argv)) + return JS_FALSE; + d = js_DoubleToInteger(d); + if (d < 0) { + d += length; + if (d < 0) + d = 0; + } else if (d > length) { + d = length; + } + begin = (jsuint)d; /* d has been clamped to uint32 */ + argc--; + argv++; + + /* Convert the second argument from a count into a fencepost index. */ + delta = length - begin; + if (argc == 0) { + count = delta; + end = length; + } else { + d = js_ValueToNumber(cx, argv); + if (JSVAL_IS_NULL(*argv)) + return JS_FALSE; + d = js_DoubleToInteger(d); + if (d < 0) + d = 0; + else if (d > delta) + d = delta; + count = (jsuint)d; + end = begin + count; + argc--; + argv++; + } + + JSAutoTempValueRooter tvr(cx, JSVAL_NULL); + + /* If there are elements to remove, put them into the return value. */ + if (count > 0) { + if (OBJ_IS_DENSE_ARRAY(cx, obj) && !js_PrototypeHasIndexedProperties(cx, obj) && + !js_PrototypeHasIndexedProperties(cx, obj2) && + end <= js_DenseArrayCapacity(obj)) { + if (!InitArrayObject(cx, obj2, count, obj->dslots + begin, + obj->fslots[JSSLOT_ARRAY_COUNT] != + obj->fslots[JSSLOT_ARRAY_LENGTH])) { + return JS_FALSE; + } + } else { + for (last = begin; last < end; last++) { + if (!JS_CHECK_OPERATION_LIMIT(cx) || + !GetArrayElement(cx, obj, last, &hole, tvr.addr())) { + return JS_FALSE; + } + + /* Copy tvr.value() to the new array unless it's a hole. */ + if (!hole && !SetArrayElement(cx, obj2, last - begin, tvr.value())) + return JS_FALSE; + } + + if (!js_SetLengthProperty(cx, obj2, count)) + return JS_FALSE; + } + } + + /* Find the direction (up or down) to copy and make way for argv. */ + if (argc > count) { + delta = (jsuint)argc - count; + last = length; + if (OBJ_IS_DENSE_ARRAY(cx, obj) && !js_PrototypeHasIndexedProperties(cx, obj) && + length <= js_DenseArrayCapacity(obj) && + (length == 0 || obj->dslots[length - 1] != JSVAL_HOLE)) { + if (!EnsureCapacity(cx, obj, length + delta)) + return JS_FALSE; + /* (uint) end could be 0, so we can't use a vanilla >= test. */ + while (last-- > end) { + jsval srcval = obj->dslots[last]; + jsval* dest = &obj->dslots[last + delta]; + if (*dest == JSVAL_HOLE && srcval != JSVAL_HOLE) + obj->fslots[JSSLOT_ARRAY_COUNT]++; + *dest = srcval; + } + obj->fslots[JSSLOT_ARRAY_LENGTH] += delta; + } else { + /* (uint) end could be 0, so we can't use a vanilla >= test. */ + while (last-- > end) { + if (!JS_CHECK_OPERATION_LIMIT(cx) || + !GetArrayElement(cx, obj, last, &hole, tvr.addr()) || + !SetOrDeleteArrayElement(cx, obj, last + delta, hole, tvr.value())) { + return JS_FALSE; + } + } + } + length += delta; + } else if (argc < count) { + delta = count - (jsuint)argc; + if (OBJ_IS_DENSE_ARRAY(cx, obj) && !js_PrototypeHasIndexedProperties(cx, obj) && + length <= js_DenseArrayCapacity(obj)) { + /* (uint) end could be 0, so we can't use a vanilla >= test. */ + for (last = end; last < length; last++) { + jsval srcval = obj->dslots[last]; + jsval* dest = &obj->dslots[last - delta]; + if (*dest == JSVAL_HOLE && srcval != JSVAL_HOLE) + obj->fslots[JSSLOT_ARRAY_COUNT]++; + *dest = srcval; + } + } else { + for (last = end; last < length; last++) { + if (!JS_CHECK_OPERATION_LIMIT(cx) || + !GetArrayElement(cx, obj, last, &hole, tvr.addr()) || + !SetOrDeleteArrayElement(cx, obj, last - delta, hole, tvr.value())) { + return JS_FALSE; + } + } + } + length -= delta; + } + + /* + * Copy from argv into the hole to complete the splice, and update length in + * case we deleted elements from the end. + */ + return InitArrayElements(cx, obj, begin, argc, argv, TargetElementsMayContainValues, + SourceVectorAllValues) && + js_SetLengthProperty(cx, obj, length); +} + +/* + * Python-esque sequence operations. + */ +static JSBool +array_concat(JSContext *cx, uintN argc, jsval *vp) +{ + jsval *argv, v; + JSObject *aobj, *nobj; + jsuint length, alength, slot; + uintN i; + JSBool hole; + + /* Treat our |this| object as the first argument; see ECMA 15.4.4.4. */ + argv = JS_ARGV(cx, vp) - 1; + JS_ASSERT(JS_THIS_OBJECT(cx, vp) == JSVAL_TO_OBJECT(argv[0])); + + /* Create a new Array object and root it using *vp. */ + aobj = JS_THIS_OBJECT(cx, vp); + if (OBJ_IS_DENSE_ARRAY(cx, aobj)) { + /* + * Clone aobj but pass the minimum of its length and capacity, to + * handle a = [1,2,3]; a.length = 10000 "dense" cases efficiently. In + * such a case we'll pass 8 (not 3) due to ARRAY_CAPACITY_MIN, which + * will cause nobj to be over-allocated to 16. But in the normal case + * where length is <= capacity, nobj and aobj will have the same + * capacity. + */ + length = aobj->fslots[JSSLOT_ARRAY_LENGTH]; + jsuint capacity = js_DenseArrayCapacity(aobj); + nobj = js_NewArrayObject(cx, JS_MIN(length, capacity), aobj->dslots, + aobj->fslots[JSSLOT_ARRAY_COUNT] != + (jsval) length); + if (!nobj) + return JS_FALSE; + nobj->fslots[JSSLOT_ARRAY_LENGTH] = length; + *vp = OBJECT_TO_JSVAL(nobj); + if (argc == 0) + return JS_TRUE; + argc--; + argv++; + } else { + nobj = js_NewArrayObject(cx, 0, NULL); + if (!nobj) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(nobj); + length = 0; + } + + JSAutoTempValueRooter tvr(cx, JSVAL_NULL); + + /* Loop over [0, argc] to concat args into nobj, expanding all Arrays. */ + for (i = 0; i <= argc; i++) { + if (!JS_CHECK_OPERATION_LIMIT(cx)) + return false; + v = argv[i]; + if (!JSVAL_IS_PRIMITIVE(v)) { + JSObject *wobj; + + aobj = JSVAL_TO_OBJECT(v); + wobj = js_GetWrappedObject(cx, aobj); + if (OBJ_IS_ARRAY(cx, wobj)) { + jsid id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); + if (!aobj->getProperty(cx, id, tvr.addr())) + return false; + alength = ValueIsLength(cx, tvr.addr()); + if (JSVAL_IS_NULL(tvr.value())) + return false; + for (slot = 0; slot < alength; slot++) { + if (!JS_CHECK_OPERATION_LIMIT(cx) || + !GetArrayElement(cx, aobj, slot, &hole, tvr.addr())) { + return false; + } + + /* + * Per ECMA 262, 15.4.4.4, step 9, ignore non-existent + * properties. + */ + if (!hole && + !SetArrayElement(cx, nobj, length+slot, tvr.value())) { + return false; + } + } + length += alength; + continue; + } + } + + if (!SetArrayElement(cx, nobj, length, v)) + return false; + length++; + } + + return js_SetLengthProperty(cx, nobj, length); +} + +static JSBool +array_slice(JSContext *cx, uintN argc, jsval *vp) +{ + jsval *argv; + JSObject *nobj, *obj; + jsuint length, begin, end, slot; + jsdouble d; + JSBool hole; + + argv = JS_ARGV(cx, vp); + + obj = JS_THIS_OBJECT(cx, vp); + if (!obj || !js_GetLengthProperty(cx, obj, &length)) + return JS_FALSE; + begin = 0; + end = length; + + if (argc > 0) { + d = js_ValueToNumber(cx, &argv[0]); + if (JSVAL_IS_NULL(argv[0])) + return JS_FALSE; + d = js_DoubleToInteger(d); + if (d < 0) { + d += length; + if (d < 0) + d = 0; + } else if (d > length) { + d = length; + } + begin = (jsuint)d; + + if (argc > 1) { + d = js_ValueToNumber(cx, &argv[1]); + if (JSVAL_IS_NULL(argv[1])) + return JS_FALSE; + d = js_DoubleToInteger(d); + if (d < 0) { + d += length; + if (d < 0) + d = 0; + } else if (d > length) { + d = length; + } + end = (jsuint)d; + } + } + + if (begin > end) + begin = end; + + if (OBJ_IS_DENSE_ARRAY(cx, obj) && end <= js_DenseArrayCapacity(obj) && + !js_PrototypeHasIndexedProperties(cx, obj)) { + nobj = js_NewArrayObject(cx, end - begin, obj->dslots + begin, + obj->fslots[JSSLOT_ARRAY_COUNT] != + obj->fslots[JSSLOT_ARRAY_LENGTH]); + if (!nobj) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(nobj); + return JS_TRUE; + } + + /* Create a new Array object and root it using *vp. */ + nobj = js_NewArrayObject(cx, 0, NULL); + if (!nobj) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(nobj); + + JSAutoTempValueRooter tvr(cx, JSVAL_NULL); + for (slot = begin; slot < end; slot++) { + if (!JS_CHECK_OPERATION_LIMIT(cx) || + !GetArrayElement(cx, obj, slot, &hole, tvr.addr())) { + return JS_FALSE; + } + if (!hole && !SetArrayElement(cx, nobj, slot - begin, tvr.value())) + return JS_FALSE; + } + + return js_SetLengthProperty(cx, nobj, end - begin); +} + +#if JS_HAS_ARRAY_EXTRAS + +static JSBool +array_indexOfHelper(JSContext *cx, JSBool isLast, uintN argc, jsval *vp) +{ + JSObject *obj; + jsuint length, i, stop; + jsval tosearch; + jsint direction; + JSBool hole; + + obj = JS_THIS_OBJECT(cx, vp); + if (!obj || !js_GetLengthProperty(cx, obj, &length)) + return JS_FALSE; + if (length == 0) + goto not_found; + + if (argc <= 1) { + i = isLast ? length - 1 : 0; + tosearch = (argc != 0) ? vp[2] : JSVAL_VOID; + } else { + jsdouble start; + + tosearch = vp[2]; + start = js_ValueToNumber(cx, &vp[3]); + if (JSVAL_IS_NULL(vp[3])) + return JS_FALSE; + start = js_DoubleToInteger(start); + if (start < 0) { + start += length; + if (start < 0) { + if (isLast) + goto not_found; + i = 0; + } else { + i = (jsuint)start; + } + } else if (start >= length) { + if (!isLast) + goto not_found; + i = length - 1; + } else { + i = (jsuint)start; + } + } + + if (isLast) { + stop = 0; + direction = -1; + } else { + stop = length - 1; + direction = 1; + } + + for (;;) { + if (!JS_CHECK_OPERATION_LIMIT(cx) || + !GetArrayElement(cx, obj, (jsuint)i, &hole, vp)) { + return JS_FALSE; + } + if (!hole && js_StrictlyEqual(cx, *vp, tosearch)) + return js_NewNumberInRootedValue(cx, i, vp); + if (i == stop) + goto not_found; + i += direction; + } + + not_found: + *vp = INT_TO_JSVAL(-1); + return JS_TRUE; +} + +static JSBool +array_indexOf(JSContext *cx, uintN argc, jsval *vp) +{ + return array_indexOfHelper(cx, JS_FALSE, argc, vp); +} + +static JSBool +array_lastIndexOf(JSContext *cx, uintN argc, jsval *vp) +{ + return array_indexOfHelper(cx, JS_TRUE, argc, vp); +} + +/* Order is important; extras that take a predicate funarg must follow MAP. */ +typedef enum ArrayExtraMode { + FOREACH, + REDUCE, + REDUCE_RIGHT, + MAP, + FILTER, + SOME, + EVERY +} ArrayExtraMode; + +#define REDUCE_MODE(mode) ((mode) == REDUCE || (mode) == REDUCE_RIGHT) + +static JSBool +array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, jsval *vp) +{ + JSObject *obj; + jsuint length, newlen; + jsval *argv, *elemroot, *invokevp, *sp; + JSBool ok, cond, hole; + JSObject *callable, *thisp, *newarr; + jsint start, end, step, i; + void *mark; + + obj = JS_THIS_OBJECT(cx, vp); + if (!obj || !js_GetLengthProperty(cx, obj, &length)) + return JS_FALSE; + + /* + * First, get or compute our callee, so that we error out consistently + * when passed a non-callable object. + */ + if (argc == 0) { + js_ReportMissingArg(cx, vp, 0); + return JS_FALSE; + } + argv = vp + 2; + callable = js_ValueToCallableObject(cx, &argv[0], JSV2F_SEARCH_STACK); + if (!callable) + return JS_FALSE; + + /* + * Set our initial return condition, used for zero-length array cases + * (and pre-size our map return to match our known length, for all cases). + */ +#ifdef __GNUC__ /* quell GCC overwarning */ + newlen = 0; + newarr = NULL; +#endif + start = 0, end = length, step = 1; + + switch (mode) { + case REDUCE_RIGHT: + start = length - 1, end = -1, step = -1; + /* FALL THROUGH */ + case REDUCE: + if (length == 0 && argc == 1) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_EMPTY_ARRAY_REDUCE); + return JS_FALSE; + } + if (argc >= 2) { + *vp = argv[1]; + } else { + do { + if (!GetArrayElement(cx, obj, start, &hole, vp)) + return JS_FALSE; + start += step; + } while (hole && start != end); + + if (hole && start == end) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_EMPTY_ARRAY_REDUCE); + return JS_FALSE; + } + } + break; + case MAP: + case FILTER: + newlen = (mode == MAP) ? length : 0; + newarr = js_NewArrayObject(cx, newlen, NULL); + if (!newarr) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(newarr); + break; + case SOME: + *vp = JSVAL_FALSE; + break; + case EVERY: + *vp = JSVAL_TRUE; + break; + case FOREACH: + *vp = JSVAL_VOID; + break; + } + + if (length == 0) + return JS_TRUE; + + if (argc > 1 && !REDUCE_MODE(mode)) { + if (!js_ValueToObject(cx, argv[1], &thisp)) + return JS_FALSE; + argv[1] = OBJECT_TO_JSVAL(thisp); + } else { + thisp = NULL; + } + + /* + * For all but REDUCE, we call with 3 args (value, index, array). REDUCE + * requires 4 args (accum, value, index, array). + */ + js_LeaveTrace(cx); + argc = 3 + REDUCE_MODE(mode); + elemroot = js_AllocStack(cx, 1 + 2 + argc, &mark); + if (!elemroot) + return JS_FALSE; + + MUST_FLOW_THROUGH("out"); + ok = JS_TRUE; + invokevp = elemroot + 1; + + for (i = start; i != end; i += step) { + ok = JS_CHECK_OPERATION_LIMIT(cx) && + GetArrayElement(cx, obj, i, &hole, elemroot); + if (!ok) + goto out; + if (hole) + continue; + + /* + * Push callable and 'this', then args. We must do this for every + * iteration around the loop since js_Invoke uses spbase[0] for return + * value storage, while some native functions use spbase[1] for local + * rooting. + */ + sp = invokevp; + *sp++ = OBJECT_TO_JSVAL(callable); + *sp++ = OBJECT_TO_JSVAL(thisp); + if (REDUCE_MODE(mode)) + *sp++ = *vp; + *sp++ = *elemroot; + *sp++ = INT_TO_JSVAL(i); + *sp++ = OBJECT_TO_JSVAL(obj); + + /* Do the call. */ + ok = js_Invoke(cx, argc, invokevp, 0); + if (!ok) + break; + + if (mode > MAP) + cond = js_ValueToBoolean(*invokevp); +#ifdef __GNUC__ /* quell GCC overwarning */ + else + cond = JS_FALSE; +#endif + + switch (mode) { + case FOREACH: + break; + case REDUCE: + case REDUCE_RIGHT: + *vp = *invokevp; + break; + case MAP: + ok = SetArrayElement(cx, newarr, i, *invokevp); + if (!ok) + goto out; + break; + case FILTER: + if (!cond) + break; + /* The filter passed *elemroot, so push it onto our result. */ + ok = SetArrayElement(cx, newarr, newlen++, *elemroot); + if (!ok) + goto out; + break; + case SOME: + if (cond) { + *vp = JSVAL_TRUE; + goto out; + } + break; + case EVERY: + if (!cond) { + *vp = JSVAL_FALSE; + goto out; + } + break; + } + } + + out: + js_FreeStack(cx, mark); + if (ok && mode == FILTER) + ok = js_SetLengthProperty(cx, newarr, newlen); + return ok; +} + +static JSBool +array_forEach(JSContext *cx, uintN argc, jsval *vp) +{ + return array_extra(cx, FOREACH, argc, vp); +} + +static JSBool +array_map(JSContext *cx, uintN argc, jsval *vp) +{ + return array_extra(cx, MAP, argc, vp); +} + +static JSBool +array_reduce(JSContext *cx, uintN argc, jsval *vp) +{ + return array_extra(cx, REDUCE, argc, vp); +} + +static JSBool +array_reduceRight(JSContext *cx, uintN argc, jsval *vp) +{ + return array_extra(cx, REDUCE_RIGHT, argc, vp); +} + +static JSBool +array_filter(JSContext *cx, uintN argc, jsval *vp) +{ + return array_extra(cx, FILTER, argc, vp); +} + +static JSBool +array_some(JSContext *cx, uintN argc, jsval *vp) +{ + return array_extra(cx, SOME, argc, vp); +} + +static JSBool +array_every(JSContext *cx, uintN argc, jsval *vp) +{ + return array_extra(cx, EVERY, argc, vp); +} +#endif + +static JSBool +array_isArray(JSContext *cx, uintN argc, jsval *vp) +{ + *vp = BOOLEAN_TO_JSVAL(argc > 0 && + !JSVAL_IS_PRIMITIVE(vp[2]) && + OBJ_IS_ARRAY(cx, js_GetWrappedObject(cx, JSVAL_TO_OBJECT(vp[2])))); + return JS_TRUE; +} + +static JSPropertySpec array_props[] = { + {js_length_str, -1, JSPROP_SHARED | JSPROP_PERMANENT, + array_length_getter, array_length_setter}, + {0,0,0,0,0} +}; + +JS_DEFINE_TRCINFO_1(array_toString, + (2, (static, STRING_FAIL, Array_p_toString, CONTEXT, THIS, 0, 0))) +JS_DEFINE_TRCINFO_1(array_join, + (3, (static, STRING_FAIL, Array_p_join, CONTEXT, THIS, STRING, 0, 0))) +JS_DEFINE_TRCINFO_1(array_push, + (3, (static, JSVAL_FAIL, Array_p_push1, CONTEXT, THIS, JSVAL, 0, 0))) +JS_DEFINE_TRCINFO_1(array_pop, + (2, (static, JSVAL_FAIL, Array_p_pop, CONTEXT, THIS, 0, 0))) + +static JSFunctionSpec array_methods[] = { +#if JS_HAS_TOSOURCE + JS_FN(js_toSource_str, array_toSource, 0,0), +#endif + JS_TN(js_toString_str, array_toString, 0,0, &array_toString_trcinfo), + JS_FN(js_toLocaleString_str,array_toLocaleString,0,0), + + /* Perl-ish methods. */ + JS_TN("join", array_join, 1,JSFUN_GENERIC_NATIVE, &array_join_trcinfo), + JS_FN("reverse", array_reverse, 0,JSFUN_GENERIC_NATIVE), + JS_FN("sort", array_sort, 1,JSFUN_GENERIC_NATIVE), + JS_TN("push", array_push, 1,JSFUN_GENERIC_NATIVE, &array_push_trcinfo), + JS_TN("pop", array_pop, 0,JSFUN_GENERIC_NATIVE, &array_pop_trcinfo), + JS_FN("shift", array_shift, 0,JSFUN_GENERIC_NATIVE), + JS_FN("unshift", array_unshift, 1,JSFUN_GENERIC_NATIVE), + JS_FN("splice", array_splice, 2,JSFUN_GENERIC_NATIVE), + + /* Pythonic sequence methods. */ + JS_FN("concat", array_concat, 1,JSFUN_GENERIC_NATIVE), + JS_FN("slice", array_slice, 2,JSFUN_GENERIC_NATIVE), + +#if JS_HAS_ARRAY_EXTRAS + JS_FN("indexOf", array_indexOf, 1,JSFUN_GENERIC_NATIVE), + JS_FN("lastIndexOf", array_lastIndexOf, 1,JSFUN_GENERIC_NATIVE), + JS_FN("forEach", array_forEach, 1,JSFUN_GENERIC_NATIVE), + JS_FN("map", array_map, 1,JSFUN_GENERIC_NATIVE), + JS_FN("reduce", array_reduce, 1,JSFUN_GENERIC_NATIVE), + JS_FN("reduceRight", array_reduceRight, 1,JSFUN_GENERIC_NATIVE), + JS_FN("filter", array_filter, 1,JSFUN_GENERIC_NATIVE), + JS_FN("some", array_some, 1,JSFUN_GENERIC_NATIVE), + JS_FN("every", array_every, 1,JSFUN_GENERIC_NATIVE), +#endif + + JS_FS_END +}; + +static JSFunctionSpec array_static_methods[] = { + JS_FN("isArray", array_isArray, 1,0), + JS_FS_END +}; + +JSBool +js_Array(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsuint length; + jsval *vector; + + /* If called without new, replace obj with a new Array object. */ + if (!JS_IsConstructing(cx)) { + obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL); + if (!obj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + } + + if (argc == 0) { + length = 0; + vector = NULL; + } else if (argc > 1) { + length = (jsuint) argc; + vector = argv; + } else if (!JSVAL_IS_NUMBER(argv[0])) { + length = 1; + vector = argv; + } else { + length = ValueIsLength(cx, &argv[0]); + if (JSVAL_IS_NULL(argv[0])) + return JS_FALSE; + vector = NULL; + } + return InitArrayObject(cx, obj, length, vector); +} + +JS_STATIC_ASSERT(JSSLOT_PRIVATE == JSSLOT_ARRAY_LENGTH); +JS_STATIC_ASSERT(JSSLOT_ARRAY_LENGTH + 1 == JSSLOT_ARRAY_COUNT); + +JSObject* JS_FASTCALL +js_NewEmptyArray(JSContext* cx, JSObject* proto) +{ + JS_ASSERT(OBJ_IS_ARRAY(cx, proto)); + + JSObject* obj = js_NewGCObject(cx); + if (!obj) + return NULL; + + /* Initialize all fields of JSObject. */ + obj->map = const_cast(&SharedArrayMap); + obj->classword = jsuword(&js_ArrayClass); + obj->setProto(proto); + obj->setParent(proto->getParent()); + + obj->fslots[JSSLOT_ARRAY_LENGTH] = 0; + obj->fslots[JSSLOT_ARRAY_COUNT] = 0; + for (unsigned i = JSSLOT_ARRAY_COUNT + 1; i != JS_INITIAL_NSLOTS; ++i) + obj->fslots[i] = JSVAL_VOID; + obj->dslots = NULL; + return obj; +} +#ifdef JS_TRACER +JS_DEFINE_CALLINFO_2(extern, OBJECT, js_NewEmptyArray, CONTEXT, OBJECT, 0, 0) +#endif + +JSObject* JS_FASTCALL +js_NewEmptyArrayWithLength(JSContext* cx, JSObject* proto, int32 len) +{ + if (len < 0) + return NULL; + JSObject *obj = js_NewEmptyArray(cx, proto); + if (!obj) + return NULL; + obj->fslots[JSSLOT_ARRAY_LENGTH] = len; + return obj; +} +#ifdef JS_TRACER +JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewEmptyArrayWithLength, CONTEXT, OBJECT, INT32, 0, 0) +#endif + +JSObject* JS_FASTCALL +js_NewArrayWithSlots(JSContext* cx, JSObject* proto, uint32 len) +{ + JSObject* obj = js_NewEmptyArray(cx, proto); + if (!obj) + return NULL; + obj->fslots[JSSLOT_ARRAY_LENGTH] = len; + if (!ResizeSlots(cx, obj, 0, JS_MAX(len, ARRAY_CAPACITY_MIN))) + return NULL; + return obj; +} +#ifdef JS_TRACER +JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewArrayWithSlots, CONTEXT, OBJECT, UINT32, 0, 0) +#endif + +JSObject * +js_InitArrayClass(JSContext *cx, JSObject *obj) +{ + JSObject *proto; + + /* Initialize the ops structure used by slow arrays */ + memcpy(&js_SlowArrayObjectOps, &js_ObjectOps, sizeof(JSObjectOps)); + js_SlowArrayObjectOps.trace = slowarray_trace; + js_SlowArrayObjectOps.enumerate = slowarray_enumerate; + js_SlowArrayObjectOps.call = NULL; + + proto = JS_InitClass(cx, obj, NULL, &js_ArrayClass, js_Array, 1, + array_props, array_methods, NULL, array_static_methods); + + /* Initialize the Array prototype object so it gets a length property. */ + if (!proto || !InitArrayObject(cx, proto, 0, NULL)) + return NULL; + return proto; +} + +JSObject * +js_NewArrayObject(JSContext *cx, jsuint length, jsval *vector, JSBool holey) +{ + JSTempValueRooter tvr; + JSObject *obj; + + obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL); + if (!obj) + return NULL; + + /* + * If this fails, the global object was not initialized and its class does + * not have JSCLASS_IS_GLOBAL. + */ + JS_ASSERT(obj->getProto()); + + JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr); + if (!InitArrayObject(cx, obj, length, vector, holey)) + obj = NULL; + JS_POP_TEMP_ROOT(cx, &tvr); + + /* Set/clear newborn root, in case we lost it. */ + cx->weakRoots.finalizableNewborns[FINALIZE_OBJECT] = obj; + return obj; +} + +JSObject * +js_NewSlowArrayObject(JSContext *cx) +{ + JSObject *obj = js_NewObject(cx, &js_SlowArrayClass, NULL, NULL); + if (obj) + obj->fslots[JSSLOT_ARRAY_LENGTH] = 0; + return obj; +} + +#ifdef DEBUG_ARRAYS +JSBool +js_ArrayInfo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + uintN i; + JSObject *array; + + for (i = 0; i < argc; i++) { + char *bytes; + + bytes = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, argv[i], + NULL); + if (!bytes) + return JS_FALSE; + if (JSVAL_IS_PRIMITIVE(argv[i]) || + !OBJ_IS_ARRAY(cx, (array = JSVAL_TO_OBJECT(argv[i])))) { + fprintf(stderr, "%s: not array\n", bytes); + cx->free(bytes); + continue; + } + fprintf(stderr, "%s: %s (len %lu", bytes, + OBJ_IS_DENSE_ARRAY(cx, array) ? "dense" : "sparse", + array->fslots[JSSLOT_ARRAY_LENGTH]); + if (OBJ_IS_DENSE_ARRAY(cx, array)) { + fprintf(stderr, ", count %lu, capacity %lu", + array->fslots[JSSLOT_ARRAY_COUNT], + js_DenseArrayCapacity(array)); + } + fputs(")\n", stderr); + cx->free(bytes); + } + return JS_TRUE; +} +#endif + +JS_FRIEND_API(JSBool) +js_CoerceArrayToCanvasImageData(JSObject *obj, jsuint offset, jsuint count, + JSUint8 *dest) +{ + uint32 length; + + if (!obj || !js_IsDenseArray(obj)) + return JS_FALSE; + + length = obj->fslots[JSSLOT_ARRAY_LENGTH]; + if (length < offset + count) + return JS_FALSE; + + JSUint8 *dp = dest; + for (uintN i = offset; i < offset+count; i++) { + jsval v = obj->dslots[i]; + if (JSVAL_IS_INT(v)) { + jsint vi = JSVAL_TO_INT(v); + if (jsuint(vi) > 255) + vi = (vi < 0) ? 0 : 255; + *dp++ = JSUint8(vi); + } else if (JSVAL_IS_DOUBLE(v)) { + jsdouble vd = *JSVAL_TO_DOUBLE(v); + if (!(vd >= 0)) /* Not < so that NaN coerces to 0 */ + *dp++ = 0; + else if (vd > 255) + *dp++ = 255; + else { + jsdouble toTruncate = vd + 0.5; + JSUint8 val = JSUint8(toTruncate); + + /* + * now val is rounded to nearest, ties rounded up. We want + * rounded to nearest ties to even, so check whether we had a + * tie. + */ + if (val == toTruncate) { + /* + * It was a tie (since adding 0.5 gave us the exact integer + * we want). Since we rounded up, we either already have an + * even number or we have an odd number but the number we + * want is one less. So just unconditionally masking out the + * ones bit should do the trick to get us the value we + * want. + */ + *dp++ = (val & ~1); + } else { + *dp++ = val; + } + } + } else { + return JS_FALSE; + } + } + + return JS_TRUE; +} + +JS_FRIEND_API(JSObject *) +js_NewArrayObjectWithCapacity(JSContext *cx, jsuint capacity, jsval **vector) +{ + JSObject *obj = js_NewArrayObject(cx, capacity, NULL); + if (!obj) + return NULL; + + JSAutoTempValueRooter tvr(cx, obj); + if (!EnsureCapacity(cx, obj, capacity, JS_FALSE)) + obj = NULL; + + /* Set/clear newborn root, in case we lost it. */ + cx->weakRoots.finalizableNewborns[FINALIZE_OBJECT] = obj; + if (!obj) + return NULL; + + obj->fslots[JSSLOT_ARRAY_COUNT] = capacity; + *vector = obj->dslots; + return obj; +} diff --git a/ape-server/deps/js/src/jsarray.h b/ape-server/deps/js/src/jsarray.h new file mode 100755 index 0000000..9a44afd --- /dev/null +++ b/ape-server/deps/js/src/jsarray.h @@ -0,0 +1,234 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsarray_h___ +#define jsarray_h___ +/* + * JS Array interface. + */ +#include "jsprvtd.h" +#include "jspubtd.h" +#include "jsobj.h" + +JS_BEGIN_EXTERN_C + +#define ARRAY_CAPACITY_MIN 7 + +extern JSBool +js_IdIsIndex(jsval id, jsuint *indexp); + +extern JSClass js_ArrayClass, js_SlowArrayClass; + +static JS_INLINE JSBool +js_IsDenseArray(JSObject *obj) +{ + return STOBJ_GET_CLASS(obj) == &js_ArrayClass; +} + +#define OBJ_IS_DENSE_ARRAY(cx, obj) js_IsDenseArray(obj) + +#define OBJ_IS_ARRAY(cx,obj) (OBJ_IS_DENSE_ARRAY(cx, obj) || \ + OBJ_GET_CLASS(cx, obj) == &js_SlowArrayClass) + +/* + * Dense arrays are not native (OBJ_IS_NATIVE(cx, aobj) for a dense array aobj + * results in false, meaning aobj->map does not point to a JSScope). + * + * But Array methods are called via aobj.sort(), e.g., and the interpreter and + * the trace recorder must consult the property cache in order to perform well. + * The cache works only for native objects. + * + * Therefore the interpreter (js_Interpret in JSOP_GETPROP and JSOP_CALLPROP) + * and js_GetPropertyHelper use this inline function to skip up one link in the + * prototype chain when obj is a dense array, in order to find a native object + * (to wit, Array.prototype) in which to probe for cached methods. + * + * Note that setting aobj.__proto__ for a dense array aobj turns aobj into a + * slow array, avoiding the neede to skip. + * + * Callers of js_GetProtoIfDenseArray must take care to use the original object + * (obj) for the |this| value of a getter, setter, or method call (bug 476447). + */ +static JS_INLINE JSObject * +js_GetProtoIfDenseArray(JSContext *cx, JSObject *obj) +{ + return OBJ_IS_DENSE_ARRAY(cx, obj) ? OBJ_GET_PROTO(cx, obj) : obj; +} + +extern JSObject * +js_InitArrayClass(JSContext *cx, JSObject *obj); + +extern bool +js_InitContextBusyArrayTable(JSContext *cx); + +/* + * Creates a new array with the given length and proto (NB: NULL is not + * translated to Array.prototype), with len slots preallocated. + */ +extern JSObject * JS_FASTCALL +js_NewArrayWithSlots(JSContext* cx, JSObject* proto, uint32 len); + +extern JSObject * +js_NewArrayObject(JSContext *cx, jsuint length, jsval *vector, + JSBool holey = JS_FALSE); + +/* Create an array object that starts out already made slow/sparse. */ +extern JSObject * +js_NewSlowArrayObject(JSContext *cx); + +extern JSBool +js_MakeArraySlow(JSContext *cx, JSObject *obj); + +#define JSSLOT_ARRAY_LENGTH JSSLOT_PRIVATE +#define JSSLOT_ARRAY_COUNT (JSSLOT_ARRAY_LENGTH + 1) +#define JSSLOT_ARRAY_UNUSED (JSSLOT_ARRAY_COUNT + 1) + +static JS_INLINE uint32 +js_DenseArrayCapacity(JSObject *obj) +{ + JS_ASSERT(js_IsDenseArray(obj)); + return obj->dslots ? (uint32) obj->dslots[-1] : 0; +} + +static JS_INLINE void +js_SetDenseArrayCapacity(JSObject *obj, uint32 capacity) +{ + JS_ASSERT(js_IsDenseArray(obj)); + JS_ASSERT(obj->dslots); + obj->dslots[-1] = (jsval) capacity; +} + +extern JSBool +js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp); + +extern JSBool +js_SetLengthProperty(JSContext *cx, JSObject *obj, jsdouble length); + +extern JSBool +js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp); + +extern JSBool JS_FASTCALL +js_IndexToId(JSContext *cx, jsuint index, jsid *idp); + +/* + * Test whether an object is "array-like". Currently this means whether obj + * is an Array or an arguments object. We would like an API, and probably a + * way in the language, to bless other objects as array-like: having indexed + * properties, and a 'length' property of uint32 value equal to one more than + * the greatest index. + */ +extern JSBool +js_IsArrayLike(JSContext *cx, JSObject *obj, JSBool *answerp, jsuint *lengthp); + +/* + * JS-specific merge sort function. + */ +typedef JSBool (*JSComparator)(void *arg, const void *a, const void *b, + int *result); +/* + * NB: vec is the array to be sorted, tmp is temporary space at least as big + * as vec. Both should be GC-rooted if appropriate. + * + * The sorted result is in vec. vec may be in an inconsistent state if the + * comparator function cmp returns an error inside a comparison, so remember + * to check the return value of this function. + */ +extern JSBool +js_MergeSort(void *vec, size_t nel, size_t elsize, JSComparator cmp, + void *arg, void *tmp); + +#ifdef DEBUG_ARRAYS +extern JSBool +js_ArrayInfo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); +#endif + +extern JSBool JS_FASTCALL +js_ArrayCompPush(JSContext *cx, JSObject *obj, jsval v); + +/* + * Fast dense-array-to-buffer conversion for use by canvas. + * + * If the array is a dense array, fill [offset..offset+count] values into + * destination, assuming that types are consistent. Return JS_TRUE if + * successful, otherwise JS_FALSE -- note that the destination buffer may be + * modified even if JS_FALSE is returned (e.g. due to finding an inappropriate + * type later on in the array). If JS_FALSE is returned, no error conditions + * or exceptions are set on the context. + * + * This method succeeds if each element of the array is an integer or a double. + * Values outside the 0-255 range are clamped to that range. Double values are + * converted to integers in this range by clamping and then rounding to + * nearest, ties to even. + */ + +JS_FRIEND_API(JSBool) +js_CoerceArrayToCanvasImageData(JSObject *obj, jsuint offset, jsuint count, + JSUint8 *dest); + +JSBool +js_PrototypeHasIndexedProperties(JSContext *cx, JSObject *obj); + +/* + * Utility to access the value from the id returned by array_lookupProperty. + */ +JSBool +js_GetDenseArrayElementValue(JSContext *cx, JSObject *obj, JSProperty *prop, + jsval *vp); + +/* Array constructor native. Exposed only so the JIT can know its address. */ +JSBool +js_Array(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval); + +/* + * Friend api function that allows direct creation of an array object with a + * given capacity. Non-null return value means allocation of the internal + * buffer for a capacity of at least |capacity| succeeded. A pointer to the + * first element of this internal buffer is returned in the |vector| out + * parameter. The caller promises to fill in the first |capacity| values + * starting from that pointer immediately after this function returns and + * without triggering GC (so this method is allowed to leave those + * uninitialized) and to set them to non-JSVAL_HOLE values, so that the + * resulting array has length and count both equal to |capacity|. + */ +JS_FRIEND_API(JSObject *) +js_NewArrayObjectWithCapacity(JSContext *cx, jsuint capacity, jsval **vector); + +JS_END_EXTERN_C + +#endif /* jsarray_h___ */ diff --git a/ape-server/deps/js/src/jsatom.cpp b/ape-server/deps/js/src/jsatom.cpp new file mode 100755 index 0000000..6b6eaf9 --- /dev/null +++ b/ape-server/deps/js/src/jsatom.cpp @@ -0,0 +1,1269 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * JS atom table. + */ +#include +#include +#include "jstypes.h" +#include "jsstdint.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jshash.h" /* Added by JSIFY */ +#include "jsprf.h" +#include "jsapi.h" +#include "jsatom.h" +#include "jsbit.h" +#include "jscntxt.h" +#include "jsgc.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsparse.h" +#include "jsscan.h" +#include "jsstr.h" +#include "jsversion.h" +#include "jsstrinlines.h" + +/* + * ATOM_HASH assumes that JSHashNumber is 32-bit even on 64-bit systems. + */ +JS_STATIC_ASSERT(sizeof(JSHashNumber) == 4); +JS_STATIC_ASSERT(sizeof(JSAtom *) == JS_BYTES_PER_WORD); + +/* + * Start and limit offsets for atom pointers in JSAtomState must be aligned + * on the word boundary. + */ +JS_STATIC_ASSERT(ATOM_OFFSET_START % sizeof(JSAtom *) == 0); +JS_STATIC_ASSERT(ATOM_OFFSET_LIMIT % sizeof(JSAtom *) == 0); + +/* + * JS_BOOLEAN_STR and JS_TYPE_STR assume that boolean names starts from the + * index 1 and type name starts from the index 1+2 atoms in JSAtomState. + */ +JS_STATIC_ASSERT(1 * sizeof(JSAtom *) == + offsetof(JSAtomState, booleanAtoms) - ATOM_OFFSET_START); +JS_STATIC_ASSERT((1 + 2) * sizeof(JSAtom *) == + offsetof(JSAtomState, typeAtoms) - ATOM_OFFSET_START); + +const char * +js_AtomToPrintableString(JSContext *cx, JSAtom *atom) +{ + return js_ValueToPrintableString(cx, ATOM_KEY(atom)); +} + +#define JS_PROTO(name,code,init) const char js_##name##_str[] = #name; +#include "jsproto.tbl" +#undef JS_PROTO + +/* + * String constants for common atoms defined in JSAtomState starting from + * JSAtomState.emptyAtom until JSAtomState.lazy. + * + * The elements of the array after the first empty string define strings + * corresponding to the two boolean literals, false and true, followed by the + * JSType enumerators from jspubtd.h starting with "undefined" for JSTYPE_VOID + * (which is special-value 2) and continuing as initialized below. The static + * asserts check these relations. + */ +JS_STATIC_ASSERT(JSTYPE_LIMIT == 8); +JS_STATIC_ASSERT(JSTYPE_VOID == 0); + +const char *const js_common_atom_names[] = { + "", /* emptyAtom */ + js_false_str, /* booleanAtoms[0] */ + js_true_str, /* booleanAtoms[1] */ + js_undefined_str, /* typeAtoms[JSTYPE_VOID] */ + js_object_str, /* typeAtoms[JSTYPE_OBJECT] */ + js_function_str, /* typeAtoms[JSTYPE_FUNCTION] */ + "string", /* typeAtoms[JSTYPE_STRING] */ + "number", /* typeAtoms[JSTYPE_NUMBER] */ + "boolean", /* typeAtoms[JSTYPE_BOOLEAN] */ + js_null_str, /* typeAtoms[JSTYPE_NULL] */ + "xml", /* typeAtoms[JSTYPE_XML] */ + js_null_str, /* nullAtom */ + +#define JS_PROTO(name,code,init) js_##name##_str, +#include "jsproto.tbl" +#undef JS_PROTO + + js_anonymous_str, /* anonymousAtom */ + js_apply_str, /* applyAtom */ + js_arguments_str, /* argumentsAtom */ + js_arity_str, /* arityAtom */ + js_call_str, /* callAtom */ + js_callee_str, /* calleeAtom */ + js_caller_str, /* callerAtom */ + js_class_prototype_str, /* classPrototypeAtom */ + js_constructor_str, /* constructorAtom */ + js_count_str, /* countAtom */ + js_each_str, /* eachAtom */ + js_eval_str, /* evalAtom */ + js_fileName_str, /* fileNameAtom */ + js_get_str, /* getAtom */ + js_getter_str, /* getterAtom */ + js_index_str, /* indexAtom */ + js_input_str, /* inputAtom */ + js_iterator_str, /* iteratorAtom */ + js_length_str, /* lengthAtom */ + js_lineNumber_str, /* lineNumberAtom */ + js_message_str, /* messageAtom */ + js_name_str, /* nameAtom */ + js_next_str, /* nextAtom */ + js_noSuchMethod_str, /* noSuchMethodAtom */ + js_parent_str, /* parentAtom */ + js_proto_str, /* protoAtom */ + js_set_str, /* setAtom */ + js_setter_str, /* setterAtom */ + js_stack_str, /* stackAtom */ + js_toLocaleString_str, /* toLocaleStringAtom */ + js_toSource_str, /* toSourceAtom */ + js_toString_str, /* toStringAtom */ + js_valueOf_str, /* valueOfAtom */ + js_toJSON_str, /* toJSONAtom */ + "(void 0)", /* void0Atom */ + js_enumerable_str, /* enumerableAtom */ + js_configurable_str, /* configurableAtom */ + js_writable_str, /* writableAtom */ + js_value_str, /* valueAtom */ + "use strict", /* useStrictAtom */ + +#if JS_HAS_XML_SUPPORT + js_etago_str, /* etagoAtom */ + js_namespace_str, /* namespaceAtom */ + js_ptagc_str, /* ptagcAtom */ + js_qualifier_str, /* qualifierAtom */ + js_space_str, /* spaceAtom */ + js_stago_str, /* stagoAtom */ + js_star_str, /* starAtom */ + js_starQualifier_str, /* starQualifierAtom */ + js_tagc_str, /* tagcAtom */ + js_xml_str, /* xmlAtom */ +#endif + +#ifdef NARCISSUS + js___call___str, /* __call__Atom */ + js___construct___str, /* __construct__Atom */ + js___hasInstance___str, /* __hasInstance__Atom */ + js_ExecutionContext_str, /* ExecutionContextAtom */ + js_current_str, /* currentAtom */ +#endif +}; + +JS_STATIC_ASSERT(JS_ARRAY_LENGTH(js_common_atom_names) * sizeof(JSAtom *) == + LAZY_ATOM_OFFSET_START - ATOM_OFFSET_START); + +/* + * Interpreter macros called by the trace recorder assume common atom indexes + * fit in one byte of immediate operand. + */ +JS_STATIC_ASSERT(JS_ARRAY_LENGTH(js_common_atom_names) < 256); + +const size_t js_common_atom_count = JS_ARRAY_LENGTH(js_common_atom_names); + +const char js_anonymous_str[] = "anonymous"; +const char js_apply_str[] = "apply"; +const char js_arguments_str[] = "arguments"; +const char js_arity_str[] = "arity"; +const char js_call_str[] = "call"; +const char js_callee_str[] = "callee"; +const char js_caller_str[] = "caller"; +const char js_class_prototype_str[] = "prototype"; +const char js_constructor_str[] = "constructor"; +const char js_count_str[] = "__count__"; +const char js_each_str[] = "each"; +const char js_eval_str[] = "eval"; +const char js_fileName_str[] = "fileName"; +const char js_get_str[] = "get"; +const char js_getter_str[] = "getter"; +const char js_index_str[] = "index"; +const char js_input_str[] = "input"; +const char js_iterator_str[] = "__iterator__"; +const char js_length_str[] = "length"; +const char js_lineNumber_str[] = "lineNumber"; +const char js_message_str[] = "message"; +const char js_name_str[] = "name"; +const char js_next_str[] = "next"; +const char js_noSuchMethod_str[] = "__noSuchMethod__"; +const char js_object_str[] = "object"; +const char js_parent_str[] = "__parent__"; +const char js_proto_str[] = "__proto__"; +const char js_setter_str[] = "setter"; +const char js_set_str[] = "set"; +const char js_stack_str[] = "stack"; +const char js_toSource_str[] = "toSource"; +const char js_toString_str[] = "toString"; +const char js_toLocaleString_str[] = "toLocaleString"; +const char js_undefined_str[] = "undefined"; +const char js_valueOf_str[] = "valueOf"; +const char js_toJSON_str[] = "toJSON"; +const char js_enumerable_str[] = "enumerable"; +const char js_configurable_str[] = "configurable"; +const char js_writable_str[] = "writable"; +const char js_value_str[] = "value"; + +#if JS_HAS_XML_SUPPORT +const char js_etago_str[] = ""; +const char js_qualifier_str[] = "::"; +const char js_space_str[] = " "; +const char js_stago_str[] = "<"; +const char js_star_str[] = "*"; +const char js_starQualifier_str[] = "*::"; +const char js_tagc_str[] = ">"; +const char js_xml_str[] = "xml"; +#endif + +#if JS_HAS_GENERATORS +const char js_close_str[] = "close"; +const char js_send_str[] = "send"; +#endif + +#ifdef NARCISSUS +const char js___call___str[] = "__call__"; +const char js___construct___str[] = "__construct__"; +const char js___hasInstance___str[] = "__hasInstance__"; +const char js_ExecutionContext_str[] = "ExecutionContext"; +const char js_current_str[] = "current"; +#endif + +/* + * JSAtomState.doubleAtoms and JSAtomState.stringAtoms hashtable entry. To + * support pinned and interned string atoms, we use the lowest bits of the + * keyAndFlags field to store ATOM_PINNED and ATOM_INTERNED flags. + */ +typedef struct JSAtomHashEntry { + JSDHashEntryHdr hdr; + jsuword keyAndFlags; +} JSAtomHashEntry; + +#define ATOM_ENTRY_FLAG_MASK (ATOM_PINNED | ATOM_INTERNED) + +JS_STATIC_ASSERT(ATOM_ENTRY_FLAG_MASK < JSVAL_ALIGN); + +/* + * Helper macros to access and modify JSAtomHashEntry. + */ +#define TO_ATOM_ENTRY(hdr) ((JSAtomHashEntry *) hdr) +#define ATOM_ENTRY_KEY(entry) \ + ((void *)((entry)->keyAndFlags & ~ATOM_ENTRY_FLAG_MASK)) +#define ATOM_ENTRY_FLAGS(entry) \ + ((uintN)((entry)->keyAndFlags & ATOM_ENTRY_FLAG_MASK)) +#define INIT_ATOM_ENTRY(entry, key) \ + ((void)((entry)->keyAndFlags = (jsuword)(key))) +#define ADD_ATOM_ENTRY_FLAGS(entry, flags) \ + ((void)((entry)->keyAndFlags |= (jsuword)(flags))) +#define CLEAR_ATOM_ENTRY_FLAGS(entry, flags) \ + ((void)((entry)->keyAndFlags &= ~(jsuword)(flags))) + +static JSDHashNumber +HashDouble(JSDHashTable *table, const void *key); + +static JSBool +MatchDouble(JSDHashTable *table, const JSDHashEntryHdr *hdr, const void *key); + +static JSDHashNumber +HashString(JSDHashTable *table, const void *key); + +static JSBool +MatchString(JSDHashTable *table, const JSDHashEntryHdr *hdr, const void *key); + +static const JSDHashTableOps DoubleHashOps = { + JS_DHashAllocTable, + JS_DHashFreeTable, + HashDouble, + MatchDouble, + JS_DHashMoveEntryStub, + JS_DHashClearEntryStub, + JS_DHashFinalizeStub, + NULL +}; + +static const JSDHashTableOps StringHashOps = { + JS_DHashAllocTable, + JS_DHashFreeTable, + HashString, + MatchString, + JS_DHashMoveEntryStub, + JS_DHashClearEntryStub, + JS_DHashFinalizeStub, + NULL +}; + +#define IS_DOUBLE_TABLE(table) ((table)->ops == &DoubleHashOps) +#define IS_STRING_TABLE(table) ((table)->ops == &StringHashOps) + +#define IS_INITIALIZED_STATE(state) IS_DOUBLE_TABLE(&(state)->doubleAtoms) + +static JSDHashNumber +HashDouble(JSDHashTable *table, const void *key) +{ + JS_ASSERT(IS_DOUBLE_TABLE(table)); + return JS_HASH_DOUBLE(*(jsdouble *)key); +} + +static JSDHashNumber +HashString(JSDHashTable *table, const void *key) +{ + JS_ASSERT(IS_STRING_TABLE(table)); + return js_HashString((JSString *)key); +} + +static JSBool +MatchDouble(JSDHashTable *table, const JSDHashEntryHdr *hdr, const void *key) +{ + JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr); + jsdouble d1, d2; + + JS_ASSERT(IS_DOUBLE_TABLE(table)); + if (entry->keyAndFlags == 0) { + /* See comments in MatchString. */ + return JS_FALSE; + } + + d1 = *(jsdouble *)ATOM_ENTRY_KEY(entry); + d2 = *(jsdouble *)key; + if (JSDOUBLE_IS_NaN(d1)) + return JSDOUBLE_IS_NaN(d2); +#if defined(XP_WIN) + /* XXX MSVC miscompiles such that (NaN == 0) */ + if (JSDOUBLE_IS_NaN(d2)) + return JS_FALSE; +#endif + return d1 == d2; +} + +static JSBool +MatchString(JSDHashTable *table, const JSDHashEntryHdr *hdr, const void *key) +{ + JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr); + + JS_ASSERT(IS_STRING_TABLE(table)); + if (entry->keyAndFlags == 0) { + /* + * This happens when js_AtomizeString adds a new hash entry and + * releases the lock but before it takes the lock the second time to + * initialize keyAndFlags for the entry. + * + * We always return false for such entries so JS_DHashTableOperate + * never finds them. We clean them during GC's sweep phase. + * + * It means that with a contested lock or when GC is triggered outside + * the lock we may end up adding two entries, but this is a price for + * simpler code. + */ + return JS_FALSE; + } + return js_EqualStrings((JSString *)ATOM_ENTRY_KEY(entry), (JSString *)key); +} + +/* + * For a browser build from 2007-08-09 after the browser starts up there are + * just 55 double atoms, but over 15000 string atoms. Not to penalize more + * economical embeddings allocating too much memory initially we initialize + * atomized strings with just 1K entries. + */ +#define JS_STRING_HASH_COUNT 1024 +#define JS_DOUBLE_HASH_COUNT 64 + +JSBool +js_InitAtomState(JSRuntime *rt) +{ + JSAtomState *state = &rt->atomState; + + /* + * The caller must zero the state before calling this function. + */ + JS_ASSERT(!state->stringAtoms.ops); + JS_ASSERT(!state->doubleAtoms.ops); + + if (!JS_DHashTableInit(&state->stringAtoms, &StringHashOps, + NULL, sizeof(JSAtomHashEntry), + JS_DHASH_DEFAULT_CAPACITY(JS_STRING_HASH_COUNT))) { + state->stringAtoms.ops = NULL; + return JS_FALSE; + } + JS_ASSERT(IS_STRING_TABLE(&state->stringAtoms)); + + if (!JS_DHashTableInit(&state->doubleAtoms, &DoubleHashOps, + NULL, sizeof(JSAtomHashEntry), + JS_DHASH_DEFAULT_CAPACITY(JS_DOUBLE_HASH_COUNT))) { + state->doubleAtoms.ops = NULL; + JS_DHashTableFinish(&state->stringAtoms); + state->stringAtoms.ops = NULL; + return JS_FALSE; + } + JS_ASSERT(IS_DOUBLE_TABLE(&state->doubleAtoms)); + +#ifdef JS_THREADSAFE + js_InitLock(&state->lock); +#endif + JS_ASSERT(IS_INITIALIZED_STATE(state)); + return JS_TRUE; +} + +static JSDHashOperator +js_string_uninterner(JSDHashTable *table, JSDHashEntryHdr *hdr, + uint32 number, void *arg) +{ + JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr); + JSRuntime *rt = (JSRuntime *)arg; + JSString *str; + + /* + * Any string entry that remains at this point must be initialized, as the + * last GC should clean any uninitialized ones. + */ + JS_ASSERT(IS_STRING_TABLE(table)); + JS_ASSERT(entry->keyAndFlags != 0); + str = (JSString *)ATOM_ENTRY_KEY(entry); + + js_FinalizeStringRT(rt, str); + return JS_DHASH_NEXT; +} + +void +js_FinishAtomState(JSRuntime *rt) +{ + JSAtomState *state = &rt->atomState; + + if (!IS_INITIALIZED_STATE(state)) { + /* + * We are called with uninitialized state when JS_NewRuntime fails and + * calls JS_DestroyRuntime on a partially initialized runtime. + */ + return; + } + + JS_DHashTableEnumerate(&state->stringAtoms, js_string_uninterner, rt); + JS_DHashTableFinish(&state->stringAtoms); + JS_DHashTableFinish(&state->doubleAtoms); + +#ifdef JS_THREADSAFE + js_FinishLock(&state->lock); +#endif +#ifdef DEBUG + memset(state, JS_FREE_PATTERN, sizeof *state); +#endif +} + +JSBool +js_InitCommonAtoms(JSContext *cx) +{ + JSAtomState *state = &cx->runtime->atomState; + uintN i; + JSAtom **atoms; + + atoms = COMMON_ATOMS_START(state); + for (i = 0; i < JS_ARRAY_LENGTH(js_common_atom_names); i++, atoms++) { + *atoms = js_Atomize(cx, js_common_atom_names[i], + strlen(js_common_atom_names[i]), ATOM_PINNED); + if (!*atoms) + return JS_FALSE; + } + JS_ASSERT((uint8 *)atoms - (uint8 *)state == LAZY_ATOM_OFFSET_START); + memset(atoms, 0, ATOM_OFFSET_LIMIT - LAZY_ATOM_OFFSET_START); + + return JS_TRUE; +} + +static JSDHashOperator +js_atom_unpinner(JSDHashTable *table, JSDHashEntryHdr *hdr, + uint32 number, void *arg) +{ + JS_ASSERT(IS_STRING_TABLE(table)); + CLEAR_ATOM_ENTRY_FLAGS(TO_ATOM_ENTRY(hdr), ATOM_PINNED); + return JS_DHASH_NEXT; +} + +void +js_FinishCommonAtoms(JSContext *cx) +{ + JSAtomState *state = &cx->runtime->atomState; + + JS_DHashTableEnumerate(&state->stringAtoms, js_atom_unpinner, NULL); +#ifdef DEBUG + memset(COMMON_ATOMS_START(state), JS_FREE_PATTERN, + ATOM_OFFSET_LIMIT - ATOM_OFFSET_START); +#endif +} + +static JSDHashOperator +js_locked_atom_tracer(JSDHashTable *table, JSDHashEntryHdr *hdr, + uint32 number, void *arg) +{ + JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr); + JSTracer *trc = (JSTracer *)arg; + + if (entry->keyAndFlags == 0) { + /* Ignore uninitialized entries during tracing. */ + return JS_DHASH_NEXT; + } + JS_SET_TRACING_INDEX(trc, "locked_atom", (size_t)number); + JS_CallTracer(trc, ATOM_ENTRY_KEY(entry), + IS_STRING_TABLE(table) ? JSTRACE_STRING : JSTRACE_DOUBLE); + return JS_DHASH_NEXT; +} + +static JSDHashOperator +js_pinned_atom_tracer(JSDHashTable *table, JSDHashEntryHdr *hdr, + uint32 number, void *arg) +{ + JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr); + JSTracer *trc = (JSTracer *)arg; + uintN flags = ATOM_ENTRY_FLAGS(entry); + + JS_ASSERT(IS_STRING_TABLE(table)); + if (flags & (ATOM_PINNED | ATOM_INTERNED)) { + JS_SET_TRACING_INDEX(trc, + flags & ATOM_PINNED + ? "pinned_atom" + : "interned_atom", + (size_t)number); + JS_CallTracer(trc, ATOM_ENTRY_KEY(entry), JSTRACE_STRING); + } + return JS_DHASH_NEXT; +} + +void +js_TraceAtomState(JSTracer *trc, JSBool allAtoms) +{ + JSRuntime *rt = trc->context->runtime; + JSAtomState *state = &rt->atomState; + + if (allAtoms) { + JS_DHashTableEnumerate(&state->doubleAtoms, js_locked_atom_tracer, trc); + JS_DHashTableEnumerate(&state->stringAtoms, js_locked_atom_tracer, trc); + } else { + JS_DHashTableEnumerate(&state->stringAtoms, js_pinned_atom_tracer, trc); + } +} + +static JSDHashOperator +js_atom_sweeper(JSDHashTable *table, JSDHashEntryHdr *hdr, + uint32 number, void *arg) +{ + JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr); + JSContext *cx = (JSContext *)arg; + + /* Remove uninitialized entries. */ + if (entry->keyAndFlags == 0) + return JS_DHASH_REMOVE; + + if (ATOM_ENTRY_FLAGS(entry) & (ATOM_PINNED | ATOM_INTERNED)) { + /* Pinned or interned key cannot be finalized. */ + JS_ASSERT(!js_IsAboutToBeFinalized(cx, ATOM_ENTRY_KEY(entry))); + } else if (js_IsAboutToBeFinalized(cx, ATOM_ENTRY_KEY(entry))) { + /* Remove entries with things about to be GC'ed. */ + return JS_DHASH_REMOVE; + } + return JS_DHASH_NEXT; +} + +void +js_SweepAtomState(JSContext *cx) +{ + JSAtomState *state = &cx->runtime->atomState; + + JS_DHashTableEnumerate(&state->doubleAtoms, js_atom_sweeper, cx); + JS_DHashTableEnumerate(&state->stringAtoms, js_atom_sweeper, cx); + + /* + * Optimize for simplicity and mutate table generation numbers even if the + * sweeper has not removed any entries. + */ + state->doubleAtoms.generation++; + state->stringAtoms.generation++; +} + +JSAtom * +js_AtomizeDouble(JSContext *cx, jsdouble d) +{ + JSAtomState *state; + JSDHashTable *table; + JSAtomHashEntry *entry; + uint32 gen; + jsdouble *key; + jsval v; + + state = &cx->runtime->atomState; + table = &state->doubleAtoms; + + JS_LOCK(cx, &state->lock); + entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, &d, JS_DHASH_ADD)); + if (!entry) + goto failed_hash_add; + if (entry->keyAndFlags == 0) { + gen = ++table->generation; + JS_UNLOCK(cx, &state->lock); + + key = js_NewWeaklyRootedDouble(cx, d); + if (!key) + return NULL; + + JS_LOCK(cx, &state->lock); + if (table->generation == gen) { + JS_ASSERT(entry->keyAndFlags == 0); + } else { + entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, key, + JS_DHASH_ADD)); + if (!entry) + goto failed_hash_add; + if (entry->keyAndFlags != 0) + goto finish; + ++table->generation; + } + INIT_ATOM_ENTRY(entry, key); + } + + finish: + v = DOUBLE_TO_JSVAL((jsdouble *)ATOM_ENTRY_KEY(entry)); + cx->weakRoots.lastAtom = v; + JS_UNLOCK(cx, &state->lock); + + return (JSAtom *)v; + + failed_hash_add: + JS_UNLOCK(cx, &state->lock); + JS_ReportOutOfMemory(cx); + return NULL; +} + +JSAtom * +js_AtomizeString(JSContext *cx, JSString *str, uintN flags) +{ + jsval v; + JSAtomState *state; + JSDHashTable *table; + JSAtomHashEntry *entry; + JSString *key; + uint32 gen; + + JS_ASSERT(!(flags & ~(ATOM_PINNED|ATOM_INTERNED|ATOM_TMPSTR|ATOM_NOCOPY))); + JS_ASSERT_IF(flags & ATOM_NOCOPY, flags & ATOM_TMPSTR); + + if (str->isAtomized()) + return (JSAtom *) STRING_TO_JSVAL(str); + + size_t length = str->length(); + if (length == 1) { + jschar c = str->chars()[0]; + if (c < UNIT_STRING_LIMIT) + return (JSAtom *) STRING_TO_JSVAL(JSString::unitString(c)); + } + + /* + * Here we know that JSString::intStringTable covers only 256 (or at least + * not 1000 or more) chars. We rely on order here to resolve the unit vs. + * int string atom identity issue by giving priority to unit strings for + * '0' through '9' (see JSString::intString in jsstrinlines.h). + */ + JS_STATIC_ASSERT(INT_STRING_LIMIT <= 999); + if (2 <= length && length <= 3) { + const jschar *chars = str->chars(); + + if ('1' <= chars[0] && chars[0] <= '9' && + '0' <= chars[1] && chars[1] <= '9' && + (length == 2 || ('0' <= chars[2] && chars[2] <= '9'))) { + jsint i = (chars[0] - '0') * 10 + chars[1] - '0'; + + if (length == 3) + i = i * 10 + chars[2] - '0'; + if (jsuint(i) < INT_STRING_LIMIT) + return (JSAtom *) STRING_TO_JSVAL(JSString::intString(i)); + } + } + + state = &cx->runtime->atomState; + table = &state->stringAtoms; + + JS_LOCK(cx, &state->lock); + entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, str, JS_DHASH_ADD)); + if (!entry) + goto failed_hash_add; + if (entry->keyAndFlags != 0) { + key = (JSString *)ATOM_ENTRY_KEY(entry); + } else { + /* + * We created a new hashtable entry. Unless str is already allocated + * from the GC heap and flat, we have to release state->lock as + * string construction is a complex operation. For example, it can + * trigger GC which may rehash the table and make the entry invalid. + */ + ++table->generation; + if (!(flags & ATOM_TMPSTR) && str->isFlat()) { + str->flatClearMutable(); + key = str; + } else { + gen = table->generation; + JS_UNLOCK(cx, &state->lock); + + if (flags & ATOM_TMPSTR) { + if (flags & ATOM_NOCOPY) { + key = js_NewString(cx, str->flatChars(), str->flatLength()); + if (!key) + return NULL; + + /* Finish handing off chars to the GC'ed key string. */ + str->mChars = NULL; + } else { + key = js_NewStringCopyN(cx, str->flatChars(), str->flatLength()); + if (!key) + return NULL; + } + } else { + JS_ASSERT(str->isDependent()); + if (!js_UndependString(cx, str)) + return NULL; + key = str; + } + + JS_LOCK(cx, &state->lock); + if (table->generation == gen) { + JS_ASSERT(entry->keyAndFlags == 0); + } else { + entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, key, + JS_DHASH_ADD)); + if (!entry) + goto failed_hash_add; + if (entry->keyAndFlags != 0) { + key = (JSString *)ATOM_ENTRY_KEY(entry); + goto finish; + } + ++table->generation; + } + } + INIT_ATOM_ENTRY(entry, key); + key->flatSetAtomized(); + } + + finish: + ADD_ATOM_ENTRY_FLAGS(entry, flags & (ATOM_PINNED | ATOM_INTERNED)); + JS_ASSERT(key->isAtomized()); + v = STRING_TO_JSVAL(key); + cx->weakRoots.lastAtom = v; + JS_UNLOCK(cx, &state->lock); + return (JSAtom *)v; + + failed_hash_add: + JS_UNLOCK(cx, &state->lock); + JS_ReportOutOfMemory(cx); + return NULL; +} + +JSAtom * +js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags) +{ + jschar *chars; + JSString str; + JSAtom *atom; + + /* + * Avoiding the malloc in js_InflateString on shorter strings saves us + * over 20,000 malloc calls on mozilla browser startup. This compares to + * only 131 calls where the string is longer than a 31 char (net) buffer. + * The vast majority of atomized strings are already in the hashtable. So + * js_AtomizeString rarely has to copy the temp string we make. + */ +#define ATOMIZE_BUF_MAX 32 + jschar inflated[ATOMIZE_BUF_MAX]; + size_t inflatedLength = ATOMIZE_BUF_MAX - 1; + + if (length < ATOMIZE_BUF_MAX) { + js_InflateStringToBuffer(cx, bytes, length, inflated, &inflatedLength); + inflated[inflatedLength] = 0; + chars = inflated; + } else { + inflatedLength = length; + chars = js_InflateString(cx, bytes, &inflatedLength); + if (!chars) + return NULL; + flags |= ATOM_NOCOPY; + } + + str.initFlat(chars, inflatedLength); + atom = js_AtomizeString(cx, &str, ATOM_TMPSTR | flags); + if (chars != inflated && str.flatChars()) + cx->free(chars); + return atom; +} + +JSAtom * +js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length, uintN flags) +{ + JSString str; + + str.initFlat((jschar *)chars, length); + return js_AtomizeString(cx, &str, ATOM_TMPSTR | flags); +} + +JSAtom * +js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length) +{ + JSString str, *str2; + JSAtomState *state; + JSDHashEntryHdr *hdr; + + if (length == 1) { + jschar c = *chars; + if (c < UNIT_STRING_LIMIT) + return (JSAtom *) STRING_TO_JSVAL(JSString::unitString(c)); + } + + str.initFlat((jschar *)chars, length); + state = &cx->runtime->atomState; + + JS_LOCK(cx, &state->lock); + hdr = JS_DHashTableOperate(&state->stringAtoms, &str, JS_DHASH_LOOKUP); + str2 = JS_DHASH_ENTRY_IS_BUSY(hdr) + ? (JSString *)ATOM_ENTRY_KEY(TO_ATOM_ENTRY(hdr)) + : NULL; + JS_UNLOCK(cx, &state->lock); + + return str2 ? (JSAtom *)STRING_TO_JSVAL(str2) : NULL; +} + +JSBool +js_AtomizePrimitiveValue(JSContext *cx, jsval v, JSAtom **atomp) +{ + JSAtom *atom; + + if (JSVAL_IS_STRING(v)) { + atom = js_AtomizeString(cx, JSVAL_TO_STRING(v), 0); + if (!atom) + return JS_FALSE; + } else if (JSVAL_IS_DOUBLE(v)) { + atom = js_AtomizeDouble(cx, *JSVAL_TO_DOUBLE(v)); + if (!atom) + return JS_FALSE; + } else { + JS_ASSERT(JSVAL_IS_INT(v) || JSVAL_IS_BOOLEAN(v) || + JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)); + atom = (JSAtom *)v; + } + *atomp = atom; + return JS_TRUE; +} + +#ifdef DEBUG + +static JSDHashOperator +atom_dumper(JSDHashTable *table, JSDHashEntryHdr *hdr, + uint32 number, void *arg) +{ + JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr); + FILE *fp = (FILE *)arg; + void *key; + uintN flags; + + fprintf(fp, "%3u %08x ", number, (uintN)entry->hdr.keyHash); + if (entry->keyAndFlags == 0) { + fputs("", fp); + } else { + key = ATOM_ENTRY_KEY(entry); + if (IS_DOUBLE_TABLE(table)) { + fprintf(fp, "%.16g", *(jsdouble *)key); + } else { + JS_ASSERT(IS_STRING_TABLE(table)); + js_FileEscapedString(fp, (JSString *)key, '"'); + } + flags = ATOM_ENTRY_FLAGS(entry); + if (flags != 0) { + fputs((flags & (ATOM_PINNED | ATOM_INTERNED)) + ? " pinned | interned" + : (flags & ATOM_PINNED) ? " pinned" : " interned", + fp); + } + } + putc('\n', fp); + return JS_DHASH_NEXT; +} + +JS_FRIEND_API(void) +js_DumpAtoms(JSContext *cx, FILE *fp) +{ + JSAtomState *state = &cx->runtime->atomState; + + fprintf(fp, "stringAtoms table contents:\n"); + JS_DHashTableEnumerate(&state->stringAtoms, atom_dumper, fp); +#ifdef JS_DHASHMETER + JS_DHashTableDumpMeter(&state->stringAtoms, atom_dumper, fp); +#endif + putc('\n', fp); + + fprintf(fp, "doubleAtoms table contents:\n"); + JS_DHashTableEnumerate(&state->doubleAtoms, atom_dumper, fp); +#ifdef JS_DHASHMETER + JS_DHashTableDumpMeter(&state->doubleAtoms, atom_dumper, fp); +#endif + putc('\n', fp); +} + +#endif + +static JSHashNumber +js_hash_atom_ptr(const void *key) +{ + const JSAtom *atom = (const JSAtom *) key; + return ATOM_HASH(atom); +} + +#if JS_BITS_PER_WORD == 32 +# define TEMP_SIZE_START_LOG2 5 +#else +# define TEMP_SIZE_START_LOG2 6 +#endif +#define TEMP_SIZE_LIMIT_LOG2 (TEMP_SIZE_START_LOG2 + NUM_TEMP_FREELISTS) + +#define TEMP_SIZE_START JS_BIT(TEMP_SIZE_START_LOG2) +#define TEMP_SIZE_LIMIT JS_BIT(TEMP_SIZE_LIMIT_LOG2) + +JS_STATIC_ASSERT(TEMP_SIZE_START >= sizeof(JSHashTable)); + +static void * +js_alloc_temp_space(void *priv, size_t size) +{ + JSCompiler *jsc = (JSCompiler *) priv; + + void *space; + if (size < TEMP_SIZE_LIMIT) { + int bin = JS_CeilingLog2(size) - TEMP_SIZE_START_LOG2; + JS_ASSERT(unsigned(bin) < NUM_TEMP_FREELISTS); + + space = jsc->tempFreeList[bin]; + if (space) { + jsc->tempFreeList[bin] = *(void **)space; + return space; + } + } + + JS_ARENA_ALLOCATE(space, &jsc->context->tempPool, size); + if (!space) + js_ReportOutOfScriptQuota(jsc->context); + return space; +} + +static void +js_free_temp_space(void *priv, void *item, size_t size) +{ + if (size >= TEMP_SIZE_LIMIT) + return; + + JSCompiler *jsc = (JSCompiler *) priv; + int bin = JS_CeilingLog2(size) - TEMP_SIZE_START_LOG2; + JS_ASSERT(unsigned(bin) < NUM_TEMP_FREELISTS); + + *(void **)item = jsc->tempFreeList[bin]; + jsc->tempFreeList[bin] = item; +} + +static JSHashEntry * +js_alloc_temp_entry(void *priv, const void *key) +{ + JSCompiler *jsc = (JSCompiler *) priv; + JSAtomListElement *ale; + + ale = jsc->aleFreeList; + if (ale) { + jsc->aleFreeList = ALE_NEXT(ale); + return &ale->entry; + } + + JS_ARENA_ALLOCATE_TYPE(ale, JSAtomListElement, &jsc->context->tempPool); + if (!ale) { + js_ReportOutOfScriptQuota(jsc->context); + return NULL; + } + return &ale->entry; +} + +static void +js_free_temp_entry(void *priv, JSHashEntry *he, uintN flag) +{ + JSCompiler *jsc = (JSCompiler *) priv; + JSAtomListElement *ale = (JSAtomListElement *) he; + + ALE_SET_NEXT(ale, jsc->aleFreeList); + jsc->aleFreeList = ale; +} + +static JSHashAllocOps temp_alloc_ops = { + js_alloc_temp_space, js_free_temp_space, + js_alloc_temp_entry, js_free_temp_entry +}; + +JSAtomListElement * +JSAtomList::rawLookup(JSAtom *atom, JSHashEntry **&hep) +{ + JSAtomListElement *ale; + + if (table) { + hep = JS_HashTableRawLookup(table, ATOM_HASH(atom), atom); + ale = *hep ? (JSAtomListElement *) *hep : NULL; + } else { + JSHashEntry **alep = &list; + hep = NULL; + while ((ale = (JSAtomListElement *)*alep) != NULL) { + if (ALE_ATOM(ale) == atom) { + /* Hit, move atom's element to the front of the list. */ + *alep = ale->entry.next; + ale->entry.next = list; + list = &ale->entry; + break; + } + alep = &ale->entry.next; + } + } + return ale; +} + +#define ATOM_LIST_HASH_THRESHOLD 12 + +JSAtomListElement * +JSAtomList::add(JSCompiler *jsc, JSAtom *atom, AddHow how) +{ + JS_ASSERT(!set); + + JSAtomListElement *ale, *ale2, *next; + JSHashEntry **hep; + + ale = rawLookup(atom, hep); + if (!ale || how != UNIQUE) { + if (count < ATOM_LIST_HASH_THRESHOLD && !table) { + /* Few enough for linear search and no hash table yet needed. */ + ale = (JSAtomListElement *)js_alloc_temp_entry(jsc, atom); + if (!ale) + return NULL; + ALE_SET_ATOM(ale, atom); + + if (how == HOIST) { + ale->entry.next = NULL; + hep = (JSHashEntry **) &list; + while (*hep) + hep = &(*hep)->next; + *hep = &ale->entry; + } else { + ale->entry.next = list; + list = &ale->entry; + } + } else { + /* + * We should hash, or else we already are hashing, but count was + * reduced by JSAtomList::rawRemove below ATOM_LIST_HASH_THRESHOLD. + * Check whether we should create the table. + */ + if (!table) { + /* No hash table yet, so hep had better be null! */ + JS_ASSERT(!hep); + table = JS_NewHashTable(count + 1, js_hash_atom_ptr, + JS_CompareValues, JS_CompareValues, + &temp_alloc_ops, jsc); + if (!table) + return NULL; + + /* + * Set ht->nentries explicitly, because we are moving entries + * from list to ht, not calling JS_HashTable(Raw|)Add. + */ + table->nentries = count; + + /* + * Insert each ale on list into the new hash table. Append to + * the hash chain rather than inserting at the bucket head, to + * preserve order among entries with the same key. + */ + for (ale2 = (JSAtomListElement *)list; ale2; ale2 = next) { + next = ALE_NEXT(ale2); + ale2->entry.keyHash = ATOM_HASH(ALE_ATOM(ale2)); + hep = JS_HashTableRawLookup(table, ale2->entry.keyHash, + ale2->entry.key); + while (*hep) + hep = &(*hep)->next; + *hep = &ale2->entry; + ale2->entry.next = NULL; + } + list = NULL; + + /* Set hep for insertion of atom's ale, immediately below. */ + hep = JS_HashTableRawLookup(table, ATOM_HASH(atom), atom); + } + + /* Finally, add an entry for atom into the hash bucket at hep. */ + ale = (JSAtomListElement *) + JS_HashTableRawAdd(table, hep, ATOM_HASH(atom), atom, NULL); + if (!ale) + return NULL; + + /* + * If hoisting, move ale to the end of its chain after we called + * JS_HashTableRawAdd, since RawAdd may have grown the table and + * then recomputed hep to refer to the pointer to the first entry + * with the given key. + */ + if (how == HOIST && ale->entry.next) { + JS_ASSERT(*hep == &ale->entry); + *hep = ale->entry.next; + ale->entry.next = NULL; + do { + hep = &(*hep)->next; + } while (*hep); + *hep = &ale->entry; + } + } + + ALE_SET_INDEX(ale, count++); + } + return ale; +} + +void +JSAtomList::rawRemove(JSCompiler *jsc, JSAtomListElement *ale, JSHashEntry **hep) +{ + JS_ASSERT(!set); + JS_ASSERT(count != 0); + + if (table) { + JS_ASSERT(hep); + JS_HashTableRawRemove(table, hep, &ale->entry); + } else { + JS_ASSERT(!hep); + hep = &list; + while (*hep != &ale->entry) { + JS_ASSERT(*hep); + hep = &(*hep)->next; + } + *hep = ale->entry.next; + js_free_temp_entry(jsc, &ale->entry, HT_FREE_ENTRY); + } + + --count; +} + +JSAutoAtomList::~JSAutoAtomList() +{ + if (table) { + JS_HashTableDestroy(table); + } else { + JSHashEntry *hep = list; + while (hep) { + JSHashEntry *next = hep->next; + js_free_temp_entry(compiler, hep, HT_FREE_ENTRY); + hep = next; + } + } +} + +JSAtomListElement * +JSAtomListIterator::operator ()() +{ + JSAtomListElement *ale; + JSHashTable *ht; + + if (index == uint32(-1)) + return NULL; + + ale = next; + if (!ale) { + ht = list->table; + if (!ht) + goto done; + do { + if (index == JS_BIT(JS_HASH_BITS - ht->shift)) + goto done; + next = (JSAtomListElement *) ht->buckets[index++]; + } while (!next); + ale = next; + } + + next = ALE_NEXT(ale); + return ale; + + done: + index = uint32(-1); + return NULL; +} + +static intN +js_map_atom(JSHashEntry *he, intN i, void *arg) +{ + JSAtomListElement *ale = (JSAtomListElement *)he; + JSAtom **vector = (JSAtom **) arg; + + vector[ALE_INDEX(ale)] = ALE_ATOM(ale); + return HT_ENUMERATE_NEXT; +} + +#ifdef DEBUG +static jsrefcount js_atom_map_count; +static jsrefcount js_atom_map_hash_table_count; +#endif + +void +js_InitAtomMap(JSContext *cx, JSAtomMap *map, JSAtomList *al) +{ + JSAtom **vector; + JSAtomListElement *ale; + uint32 count; + + /* Map length must already be initialized. */ + JS_ASSERT(al->count == map->length); +#ifdef DEBUG + JS_ATOMIC_INCREMENT(&js_atom_map_count); +#endif + ale = (JSAtomListElement *)al->list; + if (!ale && !al->table) { + JS_ASSERT(!map->vector); + return; + } + + count = al->count; + vector = map->vector; + if (al->table) { +#ifdef DEBUG + JS_ATOMIC_INCREMENT(&js_atom_map_hash_table_count); +#endif + JS_HashTableEnumerateEntries(al->table, js_map_atom, vector); + } else { + do { + vector[ALE_INDEX(ale)] = ALE_ATOM(ale); + } while ((ale = ALE_NEXT(ale)) != NULL); + } + al->clear(); +} diff --git a/ape-server/deps/js/src/jsatom.h b/ape-server/deps/js/src/jsatom.h new file mode 100755 index 0000000..585d1c8 --- /dev/null +++ b/ape-server/deps/js/src/jsatom.h @@ -0,0 +1,508 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsatom_h___ +#define jsatom_h___ +/* + * JS atom table. + */ +#include +#include "jsversion.h" +#include "jstypes.h" +#include "jshash.h" /* Added by JSIFY */ +#include "jsdhash.h" +#include "jsapi.h" +#include "jsprvtd.h" +#include "jspubtd.h" +#include "jslock.h" + +JS_BEGIN_EXTERN_C + +#define ATOM_PINNED 0x1 /* atom is pinned against GC */ +#define ATOM_INTERNED 0x2 /* pinned variant for JS_Intern* API */ +#define ATOM_NOCOPY 0x4 /* don't copy atom string bytes */ +#define ATOM_TMPSTR 0x8 /* internal, to avoid extra string */ + +#define ATOM_KEY(atom) ((jsval)(atom)) +#define ATOM_IS_DOUBLE(atom) JSVAL_IS_DOUBLE(ATOM_KEY(atom)) +#define ATOM_TO_DOUBLE(atom) JSVAL_TO_DOUBLE(ATOM_KEY(atom)) +#define ATOM_IS_STRING(atom) JSVAL_IS_STRING(ATOM_KEY(atom)) +#define ATOM_TO_STRING(atom) JSVAL_TO_STRING(ATOM_KEY(atom)) + +#if JS_BYTES_PER_WORD == 4 +# define ATOM_HASH(atom) ((JSHashNumber)(atom) >> 2) +#elif JS_BYTES_PER_WORD == 8 +# define ATOM_HASH(atom) (((JSHashNumber)(jsuword)(atom) >> 3) ^ \ + (JSHashNumber)((jsuword)(atom) >> 32)) +#else +# error "Unsupported configuration" +#endif + +/* + * Return a printable, lossless char[] representation of a string-type atom. + * The lifetime of the result extends at least until the next GC activation, + * longer if cx's string newborn root is not overwritten. + */ +extern const char * +js_AtomToPrintableString(JSContext *cx, JSAtom *atom); + +struct JSAtomListElement { + JSHashEntry entry; +}; + +#define ALE_ATOM(ale) ((JSAtom *) (ale)->entry.key) +#define ALE_INDEX(ale) ((jsatomid) JS_PTR_TO_UINT32((ale)->entry.value)) +#define ALE_VALUE(ale) ((jsval) (ale)->entry.value) +#define ALE_NEXT(ale) ((JSAtomListElement *) (ale)->entry.next) + +/* + * In an upvars list, ALE_DEFN(ale)->resolve() is the outermost definition the + * name may reference. If a with block or a function that calls eval encloses + * the use, the name may end up referring to something else at runtime. + */ +#define ALE_DEFN(ale) ((JSDefinition *) (ale)->entry.value) + +#define ALE_SET_ATOM(ale,atom) ((ale)->entry.key = (const void *)(atom)) +#define ALE_SET_INDEX(ale,index)((ale)->entry.value = JS_UINT32_TO_PTR(index)) +#define ALE_SET_DEFN(ale, dn) ((ale)->entry.value = (void *)(dn)) +#define ALE_SET_VALUE(ale, v) ((ale)->entry.value = (void *)(v)) +#define ALE_SET_NEXT(ale,nxt) ((ale)->entry.next = (JSHashEntry *)(nxt)) + +/* + * NB: JSAtomSet must be plain-old-data as it is embedded in the pn_u union in + * JSParseNode. JSAtomList encapsulates all operational uses of a JSAtomSet. + * + * The JSAtomList name is traditional, even though the implementation is a map + * (not to be confused with JSAtomMap). In particular the "ALE" and "ale" short + * names for JSAtomListElement variables roll off the fingers, compared to ASE + * or AME alternatives. + */ +struct JSAtomSet { + JSHashEntry *list; /* literals indexed for mapping */ + JSHashTable *table; /* hash table if list gets too long */ + jsuint count; /* count of indexed literals */ +}; + +#ifdef __cplusplus + +struct JSAtomList : public JSAtomSet +{ +#ifdef DEBUG + const JSAtomSet* set; /* asserted null in mutating methods */ +#endif + + JSAtomList() { + list = NULL; table = NULL; count = 0; +#ifdef DEBUG + set = NULL; +#endif + } + + JSAtomList(const JSAtomSet& as) { + list = as.list; table = as.table; count = as.count; +#ifdef DEBUG + set = &as; +#endif + } + + void clear() { JS_ASSERT(!set); list = NULL; table = NULL; count = 0; } + + JSAtomListElement *lookup(JSAtom *atom) { + JSHashEntry **hep; + return rawLookup(atom, hep); + } + + JSAtomListElement *rawLookup(JSAtom *atom, JSHashEntry **&hep); + + enum AddHow { UNIQUE, SHADOW, HOIST }; + + JSAtomListElement *add(JSCompiler *jsc, JSAtom *atom, AddHow how = UNIQUE); + + void remove(JSCompiler *jsc, JSAtom *atom) { + JSHashEntry **hep; + JSAtomListElement *ale = rawLookup(atom, hep); + if (ale) + rawRemove(jsc, ale, hep); + } + + void rawRemove(JSCompiler *jsc, JSAtomListElement *ale, JSHashEntry **hep); +}; + +/* + * A subclass of JSAtomList with a destructor. This atom list owns its + * hash table and its entries, but no keys or values. + */ +struct JSAutoAtomList: public JSAtomList +{ + JSAutoAtomList(JSCompiler *c): compiler(c) {} + ~JSAutoAtomList(); + private: + JSCompiler *compiler; /* For freeing list entries. */ +}; + +/* + * Iterate over an atom list. We define a call operator to minimize the syntax + * tax for users. We do not use a more standard pattern using ++ and * because + * (a) it's the wrong pattern for a non-scalar; (b) it's overkill -- one method + * is enough. (This comment is overkill!) + */ +class JSAtomListIterator { + JSAtomList* list; + JSAtomListElement* next; + uint32 index; + + public: + JSAtomListIterator(JSAtomList* al) : list(al) { reset(); } + + void reset() { + next = (JSAtomListElement *) list->list; + index = 0; + } + + JSAtomListElement* operator ()(); +}; + +#endif /* __cplusplus */ + +struct JSAtomMap { + JSAtom **vector; /* array of ptrs to indexed atoms */ + jsatomid length; /* count of (to-be-)indexed atoms */ +}; + +struct JSAtomState { + JSDHashTable stringAtoms; /* hash table with shared strings */ + JSDHashTable doubleAtoms; /* hash table with shared doubles */ +#ifdef JS_THREADSAFE + JSThinLock lock; +#endif + + /* + * From this point until the end of struct definition the struct must + * contain only JSAtom fields. We use this to access the storage occupied + * by the common atoms in js_FinishCommonAtoms. + * + * js_common_atom_names defined in jsatom.c contains C strings for atoms + * in the order of atom fields here. Therefore you must update that array + * if you change member order here. + */ + + /* The rt->emptyString atom, see jsstr.c's js_InitRuntimeStringState. */ + JSAtom *emptyAtom; + + /* + * Literal value and type names. + * NB: booleanAtoms must come right before typeAtoms! + */ + JSAtom *booleanAtoms[2]; + JSAtom *typeAtoms[JSTYPE_LIMIT]; + JSAtom *nullAtom; + + /* Standard class constructor or prototype names. */ + JSAtom *classAtoms[JSProto_LIMIT]; + + /* Various built-in or commonly-used atoms, pinned on first context. */ + JSAtom *anonymousAtom; + JSAtom *applyAtom; + JSAtom *argumentsAtom; + JSAtom *arityAtom; + JSAtom *callAtom; + JSAtom *calleeAtom; + JSAtom *callerAtom; + JSAtom *classPrototypeAtom; + JSAtom *constructorAtom; + JSAtom *countAtom; + JSAtom *eachAtom; + JSAtom *evalAtom; + JSAtom *fileNameAtom; + JSAtom *getAtom; + JSAtom *getterAtom; + JSAtom *indexAtom; + JSAtom *inputAtom; + JSAtom *iteratorAtom; + JSAtom *lengthAtom; + JSAtom *lineNumberAtom; + JSAtom *messageAtom; + JSAtom *nameAtom; + JSAtom *nextAtom; + JSAtom *noSuchMethodAtom; + JSAtom *parentAtom; + JSAtom *protoAtom; + JSAtom *setAtom; + JSAtom *setterAtom; + JSAtom *stackAtom; + JSAtom *toLocaleStringAtom; + JSAtom *toSourceAtom; + JSAtom *toStringAtom; + JSAtom *valueOfAtom; + JSAtom *toJSONAtom; + JSAtom *void0Atom; + JSAtom *enumerableAtom; + JSAtom *configurableAtom; + JSAtom *writableAtom; + JSAtom *valueAtom; + JSAtom *useStrictAtom; + +#if JS_HAS_XML_SUPPORT + JSAtom *etagoAtom; + JSAtom *namespaceAtom; + JSAtom *ptagcAtom; + JSAtom *qualifierAtom; + JSAtom *spaceAtom; + JSAtom *stagoAtom; + JSAtom *starAtom; + JSAtom *starQualifierAtom; + JSAtom *tagcAtom; + JSAtom *xmlAtom; +#endif + +#ifdef NARCISSUS + JSAtom *__call__Atom; + JSAtom *__construct__Atom; + JSAtom *__hasInstance__Atom; + JSAtom *ExecutionContextAtom; + JSAtom *currentAtom; +#endif + + /* Less frequently used atoms, pinned lazily by JS_ResolveStandardClass. */ + struct { + JSAtom *InfinityAtom; + JSAtom *NaNAtom; + JSAtom *XMLListAtom; + JSAtom *decodeURIAtom; + JSAtom *decodeURIComponentAtom; + JSAtom *defineGetterAtom; + JSAtom *defineSetterAtom; + JSAtom *encodeURIAtom; + JSAtom *encodeURIComponentAtom; + JSAtom *escapeAtom; + JSAtom *functionNamespaceURIAtom; + JSAtom *hasOwnPropertyAtom; + JSAtom *isFiniteAtom; + JSAtom *isNaNAtom; + JSAtom *isPrototypeOfAtom; + JSAtom *isXMLNameAtom; + JSAtom *lookupGetterAtom; + JSAtom *lookupSetterAtom; + JSAtom *parseFloatAtom; + JSAtom *parseIntAtom; + JSAtom *propertyIsEnumerableAtom; + JSAtom *unescapeAtom; + JSAtom *unevalAtom; + JSAtom *unwatchAtom; + JSAtom *watchAtom; + } lazy; +}; + +#define ATOM_OFFSET_START offsetof(JSAtomState, emptyAtom) +#define LAZY_ATOM_OFFSET_START offsetof(JSAtomState, lazy) +#define ATOM_OFFSET_LIMIT (sizeof(JSAtomState)) + +#define COMMON_ATOMS_START(state) \ + ((JSAtom **)((uint8 *)(state) + ATOM_OFFSET_START)) +#define COMMON_ATOM_INDEX(name) \ + ((offsetof(JSAtomState, name##Atom) - ATOM_OFFSET_START) \ + / sizeof(JSAtom*)) +#define COMMON_TYPE_ATOM_INDEX(type) \ + ((offsetof(JSAtomState, typeAtoms[type]) - ATOM_OFFSET_START) \ + / sizeof(JSAtom*)) + +#define ATOM_OFFSET(name) offsetof(JSAtomState, name##Atom) +#define OFFSET_TO_ATOM(rt,off) (*(JSAtom **)((char*)&(rt)->atomState + (off))) +#define CLASS_ATOM_OFFSET(name) offsetof(JSAtomState,classAtoms[JSProto_##name]) + +#define CLASS_ATOM(cx,name) \ + ((cx)->runtime->atomState.classAtoms[JSProto_##name]) + +extern const char *const js_common_atom_names[]; +extern const size_t js_common_atom_count; + +/* + * Macros to access C strings for JSType and boolean literals. + */ +#define JS_BOOLEAN_STR(type) (js_common_atom_names[1 + (type)]) +#define JS_TYPE_STR(type) (js_common_atom_names[1 + 2 + (type)]) + +/* Well-known predefined C strings. */ +#define JS_PROTO(name,code,init) extern const char js_##name##_str[]; +#include "jsproto.tbl" +#undef JS_PROTO + +extern const char js_anonymous_str[]; +extern const char js_apply_str[]; +extern const char js_arguments_str[]; +extern const char js_arity_str[]; +extern const char js_call_str[]; +extern const char js_callee_str[]; +extern const char js_caller_str[]; +extern const char js_class_prototype_str[]; +extern const char js_close_str[]; +extern const char js_constructor_str[]; +extern const char js_count_str[]; +extern const char js_etago_str[]; +extern const char js_each_str[]; +extern const char js_eval_str[]; +extern const char js_fileName_str[]; +extern const char js_get_str[]; +extern const char js_getter_str[]; +extern const char js_index_str[]; +extern const char js_input_str[]; +extern const char js_iterator_str[]; +extern const char js_length_str[]; +extern const char js_lineNumber_str[]; +extern const char js_message_str[]; +extern const char js_name_str[]; +extern const char js_namespace_str[]; +extern const char js_next_str[]; +extern const char js_noSuchMethod_str[]; +extern const char js_object_str[]; +extern const char js_parent_str[]; +extern const char js_proto_str[]; +extern const char js_ptagc_str[]; +extern const char js_qualifier_str[]; +extern const char js_send_str[]; +extern const char js_setter_str[]; +extern const char js_set_str[]; +extern const char js_space_str[]; +extern const char js_stack_str[]; +extern const char js_stago_str[]; +extern const char js_star_str[]; +extern const char js_starQualifier_str[]; +extern const char js_tagc_str[]; +extern const char js_toSource_str[]; +extern const char js_toString_str[]; +extern const char js_toLocaleString_str[]; +extern const char js_undefined_str[]; +extern const char js_valueOf_str[]; +extern const char js_toJSON_str[]; +extern const char js_xml_str[]; +extern const char js_enumerable_str[]; +extern const char js_configurable_str[]; +extern const char js_writable_str[]; +extern const char js_value_str[]; + +#ifdef NARCISSUS +extern const char js___call___str[]; +extern const char js___construct___str[]; +extern const char js___hasInstance___str[]; +extern const char js_ExecutionContext_str[]; +extern const char js_current_str[]; +#endif + +/* + * Initialize atom state. Return true on success, false on failure to allocate + * memory. The caller must zero rt->atomState before calling this function and + * only call it after js_InitGC successfully returns. + */ +extern JSBool +js_InitAtomState(JSRuntime *rt); + +/* + * Free and clear atom state including any interned string atoms. This + * function must be called before js_FinishGC. + */ +extern void +js_FinishAtomState(JSRuntime *rt); + +/* + * Atom tracing and garbage collection hooks. + */ + +extern void +js_TraceAtomState(JSTracer *trc, JSBool allAtoms); + +extern void +js_SweepAtomState(JSContext *cx); + +extern JSBool +js_InitCommonAtoms(JSContext *cx); + +extern void +js_FinishCommonAtoms(JSContext *cx); + +/* + * Find or create the atom for a double value. Return null on failure to + * allocate memory. + */ +extern JSAtom * +js_AtomizeDouble(JSContext *cx, jsdouble d); + +/* + * Find or create the atom for a string. Return null on failure to allocate + * memory. + */ +extern JSAtom * +js_AtomizeString(JSContext *cx, JSString *str, uintN flags); + +extern JSAtom * +js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags); + +extern JSAtom * +js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length, uintN flags); + +/* + * Return an existing atom for the given char array or null if the char + * sequence is currently not atomized. + */ +extern JSAtom * +js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length); + +/* + * This variant handles all primitive values. + */ +JSBool +js_AtomizePrimitiveValue(JSContext *cx, jsval v, JSAtom **atomp); + +#ifdef DEBUG + +extern JS_FRIEND_API(void) +js_DumpAtoms(JSContext *cx, FILE *fp); + +#endif + +/* + * For all unmapped atoms recorded in al, add a mapping from the atom's index + * to its address. map->length must already be set to the number of atoms in + * the list and map->vector must point to pre-allocated memory. + */ +extern void +js_InitAtomMap(JSContext *cx, JSAtomMap *map, JSAtomList *al); + +JS_END_EXTERN_C + +#endif /* jsatom_h___ */ diff --git a/ape-server/deps/js/src/jsatominlines.h b/ape-server/deps/js/src/jsatominlines.h new file mode 100755 index 0000000..fb45543 --- /dev/null +++ b/ape-server/deps/js/src/jsatominlines.h @@ -0,0 +1,95 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsatominlines_h___ +#define jsatominlines_h___ + +#include "jsatom.h" +#include "jsnum.h" + +/* + * Convert v to an atomized string and wrap it as an id. + */ +inline JSBool +js_ValueToStringId(JSContext *cx, jsval v, jsid *idp) +{ + JSString *str; + JSAtom *atom; + + /* + * Optimize for the common case where v is an already-atomized string. The + * comment in jsstr.h before JSString::flatSetAtomized explains why this is + * thread-safe. The extra rooting via lastAtom (which would otherwise be + * done in js_js_AtomizeString) ensures the caller that the resulting id at + * is least weakly rooted. + */ + if (JSVAL_IS_STRING(v)) { + str = JSVAL_TO_STRING(v); + if (str->isAtomized()) { + cx->weakRoots.lastAtom = v; + *idp = ATOM_TO_JSID((JSAtom *) v); + return JS_TRUE; + } + } else { + str = js_ValueToString(cx, v); + if (!str) + return JS_FALSE; + } + atom = js_AtomizeString(cx, str, 0); + if (!atom) + return JS_FALSE; + *idp = ATOM_TO_JSID(atom); + return JS_TRUE; +} + +inline JSBool +js_Int32ToId(JSContext* cx, int32 index, jsid* id) +{ + if (INT_FITS_IN_JSVAL(index)) { + *id = INT_TO_JSID(index); + JS_ASSERT(INT_JSID_TO_JSVAL(*id) == INT_TO_JSVAL(index)); + return JS_TRUE; + } + JSString* str = js_NumberToString(cx, index); + if (!str) + return JS_FALSE; + return js_ValueToStringId(cx, STRING_TO_JSVAL(str), id); +} + +#endif /* jsatominlines_h___ */ diff --git a/ape-server/deps/js/src/jsbit.h b/ape-server/deps/js/src/jsbit.h new file mode 100755 index 0000000..ea19b3a --- /dev/null +++ b/ape-server/deps/js/src/jsbit.h @@ -0,0 +1,274 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsbit_h___ +#define jsbit_h___ + +#include "jstypes.h" +#include "jsutil.h" + +JS_BEGIN_EXTERN_C + +/* +** A jsbitmap_t is a long integer that can be used for bitmaps +*/ +typedef JSUword jsbitmap_t; /* NSPR name, a la Unix system types */ +typedef jsbitmap_t jsbitmap; /* JS-style scalar typedef name */ + +#define JS_BITMAP_SIZE(bits) (JS_HOWMANY(bits, JS_BITS_PER_WORD) * \ + sizeof(jsbitmap)) + +#define JS_TEST_BIT(_map,_bit) ((_map)[(_bit)>>JS_BITS_PER_WORD_LOG2] & \ + ((jsbitmap)1<<((_bit)&(JS_BITS_PER_WORD-1)))) +#define JS_SET_BIT(_map,_bit) ((_map)[(_bit)>>JS_BITS_PER_WORD_LOG2] |= \ + ((jsbitmap)1<<((_bit)&(JS_BITS_PER_WORD-1)))) +#define JS_CLEAR_BIT(_map,_bit) ((_map)[(_bit)>>JS_BITS_PER_WORD_LOG2] &= \ + ~((jsbitmap)1<<((_bit)&(JS_BITS_PER_WORD-1)))) + +/* +** Compute the log of the least power of 2 greater than or equal to n +*/ +extern JS_PUBLIC_API(JSIntn) JS_CeilingLog2(JSUint32 i); + +/* +** Compute the log of the greatest power of 2 less than or equal to n +*/ +extern JS_PUBLIC_API(JSIntn) JS_FloorLog2(JSUint32 i); + +/* + * Replace bit-scanning code sequences with CPU-specific instructions to + * speedup calculations of ceiling/floor log2. + * + * With GCC 3.4 or later we can use __builtin_clz for that, see bug 327129. + * + * SWS: Added MSVC intrinsic bitscan support. See bugs 349364 and 356856. + */ +#if defined(_WIN32) && (_MSC_VER >= 1300) && (defined(_M_IX86) || defined(_M_AMD64) || defined(_M_X64)) + +unsigned char _BitScanForward(unsigned long * Index, unsigned long Mask); +unsigned char _BitScanReverse(unsigned long * Index, unsigned long Mask); +# pragma intrinsic(_BitScanForward,_BitScanReverse) + +__forceinline static int +__BitScanForward32(unsigned int val) +{ + unsigned long idx; + + _BitScanForward(&idx, (unsigned long)val); + return (int)idx; +} +__forceinline static int +__BitScanReverse32(unsigned int val) +{ + unsigned long idx; + + _BitScanReverse(&idx, (unsigned long)val); + return (int)(31-idx); +} +# define js_bitscan_ctz32(val) __BitScanForward32(val) +# define js_bitscan_clz32(val) __BitScanReverse32(val) +# define JS_HAS_BUILTIN_BITSCAN32 + +#if defined(_M_AMD64) || defined(_M_X64) +unsigned char _BitScanForward64(unsigned long * Index, unsigned __int64 Mask); +unsigned char _BitScanReverse64(unsigned long * Index, unsigned __int64 Mask); +# pragma intrinsic(_BitScanForward64,_BitScanReverse64) + +__forceinline static int +__BitScanForward64(unsigned __int64 val) +{ + unsigned long idx; + + _BitScanForward64(&idx, val); + return (int)idx; +} +__forceinline static int +__BitScanReverse64(unsigned __int64 val) +{ + unsigned long idx; + + _BitScanReverse64(&idx, val); + return (int)(63-idx); +} +# define js_bitscan_ctz64(val) __BitScanForward64(val) +# define js_bitscan_clz64(val) __BitScanReverse64(val) +# define JS_HAS_BUILTIN_BITSCAN64 +#endif +#elif (__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) + +# define js_bitscan_ctz32(val) __builtin_ctz(val) +# define js_bitscan_clz32(val) __builtin_clz(val) +# define JS_HAS_BUILTIN_BITSCAN32 +# if (JS_BYTES_PER_WORD == 8) +# define js_bitscan_ctz64(val) __builtin_ctzll(val) +# define js_bitscan_clz64(val) __builtin_clzll(val) +# define JS_HAS_BUILTIN_BITSCAN64 +# endif + +#endif + +/* +** Macro version of JS_CeilingLog2: Compute the log of the least power of +** 2 greater than or equal to _n. The result is returned in _log2. +*/ +#ifdef JS_HAS_BUILTIN_BITSCAN32 +/* + * Use intrinsic function or count-leading-zeros to calculate ceil(log2(_n)). + * The macro checks for "n <= 1" and not "n != 0" as js_bitscan_clz32(0) is + * undefined. + */ +# define JS_CEILING_LOG2(_log2,_n) \ + JS_BEGIN_MACRO \ + unsigned int j_ = (unsigned int)(_n); \ + (_log2) = (j_ <= 1 ? 0 : 32 - js_bitscan_clz32(j_ - 1)); \ + JS_END_MACRO +#else +# define JS_CEILING_LOG2(_log2,_n) \ + JS_BEGIN_MACRO \ + JSUint32 j_ = (JSUint32)(_n); \ + (_log2) = 0; \ + if ((j_) & ((j_)-1)) \ + (_log2) += 1; \ + if ((j_) >> 16) \ + (_log2) += 16, (j_) >>= 16; \ + if ((j_) >> 8) \ + (_log2) += 8, (j_) >>= 8; \ + if ((j_) >> 4) \ + (_log2) += 4, (j_) >>= 4; \ + if ((j_) >> 2) \ + (_log2) += 2, (j_) >>= 2; \ + if ((j_) >> 1) \ + (_log2) += 1; \ + JS_END_MACRO +#endif + +/* +** Macro version of JS_FloorLog2: Compute the log of the greatest power of +** 2 less than or equal to _n. The result is returned in _log2. +** +** This is equivalent to finding the highest set bit in the word. +*/ +#ifdef JS_HAS_BUILTIN_BITSCAN32 +/* + * Use js_bitscan_clz32 or count-leading-zeros to calculate floor(log2(_n)). + * Since js_bitscan_clz32(0) is undefined, the macro set the loweset bit to 1 + * to ensure 0 result when _n == 0. + */ +# define JS_FLOOR_LOG2(_log2,_n) \ + JS_BEGIN_MACRO \ + (_log2) = 31 - js_bitscan_clz32(((unsigned int)(_n)) | 1); \ + JS_END_MACRO +#else +# define JS_FLOOR_LOG2(_log2,_n) \ + JS_BEGIN_MACRO \ + JSUint32 j_ = (JSUint32)(_n); \ + (_log2) = 0; \ + if ((j_) >> 16) \ + (_log2) += 16, (j_) >>= 16; \ + if ((j_) >> 8) \ + (_log2) += 8, (j_) >>= 8; \ + if ((j_) >> 4) \ + (_log2) += 4, (j_) >>= 4; \ + if ((j_) >> 2) \ + (_log2) += 2, (j_) >>= 2; \ + if ((j_) >> 1) \ + (_log2) += 1; \ + JS_END_MACRO +#endif + +/* + * Internal function. + * Compute the log of the least power of 2 greater than or equal to n. + * This is a version of JS_CeilingLog2 that operates on jsuword with + * CPU-dependant size. + */ +#define JS_CEILING_LOG2W(n) ((n) <= 1 ? 0 : 1 + JS_FLOOR_LOG2W((n) - 1)) + +/* + * Internal function. + * Compute the log of the greatest power of 2 less than or equal to n. + * This is a version of JS_FloorLog2 that operates on jsuword with + * CPU-dependant size and requires that n != 0. + */ +#define JS_FLOOR_LOG2W(n) (JS_ASSERT((n) != 0), js_FloorLog2wImpl(n)) + +#if JS_BYTES_PER_WORD == 4 + +# ifdef JS_HAS_BUILTIN_BITSCAN32 +# define js_FloorLog2wImpl(n) \ + ((JSUword)(JS_BITS_PER_WORD - 1 - js_bitscan_clz32(n))) +# else +# define js_FloorLog2wImpl(n) ((JSUword)JS_FloorLog2(n)) +#endif + +#elif JS_BYTES_PER_WORD == 8 + +# ifdef JS_HAS_BUILTIN_BITSCAN64 +# define js_FloorLog2wImpl(n) \ + ((JSUword)(JS_BITS_PER_WORD - 1 - js_bitscan_clz64(n))) +# else +extern JSUword js_FloorLog2wImpl(JSUword n); +# endif + +#else + +# error "NOT SUPPORTED" + +#endif + +/* + * Macros for rotate left. There is no rotate operation in the C Language so + * the construct (a << 4) | (a >> 28) is used instead. Most compilers convert + * this to a rotate instruction but some versions of MSVC don't without a + * little help. To get MSVC to generate a rotate instruction, we have to use + * the _rotl intrinsic and use a pragma to make _rotl inline. + * + * MSVC in VS2005 will do an inline rotate instruction on the above construct. + */ + +#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_AMD64) || \ + defined(_M_X64)) +#include +#pragma intrinsic(_rotl) +#define JS_ROTATE_LEFT32(a, bits) _rotl(a, bits) +#else +#define JS_ROTATE_LEFT32(a, bits) (((a) << (bits)) | ((a) >> (32 - (bits)))) +#endif + +JS_END_EXTERN_C +#endif /* jsbit_h___ */ diff --git a/ape-server/deps/js/src/jsbool.cpp b/ape-server/deps/js/src/jsbool.cpp new file mode 100755 index 0000000..6d5a7d9 --- /dev/null +++ b/ape-server/deps/js/src/jsbool.cpp @@ -0,0 +1,191 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * JS boolean implementation. + */ +#include "jstypes.h" +#include "jsstdint.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jsapi.h" +#include "jsatom.h" +#include "jsbool.h" +#include "jscntxt.h" +#include "jsversion.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsobj.h" +#include "jsstr.h" +#include "jsvector.h" + +/* Check pseudo-booleans values. */ +JS_STATIC_ASSERT(!(JSVAL_TRUE & JSVAL_HOLE_FLAG)); +JS_STATIC_ASSERT(!(JSVAL_FALSE & JSVAL_HOLE_FLAG)); +JS_STATIC_ASSERT(!(JSVAL_VOID & JSVAL_HOLE_FLAG)); +JS_STATIC_ASSERT((JSVAL_HOLE & JSVAL_HOLE_FLAG)); +JS_STATIC_ASSERT((JSVAL_HOLE & ~JSVAL_HOLE_FLAG) == JSVAL_VOID); +JS_STATIC_ASSERT(!(JSVAL_ARETURN & JSVAL_HOLE_FLAG)); + +JSClass js_BooleanClass = { + "Boolean", + JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_HAS_CACHED_PROTO(JSProto_Boolean), + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +#if JS_HAS_TOSOURCE +#include "jsprf.h" + +static JSBool +bool_toSource(JSContext *cx, uintN argc, jsval *vp) +{ + jsval v; + char buf[32]; + JSString *str; + + if (!js_GetPrimitiveThis(cx, vp, &js_BooleanClass, &v)) + return JS_FALSE; + JS_ASSERT(JSVAL_IS_BOOLEAN(v)); + JS_snprintf(buf, sizeof buf, "(new %s(%s))", + js_BooleanClass.name, + JS_BOOLEAN_STR(JSVAL_TO_BOOLEAN(v))); + str = JS_NewStringCopyZ(cx, buf); + if (!str) + return JS_FALSE; + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; +} +#endif + +static JSBool +bool_toString(JSContext *cx, uintN argc, jsval *vp) +{ + jsval v; + JSAtom *atom; + JSString *str; + + if (!js_GetPrimitiveThis(cx, vp, &js_BooleanClass, &v)) + return JS_FALSE; + JS_ASSERT(JSVAL_IS_BOOLEAN(v)); + atom = cx->runtime->atomState.booleanAtoms[JSVAL_TO_BOOLEAN(v) ? 1 : 0]; + str = ATOM_TO_STRING(atom); + if (!str) + return JS_FALSE; + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +bool_valueOf(JSContext *cx, uintN argc, jsval *vp) +{ + return js_GetPrimitiveThis(cx, vp, &js_BooleanClass, vp); +} + +static JSFunctionSpec boolean_methods[] = { +#if JS_HAS_TOSOURCE + JS_FN(js_toSource_str, bool_toSource, 0, JSFUN_THISP_BOOLEAN), +#endif + JS_FN(js_toString_str, bool_toString, 0, JSFUN_THISP_BOOLEAN), + JS_FN(js_valueOf_str, bool_valueOf, 0, JSFUN_THISP_BOOLEAN), + JS_FN(js_toJSON_str, bool_valueOf, 0, JSFUN_THISP_BOOLEAN), + JS_FS_END +}; + +static JSBool +Boolean(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval bval; + + bval = (argc != 0) + ? BOOLEAN_TO_JSVAL(js_ValueToBoolean(argv[0])) + : JSVAL_FALSE; + if (!JS_IsConstructing(cx)) + *rval = bval; + else + obj->fslots[JSSLOT_PRIMITIVE_THIS] = bval; + return true; +} + +JSObject * +js_InitBooleanClass(JSContext *cx, JSObject *obj) +{ + JSObject *proto; + + proto = JS_InitClass(cx, obj, NULL, &js_BooleanClass, Boolean, 1, + NULL, boolean_methods, NULL, NULL); + if (!proto) + return NULL; + proto->fslots[JSSLOT_PRIMITIVE_THIS] = JSVAL_FALSE; + return proto; +} + +JSString * +js_BooleanToString(JSContext *cx, JSBool b) +{ + return ATOM_TO_STRING(cx->runtime->atomState.booleanAtoms[b ? 1 : 0]); +} + +/* This function implements E-262-3 section 9.8, toString. */ +JSBool +js_BooleanToCharBuffer(JSContext *cx, JSBool b, JSCharBuffer &cb) +{ + return b ? js_AppendLiteral(cb, "true") : js_AppendLiteral(cb, "false"); +} + +JSBool +js_ValueToBoolean(jsval v) +{ + if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) + return JS_FALSE; + if (JSVAL_IS_OBJECT(v)) + return JS_TRUE; + if (JSVAL_IS_STRING(v)) + return JSVAL_TO_STRING(v)->length() != 0; + if (JSVAL_IS_INT(v)) + return JSVAL_TO_INT(v) != 0; + if (JSVAL_IS_DOUBLE(v)) { + jsdouble d; + + d = *JSVAL_TO_DOUBLE(v); + return !JSDOUBLE_IS_NaN(d) && d != 0; + } + JS_ASSERT(JSVAL_IS_BOOLEAN(v)); + return JSVAL_TO_BOOLEAN(v); +} diff --git a/ape-server/deps/js/src/jsbool.h b/ape-server/deps/js/src/jsbool.h new file mode 100755 index 0000000..b9f64f4 --- /dev/null +++ b/ape-server/deps/js/src/jsbool.h @@ -0,0 +1,84 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsbool_h___ +#define jsbool_h___ +/* + * JS boolean interface. + */ + +#include "jsapi.h" + +JS_BEGIN_EXTERN_C + +/* + * Special values, not visible to script but used internally by the engine. + * + * JSVAL_HOLE is a useful value for identifying a hole in an array. It's also + * used in the interpreter to represent "no exception pending". In general it + * can be used to represent "no value". + * + * A JSVAL_HOLE can be cheaply converted to undefined without affecting any + * other boolean (or special value) by masking out JSVAL_HOLE_FLAG. + * + * JSVAL_ARETURN is used to throw asynchronous return for generator.close(). + * + * NB: SPECIAL_TO_JSVAL(2) is JSVAL_VOID (see jsapi.h). + */ +#define JSVAL_HOLE_FLAG jsval(4 << JSVAL_TAGBITS) +#define JSVAL_HOLE (JSVAL_VOID | JSVAL_HOLE_FLAG) +#define JSVAL_ARETURN SPECIAL_TO_JSVAL(8) + +extern JSClass js_BooleanClass; + +extern JSObject * +js_InitBooleanClass(JSContext *cx, JSObject *obj); + +extern JSString * +js_BooleanToString(JSContext *cx, JSBool b); + +extern JSBool +js_BooleanToCharBuffer(JSContext *cx, JSBool b, JSCharBuffer &cb); + +extern JSBool +js_ValueToBoolean(jsval v); + +JS_END_EXTERN_C + +#endif /* jsbool_h___ */ diff --git a/ape-server/deps/js/src/jsbuiltins.cpp b/ape-server/deps/js/src/jsbuiltins.cpp new file mode 100755 index 0000000..1c5625f --- /dev/null +++ b/ape-server/deps/js/src/jsbuiltins.cpp @@ -0,0 +1,458 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4; -*- + * vim: set ts=4 sw=4 et tw=99: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released + * May 28, 2008. + * + * The Initial Developer of the Original Code is + * Andreas Gal + * + * Contributor(s): + * Brendan Eich + * Mike Shaver + * David Anderson + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include + +#include "jsapi.h" +#include "jsstdint.h" +#include "jsarray.h" +#include "jsbool.h" +#include "jscntxt.h" +#include "jsgc.h" +#include "jsiter.h" +#include "jsnum.h" +#include "jslibmath.h" +#include "jsmath.h" +#include "jsnum.h" +#include "prmjtime.h" +#include "jsdate.h" +#include "jsscope.h" +#include "jsstr.h" +#include "jsbuiltins.h" +#include "jstracer.h" +#include "jsvector.h" + +#include "jsatominlines.h" +#include "jsobjinlines.h" +#include "jsscopeinlines.h" + +using namespace avmplus; +using namespace nanojit; + +JS_FRIEND_API(void) +js_SetTraceableNativeFailed(JSContext *cx) +{ + js_SetBuiltinError(cx); +} + +/* + * NB: bool FASTCALL is not compatible with Nanojit's calling convention usage. + * Do not use bool FASTCALL, use JSBool only! + */ + +jsdouble FASTCALL +js_dmod(jsdouble a, jsdouble b) +{ + if (b == 0.0) { + jsdpun u; + u.s.hi = JSDOUBLE_HI32_EXPMASK | JSDOUBLE_HI32_MANTMASK; + u.s.lo = 0xffffffff; + return u.d; + } + return js_fmod(a, b); +} +JS_DEFINE_CALLINFO_2(extern, DOUBLE, js_dmod, DOUBLE, DOUBLE, 1, 1) + +int32 FASTCALL +js_imod(int32 a, int32 b) +{ + if (a < 0 || b <= 0) + return -1; + int r = a % b; + return r; +} +JS_DEFINE_CALLINFO_2(extern, INT32, js_imod, INT32, INT32, 1, 1) + +/* The following boxing/unboxing primitives we can't emit inline because + they either interact with the GC and depend on Spidermonkey's 32-bit + integer representation. */ + +jsval FASTCALL +js_BoxDouble(JSContext* cx, jsdouble d) +{ + int32 i; + if (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i)) + return INT_TO_JSVAL(i); + JS_ASSERT(JS_ON_TRACE(cx)); + jsval v; /* not rooted but ok here because we know GC won't run */ + if (!js_NewDoubleInRootedValue(cx, d, &v)) + return JSVAL_ERROR_COOKIE; + return v; +} +JS_DEFINE_CALLINFO_2(extern, JSVAL, js_BoxDouble, CONTEXT, DOUBLE, 1, 1) + +jsval FASTCALL +js_BoxInt32(JSContext* cx, int32 i) +{ + if (JS_LIKELY(INT_FITS_IN_JSVAL(i))) + return INT_TO_JSVAL(i); + JS_ASSERT(JS_ON_TRACE(cx)); + jsval v; /* not rooted but ok here because we know GC won't run */ + jsdouble d = (jsdouble)i; + if (!js_NewDoubleInRootedValue(cx, d, &v)) + return JSVAL_ERROR_COOKIE; + return v; +} +JS_DEFINE_CALLINFO_2(extern, JSVAL, js_BoxInt32, CONTEXT, INT32, 1, 1) + +jsdouble FASTCALL +js_UnboxDouble(jsval v) +{ + if (JS_LIKELY(JSVAL_IS_INT(v))) + return (jsdouble)JSVAL_TO_INT(v); + return *JSVAL_TO_DOUBLE(v); +} +JS_DEFINE_CALLINFO_1(extern, DOUBLE, js_UnboxDouble, JSVAL, 1, 1) + +int32 FASTCALL +js_UnboxInt32(jsval v) +{ + if (JS_LIKELY(JSVAL_IS_INT(v))) + return JSVAL_TO_INT(v); + return js_DoubleToECMAInt32(*JSVAL_TO_DOUBLE(v)); +} +JS_DEFINE_CALLINFO_1(extern, INT32, js_UnboxInt32, JSVAL, 1, 1) + +JSBool FASTCALL +js_TryUnboxInt32(jsval v, int32* i32p) +{ + if (JS_LIKELY(JSVAL_IS_INT(v))) { + *i32p = JSVAL_TO_INT(v); + return JS_TRUE; + } + if (!JSVAL_IS_DOUBLE(v)) + return JS_FALSE; + int32 i; + jsdouble d = *JSVAL_TO_DOUBLE(v); + if (!JSDOUBLE_IS_INT(d, i)) + return JS_FALSE; + *i32p = i; + return JS_TRUE; +} +JS_DEFINE_CALLINFO_2(extern, BOOL, js_TryUnboxInt32, JSVAL, INT32PTR, 1, 1) + +int32 FASTCALL +js_DoubleToInt32(jsdouble d) +{ + return js_DoubleToECMAInt32(d); +} +JS_DEFINE_CALLINFO_1(extern, INT32, js_DoubleToInt32, DOUBLE, 1, 1) + +uint32 FASTCALL +js_DoubleToUint32(jsdouble d) +{ + return js_DoubleToECMAUint32(d); +} +JS_DEFINE_CALLINFO_1(extern, UINT32, js_DoubleToUint32, DOUBLE, 1, 1) + +jsdouble FASTCALL +js_StringToNumber(JSContext* cx, JSString* str) +{ + const jschar* bp; + const jschar* end; + const jschar* ep; + jsdouble d; + + str->getCharsAndEnd(bp, end); + if ((!js_strtod(cx, bp, end, &ep, &d) || + js_SkipWhiteSpace(ep, end) != end) && + (!js_strtointeger(cx, bp, end, &ep, 0, &d) || + js_SkipWhiteSpace(ep, end) != end)) { + return js_NaN; + } + return d; +} +JS_DEFINE_CALLINFO_2(extern, DOUBLE, js_StringToNumber, CONTEXT, STRING, 1, 1) + +int32 FASTCALL +js_StringToInt32(JSContext* cx, JSString* str) +{ + const jschar* bp; + const jschar* end; + const jschar* ep; + jsdouble d; + + if (str->length() == 1) { + jschar c = str->chars()[0]; + if ('0' <= c && c <= '9') + return c - '0'; + return 0; + } + + str->getCharsAndEnd(bp, end); + if ((!js_strtod(cx, bp, end, &ep, &d) || + js_SkipWhiteSpace(ep, end) != end) && + (!js_strtointeger(cx, bp, end, &ep, 0, &d) || + js_SkipWhiteSpace(ep, end) != end)) { + return 0; + } + return js_DoubleToECMAInt32(d); +} +JS_DEFINE_CALLINFO_2(extern, INT32, js_StringToInt32, CONTEXT, STRING, 1, 1) + +JSBool FASTCALL +js_AddProperty(JSContext* cx, JSObject* obj, JSScopeProperty* sprop) +{ + JS_LOCK_OBJ(cx, obj); + + uint32 slot = sprop->slot; + JSScope* scope = OBJ_SCOPE(obj); + JS_ASSERT(slot == scope->freeslot); + JS_ASSERT(sprop->parent == scope->lastProperty()); + + if (scope->owned()) { + JS_ASSERT(!scope->hasProperty(sprop)); + } else { + scope = js_GetMutableScope(cx, obj); + if (!scope) + goto exit_trace; + } + + if (!scope->table) { + if (slot < STOBJ_NSLOTS(obj) && !OBJ_GET_CLASS(cx, obj)->reserveSlots) { + JS_ASSERT(JSVAL_IS_VOID(STOBJ_GET_SLOT(obj, scope->freeslot))); + ++scope->freeslot; + } else { + if (!js_AllocSlot(cx, obj, &slot)) + goto exit_trace; + + if (slot != sprop->slot) { + js_FreeSlot(cx, obj, slot); + goto exit_trace; + } + } + + scope->extend(cx, sprop); + } else { + JSScopeProperty *sprop2 = + scope->addProperty(cx, sprop->id, sprop->getter, sprop->setter, SPROP_INVALID_SLOT, + sprop->attrs, sprop->flags, sprop->shortid); + if (sprop2 != sprop) + goto exit_trace; + } + + if (js_IsPropertyCacheDisabled(cx)) + goto exit_trace; + + JS_UNLOCK_SCOPE(cx, scope); + return JS_TRUE; + + exit_trace: + JS_UNLOCK_SCOPE(cx, scope); + return JS_FALSE; +} +JS_DEFINE_CALLINFO_3(extern, BOOL, js_AddProperty, CONTEXT, OBJECT, SCOPEPROP, 0, 0) + +static JSBool +HasProperty(JSContext* cx, JSObject* obj, jsid id) +{ + // Check that we know how the lookup op will behave. + for (JSObject* pobj = obj; pobj; pobj = OBJ_GET_PROTO(cx, pobj)) { + if (pobj->map->ops->lookupProperty != js_LookupProperty) + return JSVAL_TO_SPECIAL(JSVAL_VOID); + JSClass* clasp = OBJ_GET_CLASS(cx, pobj); + if (clasp->resolve != JS_ResolveStub && clasp != &js_StringClass) + return JSVAL_TO_SPECIAL(JSVAL_VOID); + } + + JSObject* obj2; + JSProperty* prop; + if (js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_QUALIFIED, &obj2, &prop) < 0) + return JSVAL_TO_SPECIAL(JSVAL_VOID); + if (prop) + obj2->dropProperty(cx, prop); + return prop != NULL; +} + +JSBool FASTCALL +js_HasNamedProperty(JSContext* cx, JSObject* obj, JSString* idstr) +{ + jsid id; + if (!js_ValueToStringId(cx, STRING_TO_JSVAL(idstr), &id)) + return JSVAL_TO_BOOLEAN(JSVAL_VOID); + + return HasProperty(cx, obj, id); +} +JS_DEFINE_CALLINFO_3(extern, BOOL, js_HasNamedProperty, CONTEXT, OBJECT, STRING, 0, 0) + +JSBool FASTCALL +js_HasNamedPropertyInt32(JSContext* cx, JSObject* obj, int32 index) +{ + jsid id; + if (!js_Int32ToId(cx, index, &id)) + return JSVAL_TO_BOOLEAN(JSVAL_VOID); + + return HasProperty(cx, obj, id); +} +JS_DEFINE_CALLINFO_3(extern, BOOL, js_HasNamedPropertyInt32, CONTEXT, OBJECT, INT32, 0, 0) + +JSString* FASTCALL +js_TypeOfObject(JSContext* cx, JSObject* obj) +{ + JSType type = JS_TypeOfValue(cx, OBJECT_TO_JSVAL(obj)); + return ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[type]); +} +JS_DEFINE_CALLINFO_2(extern, STRING, js_TypeOfObject, CONTEXT, OBJECT, 1, 1) + +JSString* FASTCALL +js_TypeOfBoolean(JSContext* cx, int32 unboxed) +{ + /* Watch out for pseudo-booleans. */ + jsval boxed = SPECIAL_TO_JSVAL(unboxed); + JS_ASSERT(JSVAL_IS_VOID(boxed) || JSVAL_IS_BOOLEAN(boxed)); + JSType type = JS_TypeOfValue(cx, boxed); + return ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[type]); +} +JS_DEFINE_CALLINFO_2(extern, STRING, js_TypeOfBoolean, CONTEXT, INT32, 1, 1) + +jsdouble FASTCALL +js_BooleanOrUndefinedToNumber(JSContext* cx, int32 unboxed) +{ + if (unboxed == JSVAL_TO_SPECIAL(JSVAL_VOID)) + return js_NaN; + JS_ASSERT(unboxed == JS_TRUE || unboxed == JS_FALSE); + return unboxed; +} +JS_DEFINE_CALLINFO_2(extern, DOUBLE, js_BooleanOrUndefinedToNumber, CONTEXT, INT32, 1, 1) + +JSString* FASTCALL +js_BooleanOrUndefinedToString(JSContext *cx, int32 unboxed) +{ + JS_ASSERT(uint32(unboxed) <= 2); + return ATOM_TO_STRING(cx->runtime->atomState.booleanAtoms[unboxed]); +} +JS_DEFINE_CALLINFO_2(extern, STRING, js_BooleanOrUndefinedToString, CONTEXT, INT32, 1, 1) + +JSObject* FASTCALL +js_NewNullClosure(JSContext* cx, JSObject* funobj, JSObject* proto, JSObject* parent) +{ + JS_ASSERT(HAS_FUNCTION_CLASS(funobj)); + JS_ASSERT(HAS_FUNCTION_CLASS(proto)); + JS_ASSERT(JS_ON_TRACE(cx)); + + JSFunction *fun = (JSFunction*) funobj; + JS_ASSERT(GET_FUNCTION_PRIVATE(cx, funobj) == fun); + + JSObject* closure = js_NewGCObject(cx); + if (!closure) + return NULL; + + closure->initSharingEmptyScope(&js_FunctionClass, proto, parent, + reinterpret_cast(fun)); + return closure; +} +JS_DEFINE_CALLINFO_4(extern, OBJECT, js_NewNullClosure, CONTEXT, OBJECT, OBJECT, OBJECT, 0, 0) + +JS_REQUIRES_STACK JSBool FASTCALL +js_PopInterpFrame(JSContext* cx, InterpState* state) +{ + JS_ASSERT(cx->fp && cx->fp->down); + JSInlineFrame* ifp = (JSInlineFrame*)cx->fp; + + /* + * Mirror frame popping code from inline_return in js_Interpret. There are + * some things we just don't want to handle. In those cases, the trace will + * MISMATCH_EXIT. + */ + if (ifp->hookData) + return JS_FALSE; + if (cx->version != ifp->callerVersion) + return JS_FALSE; + if (cx->fp->flags & JSFRAME_CONSTRUCTING) + return JS_FALSE; + if (cx->fp->imacpc) + return JS_FALSE; + + /* Update display table. */ + if (cx->fp->script->staticLevel < JS_DISPLAY_SIZE) + cx->display[cx->fp->script->staticLevel] = cx->fp->displaySave; + + /* Pop the frame and its memory. */ + cx->fp = cx->fp->down; + JS_ASSERT(cx->fp->regs == &ifp->callerRegs); + cx->fp->regs = ifp->frame.regs; + + JS_ARENA_RELEASE(&cx->stackPool, ifp->mark); + + /* Update the inline call count. */ + *state->inlineCallCountp = *state->inlineCallCountp - 1; + return JS_TRUE; +} +JS_DEFINE_CALLINFO_2(extern, BOOL, js_PopInterpFrame, CONTEXT, INTERPSTATE, 0, 0) + +JSString* FASTCALL +js_ConcatN(JSContext *cx, JSString **strArray, uint32 size) +{ + /* Calculate total size. */ + size_t numChar = 1; + for (uint32 i = 0; i < size; ++i) { + size_t before = numChar; + numChar += strArray[i]->length(); + if (numChar < before) + return NULL; + } + + + /* Allocate buffer. */ + if (numChar & js::tl::MulOverflowMask::result) + return NULL; + jschar *buf = (jschar *)cx->malloc(numChar * sizeof(jschar)); + if (!buf) + return NULL; + + /* Fill buffer. */ + jschar *ptr = buf; + for (uint32 i = 0; i < size; ++i) { + const jschar *chars; + size_t length; + strArray[i]->getCharsAndLength(chars, length); + js_strncpy(ptr, chars, length); + ptr += length; + } + *ptr = '\0'; + + /* Create string. */ + JSString *str = js_NewString(cx, buf, numChar - 1); + if (!str) + cx->free(buf); + return str; +} +JS_DEFINE_CALLINFO_3(extern, STRING, js_ConcatN, CONTEXT, STRINGPTR, UINT32, 0, 0) diff --git a/ape-server/deps/js/src/jsbuiltins.h b/ape-server/deps/js/src/jsbuiltins.h new file mode 100755 index 0000000..20d3126 --- /dev/null +++ b/ape-server/deps/js/src/jsbuiltins.h @@ -0,0 +1,529 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=99: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released + * May 28, 2008. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * + * Contributor(s): + * Jason Orendorff + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsbuiltins_h___ +#define jsbuiltins_h___ + +#ifdef JS_TRACER + +#include "nanojit/nanojit.h" + +#ifdef THIS +#undef THIS +#endif + +enum JSTNErrType { INFALLIBLE, FAIL_STATUS, FAIL_NULL, FAIL_NEG, FAIL_VOID, FAIL_COOKIE }; +enum { JSTN_ERRTYPE_MASK = 0x07, JSTN_UNBOX_AFTER = 0x08, JSTN_MORE = 0x10, + JSTN_CONSTRUCTOR = 0x20 }; + +#define JSTN_ERRTYPE(jstn) ((jstn)->flags & JSTN_ERRTYPE_MASK) + +/* + * Type describing a type specialization of a JSFastNative. + * + * |prefix| and |argtypes| declare what arguments should be passed to the + * native function. |prefix| can contain the following characters: + * + * 'C': a JSContext* argument + * 'T': |this| as a JSObject* argument (bails if |this| is not an object) + * 'S': |this| as a JSString* argument (bails if |this| is not a string) + * 'R': a JSRuntime* argument + * 'P': the pc as a jsbytecode* + * 'D': |this| as a number (jsdouble) + * 'f': the function being called, as a JSObject* + * 'p': the .prototype of the function, as a JSObject* + * + * The corresponding things will get passed as arguments to the builtin in + * reverse order (so TC means JSContext* as the first arg, and the + * JSObject* for |this| as the second arg). + * + * |argtypes| can contain the following characters: + * 'd': a number (double) argument + * 'i': an integer argument + * 's': a JSString* argument + * 'o': a JSObject* argument + * 'r': a JSObject* argument that is of class js_RegExpClass + * 'f': a JSObject* argument that is of class js_FunctionClass + * 'v': a jsval argument (boxing whatever value is actually being passed in) + */ +struct JSSpecializedNative { + const nanojit::CallInfo *builtin; + const char *prefix; + const char *argtypes; + uintN flags; /* JSTNErrType | JSTN_UNBOX_AFTER | JSTN_MORE | + JSTN_CONSTRUCTOR */ +}; + +/* + * Type holding extra trace-specific information about a fast native. + * + * 'specializations' points to a static array of available specializations + * terminated by the lack of having the JSTN_MORE flag set. + */ +struct JSNativeTraceInfo { + JSFastNative native; + JSSpecializedNative *specializations; +}; + +/* + * We use a magic boxed pointer value to represent error conditions that + * trigger a side exit. The address is so low that it should never be actually + * in use. If it is, a performance regression occurs, not an actual runtime + * error. + */ +#define JSVAL_ERROR_COOKIE OBJECT_TO_JSVAL((JSObject*)0x10) + +/* Macros used by JS_DEFINE_CALLINFOn. */ +#ifdef DEBUG +#define _JS_CI_NAME(op) ,#op +#else +#define _JS_CI_NAME(op) +#endif + +#define _JS_I32_ARGSIZE nanojit::ARGSIZE_I +#define _JS_I32_RETSIZE nanojit::ARGSIZE_I +#define _JS_F64_ARGSIZE nanojit::ARGSIZE_F +#define _JS_F64_RETSIZE nanojit::ARGSIZE_F +#define _JS_PTR_ARGSIZE nanojit::ARGSIZE_P +#define _JS_PTR_RETSIZE nanojit::ARGSIZE_P + +struct ClosureVarInfo; + +/* + * Supported types for builtin functions. + * + * Types with -- for the two string fields are not permitted as argument types + * in JS_DEFINE_TRCINFO. + * + * There are three kinds of traceable-native error handling. + * + * - If a traceable native's return type ends with _FAIL, it always runs to + * completion. It can either succeed or fail with an error or exception; + * on success, it may or may not stay on trace. There may be side effects + * in any case. If the call succeeds but bails off trace, we resume in the + * interpreter at the next opcode. + * + * _FAIL builtins indicate failure or bailing off trace by setting bits in + * cx->interpState->builtinStatus. + * + * - If a traceable native's return type contains _RETRY, it can either + * succeed, fail with a JS exception, or tell the caller to bail off trace + * and retry the call from the interpreter. The last case happens if the + * builtin discovers that it can't do its job without examining the JS + * stack, reentering the interpreter, accessing properties of the global + * object, etc. + * + * The builtin must detect the need to retry before committing any side + * effects. If a builtin can't do this, it must use a _FAIL return type + * instead of _RETRY. + * + * _RETRY builtins indicate failure with a special return value that + * depends on the return type: + * + * BOOL_RETRY: JSVAL_TO_BOOLEAN(JSVAL_VOID) + * INT32_RETRY: any negative value + * STRING_RETRY: NULL + * OBJECT_RETRY_NULL: NULL + * JSVAL_RETRY: JSVAL_ERROR_COOKIE + * + * _RETRY function calls are faster than _FAIL calls. Each _RETRY call + * saves two writes to cx->bailExit and a read from state->builtinStatus. + * + * - All other traceable natives are infallible (e.g. Date.now, Math.log). + * + * Special builtins known to the tracer can have their own idiosyncratic + * error codes. + * + * When a traceable native returns a value indicating failure, we fall off + * trace. If an exception is pending, it is thrown; otherwise, we assume the + * builtin had no side effects and retry the current bytecode in the + * interpreter. + * + * So a builtin must not return a value indicating failure after causing side + * effects (such as reporting an error), without setting an exception pending. + * The operation would be retried, despite the first attempt's observable + * effects. + */ +#define _JS_CTYPE(ctype, size, pch, ach, flags) (ctype, size, pch, ach, flags) +#define _JS_JSVAL_CTYPE(size, pch, ach, flags) (jsval, size, pch, ach, (flags | JSTN_UNBOX_AFTER)) + +#define _JS_CTYPE_CONTEXT _JS_CTYPE(JSContext *, _JS_PTR,"C", "", INFALLIBLE) +#define _JS_CTYPE_RUNTIME _JS_CTYPE(JSRuntime *, _JS_PTR,"R", "", INFALLIBLE) +#define _JS_CTYPE_THIS _JS_CTYPE(JSObject *, _JS_PTR,"T", "", INFALLIBLE) +#define _JS_CTYPE_THIS_DOUBLE _JS_CTYPE(jsdouble, _JS_F64,"D", "", INFALLIBLE) +#define _JS_CTYPE_THIS_STRING _JS_CTYPE(JSString *, _JS_PTR,"S", "", INFALLIBLE) +#define _JS_CTYPE_CALLEE _JS_CTYPE(JSObject *, _JS_PTR,"f","", INFALLIBLE) +#define _JS_CTYPE_CALLEE_PROTOTYPE _JS_CTYPE(JSObject *, _JS_PTR,"p","", INFALLIBLE) +#define _JS_CTYPE_FUNCTION _JS_CTYPE(JSFunction *, _JS_PTR, --, --, INFALLIBLE) +#define _JS_CTYPE_PC _JS_CTYPE(jsbytecode *, _JS_PTR,"P", "", INFALLIBLE) +#define _JS_CTYPE_JSVALPTR _JS_CTYPE(jsval *, _JS_PTR,"P", "", INFALLIBLE) +#define _JS_CTYPE_JSVAL _JS_JSVAL_CTYPE( _JS_PTR, "","v", INFALLIBLE) +#define _JS_CTYPE_JSVAL_RETRY _JS_JSVAL_CTYPE( _JS_PTR, --, --, FAIL_COOKIE) +#define _JS_CTYPE_JSVAL_FAIL _JS_JSVAL_CTYPE( _JS_PTR, --, --, FAIL_STATUS) +#define _JS_CTYPE_JSID _JS_CTYPE(jsid, _JS_PTR, --, --, INFALLIBLE) +#define _JS_CTYPE_BOOL _JS_CTYPE(JSBool, _JS_I32, "","i", INFALLIBLE) +#define _JS_CTYPE_BOOL_RETRY _JS_CTYPE(JSBool, _JS_I32, --, --, FAIL_VOID) +#define _JS_CTYPE_BOOL_FAIL _JS_CTYPE(JSBool, _JS_I32, --, --, FAIL_STATUS) +#define _JS_CTYPE_INT32 _JS_CTYPE(int32, _JS_I32, "","i", INFALLIBLE) +#define _JS_CTYPE_INT32_RETRY _JS_CTYPE(int32, _JS_I32, --, --, FAIL_NEG) +#define _JS_CTYPE_INT32_FAIL _JS_CTYPE(int32, _JS_I32, --, --, FAIL_STATUS) +#define _JS_CTYPE_INT32PTR _JS_CTYPE(int32 *, _JS_PTR, --, --, INFALLIBLE) +#define _JS_CTYPE_UINT32 _JS_CTYPE(uint32, _JS_I32, "","i", INFALLIBLE) +#define _JS_CTYPE_UINT32_RETRY _JS_CTYPE(uint32, _JS_I32, --, --, FAIL_NEG) +#define _JS_CTYPE_UINT32_FAIL _JS_CTYPE(uint32, _JS_I32, --, --, FAIL_STATUS) +#define _JS_CTYPE_DOUBLE _JS_CTYPE(jsdouble, _JS_F64, "","d", INFALLIBLE) +#define _JS_CTYPE_DOUBLE_FAIL _JS_CTYPE(jsdouble, _JS_F64, --, --, FAIL_STATUS) +#define _JS_CTYPE_STRING _JS_CTYPE(JSString *, _JS_PTR, "","s", INFALLIBLE) +#define _JS_CTYPE_STRING_RETRY _JS_CTYPE(JSString *, _JS_PTR, --, --, FAIL_NULL) +#define _JS_CTYPE_STRING_FAIL _JS_CTYPE(JSString *, _JS_PTR, --, --, FAIL_STATUS) +#define _JS_CTYPE_STRINGPTR _JS_CTYPE(JSString **, _JS_PTR, --, --, INFALLIBLE) +#define _JS_CTYPE_OBJECT _JS_CTYPE(JSObject *, _JS_PTR, "","o", INFALLIBLE) +#define _JS_CTYPE_OBJECT_RETRY _JS_CTYPE(JSObject *, _JS_PTR, --, --, FAIL_NULL) +#define _JS_CTYPE_OBJECT_FAIL _JS_CTYPE(JSObject *, _JS_PTR, --, --, FAIL_STATUS) +#define _JS_CTYPE_CONSTRUCTOR_RETRY _JS_CTYPE(JSObject *, _JS_PTR, --, --, FAIL_NULL | \ + JSTN_CONSTRUCTOR) +#define _JS_CTYPE_REGEXP _JS_CTYPE(JSObject *, _JS_PTR, "","r", INFALLIBLE) +#define _JS_CTYPE_SCOPEPROP _JS_CTYPE(JSScopeProperty *, _JS_PTR, --, --, INFALLIBLE) +#define _JS_CTYPE_INTERPSTATE _JS_CTYPE(InterpState *, _JS_PTR, --, --, INFALLIBLE) +#define _JS_CTYPE_FRAGMENT _JS_CTYPE(nanojit::Fragment *, _JS_PTR, --, --, INFALLIBLE) +#define _JS_CTYPE_CLASS _JS_CTYPE(JSClass *, _JS_PTR, --, --, INFALLIBLE) +#define _JS_CTYPE_DOUBLEPTR _JS_CTYPE(double *, _JS_PTR, --, --, INFALLIBLE) +#define _JS_CTYPE_CHARPTR _JS_CTYPE(char *, _JS_PTR, --, --, INFALLIBLE) +#define _JS_CTYPE_APNPTR _JS_CTYPE(js_ArgsPrivateNative *, _JS_PTR, --, --, INFALLIBLE) +#define _JS_CTYPE_CVIPTR _JS_CTYPE(const ClosureVarInfo *, _JS_PTR, --, --, INFALLIBLE) +#define _JS_CTYPE_FRAMEINFO _JS_CTYPE(FrameInfo *, _JS_PTR, --, --, INFALLIBLE) + +#define _JS_EXPAND(tokens) tokens + +#define _JS_CTYPE_TYPE2(t,s,p,a,f) t +#define _JS_CTYPE_TYPE(tyname) _JS_EXPAND(_JS_CTYPE_TYPE2 _JS_CTYPE_##tyname) +#define _JS_CTYPE_RETSIZE2(t,s,p,a,f) s##_RETSIZE +#define _JS_CTYPE_RETSIZE(tyname) _JS_EXPAND(_JS_CTYPE_RETSIZE2 _JS_CTYPE_##tyname) +#define _JS_CTYPE_ARGSIZE2(t,s,p,a,f) s##_ARGSIZE +#define _JS_CTYPE_ARGSIZE(tyname) _JS_EXPAND(_JS_CTYPE_ARGSIZE2 _JS_CTYPE_##tyname) +#define _JS_CTYPE_PCH2(t,s,p,a,f) p +#define _JS_CTYPE_PCH(tyname) _JS_EXPAND(_JS_CTYPE_PCH2 _JS_CTYPE_##tyname) +#define _JS_CTYPE_ACH2(t,s,p,a,f) a +#define _JS_CTYPE_ACH(tyname) _JS_EXPAND(_JS_CTYPE_ACH2 _JS_CTYPE_##tyname) +#define _JS_CTYPE_FLAGS2(t,s,p,a,f) f +#define _JS_CTYPE_FLAGS(tyname) _JS_EXPAND(_JS_CTYPE_FLAGS2 _JS_CTYPE_##tyname) + +#define _JS_static_TN(t) static t +#define _JS_static_CI static +#define _JS_extern_TN(t) extern t +#define _JS_extern_CI +#define _JS_FRIEND_TN(t) extern JS_FRIEND_API(t) +#define _JS_FRIEND_CI +#define _JS_TN_LINKAGE(linkage, t) _JS_##linkage##_TN(t) +#define _JS_CI_LINKAGE(linkage) _JS_##linkage##_CI + +#define _JS_CALLINFO(name) name##_ci + +#if defined(JS_NO_FASTCALL) && defined(NANOJIT_IA32) +#define _JS_DEFINE_CALLINFO(linkage, name, crtype, cargtypes, argtypes, cse, fold) \ + _JS_TN_LINKAGE(linkage, crtype) name cargtypes; \ + _JS_CI_LINKAGE(linkage) const nanojit::CallInfo _JS_CALLINFO(name) = \ + { (intptr_t) &name, argtypes, cse, fold, nanojit::ABI_CDECL _JS_CI_NAME(name) }; +#else +#define _JS_DEFINE_CALLINFO(linkage, name, crtype, cargtypes, argtypes, cse, fold) \ + _JS_TN_LINKAGE(linkage, crtype) FASTCALL name cargtypes; \ + _JS_CI_LINKAGE(linkage) const nanojit::CallInfo _JS_CALLINFO(name) = \ + { (intptr_t) &name, argtypes, cse, fold, nanojit::ABI_FASTCALL _JS_CI_NAME(name) }; +#endif + +/* + * This macro is used for builtin functions that can be called from JITted + * code. It declares a C function named and a CallInfo struct named + * _ci so the tracer can call it. The in JS_DEFINE_CALLINFO_ is + * the number of arguments the builtin takes. Builtins with no arguments + * are not supported. Using a macro is clunky but ensures that the types + * for each C function matches those for the corresponding CallInfo struct; + * mismatched types can cause subtle problems. + * + * The macro arguments are: + * + * - The linkage for the function and the associated CallInfo global. It + * can be extern, static, or FRIEND, which specifies JS_FRIEND_API linkage + * for the function. + * + * - The return type. This identifier must name one of the _JS_TYPEINFO_* + * macros defined in jsbuiltins.h. + * + * - The builtin name. + * + * - The parameter types. + * + * - The cse flag. 1 if the builtin call can be optimized away by common + * subexpression elimination; otherwise 0. This should be 1 only if the + * function is idempotent and the return value is determined solely by the + * arguments. + * + * - The fold flag. Reserved. The same as cse for now. + */ +#define JS_DEFINE_CALLINFO_1(linkage, rt, op, at0, cse, fold) \ + _JS_DEFINE_CALLINFO(linkage, op, _JS_CTYPE_TYPE(rt), (_JS_CTYPE_TYPE(at0)), \ + (_JS_CTYPE_ARGSIZE(at0) << (1*nanojit::ARGSIZE_SHIFT)) | \ + _JS_CTYPE_RETSIZE(rt), cse, fold) +#define JS_DEFINE_CALLINFO_2(linkage, rt, op, at0, at1, cse, fold) \ + _JS_DEFINE_CALLINFO(linkage, op, _JS_CTYPE_TYPE(rt), \ + (_JS_CTYPE_TYPE(at0), _JS_CTYPE_TYPE(at1)), \ + (_JS_CTYPE_ARGSIZE(at0) << (2*nanojit::ARGSIZE_SHIFT)) | \ + (_JS_CTYPE_ARGSIZE(at1) << (1*nanojit::ARGSIZE_SHIFT)) | \ + _JS_CTYPE_RETSIZE(rt), \ + cse, fold) +#define JS_DEFINE_CALLINFO_3(linkage, rt, op, at0, at1, at2, cse, fold) \ + _JS_DEFINE_CALLINFO(linkage, op, _JS_CTYPE_TYPE(rt), \ + (_JS_CTYPE_TYPE(at0), _JS_CTYPE_TYPE(at1), _JS_CTYPE_TYPE(at2)), \ + (_JS_CTYPE_ARGSIZE(at0) << (3*nanojit::ARGSIZE_SHIFT)) | \ + (_JS_CTYPE_ARGSIZE(at1) << (2*nanojit::ARGSIZE_SHIFT)) | \ + (_JS_CTYPE_ARGSIZE(at2) << (1*nanojit::ARGSIZE_SHIFT)) | \ + _JS_CTYPE_RETSIZE(rt), \ + cse, fold) +#define JS_DEFINE_CALLINFO_4(linkage, rt, op, at0, at1, at2, at3, cse, fold) \ + _JS_DEFINE_CALLINFO(linkage, op, _JS_CTYPE_TYPE(rt), \ + (_JS_CTYPE_TYPE(at0), _JS_CTYPE_TYPE(at1), _JS_CTYPE_TYPE(at2), \ + _JS_CTYPE_TYPE(at3)), \ + (_JS_CTYPE_ARGSIZE(at0) << (4*nanojit::ARGSIZE_SHIFT)) | \ + (_JS_CTYPE_ARGSIZE(at1) << (3*nanojit::ARGSIZE_SHIFT)) | \ + (_JS_CTYPE_ARGSIZE(at2) << (2*nanojit::ARGSIZE_SHIFT)) | \ + (_JS_CTYPE_ARGSIZE(at3) << (1*nanojit::ARGSIZE_SHIFT)) | \ + _JS_CTYPE_RETSIZE(rt), \ + cse, fold) +#define JS_DEFINE_CALLINFO_5(linkage, rt, op, at0, at1, at2, at3, at4, cse, fold) \ + _JS_DEFINE_CALLINFO(linkage, op, _JS_CTYPE_TYPE(rt), \ + (_JS_CTYPE_TYPE(at0), _JS_CTYPE_TYPE(at1), _JS_CTYPE_TYPE(at2), \ + _JS_CTYPE_TYPE(at3), _JS_CTYPE_TYPE(at4)), \ + (_JS_CTYPE_ARGSIZE(at0) << (5*nanojit::ARGSIZE_SHIFT)) | \ + (_JS_CTYPE_ARGSIZE(at1) << (4*nanojit::ARGSIZE_SHIFT)) | \ + (_JS_CTYPE_ARGSIZE(at2) << (3*nanojit::ARGSIZE_SHIFT)) | \ + (_JS_CTYPE_ARGSIZE(at3) << (2*nanojit::ARGSIZE_SHIFT)) | \ + (_JS_CTYPE_ARGSIZE(at4) << (1*nanojit::ARGSIZE_SHIFT)) | \ + _JS_CTYPE_RETSIZE(rt), \ + cse, fold) + +#define JS_DEFINE_CALLINFO_6(linkage, rt, op, at0, at1, at2, at3, at4, at5, cse, fold) \ + _JS_DEFINE_CALLINFO(linkage, op, _JS_CTYPE_TYPE(rt), \ + (_JS_CTYPE_TYPE(at0), _JS_CTYPE_TYPE(at1), _JS_CTYPE_TYPE(at2), \ + _JS_CTYPE_TYPE(at3), _JS_CTYPE_TYPE(at4), _JS_CTYPE_TYPE(at5)), \ + (_JS_CTYPE_ARGSIZE(at0) << (6*nanojit::ARGSIZE_SHIFT)) | \ + (_JS_CTYPE_ARGSIZE(at1) << (5*nanojit::ARGSIZE_SHIFT)) | \ + (_JS_CTYPE_ARGSIZE(at2) << (4*nanojit::ARGSIZE_SHIFT)) | \ + (_JS_CTYPE_ARGSIZE(at3) << (3*nanojit::ARGSIZE_SHIFT)) | \ + (_JS_CTYPE_ARGSIZE(at4) << (2*nanojit::ARGSIZE_SHIFT)) | \ + (_JS_CTYPE_ARGSIZE(at5) << (1*nanojit::ARGSIZE_SHIFT)) | \ + _JS_CTYPE_RETSIZE(rt), cse, fold) + +#define JS_DECLARE_CALLINFO(name) extern const nanojit::CallInfo _JS_CALLINFO(name); + +#define _JS_TN_INIT_HELPER_n(n, args) _JS_TN_INIT_HELPER_##n args + +#define _JS_TN_INIT_HELPER_1(linkage, rt, op, at0, cse, fold) \ + &_JS_CALLINFO(op), \ + _JS_CTYPE_PCH(at0), \ + _JS_CTYPE_ACH(at0), \ + _JS_CTYPE_FLAGS(rt) + +#define _JS_TN_INIT_HELPER_2(linkage, rt, op, at0, at1, cse, fold) \ + &_JS_CALLINFO(op), \ + _JS_CTYPE_PCH(at1) _JS_CTYPE_PCH(at0), \ + _JS_CTYPE_ACH(at1) _JS_CTYPE_ACH(at0), \ + _JS_CTYPE_FLAGS(rt) + +#define _JS_TN_INIT_HELPER_3(linkage, rt, op, at0, at1, at2, cse, fold) \ + &_JS_CALLINFO(op), \ + _JS_CTYPE_PCH(at2) _JS_CTYPE_PCH(at1) _JS_CTYPE_PCH(at0), \ + _JS_CTYPE_ACH(at2) _JS_CTYPE_ACH(at1) _JS_CTYPE_ACH(at0), \ + _JS_CTYPE_FLAGS(rt) + +#define _JS_TN_INIT_HELPER_4(linkage, rt, op, at0, at1, at2, at3, cse, fold) \ + &_JS_CALLINFO(op), \ + _JS_CTYPE_PCH(at3) _JS_CTYPE_PCH(at2) _JS_CTYPE_PCH(at1) _JS_CTYPE_PCH(at0), \ + _JS_CTYPE_ACH(at3) _JS_CTYPE_ACH(at2) _JS_CTYPE_ACH(at1) _JS_CTYPE_ACH(at0), \ + _JS_CTYPE_FLAGS(rt) + +#define _JS_TN_INIT_HELPER_5(linkage, rt, op, at0, at1, at2, at3, at4, cse, fold) \ + &_JS_CALLINFO(op), \ + _JS_CTYPE_PCH(at4) _JS_CTYPE_PCH(at3) _JS_CTYPE_PCH(at2) _JS_CTYPE_PCH(at1) \ + _JS_CTYPE_PCH(at0), \ + _JS_CTYPE_ACH(at4) _JS_CTYPE_ACH(at3) _JS_CTYPE_ACH(at2) _JS_CTYPE_ACH(at1) \ + _JS_CTYPE_ACH(at0), \ + _JS_CTYPE_FLAGS(rt) + +#define _JS_TN_INIT_HELPER_6(linkage, rt, op, at0, at1, at2, at3, at4, at5, cse, fold) \ + &_JS_CALLINFO(op), \ + _JS_CTYPE_PCH(at5) _JS_CTYPE_PCH(at4) _JS_CTYPE_PCH(at3) _JS_CTYPE_PCH(at2) \ + _JS_CTYPE_PCH(at1) _JS_CTYPE_PCH(at0), \ + _JS_CTYPE_ACH(at5) _JS_CTYPE_ACH(at4) _JS_CTYPE_ACH(at3) _JS_CTYPE_ACH(at2) \ + _JS_CTYPE_ACH(at1) _JS_CTYPE_ACH(at0), \ + _JS_CTYPE_FLAGS(rt) + +#define JS_DEFINE_TRCINFO_1(name, tn0) \ + _JS_DEFINE_CALLINFO_n tn0 \ + JSSpecializedNative name##_sns[] = { \ + { _JS_TN_INIT_HELPER_n tn0 } \ + }; \ + JSNativeTraceInfo name##_trcinfo = { (JSFastNative)name, name##_sns }; + +#define JS_DEFINE_TRCINFO_2(name, tn0, tn1) \ + _JS_DEFINE_CALLINFO_n tn0 \ + _JS_DEFINE_CALLINFO_n tn1 \ + JSSpecializedNative name##_sns[] = { \ + { _JS_TN_INIT_HELPER_n tn0 | JSTN_MORE }, \ + { _JS_TN_INIT_HELPER_n tn1 } \ + }; \ + JSNativeTraceInfo name##_trcinfo = { (JSFastNative)name, name##_sns }; + +#define JS_DEFINE_TRCINFO_3(name, tn0, tn1, tn2) \ + _JS_DEFINE_CALLINFO_n tn0 \ + _JS_DEFINE_CALLINFO_n tn1 \ + _JS_DEFINE_CALLINFO_n tn2 \ + JSSpecializedNative name##_sns[] = { \ + { _JS_TN_INIT_HELPER_n tn0 | JSTN_MORE }, \ + { _JS_TN_INIT_HELPER_n tn1 | JSTN_MORE }, \ + { _JS_TN_INIT_HELPER_n tn2 } \ + }; \ + JSNativeTraceInfo name##_trcinfo = { (JSFastNative)name, name##_sns }; + +#define JS_DEFINE_TRCINFO_4(name, tn0, tn1, tn2, tn3) \ + _JS_DEFINE_CALLINFO_n tn0 \ + _JS_DEFINE_CALLINFO_n tn1 \ + _JS_DEFINE_CALLINFO_n tn2 \ + _JS_DEFINE_CALLINFO_n tn3 \ + JSSpecializedNative name##_sns[] = { \ + { _JS_TN_INIT_HELPER_n tn0 | JSTN_MORE }, \ + { _JS_TN_INIT_HELPER_n tn1 | JSTN_MORE }, \ + { _JS_TN_INIT_HELPER_n tn2 | JSTN_MORE }, \ + { _JS_TN_INIT_HELPER_n tn3 } \ + }; \ + JSNativeTraceInfo name##_trcinfo = { (JSFastNative)name, name##_sns }; + +#define _JS_DEFINE_CALLINFO_n(n, args) JS_DEFINE_CALLINFO_##n args + +jsdouble FASTCALL +js_StringToNumber(JSContext* cx, JSString* str); + +jsdouble FASTCALL +js_BooleanOrUndefinedToNumber(JSContext* cx, int32 unboxed); + +/* Extern version of js_SetBuiltinError. */ +extern JS_FRIEND_API(void) +js_SetTraceableNativeFailed(JSContext *cx); + +extern jsdouble FASTCALL +js_dmod(jsdouble a, jsdouble b); + +#else + +#define JS_DEFINE_CALLINFO_1(linkage, rt, op, at0, cse, fold) +#define JS_DEFINE_CALLINFO_2(linkage, rt, op, at0, at1, cse, fold) +#define JS_DEFINE_CALLINFO_3(linkage, rt, op, at0, at1, at2, cse, fold) +#define JS_DEFINE_CALLINFO_4(linkage, rt, op, at0, at1, at2, at3, cse, fold) +#define JS_DEFINE_CALLINFO_5(linkage, rt, op, at0, at1, at2, at3, at4, cse, fold) +#define JS_DEFINE_CALLINFO_6(linkage, rt, op, at0, at1, at2, at3, at4, at5, cse, fold) +#define JS_DECLARE_CALLINFO(name) +#define JS_DEFINE_TRCINFO_1(name, tn0) +#define JS_DEFINE_TRCINFO_2(name, tn0, tn1) +#define JS_DEFINE_TRCINFO_3(name, tn0, tn1, tn2) +#define JS_DEFINE_TRCINFO_4(name, tn0, tn1, tn2, tn3) + +#endif /* !JS_TRACER */ + +/* Defined in jsobj.cpp. */ +JS_DECLARE_CALLINFO(js_Object_tn) +JS_DECLARE_CALLINFO(js_NewInstance) + +/* Defined in jsarray.cpp. */ +JS_DECLARE_CALLINFO(js_Array_dense_setelem) +JS_DECLARE_CALLINFO(js_Array_dense_setelem_int) +JS_DECLARE_CALLINFO(js_Array_dense_setelem_double) +JS_DECLARE_CALLINFO(js_NewEmptyArray) +JS_DECLARE_CALLINFO(js_NewEmptyArrayWithLength) +JS_DECLARE_CALLINFO(js_NewArrayWithSlots) +JS_DECLARE_CALLINFO(js_ArrayCompPush) + +/* Defined in jsfun.cpp. */ +JS_DECLARE_CALLINFO(js_AllocFlatClosure) +JS_DECLARE_CALLINFO(js_PutArguments) + +/* Defined in jsfun.cpp. */ +JS_DECLARE_CALLINFO(js_SetCallVar) +JS_DECLARE_CALLINFO(js_SetCallArg) + +/* Defined in jsnum.cpp. */ +JS_DECLARE_CALLINFO(js_NumberToString) + +/* Defined in jsstr.cpp. */ +JS_DECLARE_CALLINFO(js_String_tn) +JS_DECLARE_CALLINFO(js_CompareStrings) +JS_DECLARE_CALLINFO(js_ConcatStrings) +JS_DECLARE_CALLINFO(js_EqualStrings) +JS_DECLARE_CALLINFO(js_String_getelem) +JS_DECLARE_CALLINFO(js_String_p_charCodeAt) +JS_DECLARE_CALLINFO(js_String_p_charCodeAt0) +JS_DECLARE_CALLINFO(js_String_p_charCodeAt0_int) +JS_DECLARE_CALLINFO(js_String_p_charCodeAt_int) + +/* Defined in jsbuiltins.cpp. */ +JS_DECLARE_CALLINFO(js_BoxDouble) +JS_DECLARE_CALLINFO(js_BoxInt32) +JS_DECLARE_CALLINFO(js_UnboxDouble) +JS_DECLARE_CALLINFO(js_UnboxInt32) +JS_DECLARE_CALLINFO(js_TryUnboxInt32) +JS_DECLARE_CALLINFO(js_dmod) +JS_DECLARE_CALLINFO(js_imod) +JS_DECLARE_CALLINFO(js_DoubleToInt32) +JS_DECLARE_CALLINFO(js_DoubleToUint32) + +JS_DECLARE_CALLINFO(js_StringToNumber) +JS_DECLARE_CALLINFO(js_StringToInt32) +JS_DECLARE_CALLINFO(js_CloseIterator) +JS_DECLARE_CALLINFO(js_CallTree) +JS_DECLARE_CALLINFO(js_AddProperty) +JS_DECLARE_CALLINFO(js_HasNamedProperty) +JS_DECLARE_CALLINFO(js_HasNamedPropertyInt32) +JS_DECLARE_CALLINFO(js_TypeOfObject) +JS_DECLARE_CALLINFO(js_TypeOfBoolean) +JS_DECLARE_CALLINFO(js_BooleanOrUndefinedToNumber) +JS_DECLARE_CALLINFO(js_BooleanOrUndefinedToString) +JS_DECLARE_CALLINFO(js_Arguments) +JS_DECLARE_CALLINFO(js_NewNullClosure) +JS_DECLARE_CALLINFO(js_ConcatN) +JS_DECLARE_CALLINFO(js_PopInterpFrame) + +#endif /* jsbuiltins_h___ */ diff --git a/ape-server/deps/js/src/jsclist.h b/ape-server/deps/js/src/jsclist.h new file mode 100755 index 0000000..604ec0e --- /dev/null +++ b/ape-server/deps/js/src/jsclist.h @@ -0,0 +1,139 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsclist_h___ +#define jsclist_h___ + +#include "jstypes.h" + +/* +** Circular linked list +*/ +typedef struct JSCListStr { + struct JSCListStr *next; + struct JSCListStr *prev; +} JSCList; + +/* +** Insert element "_e" into the list, before "_l". +*/ +#define JS_INSERT_BEFORE(_e,_l) \ + JS_BEGIN_MACRO \ + (_e)->next = (_l); \ + (_e)->prev = (_l)->prev; \ + (_l)->prev->next = (_e); \ + (_l)->prev = (_e); \ + JS_END_MACRO + +/* +** Insert element "_e" into the list, after "_l". +*/ +#define JS_INSERT_AFTER(_e,_l) \ + JS_BEGIN_MACRO \ + (_e)->next = (_l)->next; \ + (_e)->prev = (_l); \ + (_l)->next->prev = (_e); \ + (_l)->next = (_e); \ + JS_END_MACRO + +/* +** Return the element following element "_e" +*/ +#define JS_NEXT_LINK(_e) \ + ((_e)->next) +/* +** Return the element preceding element "_e" +*/ +#define JS_PREV_LINK(_e) \ + ((_e)->prev) + +/* +** Append an element "_e" to the end of the list "_l" +*/ +#define JS_APPEND_LINK(_e,_l) JS_INSERT_BEFORE(_e,_l) + +/* +** Insert an element "_e" at the head of the list "_l" +*/ +#define JS_INSERT_LINK(_e,_l) JS_INSERT_AFTER(_e,_l) + +/* Return the head/tail of the list */ +#define JS_LIST_HEAD(_l) (_l)->next +#define JS_LIST_TAIL(_l) (_l)->prev + +/* +** Remove the element "_e" from it's circular list. +*/ +#define JS_REMOVE_LINK(_e) \ + JS_BEGIN_MACRO \ + (_e)->prev->next = (_e)->next; \ + (_e)->next->prev = (_e)->prev; \ + JS_END_MACRO + +/* +** Remove the element "_e" from it's circular list. Also initializes the +** linkage. +*/ +#define JS_REMOVE_AND_INIT_LINK(_e) \ + JS_BEGIN_MACRO \ + (_e)->prev->next = (_e)->next; \ + (_e)->next->prev = (_e)->prev; \ + (_e)->next = (_e); \ + (_e)->prev = (_e); \ + JS_END_MACRO + +/* +** Return non-zero if the given circular list "_l" is empty, zero if the +** circular list is not empty +*/ +#define JS_CLIST_IS_EMPTY(_l) \ + ((_l)->next == (_l)) + +/* +** Initialize a circular list +*/ +#define JS_INIT_CLIST(_l) \ + JS_BEGIN_MACRO \ + (_l)->next = (_l); \ + (_l)->prev = (_l); \ + JS_END_MACRO + +#define JS_INIT_STATIC_CLIST(_l) \ + {(_l), (_l)} + +#endif /* jsclist_h___ */ diff --git a/ape-server/deps/js/src/jscntxt.cpp b/ape-server/deps/js/src/jscntxt.cpp new file mode 100755 index 0000000..ec2793a --- /dev/null +++ b/ape-server/deps/js/src/jscntxt.cpp @@ -0,0 +1,1950 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=80: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * JS execution context. + */ +#include +#include +#include +#include +#include "jstypes.h" +#include "jsstdint.h" +#include "jsarena.h" /* Added by JSIFY */ +#include "jsutil.h" /* Added by JSIFY */ +#include "jsclist.h" +#include "jsprf.h" +#include "jsatom.h" +#include "jscntxt.h" +#include "jsversion.h" +#include "jsdbgapi.h" +#include "jsexn.h" +#include "jsfun.h" +#include "jsgc.h" +#include "jslock.h" +#include "jsmath.h" +#include "jsnum.h" +#include "jsobj.h" +#include "jsopcode.h" +#include "jspubtd.h" +#include "jsscan.h" +#include "jsscope.h" +#include "jsscript.h" +#include "jsstaticcheck.h" +#include "jsstr.h" +#include "jstracer.h" + +static void +FreeContext(JSContext *cx); + +static void +MarkLocalRoots(JSTracer *trc, JSLocalRootStack *lrs); + +void +JSThreadData::init() +{ +#ifdef DEBUG + /* The data must be already zeroed. */ + for (size_t i = 0; i != sizeof(*this); ++i) + JS_ASSERT(reinterpret_cast(this)[i] == 0); +#endif +#ifdef JS_TRACER + js_InitJIT(&traceMonitor); +#endif + js_InitRandom(this); +} + +void +JSThreadData::finish() +{ +#ifdef DEBUG + /* All GC-related things must be already removed at this point. */ + JS_ASSERT(gcFreeLists.isEmpty()); + for (size_t i = 0; i != JS_ARRAY_LENGTH(scriptsToGC); ++i) + JS_ASSERT(!scriptsToGC[i]); + for (size_t i = 0; i != JS_ARRAY_LENGTH(nativeEnumCache); ++i) + JS_ASSERT(!nativeEnumCache[i]); + JS_ASSERT(!localRootStack); +#endif + + js_FinishGSNCache(&gsnCache); + js_FinishPropertyCache(&propertyCache); +#if defined JS_TRACER + js_FinishJIT(&traceMonitor); +#endif +} + +void +JSThreadData::mark(JSTracer *trc) +{ +#ifdef JS_TRACER + traceMonitor.mark(trc); +#endif + if (localRootStack) + MarkLocalRoots(trc, localRootStack); +} + +void +JSThreadData::purge(JSContext *cx) +{ + purgeGCFreeLists(); + + js_PurgeGSNCache(&gsnCache); + + /* FIXME: bug 506341. */ + js_PurgePropertyCache(cx, &propertyCache); + +#ifdef JS_TRACER + /* + * If we are about to regenerate shapes, we have to flush the JIT cache, + * which will eventually abort any current recording. + */ + if (cx->runtime->gcRegenShapes) + traceMonitor.needFlush = JS_TRUE; +#endif + + /* Destroy eval'ed scripts. */ + js_DestroyScriptsToGC(cx, this); + + js_PurgeCachedNativeEnumerators(cx, this); +} + +void +JSThreadData::purgeGCFreeLists() +{ + if (!localRootStack) { + gcFreeLists.purge(); + } else { + JS_ASSERT(gcFreeLists.isEmpty()); + localRootStack->gcFreeLists.purge(); + } +} + +#ifdef JS_THREADSAFE + +static JSThread * +NewThread(jsword id) +{ + JS_ASSERT(js_CurrentThreadId() == id); + JSThread *thread = (JSThread *) js_calloc(sizeof(JSThread)); + if (!thread) + return NULL; + JS_INIT_CLIST(&thread->contextList); + thread->id = id; + thread->data.init(); + return thread; +} + +static void +DestroyThread(JSThread *thread) +{ + /* The thread must have zero contexts. */ + JS_ASSERT(JS_CLIST_IS_EMPTY(&thread->contextList)); + JS_ASSERT(!thread->titleToShare); + thread->data.finish(); + js_free(thread); +} + +JSThread * +js_CurrentThread(JSRuntime *rt) +{ + jsword id = js_CurrentThreadId(); + JS_LOCK_GC(rt); + + /* + * We must not race with a GC that accesses cx->thread for JSContext + * instances on all threads, see bug 476934. + */ + js_WaitForGC(rt); + JSThreadsHashEntry *entry = (JSThreadsHashEntry *) + JS_DHashTableOperate(&rt->threads, + (const void *) id, + JS_DHASH_LOOKUP); + JSThread *thread; + if (JS_DHASH_ENTRY_IS_BUSY(&entry->base)) { + thread = entry->thread; + JS_ASSERT(thread->id == id); + } else { + JS_UNLOCK_GC(rt); + thread = NewThread(id); + if (!thread) + return NULL; + JS_LOCK_GC(rt); + js_WaitForGC(rt); + entry = (JSThreadsHashEntry *) + JS_DHashTableOperate(&rt->threads, (const void *) id, + JS_DHASH_ADD); + if (!entry) { + JS_UNLOCK_GC(rt); + DestroyThread(thread); + return NULL; + } + + /* Another thread cannot initialize entry->thread. */ + JS_ASSERT(!entry->thread); + entry->thread = thread; + } + + return thread; +} + +JSBool +js_InitContextThread(JSContext *cx) +{ + JSThread *thread = js_CurrentThread(cx->runtime); + if (!thread) + return false; + + JS_APPEND_LINK(&cx->threadLinks, &thread->contextList); + cx->thread = thread; + return true; +} + +void +js_ClearContextThread(JSContext *cx) +{ + JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread)); + JS_REMOVE_AND_INIT_LINK(&cx->threadLinks); + cx->thread = NULL; +} + +static JSBool +thread_matchEntry(JSDHashTable *table, + const JSDHashEntryHdr *hdr, + const void *key) +{ + const JSThreadsHashEntry *entry = (const JSThreadsHashEntry *) hdr; + + return entry->thread->id == (jsword) key; +} + +static const JSDHashTableOps threads_ops = { + JS_DHashAllocTable, + JS_DHashFreeTable, + JS_DHashVoidPtrKeyStub, + thread_matchEntry, + JS_DHashMoveEntryStub, + JS_DHashClearEntryStub, + JS_DHashFinalizeStub, + NULL +}; + +static JSDHashOperator +thread_destroyer(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 /* index */, + void * /* arg */) +{ + JSThreadsHashEntry *entry = (JSThreadsHashEntry *) hdr; + JSThread *thread = entry->thread; + + JS_ASSERT(JS_CLIST_IS_EMPTY(&thread->contextList)); + DestroyThread(thread); + return JS_DHASH_REMOVE; +} + +static JSDHashOperator +thread_purger(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 /* index */, + void *arg) +{ + JSContext* cx = (JSContext *) arg; + JSThread *thread = ((JSThreadsHashEntry *) hdr)->thread; + + if (JS_CLIST_IS_EMPTY(&thread->contextList)) { + JS_ASSERT(cx->thread != thread); + js_DestroyScriptsToGC(cx, &thread->data); + + /* + * The following is potentially suboptimal as it also zeros the caches + * in data, but the code simplicity wins here. + */ + thread->data.purgeGCFreeLists(); + js_PurgeCachedNativeEnumerators(cx, &thread->data); + DestroyThread(thread); + return JS_DHASH_REMOVE; + } + thread->data.purge(cx); + thread->gcThreadMallocBytes = JS_GC_THREAD_MALLOC_LIMIT; + return JS_DHASH_NEXT; +} + +static JSDHashOperator +thread_marker(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 /* index */, + void *arg) +{ + JSThread *thread = ((JSThreadsHashEntry *) hdr)->thread; + thread->data.mark((JSTracer *) arg); + return JS_DHASH_NEXT; +} + +#endif /* JS_THREADSAFE */ + +JSThreadData * +js_CurrentThreadData(JSRuntime *rt) +{ +#ifdef JS_THREADSAFE + JSThread *thread = js_CurrentThread(rt); + if (!thread) + return NULL; + + return &thread->data; +#else + return &rt->threadData; +#endif +} + +JSBool +js_InitThreads(JSRuntime *rt) +{ +#ifdef JS_THREADSAFE + if (!JS_DHashTableInit(&rt->threads, &threads_ops, NULL, + sizeof(JSThreadsHashEntry), 4)) { + rt->threads.ops = NULL; + return false; + } +#else + rt->threadData.init(); +#endif + return true; +} + +void +js_FinishThreads(JSRuntime *rt) +{ +#ifdef JS_THREADSAFE + if (!rt->threads.ops) + return; + JS_DHashTableEnumerate(&rt->threads, thread_destroyer, NULL); + JS_DHashTableFinish(&rt->threads); + rt->threads.ops = NULL; +#else + rt->threadData.finish(); +#endif +} + +void +js_PurgeThreads(JSContext *cx) +{ +#ifdef JS_THREADSAFE + JS_DHashTableEnumerate(&cx->runtime->threads, thread_purger, cx); +#else + cx->runtime->threadData.purge(cx); +#endif +} + +void +js_TraceThreads(JSRuntime *rt, JSTracer *trc) +{ +#ifdef JS_THREADSAFE + JS_DHashTableEnumerate(&rt->threads, thread_marker, trc); +#else + rt->threadData.mark(trc); +#endif +} + +/* + * JSOPTION_XML and JSOPTION_ANONFUNFIX must be part of the JS version + * associated with scripts, so in addition to storing them in cx->options we + * duplicate them in cx->version (script->version, etc.) and ensure each bit + * remains synchronized between the two through these two functions. + */ +void +js_SyncOptionsToVersion(JSContext* cx) +{ + if (cx->options & JSOPTION_XML) + cx->version |= JSVERSION_HAS_XML; + else + cx->version &= ~JSVERSION_HAS_XML; + if (cx->options & JSOPTION_ANONFUNFIX) + cx->version |= JSVERSION_ANONFUNFIX; + else + cx->version &= ~JSVERSION_ANONFUNFIX; +} + +inline void +js_SyncVersionToOptions(JSContext* cx) +{ + if (cx->version & JSVERSION_HAS_XML) + cx->options |= JSOPTION_XML; + else + cx->options &= ~JSOPTION_XML; + if (cx->version & JSVERSION_ANONFUNFIX) + cx->options |= JSOPTION_ANONFUNFIX; + else + cx->options &= ~JSOPTION_ANONFUNFIX; +} + +void +js_OnVersionChange(JSContext *cx) +{ +#ifdef DEBUG + JSVersion version = JSVERSION_NUMBER(cx); + + JS_ASSERT(version == JSVERSION_DEFAULT || version >= JSVERSION_ECMA_3); +#endif +} + +void +js_SetVersion(JSContext *cx, JSVersion version) +{ + cx->version = version; + js_SyncVersionToOptions(cx); + js_OnVersionChange(cx); +} + +JSContext * +js_NewContext(JSRuntime *rt, size_t stackChunkSize) +{ + JSContext *cx; + JSBool ok, first; + JSContextCallback cxCallback; + + /* + * We need to initialize the new context fully before adding it to the + * runtime list. After that it can be accessed from another thread via + * js_ContextIterator. + */ + void *mem = js_calloc(sizeof *cx); + if (!mem) + return NULL; + + cx = new (mem) JSContext(rt); + cx->debugHooks = &rt->globalDebugHooks; +#if JS_STACK_GROWTH_DIRECTION > 0 + cx->stackLimit = (jsuword) -1; +#endif + cx->scriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA; + JS_STATIC_ASSERT(JSVERSION_DEFAULT == 0); + JS_ASSERT(cx->version == JSVERSION_DEFAULT); + VOUCH_DOES_NOT_REQUIRE_STACK(); + JS_InitArenaPool(&cx->stackPool, "stack", stackChunkSize, sizeof(jsval), + &cx->scriptStackQuota); + + JS_InitArenaPool(&cx->tempPool, "temp", + 1024, /* FIXME: bug 421435 */ + sizeof(jsdouble), &cx->scriptStackQuota); + + js_InitRegExpStatics(cx); + JS_ASSERT(cx->resolveFlags == 0); + + if (!js_InitContextBusyArrayTable(cx)) { + FreeContext(cx); + return NULL; + } + +#ifdef JS_THREADSAFE + if (!js_InitContextThread(cx)) { + FreeContext(cx); + return NULL; + } +#endif + + /* + * Here the GC lock is still held after js_InitContextThread took it and + * the GC is not running on another thread. + */ + for (;;) { + if (rt->state == JSRTS_UP) { + JS_ASSERT(!JS_CLIST_IS_EMPTY(&rt->contextList)); + first = JS_FALSE; + break; + } + if (rt->state == JSRTS_DOWN) { + JS_ASSERT(JS_CLIST_IS_EMPTY(&rt->contextList)); + first = JS_TRUE; + rt->state = JSRTS_LAUNCHING; + break; + } + JS_WAIT_CONDVAR(rt->stateChange, JS_NO_TIMEOUT); + + /* + * During the above wait after we are notified about the state change + * but before we wake up, another thread could enter the GC from + * js_DestroyContext, bug 478336. So we must wait here to ensure that + * when we exit the loop with the first flag set to true, that GC is + * finished. + */ + js_WaitForGC(rt); + } + JS_APPEND_LINK(&cx->link, &rt->contextList); + JS_UNLOCK_GC(rt); + + /* + * If cx is the first context on this runtime, initialize well-known atoms, + * keywords, numbers, and strings. If one of these steps should fail, the + * runtime will be left in a partially initialized state, with zeroes and + * nulls stored in the default-initialized remainder of the struct. We'll + * clean the runtime up under js_DestroyContext, because cx will be "last" + * as well as "first". + */ + if (first) { +#ifdef JS_THREADSAFE + JS_BeginRequest(cx); +#endif + ok = js_InitCommonAtoms(cx); + + /* + * scriptFilenameTable may be left over from a previous episode of + * non-zero contexts alive in rt, so don't re-init the table if it's + * not necessary. + */ + if (ok && !rt->scriptFilenameTable) + ok = js_InitRuntimeScriptState(rt); + if (ok) + ok = js_InitRuntimeNumberState(cx); + if (ok) + ok = js_InitRuntimeStringState(cx); +#ifdef JS_THREADSAFE + JS_EndRequest(cx); +#endif + if (!ok) { + js_DestroyContext(cx, JSDCM_NEW_FAILED); + return NULL; + } + + JS_LOCK_GC(rt); + rt->state = JSRTS_UP; + JS_NOTIFY_ALL_CONDVAR(rt->stateChange); + JS_UNLOCK_GC(rt); + } + + cxCallback = rt->cxCallback; + if (cxCallback && !cxCallback(cx, JSCONTEXT_NEW)) { + js_DestroyContext(cx, JSDCM_NEW_FAILED); + return NULL; + } + + return cx; +} + +#if defined DEBUG && defined XP_UNIX +# include + +class JSAutoFile { +public: + JSAutoFile() : mFile(NULL) {} + + ~JSAutoFile() { + if (mFile) + fclose(mFile); + } + + FILE *open(const char *fname, const char *mode) { + return mFile = fopen(fname, mode); + } + operator FILE *() { + return mFile; + } + +private: + FILE *mFile; +}; + +#ifdef JS_EVAL_CACHE_METERING +static void +DumpEvalCacheMeter(JSContext *cx) +{ + struct { + const char *name; + ptrdiff_t offset; + } table[] = { +#define frob(x) { #x, offsetof(JSEvalCacheMeter, x) } + EVAL_CACHE_METER_LIST(frob) +#undef frob + }; + JSEvalCacheMeter *ecm = &JS_THREAD_DATA(cx)->evalCacheMeter; + + static JSAutoFile fp; + if (!fp) { + fp.open("/tmp/evalcache.stats", "w"); + if (!fp) + return; + } + + fprintf(fp, "eval cache meter (%p):\n", +#ifdef JS_THREADSAFE + (void *) cx->thread +#else + (void *) cx->runtime +#endif + ); + for (uintN i = 0; i < JS_ARRAY_LENGTH(table); ++i) { + fprintf(fp, "%-8.8s %llu\n", + table[i].name, + (unsigned long long int) *(uint64 *)((uint8 *)ecm + table[i].offset)); + } + fprintf(fp, "hit ratio %g%%\n", ecm->hit * 100. / ecm->probe); + fprintf(fp, "avg steps %g\n", double(ecm->step) / ecm->probe); + fflush(fp); +} +# define DUMP_EVAL_CACHE_METER(cx) DumpEvalCacheMeter(cx) +#endif + +#ifdef JS_FUNCTION_METERING +static void +DumpFunctionMeter(JSContext *cx) +{ + struct { + const char *name; + ptrdiff_t offset; + } table[] = { +#define frob(x) { #x, offsetof(JSFunctionMeter, x) } + FUNCTION_KIND_METER_LIST(frob) +#undef frob + }; + JSFunctionMeter *fm = &cx->runtime->functionMeter; + + static JSAutoFile fp; + if (!fp) { + fp.open("/tmp/function.stats", "a"); + if (!fp) + return; + } + + fprintf(fp, "function meter (%s):\n", cx->runtime->lastScriptFilename); + for (uintN i = 0; i < JS_ARRAY_LENGTH(table); ++i) { + fprintf(fp, "%-11.11s %d\n", + table[i].name, *(int32 *)((uint8 *)fm + table[i].offset)); + } + fflush(fp); +} +# define DUMP_FUNCTION_METER(cx) DumpFunctionMeter(cx) +#endif + +#endif /* DEBUG && XP_UNIX */ + +#ifndef DUMP_EVAL_CACHE_METER +# define DUMP_EVAL_CACHE_METER(cx) ((void) 0) +#endif + +#ifndef DUMP_FUNCTION_METER +# define DUMP_FUNCTION_METER(cx) ((void) 0) +#endif + +void +js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) +{ + JSRuntime *rt; + JSContextCallback cxCallback; + JSBool last; + + rt = cx->runtime; +#ifdef JS_THREADSAFE + /* + * For API compatibility we allow to destroy contexts without a thread in + * optimized builds. We assume that the embedding knows that an OOM error + * cannot happen in JS_SetContextThread. + */ + JS_ASSERT(cx->thread && CURRENT_THREAD_IS_ME(cx->thread)); + if (!cx->thread) + JS_SetContextThread(cx); + + JS_ASSERT_IF(rt->gcRunning, cx->outstandingRequests == 0); +#endif + + if (mode != JSDCM_NEW_FAILED) { + cxCallback = rt->cxCallback; + if (cxCallback) { + /* + * JSCONTEXT_DESTROY callback is not allowed to fail and must + * return true. + */ +#ifdef DEBUG + JSBool callbackStatus = +#endif + cxCallback(cx, JSCONTEXT_DESTROY); + JS_ASSERT(callbackStatus); + } + } + + JS_LOCK_GC(rt); + JS_ASSERT(rt->state == JSRTS_UP || rt->state == JSRTS_LAUNCHING); +#ifdef JS_THREADSAFE + /* + * Typically we are called outside a request, so ensure that the GC is not + * running before removing the context from rt->contextList, see bug 477021. + */ + if (cx->requestDepth == 0) + js_WaitForGC(rt); +#endif + JS_REMOVE_LINK(&cx->link); + last = (rt->contextList.next == &rt->contextList); + if (last) + rt->state = JSRTS_LANDING; + if (last || mode == JSDCM_FORCE_GC || mode == JSDCM_MAYBE_GC +#ifdef JS_THREADSAFE + || cx->requestDepth != 0 +#endif + ) { + JS_ASSERT(!rt->gcRunning); + + JS_UNLOCK_GC(rt); + + if (last) { +#ifdef JS_THREADSAFE + /* + * If cx is not in a request already, begin one now so that we wait + * for any racing GC started on a not-last context to finish, before + * we plow ahead and unpin atoms. Note that even though we begin a + * request here if necessary, we end all requests on cx below before + * forcing a final GC. This lets any not-last context destruction + * racing in another thread try to force or maybe run the GC, but by + * that point, rt->state will not be JSRTS_UP, and that GC attempt + * will return early. + */ + if (cx->requestDepth == 0) + JS_BeginRequest(cx); +#endif + + /* Unlock and clear GC things held by runtime pointers. */ + js_FinishRuntimeNumberState(cx); + js_FinishRuntimeStringState(cx); + + /* Unpin all common atoms before final GC. */ + js_FinishCommonAtoms(cx); + + /* Clear debugging state to remove GC roots. */ + JS_ClearAllTraps(cx); + JS_ClearAllWatchPoints(cx); + } + + /* Remove more GC roots in regExpStatics, then collect garbage. */ + JS_ClearRegExpRoots(cx); + +#ifdef JS_THREADSAFE + /* + * Destroying a context implicitly calls JS_EndRequest(). Also, we must + * end our request here in case we are "last" -- in that event, another + * js_DestroyContext that was not last might be waiting in the GC for our + * request to end. We'll let it run below, just before we do the truly + * final GC and then free atom state. + */ + while (cx->requestDepth != 0) + JS_EndRequest(cx); +#endif + + if (last) { + js_GC(cx, GC_LAST_CONTEXT); + DUMP_EVAL_CACHE_METER(cx); + DUMP_FUNCTION_METER(cx); + + /* Take the runtime down, now that it has no contexts or atoms. */ + JS_LOCK_GC(rt); + rt->state = JSRTS_DOWN; + JS_NOTIFY_ALL_CONDVAR(rt->stateChange); + } else { + if (mode == JSDCM_FORCE_GC) + js_GC(cx, GC_NORMAL); + else if (mode == JSDCM_MAYBE_GC) + JS_MaybeGC(cx); + JS_LOCK_GC(rt); + js_WaitForGC(rt); + } + } +#ifdef JS_THREADSAFE + js_ClearContextThread(cx); +#endif + JS_UNLOCK_GC(rt); + FreeContext(cx); +} + +static void +FreeContext(JSContext *cx) +{ +#ifdef JS_THREADSAFE + JS_ASSERT(!cx->thread); +#endif + + /* Free the stuff hanging off of cx. */ + js_FreeRegExpStatics(cx); + VOUCH_DOES_NOT_REQUIRE_STACK(); + JS_FinishArenaPool(&cx->stackPool); + JS_FinishArenaPool(&cx->tempPool); + + if (cx->lastMessage) + js_free(cx->lastMessage); + + /* Remove any argument formatters. */ + JSArgumentFormatMap *map = cx->argumentFormatMap; + while (map) { + JSArgumentFormatMap *temp = map; + map = map->next; + cx->free(temp); + } + + /* Destroy the busy array table. */ + if (cx->busyArrayTable) { + JS_HashTableDestroy(cx->busyArrayTable); + cx->busyArrayTable = NULL; + } + + /* Destroy the resolve recursion damper. */ + if (cx->resolvingTable) { + JS_DHashTableDestroy(cx->resolvingTable); + cx->resolvingTable = NULL; + } + + /* Finally, free cx itself. */ + js_free(cx); +} + +JSBool +js_ValidContextPointer(JSRuntime *rt, JSContext *cx) +{ + JSCList *cl; + + for (cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next) { + if (cl == &cx->link) + return JS_TRUE; + } + JS_RUNTIME_METER(rt, deadContexts); + return JS_FALSE; +} + +JSContext * +js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp) +{ + JSContext *cx = *iterp; + + if (unlocked) + JS_LOCK_GC(rt); + cx = js_ContextFromLinkField(cx ? cx->link.next : rt->contextList.next); + if (&cx->link == &rt->contextList) + cx = NULL; + *iterp = cx; + if (unlocked) + JS_UNLOCK_GC(rt); + return cx; +} + +JS_FRIEND_API(JSContext *) +js_NextActiveContext(JSRuntime *rt, JSContext *cx) +{ + JSContext *iter = cx; +#ifdef JS_THREADSAFE + while ((cx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) { + if (cx->requestDepth) + break; + } + return cx; +#else + return js_ContextIterator(rt, JS_FALSE, &iter); +#endif +} + +#ifdef JS_THREADSAFE + +uint32 +js_CountThreadRequests(JSContext *cx) +{ + JSCList *head, *link; + uint32 nrequests; + + JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread)); + head = &cx->thread->contextList; + nrequests = 0; + for (link = head->next; link != head; link = link->next) { + JSContext *acx = CX_FROM_THREAD_LINKS(link); + JS_ASSERT(acx->thread == cx->thread); + if (acx->requestDepth) + nrequests++; + } + return nrequests; +} + +/* + * If the GC is running and we're called on another thread, wait for this GC + * activation to finish. We can safely wait here without fear of deadlock (in + * the case where we are called within a request on another thread's context) + * because the GC doesn't set rt->gcRunning until after it has waited for all + * active requests to end. + * + * We call here js_CurrentThreadId() after checking for rt->gcRunning to avoid + * expensive calls when the GC is not running. + */ +void +js_WaitForGC(JSRuntime *rt) +{ + JS_ASSERT_IF(rt->gcRunning, rt->gcLevel > 0); + if (rt->gcRunning && rt->gcThread->id != js_CurrentThreadId()) { + do { + JS_AWAIT_GC_DONE(rt); + } while (rt->gcRunning); + } +} + +uint32 +js_DiscountRequestsForGC(JSContext *cx) +{ + uint32 requestDebit; + + JS_ASSERT(cx->thread); + JS_ASSERT(cx->runtime->gcThread != cx->thread); + +#ifdef JS_TRACER + if (JS_ON_TRACE(cx)) { + JS_UNLOCK_GC(cx->runtime); + js_LeaveTrace(cx); + JS_LOCK_GC(cx->runtime); + } +#endif + + requestDebit = js_CountThreadRequests(cx); + if (requestDebit != 0) { + JSRuntime *rt = cx->runtime; + JS_ASSERT(requestDebit <= rt->requestCount); + rt->requestCount -= requestDebit; + if (rt->requestCount == 0) + JS_NOTIFY_REQUEST_DONE(rt); + } + return requestDebit; +} + +void +js_RecountRequestsAfterGC(JSRuntime *rt, uint32 requestDebit) +{ + while (rt->gcLevel > 0) { + JS_ASSERT(rt->gcThread); + JS_AWAIT_GC_DONE(rt); + } + if (requestDebit != 0) + rt->requestCount += requestDebit; +} + +#endif + +static JSDHashNumber +resolving_HashKey(JSDHashTable *table, const void *ptr) +{ + const JSResolvingKey *key = (const JSResolvingKey *)ptr; + + return ((JSDHashNumber)JS_PTR_TO_UINT32(key->obj) >> JSVAL_TAGBITS) ^ key->id; +} + +JS_PUBLIC_API(JSBool) +resolving_MatchEntry(JSDHashTable *table, + const JSDHashEntryHdr *hdr, + const void *ptr) +{ + const JSResolvingEntry *entry = (const JSResolvingEntry *)hdr; + const JSResolvingKey *key = (const JSResolvingKey *)ptr; + + return entry->key.obj == key->obj && entry->key.id == key->id; +} + +static const JSDHashTableOps resolving_dhash_ops = { + JS_DHashAllocTable, + JS_DHashFreeTable, + resolving_HashKey, + resolving_MatchEntry, + JS_DHashMoveEntryStub, + JS_DHashClearEntryStub, + JS_DHashFinalizeStub, + NULL +}; + +JSBool +js_StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag, + JSResolvingEntry **entryp) +{ + JSDHashTable *table; + JSResolvingEntry *entry; + + table = cx->resolvingTable; + if (!table) { + table = JS_NewDHashTable(&resolving_dhash_ops, NULL, + sizeof(JSResolvingEntry), + JS_DHASH_MIN_SIZE); + if (!table) + goto outofmem; + cx->resolvingTable = table; + } + + entry = (JSResolvingEntry *) + JS_DHashTableOperate(table, key, JS_DHASH_ADD); + if (!entry) + goto outofmem; + + if (entry->flags & flag) { + /* An entry for (key, flag) exists already -- dampen recursion. */ + entry = NULL; + } else { + /* Fill in key if we were the first to add entry, then set flag. */ + if (!entry->key.obj) + entry->key = *key; + entry->flags |= flag; + } + *entryp = entry; + return JS_TRUE; + +outofmem: + JS_ReportOutOfMemory(cx); + return JS_FALSE; +} + +void +js_StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag, + JSResolvingEntry *entry, uint32 generation) +{ + JSDHashTable *table; + + /* + * Clear flag from entry->flags and return early if other flags remain. + * We must take care to re-lookup entry if the table has changed since + * it was found by js_StartResolving. + */ + table = cx->resolvingTable; + if (!entry || table->generation != generation) { + entry = (JSResolvingEntry *) + JS_DHashTableOperate(table, key, JS_DHASH_LOOKUP); + } + JS_ASSERT(JS_DHASH_ENTRY_IS_BUSY(&entry->hdr)); + entry->flags &= ~flag; + if (entry->flags) + return; + + /* + * Do a raw remove only if fewer entries were removed than would cause + * alpha to be less than .5 (alpha is at most .75). Otherwise, we just + * call JS_DHashTableOperate to re-lookup the key and remove its entry, + * compressing or shrinking the table as needed. + */ + if (table->removedCount < JS_DHASH_TABLE_SIZE(table) >> 2) + JS_DHashTableRawRemove(table, &entry->hdr); + else + JS_DHashTableOperate(table, key, JS_DHASH_REMOVE); +} + +JSBool +js_EnterLocalRootScope(JSContext *cx) +{ + JSThreadData *td = JS_THREAD_DATA(cx); + JSLocalRootStack *lrs = td->localRootStack; + if (!lrs) { + lrs = (JSLocalRootStack *) js_malloc(sizeof *lrs); + if (!lrs) { + js_ReportOutOfMemory(cx); + return false; + } + lrs->scopeMark = JSLRS_NULL_MARK; + lrs->rootCount = 0; + lrs->topChunk = &lrs->firstChunk; + lrs->firstChunk.down = NULL; + td->gcFreeLists.moveTo(&lrs->gcFreeLists); + td->localRootStack = lrs; + } + + /* Push lrs->scopeMark to save it for restore when leaving. */ + int mark = js_PushLocalRoot(cx, lrs, INT_TO_JSVAL(lrs->scopeMark)); + if (mark < 0) + return JS_FALSE; + lrs->scopeMark = (uint32) mark; + return true; +} + +void +js_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval) +{ + JSLocalRootStack *lrs; + uint32 mark, m, n; + JSLocalRootChunk *lrc; + + /* Defend against buggy native callers. */ + lrs = JS_THREAD_DATA(cx)->localRootStack; + JS_ASSERT(lrs && lrs->rootCount != 0); + if (!lrs || lrs->rootCount == 0) + return; + + mark = lrs->scopeMark; + JS_ASSERT(mark != JSLRS_NULL_MARK); + if (mark == JSLRS_NULL_MARK) + return; + + /* Free any chunks being popped by this leave operation. */ + m = mark >> JSLRS_CHUNK_SHIFT; + n = (lrs->rootCount - 1) >> JSLRS_CHUNK_SHIFT; + while (n > m) { + lrc = lrs->topChunk; + JS_ASSERT(lrc != &lrs->firstChunk); + lrs->topChunk = lrc->down; + js_free(lrc); + --n; + } + + /* + * Pop the scope, restoring lrs->scopeMark. If rval is a GC-thing, push + * it on the caller's scope, or store it in lastInternalResult if we are + * leaving the outermost scope. We don't need to allocate a new lrc + * because we can overwrite the old mark's slot with rval. + */ + lrc = lrs->topChunk; + m = mark & JSLRS_CHUNK_MASK; + lrs->scopeMark = (uint32) JSVAL_TO_INT(lrc->roots[m]); + if (JSVAL_IS_GCTHING(rval) && !JSVAL_IS_NULL(rval)) { + if (mark == 0) { + cx->weakRoots.lastInternalResult = rval; + } else { + /* + * Increment m to avoid the "else if (m == 0)" case below. If + * rval is not a GC-thing, that case would take care of freeing + * any chunk that contained only the old mark. Since rval *is* + * a GC-thing here, we want to reuse that old mark's slot. + */ + lrc->roots[m++] = rval; + ++mark; + } + } + lrs->rootCount = (uint32) mark; + + /* + * Free the stack eagerly, risking malloc churn. The alternative would + * require an lrs->entryCount member, maintained by Enter and Leave, and + * tested by the GC in addition to the cx->localRootStack non-null test. + * + * That approach would risk hoarding 264 bytes (net) per context. Right + * now it seems better to give fresh (dirty in CPU write-back cache, and + * the data is no longer needed) memory back to the malloc heap. + */ + if (mark == 0) { + JSThreadData *td = JS_THREAD_DATA(cx); + JS_ASSERT(td->gcFreeLists.isEmpty()); + lrs->gcFreeLists.moveTo(&td->gcFreeLists); + td->localRootStack = NULL; + js_free(lrs); + } else if (m == 0) { + lrs->topChunk = lrc->down; + js_free(lrc); + } +} + +void +js_ForgetLocalRoot(JSContext *cx, jsval v) +{ + JSLocalRootStack *lrs; + uint32 i, j, m, n, mark; + JSLocalRootChunk *lrc, *lrc2; + jsval top; + + lrs = JS_THREAD_DATA(cx)->localRootStack; + JS_ASSERT(lrs && lrs->rootCount); + if (!lrs || lrs->rootCount == 0) + return; + + /* Prepare to pop the top-most value from the stack. */ + n = lrs->rootCount - 1; + m = n & JSLRS_CHUNK_MASK; + lrc = lrs->topChunk; + top = lrc->roots[m]; + + /* Be paranoid about calls on an empty scope. */ + mark = lrs->scopeMark; + JS_ASSERT(mark < n); + if (mark >= n) + return; + + /* If v was not the last root pushed in the top scope, find it. */ + if (top != v) { + /* Search downward in case v was recently pushed. */ + i = n; + j = m; + lrc2 = lrc; + while (--i > mark) { + if (j == 0) + lrc2 = lrc2->down; + j = i & JSLRS_CHUNK_MASK; + if (lrc2->roots[j] == v) + break; + } + + /* If we didn't find v in this scope, assert and bail out. */ + JS_ASSERT(i != mark); + if (i == mark) + return; + + /* Swap top and v so common tail code can pop v. */ + lrc2->roots[j] = top; + } + + /* Pop the last value from the stack. */ + lrc->roots[m] = JSVAL_NULL; + lrs->rootCount = n; + if (m == 0) { + JS_ASSERT(n != 0); + JS_ASSERT(lrc != &lrs->firstChunk); + lrs->topChunk = lrc->down; + cx->free(lrc); + } +} + +int +js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, jsval v) +{ + uint32 n, m; + JSLocalRootChunk *lrc; + + n = lrs->rootCount; + m = n & JSLRS_CHUNK_MASK; + if (n == 0 || m != 0) { + /* + * At start of first chunk, or not at start of a non-first top chunk. + * Check for lrs->rootCount overflow. + */ + if ((uint32)(n + 1) == 0) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_TOO_MANY_LOCAL_ROOTS); + return -1; + } + lrc = lrs->topChunk; + JS_ASSERT(n != 0 || lrc == &lrs->firstChunk); + } else { + /* + * After lrs->firstChunk, trying to index at a power-of-two chunk + * boundary: need a new chunk. + */ + lrc = (JSLocalRootChunk *) js_malloc(sizeof *lrc); + if (!lrc) { + js_ReportOutOfMemory(cx); + return -1; + } + lrc->down = lrs->topChunk; + lrs->topChunk = lrc; + } + lrs->rootCount = n + 1; + lrc->roots[m] = v; + return (int) n; +} + +static void +MarkLocalRoots(JSTracer *trc, JSLocalRootStack *lrs) +{ + uint32 n, m, mark; + JSLocalRootChunk *lrc; + jsval v; + + n = lrs->rootCount; + if (n == 0) + return; + + mark = lrs->scopeMark; + lrc = lrs->topChunk; + do { + while (--n > mark) { + m = n & JSLRS_CHUNK_MASK; + v = lrc->roots[m]; + JS_ASSERT(JSVAL_IS_GCTHING(v) && v != JSVAL_NULL); + JS_SET_TRACING_INDEX(trc, "local_root", n); + js_CallValueTracerIfGCThing(trc, v); + if (m == 0) + lrc = lrc->down; + } + m = n & JSLRS_CHUNK_MASK; + mark = JSVAL_TO_INT(lrc->roots[m]); + if (m == 0) + lrc = lrc->down; + } while (n != 0); + JS_ASSERT(!lrc); +} + +static void +ReportError(JSContext *cx, const char *message, JSErrorReport *reportp) +{ + /* + * Check the error report, and set a JavaScript-catchable exception + * if the error is defined to have an associated exception. If an + * exception is thrown, then the JSREPORT_EXCEPTION flag will be set + * on the error report, and exception-aware hosts should ignore it. + */ + JS_ASSERT(reportp); + if (reportp->errorNumber == JSMSG_UNCAUGHT_EXCEPTION) + reportp->flags |= JSREPORT_EXCEPTION; + + /* + * Call the error reporter only if an exception wasn't raised. + * + * If an exception was raised, then we call the debugErrorHook + * (if present) to give it a chance to see the error before it + * propagates out of scope. This is needed for compatability + * with the old scheme. + */ + if (!JS_IsRunning(cx) || !js_ErrorToException(cx, message, reportp)) { + js_ReportErrorAgain(cx, message, reportp); + } else if (cx->debugHooks->debugErrorHook && cx->errorReporter) { + JSDebugErrorHook hook = cx->debugHooks->debugErrorHook; + /* test local in case debugErrorHook changed on another thread */ + if (hook) + hook(cx, message, reportp, cx->debugHooks->debugErrorHookData); + } +} + +/* The report must be initially zeroed. */ +static void +PopulateReportBlame(JSContext *cx, JSErrorReport *report) +{ + JSStackFrame *fp; + + /* + * Walk stack until we find a frame that is associated with some script + * rather than a native frame. + */ + for (fp = js_GetTopStackFrame(cx); fp; fp = fp->down) { + if (fp->regs) { + report->filename = fp->script->filename; + report->lineno = js_FramePCToLineNumber(cx, fp); + break; + } + } +} + +/* + * We don't post an exception in this case, since doing so runs into + * complications of pre-allocating an exception object which required + * running the Exception class initializer early etc. + * Instead we just invoke the errorReporter with an "Out Of Memory" + * type message, and then hope the process ends swiftly. + */ +void +js_ReportOutOfMemory(JSContext *cx) +{ +#ifdef JS_TRACER + /* + * If we are in a builtin called directly from trace, don't report an + * error. We will retry in the interpreter instead. + */ + if (JS_ON_TRACE(cx) && !cx->bailExit) + return; +#endif + + JSErrorReport report; + JSErrorReporter onError = cx->errorReporter; + + /* Get the message for this error, but we won't expand any arguments. */ + const JSErrorFormatString *efs = + js_GetLocalizedErrorMessage(cx, NULL, NULL, JSMSG_OUT_OF_MEMORY); + const char *msg = efs ? efs->format : "Out of memory"; + + /* Fill out the report, but don't do anything that requires allocation. */ + memset(&report, 0, sizeof (struct JSErrorReport)); + report.flags = JSREPORT_ERROR; + report.errorNumber = JSMSG_OUT_OF_MEMORY; + PopulateReportBlame(cx, &report); + + /* + * If debugErrorHook is present then we give it a chance to veto sending + * the error on to the regular ErrorReporter. We also clear a pending + * exception if any now so the hooks can replace the out-of-memory error + * by a script-catchable exception. + */ + cx->throwing = JS_FALSE; + if (onError) { + JSDebugErrorHook hook = cx->debugHooks->debugErrorHook; + if (hook && + !hook(cx, msg, &report, cx->debugHooks->debugErrorHookData)) { + onError = NULL; + } + } + + if (onError) + onError(cx, msg, &report); +} + +void +js_ReportOutOfScriptQuota(JSContext *cx) +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_SCRIPT_STACK_QUOTA); +} + +void +js_ReportOverRecursed(JSContext *cx) +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); +} + +void +js_ReportAllocationOverflow(JSContext *cx) +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_ALLOC_OVERFLOW); +} + +/* + * Given flags and the state of cx, decide whether we should report an + * error, a warning, or just continue execution normally. Return + * true if we should continue normally, without reporting anything; + * otherwise, adjust *flags as appropriate and return false. + */ +static bool +checkReportFlags(JSContext *cx, uintN *flags) +{ + if (JSREPORT_IS_STRICT_MODE_ERROR(*flags)) { + /* Error in strict code; warning with strict option; okay otherwise. */ + JS_ASSERT(JS_IsRunning(cx)); + if (js_GetTopStackFrame(cx)->script->strictModeCode) + *flags &= ~JSREPORT_WARNING; + else if (JS_HAS_STRICT_OPTION(cx)) + *flags |= JSREPORT_WARNING; + else + return true; + } else if (JSREPORT_IS_STRICT(*flags)) { + /* Warning/error only when JSOPTION_STRICT is set. */ + if (!JS_HAS_STRICT_OPTION(cx)) + return true; + } + + /* Warnings become errors when JSOPTION_WERROR is set. */ + if (JSREPORT_IS_WARNING(*flags) && JS_HAS_WERROR_OPTION(cx)) + *flags &= ~JSREPORT_WARNING; + + return false; +} + +JSBool +js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap) +{ + char *message; + jschar *ucmessage; + size_t messagelen; + JSErrorReport report; + JSBool warning; + + if (checkReportFlags(cx, &flags)) + return JS_TRUE; + + message = JS_vsmprintf(format, ap); + if (!message) + return JS_FALSE; + messagelen = strlen(message); + + memset(&report, 0, sizeof (struct JSErrorReport)); + report.flags = flags; + report.errorNumber = JSMSG_USER_DEFINED_ERROR; + report.ucmessage = ucmessage = js_InflateString(cx, message, &messagelen); + PopulateReportBlame(cx, &report); + + warning = JSREPORT_IS_WARNING(report.flags); + + ReportError(cx, message, &report); + js_free(message); + cx->free(ucmessage); + return warning; +} + +/* + * The arguments from ap need to be packaged up into an array and stored + * into the report struct. + * + * The format string addressed by the error number may contain operands + * identified by the format {N}, where N is a decimal digit. Each of these + * is to be replaced by the Nth argument from the va_list. The complete + * message is placed into reportp->ucmessage converted to a JSString. + * + * Returns true if the expansion succeeds (can fail if out of memory). + */ +JSBool +js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback, + void *userRef, const uintN errorNumber, + char **messagep, JSErrorReport *reportp, + bool charArgs, va_list ap) +{ + const JSErrorFormatString *efs; + int i; + int argCount; + + *messagep = NULL; + + /* Most calls supply js_GetErrorMessage; if this is so, assume NULL. */ + if (!callback || callback == js_GetErrorMessage) + efs = js_GetLocalizedErrorMessage(cx, userRef, NULL, errorNumber); + else + efs = callback(userRef, NULL, errorNumber); + if (efs) { + size_t totalArgsLength = 0; + size_t argLengths[10]; /* only {0} thru {9} supported */ + argCount = efs->argCount; + JS_ASSERT(argCount <= 10); + if (argCount > 0) { + /* + * Gather the arguments into an array, and accumulate + * their sizes. We allocate 1 more than necessary and + * null it out to act as the caboose when we free the + * pointers later. + */ + reportp->messageArgs = (const jschar **) + cx->malloc(sizeof(jschar *) * (argCount + 1)); + if (!reportp->messageArgs) + return JS_FALSE; + reportp->messageArgs[argCount] = NULL; + for (i = 0; i < argCount; i++) { + if (charArgs) { + char *charArg = va_arg(ap, char *); + size_t charArgLength = strlen(charArg); + reportp->messageArgs[i] + = js_InflateString(cx, charArg, &charArgLength); + if (!reportp->messageArgs[i]) + goto error; + } else { + reportp->messageArgs[i] = va_arg(ap, jschar *); + } + argLengths[i] = js_strlen(reportp->messageArgs[i]); + totalArgsLength += argLengths[i]; + } + /* NULL-terminate for easy copying. */ + reportp->messageArgs[i] = NULL; + } + /* + * Parse the error format, substituting the argument X + * for {X} in the format. + */ + if (argCount > 0) { + if (efs->format) { + jschar *buffer, *fmt, *out; + int expandedArgs = 0; + size_t expandedLength; + size_t len = strlen(efs->format); + + buffer = fmt = js_InflateString (cx, efs->format, &len); + if (!buffer) + goto error; + expandedLength = len + - (3 * argCount) /* exclude the {n} */ + + totalArgsLength; + + /* + * Note - the above calculation assumes that each argument + * is used once and only once in the expansion !!! + */ + reportp->ucmessage = out = (jschar *) + cx->malloc((expandedLength + 1) * sizeof(jschar)); + if (!out) { + cx->free(buffer); + goto error; + } + while (*fmt) { + if (*fmt == '{') { + if (isdigit(fmt[1])) { + int d = JS7_UNDEC(fmt[1]); + JS_ASSERT(d < argCount); + js_strncpy(out, reportp->messageArgs[d], + argLengths[d]); + out += argLengths[d]; + fmt += 3; + expandedArgs++; + continue; + } + } + *out++ = *fmt++; + } + JS_ASSERT(expandedArgs == argCount); + *out = 0; + cx->free(buffer); + *messagep = + js_DeflateString(cx, reportp->ucmessage, + (size_t)(out - reportp->ucmessage)); + if (!*messagep) + goto error; + } + } else { + /* + * Zero arguments: the format string (if it exists) is the + * entire message. + */ + if (efs->format) { + size_t len; + *messagep = JS_strdup(cx, efs->format); + if (!*messagep) + goto error; + len = strlen(*messagep); + reportp->ucmessage = js_InflateString(cx, *messagep, &len); + if (!reportp->ucmessage) + goto error; + } + } + } + if (*messagep == NULL) { + /* where's the right place for this ??? */ + const char *defaultErrorMessage + = "No error message available for error number %d"; + size_t nbytes = strlen(defaultErrorMessage) + 16; + *messagep = (char *)cx->malloc(nbytes); + if (!*messagep) + goto error; + JS_snprintf(*messagep, nbytes, defaultErrorMessage, errorNumber); + } + return JS_TRUE; + +error: + if (reportp->messageArgs) { + /* free the arguments only if we allocated them */ + if (charArgs) { + i = 0; + while (reportp->messageArgs[i]) + cx->free((void *)reportp->messageArgs[i++]); + } + cx->free((void *)reportp->messageArgs); + reportp->messageArgs = NULL; + } + if (reportp->ucmessage) { + cx->free((void *)reportp->ucmessage); + reportp->ucmessage = NULL; + } + if (*messagep) { + cx->free((void *)*messagep); + *messagep = NULL; + } + return JS_FALSE; +} + +JSBool +js_ReportErrorNumberVA(JSContext *cx, uintN flags, JSErrorCallback callback, + void *userRef, const uintN errorNumber, + JSBool charArgs, va_list ap) +{ + JSErrorReport report; + char *message; + JSBool warning; + + if (checkReportFlags(cx, &flags)) + return JS_TRUE; + warning = JSREPORT_IS_WARNING(flags); + + memset(&report, 0, sizeof (struct JSErrorReport)); + report.flags = flags; + report.errorNumber = errorNumber; + PopulateReportBlame(cx, &report); + + if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber, + &message, &report, charArgs, ap)) { + return JS_FALSE; + } + + ReportError(cx, message, &report); + + if (message) + cx->free(message); + if (report.messageArgs) { + /* + * js_ExpandErrorArguments owns its messageArgs only if it had to + * inflate the arguments (from regular |char *|s). + */ + if (charArgs) { + int i = 0; + while (report.messageArgs[i]) + cx->free((void *)report.messageArgs[i++]); + } + cx->free((void *)report.messageArgs); + } + if (report.ucmessage) + cx->free((void *)report.ucmessage); + + return warning; +} + +JS_FRIEND_API(void) +js_ReportErrorAgain(JSContext *cx, const char *message, JSErrorReport *reportp) +{ + JSErrorReporter onError; + + if (!message) + return; + + if (cx->lastMessage) + js_free(cx->lastMessage); + cx->lastMessage = JS_strdup(cx, message); + if (!cx->lastMessage) + return; + onError = cx->errorReporter; + + /* + * If debugErrorHook is present then we give it a chance to veto + * sending the error on to the regular ErrorReporter. + */ + if (onError) { + JSDebugErrorHook hook = cx->debugHooks->debugErrorHook; + if (hook && + !hook(cx, cx->lastMessage, reportp, + cx->debugHooks->debugErrorHookData)) { + onError = NULL; + } + } + if (onError) + onError(cx, cx->lastMessage, reportp); +} + +void +js_ReportIsNotDefined(JSContext *cx, const char *name) +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_DEFINED, name); +} + +JSBool +js_ReportIsNullOrUndefined(JSContext *cx, intN spindex, jsval v, + JSString *fallback) +{ + char *bytes; + JSBool ok; + + bytes = js_DecompileValueGenerator(cx, spindex, v, fallback); + if (!bytes) + return JS_FALSE; + + if (strcmp(bytes, js_undefined_str) == 0 || + strcmp(bytes, js_null_str) == 0) { + ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, + js_GetErrorMessage, NULL, + JSMSG_NO_PROPERTIES, bytes, + NULL, NULL); + } else if (JSVAL_IS_VOID(v)) { + ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, + js_GetErrorMessage, NULL, + JSMSG_UNEXPECTED_TYPE, bytes, + js_undefined_str, NULL); + } else { + JS_ASSERT(JSVAL_IS_NULL(v)); + ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, + js_GetErrorMessage, NULL, + JSMSG_UNEXPECTED_TYPE, bytes, + js_null_str, NULL); + } + + cx->free(bytes); + return ok; +} + +void +js_ReportMissingArg(JSContext *cx, jsval *vp, uintN arg) +{ + char argbuf[11]; + char *bytes; + JSAtom *atom; + + JS_snprintf(argbuf, sizeof argbuf, "%u", arg); + bytes = NULL; + if (VALUE_IS_FUNCTION(cx, *vp)) { + atom = GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(*vp))->atom; + bytes = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, *vp, + ATOM_TO_STRING(atom)); + if (!bytes) + return; + } + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_MISSING_FUN_ARG, argbuf, + bytes ? bytes : ""); + cx->free(bytes); +} + +JSBool +js_ReportValueErrorFlags(JSContext *cx, uintN flags, const uintN errorNumber, + intN spindex, jsval v, JSString *fallback, + const char *arg1, const char *arg2) +{ + char *bytes; + JSBool ok; + + JS_ASSERT(js_ErrorFormatString[errorNumber].argCount >= 1); + JS_ASSERT(js_ErrorFormatString[errorNumber].argCount <= 3); + bytes = js_DecompileValueGenerator(cx, spindex, v, fallback); + if (!bytes) + return JS_FALSE; + + ok = JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage, + NULL, errorNumber, bytes, arg1, arg2); + cx->free(bytes); + return ok; +} + +#if defined DEBUG && defined XP_UNIX +/* For gdb usage. */ +void js_traceon(JSContext *cx) { cx->tracefp = stderr; cx->tracePrevPc = NULL; } +void js_traceoff(JSContext *cx) { cx->tracefp = NULL; } +#endif + +JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = { +#define MSG_DEF(name, number, count, exception, format) \ + { format, count, exception } , +#include "js.msg" +#undef MSG_DEF +}; + +JS_FRIEND_API(const JSErrorFormatString *) +js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber) +{ + if ((errorNumber > 0) && (errorNumber < JSErr_Limit)) + return &js_ErrorFormatString[errorNumber]; + return NULL; +} + +JSBool +js_InvokeOperationCallback(JSContext *cx) +{ + JS_ASSERT(cx->operationCallbackFlag); + + /* + * Reset the callback flag first, then yield. If another thread is racing + * us here we will accumulate another callback request which will be + * serviced at the next opportunity. + */ + cx->operationCallbackFlag = 0; + + /* + * Unless we are going to run the GC, we automatically yield the current + * context every time the operation callback is hit since we might be + * called as a result of an impending GC, which would deadlock if we do + * not yield. Operation callbacks are supposed to happen rarely (seconds, + * not milliseconds) so it is acceptable to yield at every callback. + */ + if (cx->runtime->gcIsNeeded) + js_GC(cx, GC_NORMAL); +#ifdef JS_THREADSAFE + else + JS_YieldRequest(cx); +#endif + + JSOperationCallback cb = cx->operationCallback; + + /* + * Important: Additional callbacks can occur inside the callback handler + * if it re-enters the JS engine. The embedding must ensure that the + * callback is disconnected before attempting such re-entry. + */ + + return !cb || cb(cx); +} + +void +js_TriggerAllOperationCallbacks(JSRuntime *rt, JSBool gcLocked) +{ + JSContext *acx, *iter; +#ifdef JS_THREADSAFE + if (!gcLocked) + JS_LOCK_GC(rt); +#endif + iter = NULL; + while ((acx = js_ContextIterator(rt, JS_FALSE, &iter))) + JS_TriggerOperationCallback(acx); +#ifdef JS_THREADSAFE + if (!gcLocked) + JS_UNLOCK_GC(rt); +#endif +} + +JSStackFrame * +js_GetScriptedCaller(JSContext *cx, JSStackFrame *fp) +{ + if (!fp) + fp = js_GetTopStackFrame(cx); + while (fp) { + if (fp->script) + return fp; + fp = fp->down; + } + return NULL; +} + +jsbytecode* +js_GetCurrentBytecodePC(JSContext* cx) +{ + jsbytecode *pc, *imacpc; + +#ifdef JS_TRACER + if (JS_ON_TRACE(cx)) { + pc = cx->bailExit->pc; + imacpc = cx->bailExit->imacpc; + } else +#endif + { + JS_ASSERT_NOT_ON_TRACE(cx); /* for static analysis */ + JSStackFrame* fp = cx->fp; + if (fp && fp->regs) { + pc = fp->regs->pc; + imacpc = fp->imacpc; + } else { + return NULL; + } + } + + /* + * If we are inside GetProperty_tn or similar, return a pointer to the + * current instruction in the script, not the CALL instruction in the + * imacro, for the benefit of callers doing bytecode inspection. + */ + return (*pc == JSOP_CALL && imacpc) ? imacpc : pc; +} + +bool +js_CurrentPCIsInImacro(JSContext *cx) +{ +#ifdef JS_TRACER + VOUCH_DOES_NOT_REQUIRE_STACK(); + return (JS_ON_TRACE(cx) ? cx->bailExit->imacpc : cx->fp->imacpc) != NULL; +#else + return false; +#endif +} + +void +JSContext::checkMallocGCPressure(void *p) +{ + if (!p) { + js_ReportOutOfMemory(this); + return; + } + +#ifdef JS_THREADSAFE + JS_ASSERT(thread->gcThreadMallocBytes <= 0); + ptrdiff_t n = JS_GC_THREAD_MALLOC_LIMIT - thread->gcThreadMallocBytes; + thread->gcThreadMallocBytes = JS_GC_THREAD_MALLOC_LIMIT; + + JS_LOCK_GC(runtime); + runtime->gcMallocBytes -= n; + if (runtime->isGCMallocLimitReached()) +#endif + { + JS_ASSERT(runtime->isGCMallocLimitReached()); + runtime->gcMallocBytes = -1; + + /* + * Empty the GC free lists to trigger a last-ditch GC when allocating + * any GC thing later on this thread. This minimizes the amount of + * checks on the fast path of the GC allocator. Note that we cannot + * touch the free lists on other threads as their manipulation is not + * thread-safe. + */ + JS_THREAD_DATA(this)->purgeGCFreeLists(); + js_TriggerGC(this, true); + } + JS_UNLOCK_GC(runtime); +} diff --git a/ape-server/deps/js/src/jscntxt.h b/ape-server/deps/js/src/jscntxt.h new file mode 100755 index 0000000..978f872 --- /dev/null +++ b/ape-server/deps/js/src/jscntxt.h @@ -0,0 +1,2025 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jscntxt_h___ +#define jscntxt_h___ +/* + * JS execution context. + */ +#include "jsarena.h" /* Added by JSIFY */ +#include "jsclist.h" +#include "jslong.h" +#include "jsatom.h" +#include "jsversion.h" +#include "jsdhash.h" +#include "jsgc.h" +#include "jsinterp.h" +#include "jsobj.h" +#include "jsprvtd.h" +#include "jspubtd.h" +#include "jsregexp.h" +#include "jsutil.h" +#include "jsarray.h" +#include "jstask.h" +#include "jsvector.h" + +/* + * js_GetSrcNote cache to avoid O(n^2) growth in finding a source note for a + * given pc in a script. We use the script->code pointer to tag the cache, + * instead of the script address itself, so that source notes are always found + * by offset from the bytecode with which they were generated. + */ +typedef struct JSGSNCache { + jsbytecode *code; + JSDHashTable table; +#ifdef JS_GSNMETER + uint32 hits; + uint32 misses; + uint32 fills; + uint32 purges; +# define GSN_CACHE_METER(cache,cnt) (++(cache)->cnt) +#else +# define GSN_CACHE_METER(cache,cnt) /* nothing */ +#endif +} JSGSNCache; + +#define js_FinishGSNCache(cache) js_PurgeGSNCache(cache) + +extern void +js_PurgeGSNCache(JSGSNCache *cache); + +/* These helper macros take a cx as parameter and operate on its GSN cache. */ +#define JS_PURGE_GSN_CACHE(cx) js_PurgeGSNCache(&JS_GSN_CACHE(cx)) +#define JS_METER_GSN_CACHE(cx,cnt) GSN_CACHE_METER(&JS_GSN_CACHE(cx), cnt) + +/* Forward declarations of nanojit types. */ +namespace nanojit +{ + class Assembler; + class CodeAlloc; + class Fragment; + class LirBuffer; +#ifdef DEBUG + class LabelMap; +#endif + template struct DefaultHash; + template class HashMap; + template class Seq; +} + +/* Tracer constants. */ +static const size_t MONITOR_N_GLOBAL_STATES = 4; +static const size_t FRAGMENT_TABLE_SIZE = 512; +static const size_t MAX_NATIVE_STACK_SLOTS = 4096; +static const size_t MAX_CALL_STACK_ENTRIES = 500; +static const size_t MAX_GLOBAL_SLOTS = 4096; +static const size_t GLOBAL_SLOTS_BUFFER_SIZE = MAX_GLOBAL_SLOTS + 1; + +/* Forward declarations of tracer types. */ +class TreeInfo; +class VMAllocator; +class TraceRecorder; +class FrameInfoCache; +struct REHashFn; +struct REHashKey; +struct FrameInfo; +struct VMSideExit; +struct TreeFragment; +struct InterpState; +template class Queue; +typedef Queue SlotList; +struct REFragment; +typedef nanojit::HashMap REHashMap; + +#if defined(JS_JIT_SPEW) || defined(DEBUG) +struct FragPI; +typedef nanojit::HashMap > FragStatsMap; +#endif + +/* Holds the execution state during trace execution. */ +struct InterpState +{ + JSContext* cx; // current VM context handle + double* stackBase; // native stack base + double* sp; // native stack pointer, stack[0] is spbase[0] + double* eos; // first unusable word after the native stack / begin of globals + FrameInfo** callstackBase; // call stack base + void* sor; // start of rp stack + FrameInfo** rp; // call stack pointer + void* eor; // first unusable word after the call stack + VMSideExit* lastTreeExitGuard; // guard we exited on during a tree call + VMSideExit* lastTreeCallGuard; // guard we want to grow from if the tree + // call exit guard mismatched + void* rpAtLastTreeCall; // value of rp at innermost tree call guard + VMSideExit* outermostTreeExitGuard; // the last side exit returned by js_CallTree + TreeInfo* outermostTree; // the outermost tree we initially invoked + uintN* inlineCallCountp; // inline call count counter + VMSideExit** innermostNestedGuardp; + VMSideExit* innermost; + uint64 startTime; + InterpState* prev; + + // Used by _FAIL builtins; see jsbuiltins.h. The builtin sets the + // JSBUILTIN_BAILED bit if it bails off trace and the JSBUILTIN_ERROR bit + // if an error or exception occurred. + uint32 builtinStatus; + + // Used to communicate the location of the return value in case of a deep bail. + double* deepBailSp; + + // Used when calling natives from trace to root the vp vector. + uintN nativeVpLen; + jsval* nativeVp; + + InterpState(JSContext *cx, JSTraceMonitor *tm, TreeInfo *ti, + uintN &inlineCallCountp, VMSideExit** innermostNestedGuardp); + ~InterpState(); +}; + +/* + * Storage for the execution state and store during trace execution. Generated + * code depends on the fact that the globals begin |MAX_NATIVE_STACK_SLOTS| + * doubles after the stack begins. Thus, on trace, |InterpState::eos| holds a + * pointer to the first global. + */ +struct TraceNativeStorage +{ + double stack_global_buf[MAX_NATIVE_STACK_SLOTS + GLOBAL_SLOTS_BUFFER_SIZE]; + FrameInfo *callstack_buf[MAX_CALL_STACK_ENTRIES]; + + double *stack() { return stack_global_buf; } + double *global() { return stack_global_buf + MAX_NATIVE_STACK_SLOTS; } + FrameInfo **callstack() { return callstack_buf; } +}; + +/* Holds data to track a single globa. */ +struct GlobalState { + JSObject* globalObj; + uint32 globalShape; + SlotList* globalSlots; +}; + +/* + * Trace monitor. Every JSThread (if JS_THREADSAFE) or JSRuntime (if not + * JS_THREADSAFE) has an associated trace monitor that keeps track of loop + * frequencies for all JavaScript code loaded into that runtime. + */ +struct JSTraceMonitor { + /* + * The context currently executing JIT-compiled code on this thread, or + * NULL if none. Among other things, this can in certain cases prevent + * last-ditch GC and suppress calls to JS_ReportOutOfMemory. + * + * !tracecx && !recorder: not on trace + * !tracecx && recorder: recording + * tracecx && !recorder: executing a trace + * tracecx && recorder: executing inner loop, recording outer loop + */ + JSContext *tracecx; + + /* + * Cached storage to use when executing on trace. While we may enter nested + * traces, we always reuse the outer trace's storage, so never need more + * than of these. + */ + TraceNativeStorage storage; + + /* + * There are 3 allocators here. This might seem like overkill, but they + * have different lifecycles, and by keeping them separate we keep the + * amount of retained memory down significantly. + * + * The dataAlloc has the lifecycle of the monitor. It's flushed only + * when the monitor is flushed. + * + * The traceAlloc has the same flush lifecycle as the dataAlloc, but + * it is also *marked* when a recording starts and rewinds to the mark + * point if recording aborts. So you can put things in it that are only + * reachable on a successful record/compile cycle. + * + * The tempAlloc is flushed after each recording, successful or not. + */ + + VMAllocator* dataAlloc; /* A chunk allocator for fragments. */ + VMAllocator* traceAlloc; /* An allocator for trace metadata. */ + VMAllocator* tempAlloc; /* A temporary chunk allocator. */ + nanojit::CodeAlloc* codeAlloc; /* An allocator for native code. */ + nanojit::Assembler* assembler; + nanojit::LirBuffer* lirbuf; + nanojit::LirBuffer* reLirBuf; + FrameInfoCache* frameCache; +#ifdef DEBUG + nanojit::LabelMap* labels; +#endif + + TraceRecorder* recorder; + + struct GlobalState globalStates[MONITOR_N_GLOBAL_STATES]; + struct TreeFragment* vmfragments[FRAGMENT_TABLE_SIZE]; + JSDHashTable recordAttempts; + + /* + * Maximum size of the code cache before we start flushing. 1/16 of this + * size is used as threshold for the regular expression code cache. + */ + uint32 maxCodeCacheBytes; + + /* + * If nonzero, do not flush the JIT cache after a deep bail. That would + * free JITted code pages that we will later return to. Instead, set the + * needFlush flag so that it can be flushed later. + */ + JSBool needFlush; + + /* + * reservedObjects is a linked list (via fslots[0]) of preallocated JSObjects. + * The JIT uses this to ensure that leaving a trace tree can't fail. + */ + JSBool useReservedObjects; + JSObject *reservedObjects; + + /* + * Fragment map for the regular expression compiler. + */ + REHashMap* reFragments; + + /* + * A temporary allocator for RE recording. + */ + VMAllocator* reTempAlloc; + +#ifdef DEBUG + /* Fields needed for fragment/guard profiling. */ + nanojit::Seq* branches; + uint32 lastFragID; + /* + * profAlloc has a lifetime which spans exactly from js_InitJIT to + * js_FinishJIT. + */ + VMAllocator* profAlloc; + FragStatsMap* profTab; +#endif + + /* Flush the JIT cache. */ + void flush(); + + /* Mark all objects baked into native code in the code cache. */ + void mark(JSTracer *trc); + + bool outOfMemory() const; +}; + +typedef struct InterpStruct InterpStruct; + +/* + * N.B. JS_ON_TRACE(cx) is true if JIT code is on the stack in the current + * thread, regardless of whether cx is the context in which that trace is + * executing. cx must be a context on the current thread. + */ +#ifdef JS_TRACER +# define JS_ON_TRACE(cx) (JS_TRACE_MONITOR(cx).tracecx != NULL) +#else +# define JS_ON_TRACE(cx) JS_FALSE +#endif + +#ifdef DEBUG +# define JS_EVAL_CACHE_METERING 1 +# define JS_FUNCTION_METERING 1 +#endif + +/* Number of potentially reusable scriptsToGC to search for the eval cache. */ +#ifndef JS_EVAL_CACHE_SHIFT +# define JS_EVAL_CACHE_SHIFT 6 +#endif +#define JS_EVAL_CACHE_SIZE JS_BIT(JS_EVAL_CACHE_SHIFT) + +#ifdef JS_EVAL_CACHE_METERING +# define EVAL_CACHE_METER_LIST(_) _(probe), _(hit), _(step), _(noscope) +# define identity(x) x + +struct JSEvalCacheMeter { + uint64 EVAL_CACHE_METER_LIST(identity); +}; + +# undef identity +#endif + +#ifdef JS_FUNCTION_METERING +# define FUNCTION_KIND_METER_LIST(_) \ + _(allfun), _(heavy), _(nofreeupvar), _(onlyfreevar), \ + _(display), _(flat), _(setupvar), _(badfunarg) +# define identity(x) x + +struct JSFunctionMeter { + int32 FUNCTION_KIND_METER_LIST(identity); +}; + +# undef identity +#endif + +struct JSLocalRootChunk; + +#define JSLRS_CHUNK_SHIFT 8 +#define JSLRS_CHUNK_SIZE JS_BIT(JSLRS_CHUNK_SHIFT) +#define JSLRS_CHUNK_MASK JS_BITMASK(JSLRS_CHUNK_SHIFT) + +struct JSLocalRootChunk { + jsval roots[JSLRS_CHUNK_SIZE]; + JSLocalRootChunk *down; +}; + +struct JSLocalRootStack { + uint32 scopeMark; + uint32 rootCount; + JSLocalRootChunk *topChunk; + JSLocalRootChunk firstChunk; + + /* See comments in js_NewFinalizableGCThing. */ + JSGCFreeLists gcFreeLists; +}; + +const uint32 JSLRS_NULL_MARK = uint32(-1); + +struct JSThreadData { + JSGCFreeLists gcFreeLists; + + /* + * Flag indicating that we are waiving any soft limits on the GC heap + * because we want allocations to be infallible (except when we hit + * a hard quota). + */ + bool waiveGCQuota; + + /* + * The GSN cache is per thread since even multi-cx-per-thread embeddings + * do not interleave js_GetSrcNote calls. + */ + JSGSNCache gsnCache; + + /* Property cache for faster call/get/set invocation. */ + JSPropertyCache propertyCache; + + /* Random number generator state, used by jsmath.cpp. */ + int64 rngSeed; + + /* Optional stack of heap-allocated scoped local GC roots. */ + JSLocalRootStack *localRootStack; + +#ifdef JS_TRACER + /* Trace-tree JIT recorder/interpreter state. */ + JSTraceMonitor traceMonitor; +#endif + + /* Lock-free hashed lists of scripts created by eval to garbage-collect. */ + JSScript *scriptsToGC[JS_EVAL_CACHE_SIZE]; + +#ifdef JS_EVAL_CACHE_METERING + JSEvalCacheMeter evalCacheMeter; +#endif + + /* + * Cache of reusable JSNativeEnumerators mapped by shape identifiers (as + * stored in scope->shape). This cache is nulled by the GC and protected + * by gcLock. + */ +#define NATIVE_ENUM_CACHE_LOG2 8 +#define NATIVE_ENUM_CACHE_MASK JS_BITMASK(NATIVE_ENUM_CACHE_LOG2) +#define NATIVE_ENUM_CACHE_SIZE JS_BIT(NATIVE_ENUM_CACHE_LOG2) + +#define NATIVE_ENUM_CACHE_HASH(shape) \ + ((((shape) >> NATIVE_ENUM_CACHE_LOG2) ^ (shape)) & NATIVE_ENUM_CACHE_MASK) + + jsuword nativeEnumCache[NATIVE_ENUM_CACHE_SIZE]; + + void init(); + void finish(); + void mark(JSTracer *trc); + void purge(JSContext *cx); + void purgeGCFreeLists(); +}; + +#ifdef JS_THREADSAFE + +/* + * Structure uniquely representing a thread. It holds thread-private data + * that can be accessed without a global lock. + */ +struct JSThread { + /* Linked list of all contexts in use on this thread. */ + JSCList contextList; + + /* Opaque thread-id, from NSPR's PR_GetCurrentThread(). */ + jsword id; + + /* Indicates that the thread is waiting in ClaimTitle from jslock.cpp. */ + JSTitle *titleToShare; + + /* + * Thread-local version of JSRuntime.gcMallocBytes to avoid taking + * locks on each JS_malloc. + */ + ptrdiff_t gcThreadMallocBytes; + + /* + * Deallocator task for this thread. + */ + JSFreePointerListTask *deallocatorTask; + + /* Factored out of JSThread for !JS_THREADSAFE embedding in JSRuntime. */ + JSThreadData data; +}; + +/* + * Only when JSThread::gcThreadMallocBytes exhausts the following limit we + * update JSRuntime::gcMallocBytes. + * . + */ +const size_t JS_GC_THREAD_MALLOC_LIMIT = 1 << 19; + +#define JS_THREAD_DATA(cx) (&(cx)->thread->data) + +struct JSThreadsHashEntry { + JSDHashEntryHdr base; + JSThread *thread; +}; + +extern JSThread * +js_CurrentThread(JSRuntime *rt); + +/* + * The function takes the GC lock and does not release in successful return. + * On error (out of memory) the function releases the lock but delegates + * the error reporting to the caller. + */ +extern JSBool +js_InitContextThread(JSContext *cx); + +/* + * On entrance the GC lock must be held and it will be held on exit. + */ +extern void +js_ClearContextThread(JSContext *cx); + +#endif /* JS_THREADSAFE */ + +typedef enum JSDestroyContextMode { + JSDCM_NO_GC, + JSDCM_MAYBE_GC, + JSDCM_FORCE_GC, + JSDCM_NEW_FAILED +} JSDestroyContextMode; + +typedef enum JSRuntimeState { + JSRTS_DOWN, + JSRTS_LAUNCHING, + JSRTS_UP, + JSRTS_LANDING +} JSRuntimeState; + +typedef enum JSBuiltinFunctionId { + JSBUILTIN_ObjectToIterator, + JSBUILTIN_CallIteratorNext, + JSBUILTIN_LIMIT +} JSBuiltinFunctionId; + +typedef struct JSPropertyTreeEntry { + JSDHashEntryHdr hdr; + JSScopeProperty *child; +} JSPropertyTreeEntry; + +typedef struct JSSetSlotRequest JSSetSlotRequest; + +struct JSSetSlotRequest { + JSObject *obj; /* object containing slot to set */ + JSObject *pobj; /* new proto or parent reference */ + uint16 slot; /* which to set, proto or parent */ + JSPackedBool cycle; /* true if a cycle was detected */ + JSSetSlotRequest *next; /* next request in GC worklist */ +}; + +struct JSRuntime { + /* Runtime state, synchronized by the stateChange/gcLock condvar/lock. */ + JSRuntimeState state; + + /* Context create/destroy callback. */ + JSContextCallback cxCallback; + + /* + * Shape regenerated whenever a prototype implicated by an "add property" + * property cache fill and induced trace guard has a readonly property or a + * setter defined on it. This number proxies for the shapes of all objects + * along the prototype chain of all objects in the runtime on which such an + * add-property result has been cached/traced. + * + * See bug 492355 for more details. + * + * This comes early in JSRuntime to minimize the immediate format used by + * trace-JITted code that reads it. + */ + uint32 protoHazardShape; + + /* Garbage collector state, used by jsgc.c. */ + JSGCChunkInfo *gcChunkList; + JSGCArenaList gcArenaList[FINALIZE_LIMIT]; + JSGCDoubleArenaList gcDoubleArenaList; + JSDHashTable gcRootsHash; + JSDHashTable *gcLocksHash; + jsrefcount gcKeepAtoms; + size_t gcBytes; + size_t gcLastBytes; + size_t gcMaxBytes; + size_t gcMaxMallocBytes; + uint32 gcEmptyArenaPoolLifespan; + uint32 gcLevel; + uint32 gcNumber; + JSTracer *gcMarkingTracer; + uint32 gcTriggerFactor; + size_t gcTriggerBytes; + volatile JSBool gcIsNeeded; + volatile JSBool gcFlushCodeCaches; + + /* + * NB: do not pack another flag here by claiming gcPadding unless the new + * flag is written only by the GC thread. Atomic updates to packed bytes + * are not guaranteed, so stores issued by one thread may be lost due to + * unsynchronized read-modify-write cycles on other threads. + */ + JSPackedBool gcPoke; + JSPackedBool gcRunning; + JSPackedBool gcRegenShapes; + + /* + * During gc, if rt->gcRegenShapes && + * (scope->flags & JSScope::SHAPE_REGEN) == rt->gcRegenShapesScopeFlag, + * then the scope's shape has already been regenerated during this GC. + * To avoid having to sweep JSScopes, the bit's meaning toggles with each + * shape-regenerating GC. + * + * FIXME Once scopes are GC'd (bug 505004), this will be obsolete. + */ + uint8 gcRegenShapesScopeFlag; + +#ifdef JS_GC_ZEAL + jsrefcount gcZeal; +#endif + + JSGCCallback gcCallback; + + /* + * Malloc counter to measure memory pressure for GC scheduling. It runs + * from gcMaxMallocBytes down to zero. + */ + ptrdiff_t gcMallocBytes; + + /* + * Stack of GC arenas containing things that the GC marked, where children + * reached from those things have not yet been marked. This helps avoid + * using too much native stack during recursive GC marking. + */ + JSGCArenaInfo *gcUntracedArenaStackTop; +#ifdef DEBUG + size_t gcTraceLaterCount; +#endif + + /* + * Table for tracking iterators to ensure that we close iterator's state + * before finalizing the iterable object. + */ + js::Vector gcIteratorTable; + + /* + * The trace operation and its data argument to trace embedding-specific + * GC roots. + */ + JSTraceDataOp gcExtraRootsTraceOp; + void *gcExtraRootsData; + + /* + * Used to serialize cycle checks when setting __proto__ or __parent__ by + * requesting the GC handle the required cycle detection. If the GC hasn't + * been poked, it won't scan for garbage. This member is protected by + * rt->gcLock. + */ + JSSetSlotRequest *setSlotRequests; + + /* Well-known numbers held for use by this runtime's contexts. */ + jsval NaNValue; + jsval negativeInfinityValue; + jsval positiveInfinityValue; + +#ifdef JS_THREADSAFE + JSLock *deflatedStringCacheLock; +#endif + JSHashTable *deflatedStringCache; +#ifdef DEBUG + uint32 deflatedStringCacheBytes; +#endif + + JSString *emptyString; + + /* + * Builtin functions, lazily created and held for use by the trace recorder. + * + * This field would be #ifdef JS_TRACER, but XPConnect is compiled without + * -DJS_TRACER and includes this header. + */ + JSObject *builtinFunctions[JSBUILTIN_LIMIT]; + + /* List of active contexts sharing this runtime; protected by gcLock. */ + JSCList contextList; + + /* Per runtime debug hooks -- see jsprvtd.h and jsdbgapi.h. */ + JSDebugHooks globalDebugHooks; + +#ifdef JS_TRACER + /* True if any debug hooks not supported by the JIT are enabled. */ + bool debuggerInhibitsJIT() const { + return (globalDebugHooks.interruptHandler || + globalDebugHooks.callHook || + globalDebugHooks.objectHook); + } +#endif + + /* More debugging state, see jsdbgapi.c. */ + JSCList trapList; + JSCList watchPointList; + + /* Client opaque pointers */ + void *data; + +#ifdef JS_THREADSAFE + /* These combine to interlock the GC and new requests. */ + PRLock *gcLock; + PRCondVar *gcDone; + PRCondVar *requestDone; + uint32 requestCount; + JSThread *gcThread; + + /* Lock and owning thread pointer for JS_LOCK_RUNTIME. */ + PRLock *rtLock; +#ifdef DEBUG + jsword rtLockOwner; +#endif + + /* Used to synchronize down/up state change; protected by gcLock. */ + PRCondVar *stateChange; + + /* + * State for sharing single-threaded titles, once a second thread tries to + * lock a title. The titleSharingDone condvar is protected by rt->gcLock + * to minimize number of locks taken in JS_EndRequest. + * + * The titleSharingTodo linked list is likewise "global" per runtime, not + * one-list-per-context, to conserve space over all contexts, optimizing + * for the likely case that titles become shared rarely, and among a very + * small set of threads (contexts). + */ + PRCondVar *titleSharingDone; + JSTitle *titleSharingTodo; + +/* + * Magic terminator for the rt->titleSharingTodo linked list, threaded through + * title->u.link. This hack allows us to test whether a title is on the list + * by asking whether title->u.link is non-null. We use a large, likely bogus + * pointer here to distinguish this value from any valid u.count (small int) + * value. + */ +#define NO_TITLE_SHARING_TODO ((JSTitle *) 0xfeedbeef) + + /* + * Lock serializing trapList and watchPointList accesses, and count of all + * mutations to trapList and watchPointList made by debugger threads. To + * keep the code simple, we define debuggerMutations for the thread-unsafe + * case too. + */ + PRLock *debuggerLock; + + JSDHashTable threads; +#endif /* JS_THREADSAFE */ + uint32 debuggerMutations; + + /* + * Security callbacks set on the runtime are used by each context unless + * an override is set on the context. + */ + JSSecurityCallbacks *securityCallbacks; + + /* + * Shared scope property tree, and arena-pool for allocating its nodes. + * The propertyRemovals counter is incremented for every JSScope::clear, + * and for each JSScope::remove method call that frees a slot in an object. + * See js_NativeGet and js_NativeSet in jsobj.c. + */ + JSDHashTable propertyTreeHash; + JSScopeProperty *propertyFreeList; + JSArenaPool propertyArenaPool; + int32 propertyRemovals; + + /* Script filename table. */ + struct JSHashTable *scriptFilenameTable; + JSCList scriptFilenamePrefixes; +#ifdef JS_THREADSAFE + PRLock *scriptFilenameTableLock; +#endif + + /* Number localization, used by jsnum.c */ + const char *thousandsSeparator; + const char *decimalSeparator; + const char *numGrouping; + + /* + * Weak references to lazily-created, well-known XML singletons. + * + * NB: Singleton objects must be carefully disconnected from the rest of + * the object graph usually associated with a JSContext's global object, + * including the set of standard class objects. See jsxml.c for details. + */ + JSObject *anynameObject; + JSObject *functionNamespaceObject; + +#ifndef JS_THREADSAFE + JSThreadData threadData; + +#define JS_THREAD_DATA(cx) (&(cx)->runtime->threadData) +#endif + + /* + * Object shape (property cache structural type) identifier generator. + * + * Type 0 stands for the empty scope, and must not be regenerated due to + * uint32 wrap-around. Since js_GenerateShape (in jsinterp.cpp) uses + * atomic pre-increment, the initial value for the first typed non-empty + * scope will be 1. + * + * If this counter overflows into SHAPE_OVERFLOW_BIT (in jsinterp.h), the + * cache is disabled, to avoid aliasing two different types. It stays + * disabled until a triggered GC at some later moment compresses live + * types, minimizing rt->shapeGen in the process. + */ + volatile uint32 shapeGen; + + /* Literal table maintained by jsatom.c functions. */ + JSAtomState atomState; + +#ifdef JS_THREADSAFE + JSBackgroundThread *deallocatorThread; +#endif + + /* + * Various metering fields are defined at the end of JSRuntime. In this + * way there is no need to recompile all the code that refers to other + * fields of JSRuntime after enabling the corresponding metering macro. + */ +#ifdef JS_DUMP_ENUM_CACHE_STATS + int32 nativeEnumProbes; + int32 nativeEnumMisses; +# define ENUM_CACHE_METER(name) JS_ATOMIC_INCREMENT(&cx->runtime->name) +#else +# define ENUM_CACHE_METER(name) ((void) 0) +#endif + +#ifdef JS_DUMP_LOOP_STATS + /* Loop statistics, to trigger trace recording and compiling. */ + JSBasicStats loopStats; +#endif + +#ifdef DEBUG + /* Function invocation metering. */ + jsrefcount inlineCalls; + jsrefcount nativeCalls; + jsrefcount nonInlineCalls; + jsrefcount constructs; + + /* Title lock and scope property metering. */ + jsrefcount claimAttempts; + jsrefcount claimedTitles; + jsrefcount deadContexts; + jsrefcount deadlocksAvoided; + jsrefcount liveScopes; + jsrefcount sharedTitles; + jsrefcount totalScopes; + jsrefcount liveScopeProps; + jsrefcount liveScopePropsPreSweep; + jsrefcount totalScopeProps; + jsrefcount livePropTreeNodes; + jsrefcount duplicatePropTreeNodes; + jsrefcount totalPropTreeNodes; + jsrefcount propTreeKidsChunks; + + /* String instrumentation. */ + jsrefcount liveStrings; + jsrefcount totalStrings; + jsrefcount liveDependentStrings; + jsrefcount totalDependentStrings; + jsrefcount badUndependStrings; + double lengthSum; + double lengthSquaredSum; + double strdepLengthSum; + double strdepLengthSquaredSum; + + /* Script instrumentation. */ + jsrefcount liveScripts; + jsrefcount totalScripts; + jsrefcount liveEmptyScripts; + jsrefcount totalEmptyScripts; +#endif /* DEBUG */ + +#ifdef JS_SCOPE_DEPTH_METER + /* + * Stats on runtime prototype chain lookups and scope chain depths, i.e., + * counts of objects traversed on a chain until the wanted id is found. + */ + JSBasicStats protoLookupDepthStats; + JSBasicStats scopeSearchDepthStats; + + /* + * Stats on compile-time host environment and lexical scope chain lengths + * (maximum depths). + */ + JSBasicStats hostenvScopeDepthStats; + JSBasicStats lexicalScopeDepthStats; +#endif + +#ifdef JS_GCMETER + JSGCStats gcStats; +#endif + +#ifdef JS_FUNCTION_METERING + JSFunctionMeter functionMeter; + char lastScriptFilename[1024]; +#endif + + JSRuntime(); + ~JSRuntime(); + + bool init(uint32 maxbytes); + + void setGCTriggerFactor(uint32 factor); + void setGCLastBytes(size_t lastBytes); + + void* malloc(size_t bytes) { return ::js_malloc(bytes); } + + void* calloc(size_t bytes) { return ::js_calloc(bytes); } + + void* realloc(void* p, size_t bytes) { return ::js_realloc(p, bytes); } + + void free(void* p) { ::js_free(p); } + + bool isGCMallocLimitReached() const { return gcMallocBytes <= 0; } + + void resetGCMallocBytes() { gcMallocBytes = ptrdiff_t(gcMaxMallocBytes); } + + void setGCMaxMallocBytes(size_t value) { + /* + * For compatibility treat any value that exceeds PTRDIFF_T_MAX to + * mean that value. + */ + gcMaxMallocBytes = (ptrdiff_t(value) >= 0) ? value : size_t(-1) >> 1; + resetGCMallocBytes(); + } +}; + +/* Common macros to access thread-local caches in JSThread or JSRuntime. */ +#define JS_GSN_CACHE(cx) (JS_THREAD_DATA(cx)->gsnCache) +#define JS_PROPERTY_CACHE(cx) (JS_THREAD_DATA(cx)->propertyCache) +#define JS_TRACE_MONITOR(cx) (JS_THREAD_DATA(cx)->traceMonitor) +#define JS_SCRIPTS_TO_GC(cx) (JS_THREAD_DATA(cx)->scriptsToGC) + +#ifdef JS_EVAL_CACHE_METERING +# define EVAL_CACHE_METER(x) (JS_THREAD_DATA(cx)->evalCacheMeter.x++) +#else +# define EVAL_CACHE_METER(x) ((void) 0) +#endif + +#ifdef DEBUG +# define JS_RUNTIME_METER(rt, which) JS_ATOMIC_INCREMENT(&(rt)->which) +# define JS_RUNTIME_UNMETER(rt, which) JS_ATOMIC_DECREMENT(&(rt)->which) +#else +# define JS_RUNTIME_METER(rt, which) /* nothing */ +# define JS_RUNTIME_UNMETER(rt, which) /* nothing */ +#endif + +#define JS_KEEP_ATOMS(rt) JS_ATOMIC_INCREMENT(&(rt)->gcKeepAtoms); +#define JS_UNKEEP_ATOMS(rt) JS_ATOMIC_DECREMENT(&(rt)->gcKeepAtoms); + +#ifdef JS_ARGUMENT_FORMATTER_DEFINED +/* + * Linked list mapping format strings for JS_{Convert,Push}Arguments{,VA} to + * formatter functions. Elements are sorted in non-increasing format string + * length order. + */ +struct JSArgumentFormatMap { + const char *format; + size_t length; + JSArgumentFormatter formatter; + JSArgumentFormatMap *next; +}; +#endif + +struct JSStackHeader { + uintN nslots; + JSStackHeader *down; +}; + +#define JS_STACK_SEGMENT(sh) ((jsval *)(sh) + 2) + +/* + * Key and entry types for the JSContext.resolvingTable hash table, typedef'd + * here because all consumers need to see these declarations (and not just the + * typedef names, as would be the case for an opaque pointer-to-typedef'd-type + * declaration), along with cx->resolvingTable. + */ +typedef struct JSResolvingKey { + JSObject *obj; + jsid id; +} JSResolvingKey; + +typedef struct JSResolvingEntry { + JSDHashEntryHdr hdr; + JSResolvingKey key; + uint32 flags; +} JSResolvingEntry; + +#define JSRESFLAG_LOOKUP 0x1 /* resolving id from lookup */ +#define JSRESFLAG_WATCH 0x2 /* resolving id from watch */ + +/* + * Macros to push/pop JSTempValueRooter instances to context-linked stack of + * temporary GC roots. If you need to protect a result value that flows out of + * a C function across several layers of other functions, use the + * js_LeaveLocalRootScopeWithResult internal API (see further below) instead. + * + * The macros also provide a simple way to get a single rooted pointer via + * JS_PUSH_TEMP_ROOT_(cx, NULL, &tvr). Then &tvr.u. gives the + * necessary pointer. + * + * JSTempValueRooter.count defines the type of the rooted value referenced by + * JSTempValueRooter.u union of type JSTempValueUnion. When count is positive + * or zero, u.array points to a vector of jsvals. Otherwise it must be one of + * the following constants: + */ +#define JSTVU_SINGLE (-1) /* u.value or u. is single jsval + or non-JSString GC-thing pointer */ +#define JSTVU_TRACE (-2) /* u.trace is a hook to trace a custom + * structure */ +#define JSTVU_SPROP (-3) /* u.sprop roots property tree node */ +#define JSTVU_WEAK_ROOTS (-4) /* u.weakRoots points to saved weak roots */ +#define JSTVU_COMPILER (-5) /* u.compiler roots JSCompiler* */ +#define JSTVU_SCRIPT (-6) /* u.script roots JSScript* */ +#define JSTVU_ENUMERATOR (-7) /* a pointer to JSTempValueRooter points + to an instance of JSAutoEnumStateRooter + with u.object storing the enumeration + object */ + +/* + * Here single JSTVU_SINGLE covers both jsval and pointers to almost (see note + * below) any GC-thing via reinterpreting the thing as JSVAL_OBJECT. This works + * because the GC-thing is aligned on a 0 mod 8 boundary, and object has the 0 + * jsval tag. So any GC-heap-allocated thing pointer may be tagged as if it + * were an object and untagged, if it's then used only as an opaque pointer + * until discriminated by other means than tag bits. This is how, for example, + * js_GetGCThingTraceKind uses its |thing| parameter -- it consults GC-thing + * flags stored separately from the thing to decide the kind of thing. + * + * Note well that JSStrings may be statically allocated (see the intStringTable + * and unitStringTable static arrays), so this hack does not work for arbitrary + * GC-thing pointers. + */ +#define JS_PUSH_TEMP_ROOT_COMMON(cx,x,tvr,cnt,kind) \ + JS_BEGIN_MACRO \ + JS_ASSERT((cx)->tempValueRooters != (tvr)); \ + (tvr)->count = (cnt); \ + (tvr)->u.kind = (x); \ + (tvr)->down = (cx)->tempValueRooters; \ + (cx)->tempValueRooters = (tvr); \ + JS_END_MACRO + +#define JS_POP_TEMP_ROOT(cx,tvr) \ + JS_BEGIN_MACRO \ + JS_ASSERT((cx)->tempValueRooters == (tvr)); \ + (cx)->tempValueRooters = (tvr)->down; \ + JS_END_MACRO + +#define JS_PUSH_TEMP_ROOT(cx,cnt,arr,tvr) \ + JS_BEGIN_MACRO \ + JS_ASSERT((int)(cnt) >= 0); \ + JS_PUSH_TEMP_ROOT_COMMON(cx, arr, tvr, (ptrdiff_t) (cnt), array); \ + JS_END_MACRO + +#define JS_PUSH_SINGLE_TEMP_ROOT(cx,val,tvr) \ + JS_PUSH_TEMP_ROOT_COMMON(cx, val, tvr, JSTVU_SINGLE, value) + +#define JS_PUSH_TEMP_ROOT_OBJECT(cx,obj,tvr) \ + JS_PUSH_TEMP_ROOT_COMMON(cx, obj, tvr, JSTVU_SINGLE, object) + +#define JS_PUSH_TEMP_ROOT_STRING(cx,str,tvr) \ + JS_PUSH_SINGLE_TEMP_ROOT(cx, str ? STRING_TO_JSVAL(str) : JSVAL_NULL, tvr) + +#define JS_PUSH_TEMP_ROOT_XML(cx,xml_,tvr) \ + JS_PUSH_TEMP_ROOT_COMMON(cx, xml_, tvr, JSTVU_SINGLE, xml) + +#define JS_PUSH_TEMP_ROOT_TRACE(cx,trace_,tvr) \ + JS_PUSH_TEMP_ROOT_COMMON(cx, trace_, tvr, JSTVU_TRACE, trace) + +#define JS_PUSH_TEMP_ROOT_SPROP(cx,sprop_,tvr) \ + JS_PUSH_TEMP_ROOT_COMMON(cx, sprop_, tvr, JSTVU_SPROP, sprop) + +#define JS_PUSH_TEMP_ROOT_WEAK_COPY(cx,weakRoots_,tvr) \ + JS_PUSH_TEMP_ROOT_COMMON(cx, weakRoots_, tvr, JSTVU_WEAK_ROOTS, weakRoots) + +#define JS_PUSH_TEMP_ROOT_COMPILER(cx,pc,tvr) \ + JS_PUSH_TEMP_ROOT_COMMON(cx, pc, tvr, JSTVU_COMPILER, compiler) + +#define JS_PUSH_TEMP_ROOT_SCRIPT(cx,script_,tvr) \ + JS_PUSH_TEMP_ROOT_COMMON(cx, script_, tvr, JSTVU_SCRIPT, script) + +#define JSRESOLVE_INFER 0xffff /* infer bits from current bytecode */ + +struct JSContext { + /* + * If this flag is set, we were asked to call back the operation callback + * as soon as possible. + */ + volatile jsint operationCallbackFlag; + + /* JSRuntime contextList linkage. */ + JSCList link; + +#if JS_HAS_XML_SUPPORT + /* + * Bit-set formed from binary exponentials of the XML_* tiny-ids defined + * for boolean settings in jsxml.c, plus an XSF_CACHE_VALID bit. Together + * these act as a cache of the boolean XML.ignore* and XML.prettyPrinting + * property values associated with this context's global object. + */ + uint8 xmlSettingFlags; + uint8 padding; +#else + uint16 padding; +#endif + + /* + * Classic Algol "display" static link optimization. + */ +#define JS_DISPLAY_SIZE 16U + + JSStackFrame *display[JS_DISPLAY_SIZE]; + + /* Runtime version control identifier. */ + uint16 version; + + /* Per-context options. */ + uint32 options; /* see jsapi.h for JSOPTION_* */ + + /* Locale specific callbacks for string conversion. */ + JSLocaleCallbacks *localeCallbacks; + + /* + * cx->resolvingTable is non-null and non-empty if we are initializing + * standard classes lazily, or if we are otherwise recursing indirectly + * from js_LookupProperty through a JSClass.resolve hook. It is used to + * limit runaway recursion (see jsapi.c and jsobj.c). + */ + JSDHashTable *resolvingTable; + + /* + * True if generating an error, to prevent runaway recursion. + * NB: generatingError packs with insideGCMarkCallback and throwing below. + */ + JSPackedBool generatingError; + + /* Flag to indicate that we run inside gcCallback(cx, JSGC_MARK_END). */ + JSPackedBool insideGCMarkCallback; + + /* Exception state -- the exception member is a GC root by definition. */ + JSPackedBool throwing; /* is there a pending exception? */ + jsval exception; /* most-recently-thrown exception */ + + /* Limit pointer for checking native stack consumption during recursion. */ + jsuword stackLimit; + + /* Quota on the size of arenas used to compile and execute scripts. */ + size_t scriptStackQuota; + + /* Data shared by threads in an address space. */ + JSRuntime * const runtime; + + explicit JSContext(JSRuntime *rt) : runtime(rt) {} + + /* Stack arena pool and frame pointer register. */ + JS_REQUIRES_STACK + JSArenaPool stackPool; + + JS_REQUIRES_STACK + JSStackFrame *fp; + + /* Temporary arena pool used while compiling and decompiling. */ + JSArenaPool tempPool; + + /* Top-level object and pointer to top stack frame's scope chain. */ + JSObject *globalObject; + + /* Storage to root recently allocated GC things and script result. */ + JSWeakRoots weakRoots; + + /* Regular expression class statics (XXX not shared globally). */ + JSRegExpStatics regExpStatics; + + /* State for object and array toSource conversion. */ + JSSharpObjectMap sharpObjectMap; + JSHashTable *busyArrayTable; + + /* Argument formatter support for JS_{Convert,Push}Arguments{,VA}. */ + JSArgumentFormatMap *argumentFormatMap; + + /* Last message string and trace file for debugging. */ + char *lastMessage; +#ifdef DEBUG + void *tracefp; + jsbytecode *tracePrevPc; +#endif + + /* Per-context optional error reporter. */ + JSErrorReporter errorReporter; + + /* Branch callback. */ + JSOperationCallback operationCallback; + + /* Interpreter activation count. */ + uintN interpLevel; + + /* Client opaque pointers. */ + void *data; + void *data2; + + /* GC and thread-safe state. */ + JSStackFrame *dormantFrameChain; /* dormant stack frame to scan */ +#ifdef JS_THREADSAFE + JSThread *thread; + jsrefcount requestDepth; + /* Same as requestDepth but ignoring JS_SuspendRequest/JS_ResumeRequest */ + jsrefcount outstandingRequests; + JSTitle *lockedSealedTitle; /* weak ref, for low-cost sealed + title locking */ + JSCList threadLinks; /* JSThread contextList linkage */ + +#define CX_FROM_THREAD_LINKS(tl) \ + ((JSContext *)((char *)(tl) - offsetof(JSContext, threadLinks))) +#endif + + /* PDL of stack headers describing stack slots not rooted by argv, etc. */ + JSStackHeader *stackHeaders; + + /* Stack of thread-stack-allocated temporary GC roots. */ + JSTempValueRooter *tempValueRooters; + + /* Debug hooks associated with the current context. */ + const JSDebugHooks *debugHooks; + + /* Security callbacks that override any defined on the runtime. */ + JSSecurityCallbacks *securityCallbacks; + + /* Pinned regexp pool used for regular expressions. */ + JSArenaPool regexpPool; + + /* Stored here to avoid passing it around as a parameter. */ + uintN resolveFlags; + +#ifdef JS_TRACER + /* + * State for the current tree execution. bailExit is valid if the tree has + * called back into native code via a _FAIL builtin and has not yet bailed, + * else garbage (NULL in debug builds). + */ + InterpState *interpState; + VMSideExit *bailExit; + + /* + * True if traces may be executed. Invariant: The value of jitEnabled is + * always equal to the expression in updateJITEnabled below. + * + * This flag and the fields accessed by updateJITEnabled are written only + * in runtime->gcLock, to avoid race conditions that would leave the wrong + * value in jitEnabled. (But the interpreter reads this without + * locking. That can race against another thread setting debug hooks, but + * we always read cx->debugHooks without locking anyway.) + */ + bool jitEnabled; +#endif + + /* Caller must be holding runtime->gcLock. */ + void updateJITEnabled() { +#ifdef JS_TRACER + jitEnabled = ((options & JSOPTION_JIT) && + !runtime->debuggerInhibitsJIT() && + debugHooks == &runtime->globalDebugHooks); +#endif + } + +#ifdef JS_THREADSAFE + inline void createDeallocatorTask() { + JS_ASSERT(!thread->deallocatorTask); + if (runtime->deallocatorThread && !runtime->deallocatorThread->busy()) + thread->deallocatorTask = new JSFreePointerListTask(); + } + + inline void submitDeallocatorTask() { + if (thread->deallocatorTask) { + runtime->deallocatorThread->schedule(thread->deallocatorTask); + thread->deallocatorTask = NULL; + } + } +#endif + + ptrdiff_t &getMallocCounter() { +#ifdef JS_THREADSAFE + return thread->gcThreadMallocBytes; +#else + return runtime->gcMallocBytes; +#endif + } + + /* + * Call this after allocating memory held by GC things, to update memory + * pressure counters or report the OOM error if necessary. + */ + inline void updateMallocCounter(void *p, size_t nbytes) { + JS_ASSERT(ptrdiff_t(nbytes) >= 0); + ptrdiff_t &counter = getMallocCounter(); + counter -= ptrdiff_t(nbytes); + if (!p || counter <= 0) + checkMallocGCPressure(p); + } + + /* + * Call this after successfully allocating memory held by GC things, to + * update memory pressure counters. + */ + inline void updateMallocCounter(size_t nbytes) { + JS_ASSERT(ptrdiff_t(nbytes) >= 0); + ptrdiff_t &counter = getMallocCounter(); + counter -= ptrdiff_t(nbytes); + if (counter <= 0) { + /* + * Use 1 as an arbitrary non-null pointer indicating successful + * allocation. + */ + checkMallocGCPressure(reinterpret_cast(jsuword(1))); + } + } + + inline void* malloc(size_t bytes) { + JS_ASSERT(bytes != 0); + void *p = runtime->malloc(bytes); + updateMallocCounter(p, bytes); + return p; + } + + inline void* mallocNoReport(size_t bytes) { + JS_ASSERT(bytes != 0); + void *p = runtime->malloc(bytes); + if (!p) + return NULL; + updateMallocCounter(bytes); + return p; + } + + inline void* calloc(size_t bytes) { + JS_ASSERT(bytes != 0); + void *p = runtime->calloc(bytes); + updateMallocCounter(p, bytes); + return p; + } + + inline void* realloc(void* p, size_t bytes) { + void *orig = p; + p = runtime->realloc(p, bytes); + + /* + * For compatibility we do not account for realloc that increases + * previously allocated memory. + */ + updateMallocCounter(p, orig ? 0 : bytes); + return p; + } + +#ifdef JS_THREADSAFE + inline void free(void* p) { + if (!p) + return; + if (thread) { + JSFreePointerListTask* task = thread->deallocatorTask; + if (task) { + task->add(p); + return; + } + } + runtime->free(p); + } +#else + inline void free(void* p) { + if (!p) + return; + runtime->free(p); + } +#endif + + /* + * In the common case that we'd like to allocate the memory for an object + * with cx->malloc/free, we cannot use overloaded C++ operators (no + * placement delete). Factor the common workaround into one place. + */ +#define CREATE_BODY(parms) \ + void *memory = this->malloc(sizeof(T)); \ + if (!memory) \ + return NULL; \ + return new(memory) T parms; + + template + JS_ALWAYS_INLINE T *create() { + CREATE_BODY(()) + } + + template + JS_ALWAYS_INLINE T *create(const P1 &p1) { + CREATE_BODY((p1)) + } + + template + JS_ALWAYS_INLINE T *create(const P1 &p1, const P2 &p2) { + CREATE_BODY((p1, p2)) + } + + template + JS_ALWAYS_INLINE T *create(const P1 &p1, const P2 &p2, const P3 &p3) { + CREATE_BODY((p1, p2, p3)) + } +#undef CREATE_BODY + + template + JS_ALWAYS_INLINE void destroy(T *p) { + p->~T(); + this->free(p); + } + +private: + + /* + * The allocation code calls the function to indicate either OOM failure + * when p is null or that a memory pressure counter has reached some + * threshold when p is not null. The function takes the pointer and not + * a boolean flag to minimize the amount of code in its inlined callers. + */ + void checkMallocGCPressure(void *p); +}; + +#ifdef JS_THREADSAFE +# define JS_THREAD_ID(cx) ((cx)->thread ? (cx)->thread->id : 0) +#endif + +#ifdef __cplusplus + +static inline JSAtom ** +FrameAtomBase(JSContext *cx, JSStackFrame *fp) +{ + return fp->imacpc + ? COMMON_ATOMS_START(&cx->runtime->atomState) + : fp->script->atomMap.vector; +} + +/* FIXME(bug 332648): Move this into a public header. */ +class JSAutoTempValueRooter +{ + public: + JSAutoTempValueRooter(JSContext *cx, size_t len, jsval *vec + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : mContext(cx) { + JS_GUARD_OBJECT_NOTIFIER_INIT; + JS_PUSH_TEMP_ROOT(mContext, len, vec, &mTvr); + } + explicit JSAutoTempValueRooter(JSContext *cx, jsval v = JSVAL_NULL + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : mContext(cx) { + JS_GUARD_OBJECT_NOTIFIER_INIT; + JS_PUSH_SINGLE_TEMP_ROOT(mContext, v, &mTvr); + } + JSAutoTempValueRooter(JSContext *cx, JSString *str + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : mContext(cx) { + JS_GUARD_OBJECT_NOTIFIER_INIT; + JS_PUSH_TEMP_ROOT_STRING(mContext, str, &mTvr); + } + JSAutoTempValueRooter(JSContext *cx, JSObject *obj + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : mContext(cx) { + JS_GUARD_OBJECT_NOTIFIER_INIT; + JS_PUSH_TEMP_ROOT_OBJECT(mContext, obj, &mTvr); + } + JSAutoTempValueRooter(JSContext *cx, JSScopeProperty *sprop + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : mContext(cx) { + JS_GUARD_OBJECT_NOTIFIER_INIT; + JS_PUSH_TEMP_ROOT_SPROP(mContext, sprop, &mTvr); + } + + ~JSAutoTempValueRooter() { + JS_POP_TEMP_ROOT(mContext, &mTvr); + } + + jsval value() { return mTvr.u.value; } + jsval *addr() { return &mTvr.u.value; } + + protected: + JSContext *mContext; + + private: +#ifndef AIX + static void *operator new(size_t); + static void operator delete(void *, size_t); +#endif + + JSTempValueRooter mTvr; + JS_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +class JSAutoTempIdRooter +{ + public: + explicit JSAutoTempIdRooter(JSContext *cx, jsid id = INT_TO_JSID(0) + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : mContext(cx) { + JS_GUARD_OBJECT_NOTIFIER_INIT; + JS_PUSH_SINGLE_TEMP_ROOT(mContext, ID_TO_VALUE(id), &mTvr); + } + + ~JSAutoTempIdRooter() { + JS_POP_TEMP_ROOT(mContext, &mTvr); + } + + jsid id() { return (jsid) mTvr.u.value; } + jsid * addr() { return (jsid *) &mTvr.u.value; } + + private: + JSContext *mContext; + JSTempValueRooter mTvr; + JS_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +class JSAutoIdArray { + public: + JSAutoIdArray(JSContext *cx, JSIdArray *ida + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : cx(cx), idArray(ida) { + JS_GUARD_OBJECT_NOTIFIER_INIT; + if (ida) + JS_PUSH_TEMP_ROOT(cx, ida->length, ida->vector, &tvr); + } + ~JSAutoIdArray() { + if (idArray) { + JS_POP_TEMP_ROOT(cx, &tvr); + JS_DestroyIdArray(cx, idArray); + } + } + bool operator!() { + return idArray == NULL; + } + jsid operator[](size_t i) const { + JS_ASSERT(idArray); + JS_ASSERT(i < size_t(idArray->length)); + return idArray->vector[i]; + } + size_t length() const { + return idArray->length; + } + private: + JSContext * const cx; + JSIdArray * const idArray; + JSTempValueRooter tvr; + JS_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +/* The auto-root for enumeration object and its state. */ +class JSAutoEnumStateRooter : public JSTempValueRooter +{ + public: + JSAutoEnumStateRooter(JSContext *cx, JSObject *obj, jsval *statep + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : mContext(cx), mStatep(statep) + { + JS_GUARD_OBJECT_NOTIFIER_INIT; + JS_ASSERT(obj); + JS_ASSERT(statep); + JS_PUSH_TEMP_ROOT_COMMON(cx, obj, this, JSTVU_ENUMERATOR, object); + } + + ~JSAutoEnumStateRooter() { + JS_POP_TEMP_ROOT(mContext, this); + } + + void mark(JSTracer *trc) { + JS_CALL_OBJECT_TRACER(trc, u.object, "enumerator_obj"); + js_MarkEnumeratorState(trc, u.object, *mStatep); + } + + private: + JSContext *mContext; + jsval *mStatep; + JS_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +class JSAutoResolveFlags +{ + public: + JSAutoResolveFlags(JSContext *cx, uintN flags + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : mContext(cx), mSaved(cx->resolveFlags) { + JS_GUARD_OBJECT_NOTIFIER_INIT; + cx->resolveFlags = flags; + } + + ~JSAutoResolveFlags() { mContext->resolveFlags = mSaved; } + + private: + JSContext *mContext; + uintN mSaved; + JS_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +#endif /* __cpluscplus */ + +/* + * Slightly more readable macros for testing per-context option settings (also + * to hide bitset implementation detail). + * + * JSOPTION_XML must be handled specially in order to propagate from compile- + * to run-time (from cx->options to script->version/cx->version). To do that, + * we copy JSOPTION_XML from cx->options into cx->version as JSVERSION_HAS_XML + * whenever options are set, and preserve this XML flag across version number + * changes done via the JS_SetVersion API. + * + * But when executing a script or scripted function, the interpreter changes + * cx->version, including the XML flag, to script->version. Thus JSOPTION_XML + * is a compile-time option that causes a run-time version change during each + * activation of the compiled script. That version change has the effect of + * changing JS_HAS_XML_OPTION, so that any compiling done via eval enables XML + * support. If an XML-enabled script or function calls a non-XML function, + * the flag bit will be cleared during the callee's activation. + * + * Note that JS_SetVersion API calls never pass JSVERSION_HAS_XML or'd into + * that API's version parameter. + * + * Note also that script->version must contain this XML option flag in order + * for XDR'ed scripts to serialize and deserialize with that option preserved + * for detection at run-time. We can't copy other compile-time options into + * script->version because that would break backward compatibility (certain + * other options, e.g. JSOPTION_VAROBJFIX, are analogous to JSOPTION_XML). + */ +#define JS_HAS_OPTION(cx,option) (((cx)->options & (option)) != 0) +#define JS_HAS_STRICT_OPTION(cx) JS_HAS_OPTION(cx, JSOPTION_STRICT) +#define JS_HAS_WERROR_OPTION(cx) JS_HAS_OPTION(cx, JSOPTION_WERROR) +#define JS_HAS_COMPILE_N_GO_OPTION(cx) JS_HAS_OPTION(cx, JSOPTION_COMPILE_N_GO) +#define JS_HAS_ATLINE_OPTION(cx) JS_HAS_OPTION(cx, JSOPTION_ATLINE) + +#define JSVERSION_MASK 0x0FFF /* see JSVersion in jspubtd.h */ +#define JSVERSION_HAS_XML 0x1000 /* flag induced by XML option */ +#define JSVERSION_ANONFUNFIX 0x2000 /* see jsapi.h, the comments + for JSOPTION_ANONFUNFIX */ + +#define JSVERSION_NUMBER(cx) ((JSVersion)((cx)->version & \ + JSVERSION_MASK)) +#define JS_HAS_XML_OPTION(cx) ((cx)->version & JSVERSION_HAS_XML || \ + JSVERSION_NUMBER(cx) >= JSVERSION_1_6) + +extern JSThreadData * +js_CurrentThreadData(JSRuntime *rt); + +extern JSBool +js_InitThreads(JSRuntime *rt); + +extern void +js_FinishThreads(JSRuntime *rt); + +extern void +js_PurgeThreads(JSContext *cx); + +extern void +js_TraceThreads(JSRuntime *rt, JSTracer *trc); + +/* + * Ensures the JSOPTION_XML and JSOPTION_ANONFUNFIX bits of cx->options are + * reflected in cx->version, since each bit must travel with a script that has + * it set. + */ +extern void +js_SyncOptionsToVersion(JSContext *cx); + +/* + * Common subroutine of JS_SetVersion and js_SetVersion, to update per-context + * data that depends on version. + */ +extern void +js_OnVersionChange(JSContext *cx); + +/* + * Unlike the JS_SetVersion API, this function stores JSVERSION_HAS_XML and + * any future non-version-number flags induced by compiler options. + */ +extern void +js_SetVersion(JSContext *cx, JSVersion version); + +/* + * Create and destroy functions for JSContext, which is manually allocated + * and exclusively owned. + */ +extern JSContext * +js_NewContext(JSRuntime *rt, size_t stackChunkSize); + +extern void +js_DestroyContext(JSContext *cx, JSDestroyContextMode mode); + +/* + * Return true if cx points to a context in rt->contextList, else return false. + * NB: the caller (see jslock.c:ClaimTitle) must hold rt->gcLock. + */ +extern JSBool +js_ValidContextPointer(JSRuntime *rt, JSContext *cx); + +static JS_INLINE JSContext * +js_ContextFromLinkField(JSCList *link) +{ + JS_ASSERT(link); + return (JSContext *) ((uint8 *) link - offsetof(JSContext, link)); +} + +/* + * If unlocked, acquire and release rt->gcLock around *iterp update; otherwise + * the caller must be holding rt->gcLock. + */ +extern JSContext * +js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp); + +/* + * Iterate through contexts with active requests. The caller must be holding + * rt->gcLock in case of a thread-safe build, or otherwise guarantee that the + * context list is not alternated asynchroniously. + */ +extern JS_FRIEND_API(JSContext *) +js_NextActiveContext(JSRuntime *, JSContext *); + +#ifdef JS_THREADSAFE + +/* + * Count the number of contexts entered requests on the current thread. + */ +uint32 +js_CountThreadRequests(JSContext *cx); + +/* + * This is a helper for code at can potentially run outside JS request to + * ensure that the GC is not running when the function returns. + * + * This function must be called with the GC lock held. + */ +extern void +js_WaitForGC(JSRuntime *rt); + +/* + * If we're in one or more requests (possibly on more than one context) + * running on the current thread, indicate, temporarily, that all these + * requests are inactive so a possible GC can proceed on another thread. + * This function returns the number of discounted requests. The number must + * be passed later to js_ActivateRequestAfterGC to reactivate the requests. + * + * This function must be called with the GC lock held. + */ +uint32 +js_DiscountRequestsForGC(JSContext *cx); + +/* + * This function must be called with the GC lock held. + */ +void +js_RecountRequestsAfterGC(JSRuntime *rt, uint32 requestDebit); + +#else /* !JS_THREADSAFE */ + +# define js_WaitForGC(rt) ((void) 0) + +#endif + +/* + * JSClass.resolve and watchpoint recursion damping machinery. + */ +extern JSBool +js_StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag, + JSResolvingEntry **entryp); + +extern void +js_StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag, + JSResolvingEntry *entry, uint32 generation); + +/* + * Local root set management. + * + * NB: the jsval parameters below may be properly tagged jsvals, or GC-thing + * pointers cast to (jsval). This relies on JSObject's tag being zero, but + * on the up side it lets us push int-jsval-encoded scopeMark values on the + * local root stack. + */ +extern JSBool +js_EnterLocalRootScope(JSContext *cx); + +#define js_LeaveLocalRootScope(cx) \ + js_LeaveLocalRootScopeWithResult(cx, JSVAL_NULL) + +extern void +js_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval); + +extern void +js_ForgetLocalRoot(JSContext *cx, jsval v); + +extern int +js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, jsval v); + +/* + * Report an exception, which is currently realized as a printf-style format + * string and its arguments. + */ +typedef enum JSErrNum { +#define MSG_DEF(name, number, count, exception, format) \ + name = number, +#include "js.msg" +#undef MSG_DEF + JSErr_Limit +} JSErrNum; + +extern JS_FRIEND_API(const JSErrorFormatString *) +js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber); + +#ifdef va_start +extern JSBool +js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap); + +extern JSBool +js_ReportErrorNumberVA(JSContext *cx, uintN flags, JSErrorCallback callback, + void *userRef, const uintN errorNumber, + JSBool charArgs, va_list ap); + +extern JSBool +js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback, + void *userRef, const uintN errorNumber, + char **message, JSErrorReport *reportp, + bool charArgs, va_list ap); +#endif + +extern void +js_ReportOutOfMemory(JSContext *cx); + +/* + * Report that cx->scriptStackQuota is exhausted. + */ +extern void +js_ReportOutOfScriptQuota(JSContext *cx); + +extern void +js_ReportOverRecursed(JSContext *cx); + +extern void +js_ReportAllocationOverflow(JSContext *cx); + +#define JS_CHECK_RECURSION(cx, onerror) \ + JS_BEGIN_MACRO \ + int stackDummy_; \ + \ + if (!JS_CHECK_STACK_SIZE(cx, stackDummy_)) { \ + js_ReportOverRecursed(cx); \ + onerror; \ + } \ + JS_END_MACRO + +/* + * Report an exception using a previously composed JSErrorReport. + * XXXbe remove from "friend" API + */ +extern JS_FRIEND_API(void) +js_ReportErrorAgain(JSContext *cx, const char *message, JSErrorReport *report); + +extern void +js_ReportIsNotDefined(JSContext *cx, const char *name); + +/* + * Report an attempt to access the property of a null or undefined value (v). + */ +extern JSBool +js_ReportIsNullOrUndefined(JSContext *cx, intN spindex, jsval v, + JSString *fallback); + +extern void +js_ReportMissingArg(JSContext *cx, jsval *vp, uintN arg); + +/* + * Report error using js_DecompileValueGenerator(cx, spindex, v, fallback) as + * the first argument for the error message. If the error message has less + * then 3 arguments, use null for arg1 or arg2. + */ +extern JSBool +js_ReportValueErrorFlags(JSContext *cx, uintN flags, const uintN errorNumber, + intN spindex, jsval v, JSString *fallback, + const char *arg1, const char *arg2); + +#define js_ReportValueError(cx,errorNumber,spindex,v,fallback) \ + ((void)js_ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber, \ + spindex, v, fallback, NULL, NULL)) + +#define js_ReportValueError2(cx,errorNumber,spindex,v,fallback,arg1) \ + ((void)js_ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber, \ + spindex, v, fallback, arg1, NULL)) + +#define js_ReportValueError3(cx,errorNumber,spindex,v,fallback,arg1,arg2) \ + ((void)js_ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber, \ + spindex, v, fallback, arg1, arg2)) + +extern JSErrorFormatString js_ErrorFormatString[JSErr_Limit]; + +/* + * See JS_SetThreadStackLimit in jsapi.c, where we check that the stack grows + * in the expected direction. On Unix-y systems, JS_STACK_GROWTH_DIRECTION is + * computed on the build host by jscpucfg.c and written into jsautocfg.h. The + * macro is hardcoded in jscpucfg.h on Windows and Mac systems (for historical + * reasons pre-dating autoconf usage). + */ +#if JS_STACK_GROWTH_DIRECTION > 0 +# define JS_CHECK_STACK_SIZE(cx, lval) ((jsuword)&(lval) < (cx)->stackLimit) +#else +# define JS_CHECK_STACK_SIZE(cx, lval) ((jsuword)&(lval) > (cx)->stackLimit) +#endif + +/* + * If the operation callback flag was set, call the operation callback. + * This macro can run the full GC. Return true if it is OK to continue and + * false otherwise. + */ +#define JS_CHECK_OPERATION_LIMIT(cx) \ + (!(cx)->operationCallbackFlag || js_InvokeOperationCallback(cx)) + +/* + * Invoke the operation callback and return false if the current execution + * is to be terminated. + */ +extern JSBool +js_InvokeOperationCallback(JSContext *cx); + +#ifndef JS_THREADSAFE +# define js_TriggerAllOperationCallbacks(rt, gcLocked) \ + js_TriggerAllOperationCallbacks (rt) +#endif + +void +js_TriggerAllOperationCallbacks(JSRuntime *rt, JSBool gcLocked); + +extern JSStackFrame * +js_GetScriptedCaller(JSContext *cx, JSStackFrame *fp); + +extern jsbytecode* +js_GetCurrentBytecodePC(JSContext* cx); + +extern bool +js_CurrentPCIsInImacro(JSContext *cx); + +#ifdef JS_TRACER +/* + * Reconstruct the JS stack and clear cx->tracecx. We must be currently in a + * _FAIL builtin from trace on cx or another context on the same thread. The + * machine code for the trace remains on the C stack when js_DeepBail returns. + * + * Implemented in jstracer.cpp. + */ +JS_FORCES_STACK JS_FRIEND_API(void) +js_DeepBail(JSContext *cx); +#endif + +static JS_FORCES_STACK JS_INLINE void +js_LeaveTrace(JSContext *cx) +{ +#ifdef JS_TRACER + if (JS_ON_TRACE(cx)) + js_DeepBail(cx); +#endif +} + +static JS_INLINE void +js_LeaveTraceIfGlobalObject(JSContext *cx, JSObject *obj) +{ + if (!obj->fslots[JSSLOT_PARENT]) + js_LeaveTrace(cx); +} + +static JS_INLINE JSBool +js_CanLeaveTrace(JSContext *cx) +{ + JS_ASSERT(JS_ON_TRACE(cx)); +#ifdef JS_TRACER + return cx->bailExit != NULL; +#else + return JS_FALSE; +#endif +} + +/* + * Get the current cx->fp, first lazily instantiating stack frames if needed. + * (Do not access cx->fp directly except in JS_REQUIRES_STACK code.) + * + * Defined in jstracer.cpp if JS_TRACER is defined. + */ +static JS_FORCES_STACK JS_INLINE JSStackFrame * +js_GetTopStackFrame(JSContext *cx) +{ + js_LeaveTrace(cx); + return cx->fp; +} + +static JS_INLINE JSBool +js_IsPropertyCacheDisabled(JSContext *cx) +{ + return cx->runtime->shapeGen >= SHAPE_OVERFLOW_BIT; +} + +static JS_INLINE uint32 +js_RegenerateShapeForGC(JSContext *cx) +{ + JS_ASSERT(cx->runtime->gcRunning); + JS_ASSERT(cx->runtime->gcRegenShapes); + + /* + * Under the GC, compared with js_GenerateShape, we don't need to use + * atomic increments but we still must make sure that after an overflow + * the shape stays such. + */ + uint32 shape = cx->runtime->shapeGen; + shape = (shape + 1) | (shape & SHAPE_OVERFLOW_BIT); + cx->runtime->shapeGen = shape; + return shape; +} + +namespace js { + +/* + * Policy that calls JSContext:: memory functions and reports errors to the + * context. Since the JSContext* given on construction is stored for the + * lifetime of the container, this policy may only be used for containers whose + * lifetime is a shorter than the given JSContext. + */ +class ContextAllocPolicy +{ + JSContext *mCx; + + public: + ContextAllocPolicy(JSContext *cx) : mCx(cx) {} + JSContext *context() const { return mCx; } + + void *malloc(size_t bytes) { return mCx->malloc(bytes); } + void free(void *p) { mCx->free(p); } + void *realloc(void *p, size_t bytes) { return mCx->realloc(p, bytes); } + void reportAllocOverflow() const { js_ReportAllocationOverflow(mCx); } +}; + +} + +#endif /* jscntxt_h___ */ diff --git a/ape-server/deps/js/src/jscompat.h b/ape-server/deps/js/src/jscompat.h new file mode 100755 index 0000000..dc200ee --- /dev/null +++ b/ape-server/deps/js/src/jscompat.h @@ -0,0 +1,56 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jscompat_h___ +#define jscompat_h___ +/* + * Compatibility glue for various NSPR versions. We must always define int8, + * int16, jsword, and so on to minimize differences with js/ref, no matter what + * the NSPR typedef names may be. + */ +#include "jstypes.h" +#include "jslong.h" + +typedef JSIntn intN; +typedef JSUintn uintN; +typedef JSUword jsuword; +typedef JSWord jsword; +typedef float float32; +#define allocPriv allocPool +#endif /* jscompat_h___ */ diff --git a/ape-server/deps/js/src/jsconfig.mk b/ape-server/deps/js/src/jsconfig.mk new file mode 100755 index 0000000..0ee3b74 --- /dev/null +++ b/ape-server/deps/js/src/jsconfig.mk @@ -0,0 +1,169 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998-1999 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +ifndef OBJDIR + ifdef OBJDIR_NAME + OBJDIR = $(OBJDIR_NAME) + endif +endif + +NSPR_VERSION = v4.0 +NSPR_LIBSUFFIX = 4 + +NSPR_LOCAL = $(MOZ_DEPTH)/dist/$(OBJDIR)/nspr +NSPR_DIST = $(MOZ_DEPTH)/dist/$(OBJDIR) +NSPR_OBJDIR = $(OBJDIR) +ifeq ($(OS_ARCH), SunOS) + NSPR_OBJDIR := $(subst _sparc,,$(NSPR_OBJDIR)) +endif +ifeq ($(OS_ARCH), Linux) + LINUX_REL := $(shell uname -r) + ifneq (,$(findstring 2.0,$(LINUX_REL))) + NSPR_OBJDIR := $(subst _All,2.0_x86_glibc_PTH,$(NSPR_OBJDIR)) + else + NSPR_OBJDIR := $(subst _All,2.2_x86_glibc_PTH,$(NSPR_OBJDIR)) + endif +endif +ifeq ($(OS_ARCH), AIX) + NSPR_OBJDIR := $(subst 4.1,4.2,$(NSPR_OBJDIR)) +endif +ifeq ($(OS_CONFIG), IRIX6.2) + NSPR_OBJDIR := $(subst 6.2,6.2_n32_PTH,$(NSPR_OBJDIR)) +endif +ifeq ($(OS_CONFIG), IRIX6.5) + NSPR_OBJDIR := $(subst 6.5,6.5_n32_PTH,$(NSPR_OBJDIR)) +endif +ifeq ($(OS_ARCH), WINNT) + ifeq ($(OBJDIR), WIN32_D.OBJ) + NSPR_OBJDIR = WINNT4.0_DBG.OBJ + endif + ifeq ($(OBJDIR), WIN32_O.OBJ) + NSPR_OBJDIR = WINNT4.0_OPT.OBJ + endif +endif +NSPR_SHARED = /share/builds/components/nspr20/$(NSPR_VERSION)/$(NSPR_OBJDIR) +ifeq ($(OS_ARCH), WINNT) + NSPR_SHARED = nspr20/$(NSPR_VERSION)/$(NSPR_OBJDIR) +endif +NSPR_VERSIONFILE = $(NSPR_LOCAL)/Version +NSPR_CURVERSION := $(shell cat $(NSPR_VERSIONFILE) 2>/dev/null) + +get_nspr: + @echo "Grabbing NSPR component..." +ifeq ($(NSPR_VERSION), $(NSPR_CURVERSION)) + @echo "No need, NSPR is up to date in this tree (ver=$(NSPR_VERSION))." +else + mkdir -p $(NSPR_LOCAL) + mkdir -p $(NSPR_DIST) + ifneq ($(OS_ARCH), WINNT) + cp $(NSPR_SHARED)/*.jar $(NSPR_LOCAL) + else + sh $(MOZ_DEPTH)/../reltools/compftp.sh $(NSPR_SHARED) $(NSPR_LOCAL) *.jar + endif + unzip -o $(NSPR_LOCAL)/mdbinary.jar -d $(NSPR_DIST) + mkdir -p $(NSPR_DIST)/include + unzip -o $(NSPR_LOCAL)/mdheader.jar -d $(NSPR_DIST)/include + rm -rf $(NSPR_DIST)/META-INF + rm -rf $(NSPR_DIST)/include/META-INF + echo $(NSPR_VERSION) > $(NSPR_VERSIONFILE) +endif + +SHIP_DIST = $(MOZ_DEPTH)/dist/$(OBJDIR) +SHIP_DIR = $(SHIP_DIST)/SHIP + +SHIP_LIBS = libjs.$(SO_SUFFIX) libjs.a +ifeq ($(OS_ARCH), WINNT) + SHIP_LIBS = js32.dll js32.lib +endif +SHIP_LIBS += $(LCJAR) +SHIP_LIBS := $(addprefix $(SHIP_DIST)/lib/, $(SHIP_LIBS)) + +SHIP_INCS = js*.h prmjtime.h resource.h *.msg *.tbl +SHIP_INCS := $(addprefix $(SHIP_DIST)/include/, $(SHIP_INCS)) + +SHIP_BINS = js +ifeq ($(OS_ARCH), WINNT) + SHIP_BINS := $(addsuffix .exe, $(SHIP_BINS)) +endif +SHIP_BINS := $(addprefix $(SHIP_DIST)/bin/, $(SHIP_BINS)) + +ifdef BUILD_OPT + JSREFJAR = jsref_opt.jar +else +ifdef BUILD_IDG + JSREFJAR = jsref_idg.jar +else + JSREFJAR = jsref_dbg.jar +endif +endif + +ship: + mkdir -p $(SHIP_DIR)/$(LIBDIR) + mkdir -p $(SHIP_DIR)/include + mkdir -p $(SHIP_DIR)/bin + cp $(SHIP_LIBS) $(SHIP_DIR)/$(LIBDIR) + cp $(SHIP_INCS) $(SHIP_DIR)/include + cp $(SHIP_BINS) $(SHIP_DIR)/bin + cd $(SHIP_DIR); \ + zip -r $(JSREFJAR) bin lib include +ifdef BUILD_SHIP + cp $(SHIP_DIR)/$(JSREFJAR) $(BUILD_SHIP) +endif + +CWD = $(shell pwd) +shipSource: $(SHIP_DIR)/jsref_src.lst .FORCE + mkdir -p $(SHIP_DIR) + cd $(MOZ_DEPTH)/.. ; \ + zip $(CWD)/$(SHIP_DIR)/jsref_src.jar -@ < $(CWD)/$(SHIP_DIR)/jsref_src.lst +ifdef BUILD_SHIP + cp $(SHIP_DIR)/jsref_src.jar $(BUILD_SHIP) +endif + +JSREFSRCDIRS := $(shell cat $(DEPTH)/SpiderMonkey.rsp) +$(SHIP_DIR)/jsref_src.lst: .FORCE + mkdir -p $(SHIP_DIR) + rm -f $@ + touch $@ + for d in $(JSREFSRCDIRS); do \ + cd $(MOZ_DEPTH)/..; \ + ls -1 -d $$d | grep -v CVS | grep -v \.OBJ >> $(CWD)/$@; \ + cd $(CWD); \ + done + +.FORCE: diff --git a/ape-server/deps/js/src/jscpucfg.cpp b/ape-server/deps/js/src/jscpucfg.cpp new file mode 100755 index 0000000..c52d9b8 --- /dev/null +++ b/ape-server/deps/js/src/jscpucfg.cpp @@ -0,0 +1,194 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Roland Mainz + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Generate CPU-specific bit-size and similar #defines. + */ +#include +#include + +#ifdef CROSS_COMPILE +#include +#endif + +/************************************************************************/ + +#ifdef __GNUC__ +#define NS_NEVER_INLINE __attribute__((noinline)) +#else +#define NS_NEVER_INLINE +#endif + +#ifdef __SUNPRO_C +static int StackGrowthDirection(int *dummy1addr); +#pragma no_inline(StackGrowthDirection) +#endif + +static int NS_NEVER_INLINE StackGrowthDirection(int *dummy1addr) +{ + int dummy2; + + return (&dummy2 < dummy1addr) ? -1 : 1; +} + +int main(int argc, char **argv) +{ + int dummy1; + + printf("#ifndef js_cpucfg___\n"); + printf("#define js_cpucfg___\n\n"); + + printf("/* AUTOMATICALLY GENERATED - DO NOT EDIT */\n\n"); + +#ifdef CROSS_COMPILE +#if defined(__APPLE__) + /* + * Darwin NSPR uses the same MDCPUCFG (_darwin.cfg) for multiple + * processors, and determines which processor to configure for based + * on compiler predefined macros. We do the same thing here. + */ + printf("#ifdef __LITTLE_ENDIAN__\n"); + printf("#define IS_LITTLE_ENDIAN 1\n"); + printf("#undef IS_BIG_ENDIAN\n"); + printf("#else\n"); + printf("#undef IS_LITTLE_ENDIAN\n"); + printf("#define IS_BIG_ENDIAN 1\n"); + printf("#endif\n\n"); +#elif defined(IS_LITTLE_ENDIAN) + printf("#define IS_LITTLE_ENDIAN 1\n"); + printf("#undef IS_BIG_ENDIAN\n\n"); +#elif defined(IS_BIG_ENDIAN) + printf("#undef IS_LITTLE_ENDIAN\n"); + printf("#define IS_BIG_ENDIAN 1\n\n"); +#else +#error "Endianess not defined." +#endif + +#else + + /* + * We don't handle PDP-endian or similar orders: if a short is big-endian, + * so must int and long be big-endian for us to generate the IS_BIG_ENDIAN + * #define and the IS_LITTLE_ENDIAN #undef. + */ + { + int big_endian = 0, little_endian = 0, ntests = 0; + + if (sizeof(short) == 2) { + /* force |volatile| here to get rid of any compiler optimisations + * (var in register etc.) which may be appiled to |auto| vars - + * even those in |union|s... + * (|static| is used to get the same functionality for compilers + * which do not honor |volatile|...). + */ + volatile static union { + short i; + char c[2]; + } u; + + u.i = 0x0102; + big_endian += (u.c[0] == 0x01 && u.c[1] == 0x02); + little_endian += (u.c[0] == 0x02 && u.c[1] == 0x01); + ntests++; + } + + if (sizeof(int) == 4) { + /* force |volatile| here ... */ + volatile static union { + int i; + char c[4]; + } u; + + u.i = 0x01020304; + big_endian += (u.c[0] == 0x01 && u.c[1] == 0x02 && + u.c[2] == 0x03 && u.c[3] == 0x04); + little_endian += (u.c[0] == 0x04 && u.c[1] == 0x03 && + u.c[2] == 0x02 && u.c[3] == 0x01); + ntests++; + } + + if (sizeof(long) == 8) { + /* force |volatile| here ... */ + volatile static union { + long i; + char c[8]; + } u; + + /* + * Write this as portably as possible: avoid 0x0102030405060708L + * and <<= 32. + */ + u.i = 0x01020304; + u.i <<= 16, u.i <<= 16; + u.i |= 0x05060708; + big_endian += (u.c[0] == 0x01 && u.c[1] == 0x02 && + u.c[2] == 0x03 && u.c[3] == 0x04 && + u.c[4] == 0x05 && u.c[5] == 0x06 && + u.c[6] == 0x07 && u.c[7] == 0x08); + little_endian += (u.c[0] == 0x08 && u.c[1] == 0x07 && + u.c[2] == 0x06 && u.c[3] == 0x05 && + u.c[4] == 0x04 && u.c[5] == 0x03 && + u.c[6] == 0x02 && u.c[7] == 0x01); + ntests++; + } + + if (big_endian && big_endian == ntests) { + printf("#undef IS_LITTLE_ENDIAN\n"); + printf("#define IS_BIG_ENDIAN 1\n\n"); + } else if (little_endian && little_endian == ntests) { + printf("#define IS_LITTLE_ENDIAN 1\n"); + printf("#undef IS_BIG_ENDIAN\n\n"); + } else { + fprintf(stderr, "%s: unknown byte order" + "(big_endian=%d, little_endian=%d, ntests=%d)!\n", + argv[0], big_endian, little_endian, ntests); + return EXIT_FAILURE; + } + } + +#endif /* CROSS_COMPILE */ + + printf("#define JS_STACK_GROWTH_DIRECTION (%d)\n", StackGrowthDirection(&dummy1)); + + printf("#endif /* js_cpucfg___ */\n"); + + return EXIT_SUCCESS; +} + diff --git a/ape-server/deps/js/src/jscpucfg.h b/ape-server/deps/js/src/jscpucfg.h new file mode 100755 index 0000000..c52a44b --- /dev/null +++ b/ape-server/deps/js/src/jscpucfg.h @@ -0,0 +1,91 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef js_cpucfg___ +#define js_cpucfg___ + +#define JS_HAVE_LONG_LONG + +#if defined(XP_WIN) || defined(XP_OS2) || defined(WINCE) + +#if defined(_WIN64) + +#if defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_) +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#define JS_BYTES_PER_DOUBLE 8L +#define JS_BYTES_PER_WORD 8L +#define JS_BITS_PER_WORD_LOG2 6 +#define JS_ALIGN_OF_POINTER 8L +#else /* !(defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_)) */ +#error "CPU type is unknown" +#endif /* !(defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_)) */ + +#elif defined(_WIN32) || defined(XP_OS2) || defined(WINCE) + +#ifdef __WATCOMC__ +#define HAVE_VA_LIST_AS_ARRAY 1 +#endif + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#define JS_BYTES_PER_DOUBLE 8L +#define JS_BYTES_PER_WORD 4L +#define JS_BITS_PER_WORD_LOG2 5 +#define JS_ALIGN_OF_POINTER 4L + +#endif /* _WIN32 || XP_OS2 || WINCE*/ + +#elif defined(XP_UNIX) || defined(XP_BEOS) + +#error "This file is supposed to be auto-generated on UNIX platforms, but the" +#error "static version for Mac and Windows platforms is being used." +#error "Something's probably wrong with paths/headers/dependencies/Makefiles." + +#else + +#error "Must define one of XP_BEOS, XP_OS2, XP_WIN, or XP_UNIX" + +#endif + +#ifndef JS_STACK_GROWTH_DIRECTION +#define JS_STACK_GROWTH_DIRECTION (-1) +#endif + +#endif /* js_cpucfg___ */ diff --git a/ape-server/deps/js/src/jsdate.cpp b/ape-server/deps/js/src/jsdate.cpp new file mode 100755 index 0000000..b5347e2 --- /dev/null +++ b/ape-server/deps/js/src/jsdate.cpp @@ -0,0 +1,2647 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * JS date methods. + */ + +/* + * "For example, OS/360 devotes 26 bytes of the permanently + * resident date-turnover routine to the proper handling of + * December 31 on leap years (when it is Day 366). That + * might have been left to the operator." + * + * Frederick Brooks, 'The Second-System Effect'. + */ + +#include +#include +#include +#include +#include +#include "jstypes.h" +#include "jsstdint.h" +#include "jsprf.h" +#include "prmjtime.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jsapi.h" +#include "jsversion.h" +#include "jsbuiltins.h" +#include "jscntxt.h" +#include "jsdate.h" +#include "jsinterp.h" +#include "jsnum.h" +#include "jsobj.h" +#include "jsstr.h" + +/* + * The JS 'Date' object is patterned after the Java 'Date' object. + * Here is an script: + * + * today = new Date(); + * + * print(today.toLocaleString()); + * + * weekDay = today.getDay(); + * + * + * These Java (and ECMA-262) methods are supported: + * + * UTC + * getDate (getUTCDate) + * getDay (getUTCDay) + * getHours (getUTCHours) + * getMinutes (getUTCMinutes) + * getMonth (getUTCMonth) + * getSeconds (getUTCSeconds) + * getMilliseconds (getUTCMilliseconds) + * getTime + * getTimezoneOffset + * getYear + * getFullYear (getUTCFullYear) + * parse + * setDate (setUTCDate) + * setHours (setUTCHours) + * setMinutes (setUTCMinutes) + * setMonth (setUTCMonth) + * setSeconds (setUTCSeconds) + * setMilliseconds (setUTCMilliseconds) + * setTime + * setYear (setFullYear, setUTCFullYear) + * toGMTString (toUTCString) + * toLocaleString + * toString + * + * + * These Java methods are not supported + * + * setDay + * before + * after + * equals + * hashCode + */ + +/* + * 11/97 - jsdate.c has been rewritten to conform to the ECMA-262 language + * definition and reduce dependence on NSPR. NSPR is used to get the current + * time in milliseconds, the time zone offset, and the daylight savings time + * offset for a given time. NSPR is also used for Date.toLocaleString(), for + * locale-specific formatting, and to get a string representing the timezone. + * (Which turns out to be platform-dependent.) + * + * To do: + * (I did some performance tests by timing how long it took to run what + * I had of the js ECMA conformance tests.) + * + * - look at saving results across multiple calls to supporting + * functions; the toString functions compute some of the same values + * multiple times. Although - I took a quick stab at this, and I lost + * rather than gained. (Fractionally.) Hard to tell what compilers/processors + * are doing these days. + * + * - look at tweaking function return types to return double instead + * of int; this seems to make things run slightly faster sometimes. + * (though it could be architecture-dependent.) It'd be good to see + * how this does on win32. (Tried it on irix.) Types could use a + * general going-over. + */ + +/* + * Supporting functions - ECMA 15.9.1.* + */ + +#define HalfTimeDomain 8.64e15 +#define HoursPerDay 24.0 +#define MinutesPerDay (HoursPerDay * MinutesPerHour) +#define MinutesPerHour 60.0 +#define SecondsPerDay (MinutesPerDay * SecondsPerMinute) +#define SecondsPerHour (MinutesPerHour * SecondsPerMinute) +#define SecondsPerMinute 60.0 + +#if defined(XP_WIN) || defined(XP_OS2) +/* Work around msvc double optimization bug by making these runtime values; if + * they're available at compile time, msvc optimizes division by them by + * computing the reciprocal and multiplying instead of dividing - this loses + * when the reciprocal isn't representable in a double. + */ +static jsdouble msPerSecond = 1000.0; +static jsdouble msPerDay = SecondsPerDay * 1000.0; +static jsdouble msPerHour = SecondsPerHour * 1000.0; +static jsdouble msPerMinute = SecondsPerMinute * 1000.0; +#else +#define msPerDay (SecondsPerDay * msPerSecond) +#define msPerHour (SecondsPerHour * msPerSecond) +#define msPerMinute (SecondsPerMinute * msPerSecond) +#define msPerSecond 1000.0 +#endif + +#define Day(t) floor((t) / msPerDay) + +static jsdouble +TimeWithinDay(jsdouble t) +{ + jsdouble result; + result = fmod(t, msPerDay); + if (result < 0) + result += msPerDay; + return result; +} + +#define DaysInYear(y) ((y) % 4 == 0 && ((y) % 100 || ((y) % 400 == 0)) \ + ? 366 : 365) + +/* math here has to be f.p, because we need + * floor((1968 - 1969) / 4) == -1 + */ +#define DayFromYear(y) (365 * ((y)-1970) + floor(((y)-1969)/4.0) \ + - floor(((y)-1901)/100.0) + floor(((y)-1601)/400.0)) +#define TimeFromYear(y) (DayFromYear(y) * msPerDay) + +static jsint +YearFromTime(jsdouble t) +{ + jsint y = (jsint) floor(t /(msPerDay*365.2425)) + 1970; + jsdouble t2 = (jsdouble) TimeFromYear(y); + + if (t2 > t) { + y--; + } else { + if (t2 + msPerDay * DaysInYear(y) <= t) + y++; + } + return y; +} + +#define InLeapYear(t) (JSBool) (DaysInYear(YearFromTime(t)) == 366) + +#define DayWithinYear(t, year) ((intN) (Day(t) - DayFromYear(year))) + +/* + * The following array contains the day of year for the first day of + * each month, where index 0 is January, and day 0 is January 1. + */ +static jsdouble firstDayOfMonth[2][13] = { + {0.0, 31.0, 59.0, 90.0, 120.0, 151.0, 181.0, 212.0, 243.0, 273.0, 304.0, 334.0, 365.0}, + {0.0, 31.0, 60.0, 91.0, 121.0, 152.0, 182.0, 213.0, 244.0, 274.0, 305.0, 335.0, 366.0} +}; + +#define DayFromMonth(m, leap) firstDayOfMonth[leap][(intN)m] + +static intN +DaysInMonth(jsint year, jsint month) +{ + JSBool leap = (DaysInYear(year) == 366); + intN result = intN(DayFromMonth(month, leap) - DayFromMonth(month-1, leap)); + return result; +} + +static intN +MonthFromTime(jsdouble t) +{ + intN d, step; + jsint year = YearFromTime(t); + d = DayWithinYear(t, year); + + if (d < (step = 31)) + return 0; + step += (InLeapYear(t) ? 29 : 28); + if (d < step) + return 1; + if (d < (step += 31)) + return 2; + if (d < (step += 30)) + return 3; + if (d < (step += 31)) + return 4; + if (d < (step += 30)) + return 5; + if (d < (step += 31)) + return 6; + if (d < (step += 31)) + return 7; + if (d < (step += 30)) + return 8; + if (d < (step += 31)) + return 9; + if (d < (step += 30)) + return 10; + return 11; +} + +static intN +DateFromTime(jsdouble t) +{ + intN d, step, next; + jsint year = YearFromTime(t); + d = DayWithinYear(t, year); + + if (d <= (next = 30)) + return d + 1; + step = next; + next += (InLeapYear(t) ? 29 : 28); + if (d <= next) + return d - step; + step = next; + if (d <= (next += 31)) + return d - step; + step = next; + if (d <= (next += 30)) + return d - step; + step = next; + if (d <= (next += 31)) + return d - step; + step = next; + if (d <= (next += 30)) + return d - step; + step = next; + if (d <= (next += 31)) + return d - step; + step = next; + if (d <= (next += 31)) + return d - step; + step = next; + if (d <= (next += 30)) + return d - step; + step = next; + if (d <= (next += 31)) + return d - step; + step = next; + if (d <= (next += 30)) + return d - step; + step = next; + return d - step; +} + +static intN +WeekDay(jsdouble t) +{ + jsint result; + result = (jsint) Day(t) + 4; + result = result % 7; + if (result < 0) + result += 7; + return (intN) result; +} + +#define MakeTime(hour, min, sec, ms) \ +((((hour) * MinutesPerHour + (min)) * SecondsPerMinute + (sec)) * msPerSecond + (ms)) + +static jsdouble +MakeDay(jsdouble year, jsdouble month, jsdouble date) +{ + JSBool leap; + jsdouble yearday; + jsdouble monthday; + + year += floor(month / 12); + + month = fmod(month, 12.0); + if (month < 0) + month += 12; + + leap = (DaysInYear((jsint) year) == 366); + + yearday = floor(TimeFromYear(year) / msPerDay); + monthday = DayFromMonth(month, leap); + + return yearday + monthday + date - 1; +} + +#define MakeDate(day, time) ((day) * msPerDay + (time)) + +/* + * Years and leap years on which Jan 1 is a Sunday, Monday, etc. + * + * yearStartingWith[0][i] is an example non-leap year where + * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc. + * + * yearStartingWith[1][i] is an example leap year where + * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc. + */ +static jsint yearStartingWith[2][7] = { + {1978, 1973, 1974, 1975, 1981, 1971, 1977}, + {1984, 1996, 1980, 1992, 1976, 1988, 1972} +}; + +/* + * Find a year for which any given date will fall on the same weekday. + * + * This function should be used with caution when used other than + * for determining DST; it hasn't been proven not to produce an + * incorrect year for times near year boundaries. + */ +static jsint +EquivalentYearForDST(jsint year) +{ + jsint day; + JSBool isLeapYear; + + day = (jsint) DayFromYear(year) + 4; + day = day % 7; + if (day < 0) + day += 7; + + isLeapYear = (DaysInYear(year) == 366); + + return yearStartingWith[isLeapYear][day]; +} + +/* LocalTZA gets set by js_InitDateClass() */ +static jsdouble LocalTZA; + +static jsdouble +DaylightSavingTA(jsdouble t) +{ + volatile int64 PR_t; + int64 ms2us; + int64 offset; + jsdouble result; + + /* abort if NaN */ + if (JSDOUBLE_IS_NaN(t)) + return t; + + /* + * If earlier than 1970 or after 2038, potentially beyond the ken of + * many OSes, map it to an equivalent year before asking. + */ + if (t < 0.0 || t > 2145916800000.0) { + jsint year; + jsdouble day; + + year = EquivalentYearForDST(YearFromTime(t)); + day = MakeDay(year, MonthFromTime(t), DateFromTime(t)); + t = MakeDate(day, TimeWithinDay(t)); + } + + /* put our t in an LL, and map it to usec for prtime */ + JSLL_D2L(PR_t, t); + JSLL_I2L(ms2us, PRMJ_USEC_PER_MSEC); + JSLL_MUL(PR_t, PR_t, ms2us); + + offset = PRMJ_DSTOffset(PR_t); + + JSLL_DIV(offset, offset, ms2us); + JSLL_L2D(result, offset); + return result; +} + +static jsdouble +AdjustTime(jsdouble date) +{ + jsdouble t = DaylightSavingTA(date) + LocalTZA; + t = (LocalTZA >= 0) ? fmod(t, msPerDay) : -fmod(msPerDay - t, msPerDay); + return t; +} + +#define LocalTime(t) ((t) + AdjustTime(t)) + +static jsdouble +UTC(jsdouble t) +{ + return t - AdjustTime(t - LocalTZA); +} + +static intN +HourFromTime(jsdouble t) +{ + intN result = (intN) fmod(floor(t/msPerHour), HoursPerDay); + if (result < 0) + result += (intN)HoursPerDay; + return result; +} + +static intN +MinFromTime(jsdouble t) +{ + intN result = (intN) fmod(floor(t / msPerMinute), MinutesPerHour); + if (result < 0) + result += (intN)MinutesPerHour; + return result; +} + +static intN +SecFromTime(jsdouble t) +{ + intN result = (intN) fmod(floor(t / msPerSecond), SecondsPerMinute); + if (result < 0) + result += (intN)SecondsPerMinute; + return result; +} + +static intN +msFromTime(jsdouble t) +{ + intN result = (intN) fmod(t, msPerSecond); + if (result < 0) + result += (intN)msPerSecond; + return result; +} + +#define TIMECLIP(d) ((JSDOUBLE_IS_FINITE(d) \ + && !((d < 0 ? -d : d) > HalfTimeDomain)) \ + ? js_DoubleToInteger(d + (+0.)) : js_NaN) + +/** + * end of ECMA 'support' functions + */ + +/* + * Other Support routines and definitions + */ + +/* + * We use the first reseved slot to store UTC time, and the second for caching + * the local time. The initial value of the cache entry is NaN. + */ +const uint32 JSSLOT_UTC_TIME = JSSLOT_PRIVATE; +const uint32 JSSLOT_LOCAL_TIME = JSSLOT_PRIVATE + 1; + +const uint32 DATE_RESERVED_SLOTS = 2; + +JSClass js_DateClass = { + js_Date_str, + JSCLASS_HAS_RESERVED_SLOTS(DATE_RESERVED_SLOTS) | + JSCLASS_HAS_CACHED_PROTO(JSProto_Date), + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +/* for use by date_parse */ + +static const char* wtb[] = { + "am", "pm", + "monday", "tuesday", "wednesday", "thursday", "friday", + "saturday", "sunday", + "january", "february", "march", "april", "may", "june", + "july", "august", "september", "october", "november", "december", + "gmt", "ut", "utc", + "est", "edt", + "cst", "cdt", + "mst", "mdt", + "pst", "pdt" + /* time zone table needs to be expanded */ +}; + +static int ttb[] = { + -1, -2, 0, 0, 0, 0, 0, 0, 0, /* AM/PM */ + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 10000 + 0, 10000 + 0, 10000 + 0, /* GMT/UT/UTC */ + 10000 + 5 * 60, 10000 + 4 * 60, /* EST/EDT */ + 10000 + 6 * 60, 10000 + 5 * 60, /* CST/CDT */ + 10000 + 7 * 60, 10000 + 6 * 60, /* MST/MDT */ + 10000 + 8 * 60, 10000 + 7 * 60 /* PST/PDT */ +}; + +/* helper for date_parse */ +static JSBool +date_regionMatches(const char* s1, int s1off, const jschar* s2, int s2off, + int count, int ignoreCase) +{ + JSBool result = JS_FALSE; + /* return true if matches, otherwise, false */ + + while (count > 0 && s1[s1off] && s2[s2off]) { + if (ignoreCase) { + if (JS_TOLOWER((jschar)s1[s1off]) != JS_TOLOWER(s2[s2off])) { + break; + } + } else { + if ((jschar)s1[s1off] != s2[s2off]) { + break; + } + } + s1off++; + s2off++; + count--; + } + + if (count == 0) { + result = JS_TRUE; + } + + return result; +} + +/* find UTC time from given date... no 1900 correction! */ +static jsdouble +date_msecFromDate(jsdouble year, jsdouble mon, jsdouble mday, jsdouble hour, + jsdouble min, jsdouble sec, jsdouble msec) +{ + jsdouble day; + jsdouble msec_time; + jsdouble result; + + day = MakeDay(year, mon, mday); + msec_time = MakeTime(hour, min, sec, msec); + result = MakeDate(day, msec_time); + return result; +} + +/* compute the time in msec (unclipped) from the given args */ +#define MAXARGS 7 + +static JSBool +date_msecFromArgs(JSContext *cx, uintN argc, jsval *argv, jsdouble *rval) +{ + uintN loop; + jsdouble array[MAXARGS]; + jsdouble d; + jsdouble msec_time; + + for (loop = 0; loop < MAXARGS; loop++) { + if (loop < argc) { + d = js_ValueToNumber(cx, &argv[loop]); + if (JSVAL_IS_NULL(argv[loop])) + return JS_FALSE; + /* return NaN if any arg is not finite */ + if (!JSDOUBLE_IS_FINITE(d)) { + *rval = js_NaN; + return JS_TRUE; + } + array[loop] = js_DoubleToInteger(d); + } else { + if (loop == 2) { + array[loop] = 1; /* Default the date argument to 1. */ + } else { + array[loop] = 0; + } + } + } + + /* adjust 2-digit years into the 20th century */ + if (array[0] >= 0 && array[0] <= 99) + array[0] += 1900; + + msec_time = date_msecFromDate(array[0], array[1], array[2], + array[3], array[4], array[5], array[6]); + *rval = msec_time; + return JS_TRUE; +} + +/* + * See ECMA 15.9.4.[3-10]; + */ +static JSBool +date_UTC(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble msec_time; + + if (!date_msecFromArgs(cx, argc, vp + 2, &msec_time)) + return JS_FALSE; + + msec_time = TIMECLIP(msec_time); + + return js_NewNumberInRootedValue(cx, msec_time, vp); +} + +/* + * Read and convert decimal digits from s[*i] into *result + * while *i < limit. + * + * Succeed if any digits are converted. Advance *i only + * as digits are consumed. + */ +static JSBool +digits(size_t *result, const jschar *s, size_t *i, size_t limit) +{ + size_t init = *i; + *result = 0; + while (*i < limit && + ('0' <= s[*i] && s[*i] <= '9')) { + *result *= 10; + *result += (s[*i] - '0'); + ++(*i); + } + return (*i != init); +} + +/* + * Read and convert decimal digits to the right of a decimal point, + * representing a fractional integer, from s[*i] into *result + * while *i < limit. + * + * Succeed if any digits are converted. Advance *i only + * as digits are consumed. + */ +static JSBool +fractional(jsdouble *result, const jschar *s, size_t *i, size_t limit) +{ + jsdouble factor = 0.1; + size_t init = *i; + *result = 0.0; + while (*i < limit && + ('0' <= s[*i] && s[*i] <= '9')) { + *result += (s[*i] - '0') * factor; + factor *= 0.1; + ++(*i); + } + return (*i != init); +} + +/* + * Read and convert exactly n decimal digits from s[*i] + * to s[min(*i+n,limit)] into *result. + * + * Succeed if exactly n digits are converted. Advance *i only + * on success. + */ +static JSBool +ndigits(size_t n, size_t *result, const jschar *s, size_t* i, size_t limit) +{ + size_t init = *i; + + if (digits(result, s, i, JS_MIN(limit, init+n))) + return ((*i - init) == n); + + *i = init; + return JS_FALSE; +} + +/* + * Parse a string in one of the date-time formats given by the W3C + * "NOTE-datetime" specification. These formats make up a restricted + * profile of the ISO 8601 format. Quoted here: + * + * The formats are as follows. Exactly the components shown here + * must be present, with exactly this punctuation. Note that the "T" + * appears literally in the string, to indicate the beginning of the + * time element, as specified in ISO 8601. + * + * Any combination of the date formats with the time formats is + * allowed, and also either the date or the time can be missing. + * + * The specification is silent on the meaning when fields are + * ommitted so the interpretations are a guess, but hopefully a + * reasonable one. We default the month to January, the day to the + * 1st, and hours minutes and seconds all to 0. If the date is + * missing entirely then we assume 1970-01-01 so that the time can + * be aded to a date later. If the time is missing then we assume + * 00:00 UTC. If the time is present but the time zone field is + * missing then we use local time. + * + * Date part: + * + * Year: + * YYYY (eg 1997) + * + * Year and month: + * YYYY-MM (eg 1997-07) + * + * Complete date: + * YYYY-MM-DD (eg 1997-07-16) + * + * Time part: + * + * Hours and minutes: + * Thh:mmTZD (eg T19:20+01:00) + * + * Hours, minutes and seconds: + * Thh:mm:ssTZD (eg T19:20:30+01:00) + * + * Hours, minutes, seconds and a decimal fraction of a second: + * Thh:mm:ss.sTZD (eg T19:20:30.45+01:00) + * + * where: + * + * YYYY = four-digit year or six digit year as +YYYYYY or -YYYYYY + * MM = two-digit month (01=January, etc.) + * DD = two-digit day of month (01 through 31) + * hh = two digits of hour (00 through 23) (am/pm NOT allowed) + * mm = two digits of minute (00 through 59) + * ss = two digits of second (00 through 59) + * s = one or more digits representing a decimal fraction of a second + * TZD = time zone designator (Z or +hh:mm or -hh:mm or missing for local) + */ + +static JSBool +date_parseISOString(JSString *str, jsdouble *result) +{ + jsdouble msec; + + const jschar *s; + size_t limit; + size_t i = 0; + int tzMul = 1; + int dateMul = 1; + size_t year = 1970; + size_t month = 1; + size_t day = 1; + size_t hour = 0; + size_t min = 0; + size_t sec = 0; + jsdouble frac = 0; + bool isLocalTime = JS_FALSE; + size_t tzHour = 0; + size_t tzMin = 0; + +#define PEEK(ch) (i < limit && s[i] == ch) + +#define NEED(ch) \ + JS_BEGIN_MACRO \ + if (i >= limit || s[i] != ch) { goto syntax; } else { ++i; } \ + JS_END_MACRO + +#define DONE_DATE_UNLESS(ch) \ + JS_BEGIN_MACRO \ + if (i >= limit || s[i] != ch) { goto done_date; } else { ++i; } \ + JS_END_MACRO + +#define DONE_UNLESS(ch) \ + JS_BEGIN_MACRO \ + if (i >= limit || s[i] != ch) { goto done; } else { ++i; } \ + JS_END_MACRO + +#define NEED_NDIGITS(n, field) \ + JS_BEGIN_MACRO \ + if (!ndigits(n, &field, s, &i, limit)) { goto syntax; } \ + JS_END_MACRO + + str->getCharsAndLength(s, limit); + + if (PEEK('+') || PEEK('-')) { + if (PEEK('-')) + dateMul = -1; + ++i; + NEED_NDIGITS(6, year); + } else if (!PEEK('T')) { + NEED_NDIGITS(4, year); + } + DONE_DATE_UNLESS('-'); + NEED_NDIGITS(2, month); + DONE_DATE_UNLESS('-'); + NEED_NDIGITS(2, day); + + done_date: + DONE_UNLESS('T'); + NEED_NDIGITS(2, hour); + NEED(':'); + NEED_NDIGITS(2, min); + + if (PEEK(':')) { + ++i; + NEED_NDIGITS(2, sec); + if (PEEK('.')) { + ++i; + if (!fractional(&frac, s, &i, limit)) + goto syntax; + } + } + + if (PEEK('Z')) { + ++i; + } else if (PEEK('+') || PEEK('-')) { + if (PEEK('-')) + tzMul = -1; + ++i; + NEED_NDIGITS(2, tzHour); + NEED(':'); + NEED_NDIGITS(2, tzMin); + } else { + isLocalTime = JS_TRUE; + } + + done: + if (year > 275943 // ceil(1e8/365) + 1970 + || (month == 0 || month > 12) + || (day == 0 || day > size_t(DaysInMonth(year,month))) + || hour > 24 + || ((hour == 24) && (min > 0 || sec > 0)) + || min > 59 + || sec > 59 + || tzHour > 23 + || tzMin > 59) + goto syntax; + + if (i != limit) + goto syntax; + + month -= 1; /* convert month to 0-based */ + + msec = date_msecFromDate(dateMul * (jsdouble)year, month, day, + hour, min, sec, + frac * 1000.0);; + + if (isLocalTime) { + msec = UTC(msec); + } else { + msec -= ((tzMul) * ((tzHour * msPerHour) + + (tzMin * msPerMinute))); + } + + if (msec < -8.64e15 || msec > 8.64e15) + goto syntax; + + *result = msec; + + return JS_TRUE; + + syntax: + /* syntax error */ + *result = 0; + return JS_FALSE; + +#undef PEEK +#undef NEED +#undef DONE_UNLESS +#undef NEED_NDIGITS +} + +static JSBool +date_parseString(JSString *str, jsdouble *result) +{ + jsdouble msec; + + const jschar *s; + size_t limit; + size_t i = 0; + int year = -1; + int mon = -1; + int mday = -1; + int hour = -1; + int min = -1; + int sec = -1; + int c = -1; + int n = -1; + int tzoffset = -1; + int prevc = 0; + JSBool seenplusminus = JS_FALSE; + int temp; + JSBool seenmonthname = JS_FALSE; + + if (date_parseISOString(str, result)) + return JS_TRUE; + + str->getCharsAndLength(s, limit); + if (limit == 0) + goto syntax; + while (i < limit) { + c = s[i]; + i++; + if (c <= ' ' || c == ',' || c == '-') { + if (c == '-' && '0' <= s[i] && s[i] <= '9') { + prevc = c; + } + continue; + } + if (c == '(') { /* comments) */ + int depth = 1; + while (i < limit) { + c = s[i]; + i++; + if (c == '(') depth++; + else if (c == ')') + if (--depth <= 0) + break; + } + continue; + } + if ('0' <= c && c <= '9') { + n = c - '0'; + while (i < limit && '0' <= (c = s[i]) && c <= '9') { + n = n * 10 + c - '0'; + i++; + } + + /* allow TZA before the year, so + * 'Wed Nov 05 21:49:11 GMT-0800 1997' + * works */ + + /* uses of seenplusminus allow : in TZA, so Java + * no-timezone style of GMT+4:30 works + */ + + if ((prevc == '+' || prevc == '-')/* && year>=0 */) { + /* make ':' case below change tzoffset */ + seenplusminus = JS_TRUE; + + /* offset */ + if (n < 24) + n = n * 60; /* EG. "GMT-3" */ + else + n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */ + if (prevc == '+') /* plus means east of GMT */ + n = -n; + if (tzoffset != 0 && tzoffset != -1) + goto syntax; + tzoffset = n; + } else if (prevc == '/' && mon >= 0 && mday >= 0 && year < 0) { + if (c <= ' ' || c == ',' || c == '/' || i >= limit) + year = n; + else + goto syntax; + } else if (c == ':') { + if (hour < 0) + hour = /*byte*/ n; + else if (min < 0) + min = /*byte*/ n; + else + goto syntax; + } else if (c == '/') { + /* until it is determined that mon is the actual + month, keep it as 1-based rather than 0-based */ + if (mon < 0) + mon = /*byte*/ n; + else if (mday < 0) + mday = /*byte*/ n; + else + goto syntax; + } else if (i < limit && c != ',' && c > ' ' && c != '-' && c != '(') { + goto syntax; + } else if (seenplusminus && n < 60) { /* handle GMT-3:30 */ + if (tzoffset < 0) + tzoffset -= n; + else + tzoffset += n; + } else if (hour >= 0 && min < 0) { + min = /*byte*/ n; + } else if (prevc == ':' && min >= 0 && sec < 0) { + sec = /*byte*/ n; + } else if (mon < 0) { + mon = /*byte*/n; + } else if (mon >= 0 && mday < 0) { + mday = /*byte*/ n; + } else if (mon >= 0 && mday >= 0 && year < 0) { + year = n; + } else { + goto syntax; + } + prevc = 0; + } else if (c == '/' || c == ':' || c == '+' || c == '-') { + prevc = c; + } else { + size_t st = i - 1; + int k; + while (i < limit) { + c = s[i]; + if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'))) + break; + i++; + } + if (i <= st + 1) + goto syntax; + for (k = JS_ARRAY_LENGTH(wtb); --k >= 0;) + if (date_regionMatches(wtb[k], 0, s, st, i-st, 1)) { + int action = ttb[k]; + if (action != 0) { + if (action < 0) { + /* + * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as + * 12:30, instead of blindly adding 12 if PM. + */ + JS_ASSERT(action == -1 || action == -2); + if (hour > 12 || hour < 0) { + goto syntax; + } else { + if (action == -1 && hour == 12) { /* am */ + hour = 0; + } else if (action == -2 && hour != 12) { /* pm */ + hour += 12; + } + } + } else if (action <= 13) { /* month! */ + /* Adjust mon to be 1-based until the final values + for mon, mday and year are adjusted below */ + if (seenmonthname) { + goto syntax; + } + seenmonthname = JS_TRUE; + temp = /*byte*/ (action - 2) + 1; + + if (mon < 0) { + mon = temp; + } else if (mday < 0) { + mday = mon; + mon = temp; + } else if (year < 0) { + year = mon; + mon = temp; + } else { + goto syntax; + } + } else { + tzoffset = action - 10000; + } + } + break; + } + if (k < 0) + goto syntax; + prevc = 0; + } + } + if (year < 0 || mon < 0 || mday < 0) + goto syntax; + /* + Case 1. The input string contains an English month name. + The form of the string can be month f l, or f month l, or + f l month which each evaluate to the same date. + If f and l are both greater than or equal to 70, or + both less than 70, the date is invalid. + The year is taken to be the greater of the values f, l. + If the year is greater than or equal to 70 and less than 100, + it is considered to be the number of years after 1900. + Case 2. The input string is of the form "f/m/l" where f, m and l are + integers, e.g. 7/16/45. + Adjust the mon, mday and year values to achieve 100% MSIE + compatibility. + a. If 0 <= f < 70, f/m/l is interpreted as month/day/year. + i. If year < 100, it is the number of years after 1900 + ii. If year >= 100, it is the number of years after 0. + b. If 70 <= f < 100 + i. If m < 70, f/m/l is interpreted as + year/month/day where year is the number of years after + 1900. + ii. If m >= 70, the date is invalid. + c. If f >= 100 + i. If m < 70, f/m/l is interpreted as + year/month/day where year is the number of years after 0. + ii. If m >= 70, the date is invalid. + */ + if (seenmonthname) { + if ((mday >= 70 && year >= 70) || (mday < 70 && year < 70)) { + goto syntax; + } + if (mday > year) { + temp = year; + year = mday; + mday = temp; + } + if (year >= 70 && year < 100) { + year += 1900; + } + } else if (mon < 70) { /* (a) month/day/year */ + if (year < 100) { + year += 1900; + } + } else if (mon < 100) { /* (b) year/month/day */ + if (mday < 70) { + temp = year; + year = mon + 1900; + mon = mday; + mday = temp; + } else { + goto syntax; + } + } else { /* (c) year/month/day */ + if (mday < 70) { + temp = year; + year = mon; + mon = mday; + mday = temp; + } else { + goto syntax; + } + } + mon -= 1; /* convert month to 0-based */ + if (sec < 0) + sec = 0; + if (min < 0) + min = 0; + if (hour < 0) + hour = 0; + + msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0); + + if (tzoffset == -1) { /* no time zone specified, have to use local */ + msec = UTC(msec); + } else { + msec += tzoffset * msPerMinute; + } + + *result = msec; + return JS_TRUE; + +syntax: + /* syntax error */ + *result = 0; + return JS_FALSE; +} + +static JSBool +date_parse(JSContext *cx, uintN argc, jsval *vp) +{ + JSString *str; + jsdouble result; + + if (argc == 0) { + *vp = cx->runtime->NaNValue; + return true; + } + str = js_ValueToString(cx, vp[2]); + if (!str) + return JS_FALSE; + vp[2] = STRING_TO_JSVAL(str); + if (!date_parseString(str, &result)) { + *vp = cx->runtime->NaNValue; + return true; + } + + result = TIMECLIP(result); + return js_NewNumberInRootedValue(cx, result, vp); +} + +static inline jsdouble +NowAsMillis() +{ + return (jsdouble) (PRMJ_Now() / PRMJ_USEC_PER_MSEC); +} + +static JSBool +date_now(JSContext *cx, uintN argc, jsval *vp) +{ + return js_NewDoubleInRootedValue(cx, NowAsMillis(), vp); +} + +#ifdef JS_TRACER +static jsdouble FASTCALL +date_now_tn(JSContext*) +{ + return NowAsMillis(); +} +#endif + +/* + * Get UTC time from the date object. Returns false if the object is not + * Date type. + */ +static JSBool +GetUTCTime(JSContext *cx, JSObject *obj, jsval *vp, jsdouble *dp) +{ + if (!JS_InstanceOf(cx, obj, &js_DateClass, vp ? vp + 2 : NULL)) + return JS_FALSE; + *dp = *JSVAL_TO_DOUBLE(obj->fslots[JSSLOT_UTC_TIME]); + return JS_TRUE; +} + +static void +SetDateToNaN(JSContext *cx, JSObject *obj, jsval *vp = NULL) +{ + JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_DateClass); + + obj->fslots[JSSLOT_LOCAL_TIME] = cx->runtime->NaNValue; + obj->fslots[JSSLOT_UTC_TIME] = cx->runtime->NaNValue; + if (vp) + *vp = cx->runtime->NaNValue; +} + +/* + * Set UTC time to a given time and invalidate cached local time. + */ +static JSBool +SetUTCTime(JSContext *cx, JSObject *obj, jsdouble t, jsval *vp = NULL) +{ + JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_DateClass); + + obj->fslots[JSSLOT_LOCAL_TIME] = cx->runtime->NaNValue; + if (!js_NewDoubleInRootedValue(cx, t, &obj->fslots[JSSLOT_UTC_TIME])) + return false; + if (vp) + *vp = obj->fslots[JSSLOT_UTC_TIME]; + return true; +} + +/* + * Get the local time, cache it if necessary. If UTC time is not finite + * (e.g., NaN), the local time slot is set to the UTC time without conversion. + */ +static JSBool +GetAndCacheLocalTime(JSContext *cx, JSObject *obj, jsval *vp, jsdouble *dp) +{ + if (!obj || !JS_InstanceOf(cx, obj, &js_DateClass, vp ? vp + 2 : NULL)) + return false; + + jsval *slotp = &obj->fslots[JSSLOT_LOCAL_TIME]; + jsdouble result = *JSVAL_TO_DOUBLE(*slotp); + if (JSDOUBLE_IS_NaN(result)) { + result = *JSVAL_TO_DOUBLE(obj->fslots[JSSLOT_UTC_TIME]); + + /* if result is NaN, it couldn't be finite. */ + if (JSDOUBLE_IS_FINITE(result)) + result = LocalTime(result); + + if (!js_NewDoubleInRootedValue(cx, result, slotp)) + return false; + } + + *dp = result; + return true; +} + +/* + * See ECMA 15.9.5.4 thru 15.9.5.23 + */ +static JSBool +date_getTime(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble result; + + return GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result) && + js_NewNumberInRootedValue(cx, result, vp); +} + +static JSBool +GetYear(JSContext *cx, JSBool fullyear, jsval *vp) +{ + jsdouble result; + + if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result)) + return JS_FALSE; + + if (JSDOUBLE_IS_FINITE(result)) { + result = YearFromTime(result); + + /* Follow ECMA-262 to the letter, contrary to IE JScript. */ + if (!fullyear) + result -= 1900; + } + + return js_NewNumberInRootedValue(cx, result, vp); +} + +static JSBool +date_getYear(JSContext *cx, uintN argc, jsval *vp) +{ + return GetYear(cx, JS_FALSE, vp); +} + +static JSBool +date_getFullYear(JSContext *cx, uintN argc, jsval *vp) +{ + return GetYear(cx, JS_TRUE, vp); +} + +static JSBool +date_getUTCFullYear(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble result; + + if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result)) + return JS_FALSE; + + if (JSDOUBLE_IS_FINITE(result)) + result = YearFromTime(result); + + return js_NewNumberInRootedValue(cx, result, vp); +} + +static JSBool +date_getMonth(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble result; + + if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result)) + return JS_FALSE; + + if (JSDOUBLE_IS_FINITE(result)) + result = MonthFromTime(result); + + return js_NewNumberInRootedValue(cx, result, vp); +} + +static JSBool +date_getUTCMonth(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble result; + + if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result)) + return JS_FALSE; + + if (JSDOUBLE_IS_FINITE(result)) + result = MonthFromTime(result); + + return js_NewNumberInRootedValue(cx, result, vp); +} + +static JSBool +date_getDate(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble result; + + if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result)) + return JS_FALSE; + + if (JSDOUBLE_IS_FINITE(result)) + result = DateFromTime(result); + + return js_NewNumberInRootedValue(cx, result, vp); +} + +static JSBool +date_getUTCDate(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble result; + + if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result)) + return JS_FALSE; + + if (JSDOUBLE_IS_FINITE(result)) + result = DateFromTime(result); + + return js_NewNumberInRootedValue(cx, result, vp); +} + +static JSBool +date_getDay(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble result; + + if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result)) + return JS_FALSE; + + if (JSDOUBLE_IS_FINITE(result)) + result = WeekDay(result); + + return js_NewNumberInRootedValue(cx, result, vp); +} + +static JSBool +date_getUTCDay(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble result; + + if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result)) + return JS_FALSE; + + if (JSDOUBLE_IS_FINITE(result)) + result = WeekDay(result); + + return js_NewNumberInRootedValue(cx, result, vp); +} + +static JSBool +date_getHours(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble result; + + if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result)) + return JS_FALSE; + + if (JSDOUBLE_IS_FINITE(result)) + result = HourFromTime(result); + + return js_NewNumberInRootedValue(cx, result, vp); +} + +static JSBool +date_getUTCHours(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble result; + + if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result)) + return JS_FALSE; + + if (JSDOUBLE_IS_FINITE(result)) + result = HourFromTime(result); + + return js_NewNumberInRootedValue(cx, result, vp); +} + +static JSBool +date_getMinutes(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble result; + + if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result)) + return JS_FALSE; + + if (JSDOUBLE_IS_FINITE(result)) + result = MinFromTime(result); + + return js_NewNumberInRootedValue(cx, result, vp); +} + +static JSBool +date_getUTCMinutes(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble result; + + if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result)) + return JS_FALSE; + + if (JSDOUBLE_IS_FINITE(result)) + result = MinFromTime(result); + + return js_NewNumberInRootedValue(cx, result, vp); +} + +/* Date.getSeconds is mapped to getUTCSeconds */ + +static JSBool +date_getUTCSeconds(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble result; + + if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result)) + return JS_FALSE; + + if (JSDOUBLE_IS_FINITE(result)) + result = SecFromTime(result); + + return js_NewNumberInRootedValue(cx, result, vp); +} + +/* Date.getMilliseconds is mapped to getUTCMilliseconds */ + +static JSBool +date_getUTCMilliseconds(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble result; + + if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result)) + return JS_FALSE; + + if (JSDOUBLE_IS_FINITE(result)) + result = msFromTime(result); + + return js_NewNumberInRootedValue(cx, result, vp); +} + +static JSBool +date_getTimezoneOffset(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *obj; + jsdouble utctime, localtime, result; + + obj = JS_THIS_OBJECT(cx, vp); + if (!GetUTCTime(cx, obj, vp, &utctime)) + return JS_FALSE; + if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime)) + return JS_FALSE; + + /* + * Return the time zone offset in minutes for the current locale that is + * appropriate for this time. This value would be a constant except for + * daylight savings time. + */ + result = (utctime - localtime) / msPerMinute; + return js_NewNumberInRootedValue(cx, result, vp); +} + +static JSBool +date_setTime(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *obj = JS_THIS_OBJECT(cx, vp); + if (!JS_InstanceOf(cx, obj, &js_DateClass, vp + 2)) + return false; + + if (argc == 0) { + SetDateToNaN(cx, obj, vp); + return true; + } + + jsdouble result = js_ValueToNumber(cx, &vp[2]); + if (JSVAL_IS_NULL(vp[2])) + return false; + + return SetUTCTime(cx, obj, TIMECLIP(result), vp); +} + +static JSBool +date_makeTime(JSContext *cx, uintN maxargs, JSBool local, uintN argc, jsval *vp) +{ + JSObject *obj; + jsval *argv; + uintN i; + jsdouble args[4], *argp, *stop; + jsdouble hour, min, sec, msec; + jsdouble lorutime; /* Local or UTC version of *date */ + + jsdouble msec_time; + jsdouble result; + + obj = JS_THIS_OBJECT(cx, vp); + if (!GetUTCTime(cx, obj, vp, &result)) + return false; + + /* just return NaN if the date is already NaN */ + if (!JSDOUBLE_IS_FINITE(result)) + return js_NewNumberInRootedValue(cx, result, vp); + + /* + * Satisfy the ECMA rule that if a function is called with + * fewer arguments than the specified formal arguments, the + * remaining arguments are set to undefined. Seems like all + * the Date.setWhatever functions in ECMA are only varargs + * beyond the first argument; this should be set to undefined + * if it's not given. This means that "d = new Date(); + * d.setMilliseconds()" returns NaN. Blech. + */ + if (argc == 0) { + SetDateToNaN(cx, obj, vp); + return true; + } + if (argc > maxargs) + argc = maxargs; /* clamp argc */ + JS_ASSERT(argc <= 4); + + argv = vp + 2; + for (i = 0; i < argc; i++) { + args[i] = js_ValueToNumber(cx, &argv[i]); + if (JSVAL_IS_NULL(argv[i])) + return false; + if (!JSDOUBLE_IS_FINITE(args[i])) { + SetDateToNaN(cx, obj, vp); + return true; + } + args[i] = js_DoubleToInteger(args[i]); + } + + if (local) + lorutime = LocalTime(result); + else + lorutime = result; + + argp = args; + stop = argp + argc; + if (maxargs >= 4 && argp < stop) + hour = *argp++; + else + hour = HourFromTime(lorutime); + + if (maxargs >= 3 && argp < stop) + min = *argp++; + else + min = MinFromTime(lorutime); + + if (maxargs >= 2 && argp < stop) + sec = *argp++; + else + sec = SecFromTime(lorutime); + + if (maxargs >= 1 && argp < stop) + msec = *argp; + else + msec = msFromTime(lorutime); + + msec_time = MakeTime(hour, min, sec, msec); + result = MakeDate(Day(lorutime), msec_time); + +/* fprintf(stderr, "%f\n", result); */ + + if (local) + result = UTC(result); + +/* fprintf(stderr, "%f\n", result); */ + + return SetUTCTime(cx, obj, TIMECLIP(result), vp); +} + +static JSBool +date_setMilliseconds(JSContext *cx, uintN argc, jsval *vp) +{ + return date_makeTime(cx, 1, JS_TRUE, argc, vp); +} + +static JSBool +date_setUTCMilliseconds(JSContext *cx, uintN argc, jsval *vp) +{ + return date_makeTime(cx, 1, JS_FALSE, argc, vp); +} + +static JSBool +date_setSeconds(JSContext *cx, uintN argc, jsval *vp) +{ + return date_makeTime(cx, 2, JS_TRUE, argc, vp); +} + +static JSBool +date_setUTCSeconds(JSContext *cx, uintN argc, jsval *vp) +{ + return date_makeTime(cx, 2, JS_FALSE, argc, vp); +} + +static JSBool +date_setMinutes(JSContext *cx, uintN argc, jsval *vp) +{ + return date_makeTime(cx, 3, JS_TRUE, argc, vp); +} + +static JSBool +date_setUTCMinutes(JSContext *cx, uintN argc, jsval *vp) +{ + return date_makeTime(cx, 3, JS_FALSE, argc, vp); +} + +static JSBool +date_setHours(JSContext *cx, uintN argc, jsval *vp) +{ + return date_makeTime(cx, 4, JS_TRUE, argc, vp); +} + +static JSBool +date_setUTCHours(JSContext *cx, uintN argc, jsval *vp) +{ + return date_makeTime(cx, 4, JS_FALSE, argc, vp); +} + +static JSBool +date_makeDate(JSContext *cx, uintN maxargs, JSBool local, uintN argc, jsval *vp) +{ + JSObject *obj; + jsval *argv; + uintN i; + jsdouble lorutime; /* local or UTC version of *date */ + jsdouble args[3], *argp, *stop; + jsdouble year, month, day; + jsdouble result; + + obj = JS_THIS_OBJECT(cx, vp); + if (!GetUTCTime(cx, obj, vp, &result)) + return false; + + /* see complaint about ECMA in date_MakeTime */ + if (argc == 0) { + SetDateToNaN(cx, obj, vp); + return true; + } + if (argc > maxargs) + argc = maxargs; /* clamp argc */ + JS_ASSERT(1 <= argc && argc <= 3); + + argv = vp + 2; + for (i = 0; i < argc; i++) { + args[i] = js_ValueToNumber(cx, &argv[i]); + if (JSVAL_IS_NULL(argv[i])) + return JS_FALSE; + if (!JSDOUBLE_IS_FINITE(args[i])) { + SetDateToNaN(cx, obj, vp); + return true; + } + args[i] = js_DoubleToInteger(args[i]); + } + + /* return NaN if date is NaN and we're not setting the year, + * If we are, use 0 as the time. */ + if (!(JSDOUBLE_IS_FINITE(result))) { + if (maxargs < 3) + return js_NewNumberInRootedValue(cx, result, vp); + lorutime = +0.; + } else { + lorutime = local ? LocalTime(result) : result; + } + + argp = args; + stop = argp + argc; + if (maxargs >= 3 && argp < stop) + year = *argp++; + else + year = YearFromTime(lorutime); + + if (maxargs >= 2 && argp < stop) + month = *argp++; + else + month = MonthFromTime(lorutime); + + if (maxargs >= 1 && argp < stop) + day = *argp++; + else + day = DateFromTime(lorutime); + + day = MakeDay(year, month, day); /* day within year */ + result = MakeDate(day, TimeWithinDay(lorutime)); + + if (local) + result = UTC(result); + + return SetUTCTime(cx, obj, TIMECLIP(result), vp); +} + +static JSBool +date_setDate(JSContext *cx, uintN argc, jsval *vp) +{ + return date_makeDate(cx, 1, JS_TRUE, argc, vp); +} + +static JSBool +date_setUTCDate(JSContext *cx, uintN argc, jsval *vp) +{ + return date_makeDate(cx, 1, JS_FALSE, argc, vp); +} + +static JSBool +date_setMonth(JSContext *cx, uintN argc, jsval *vp) +{ + return date_makeDate(cx, 2, JS_TRUE, argc, vp); +} + +static JSBool +date_setUTCMonth(JSContext *cx, uintN argc, jsval *vp) +{ + return date_makeDate(cx, 2, JS_FALSE, argc, vp); +} + +static JSBool +date_setFullYear(JSContext *cx, uintN argc, jsval *vp) +{ + return date_makeDate(cx, 3, JS_TRUE, argc, vp); +} + +static JSBool +date_setUTCFullYear(JSContext *cx, uintN argc, jsval *vp) +{ + return date_makeDate(cx, 3, JS_FALSE, argc, vp); +} + +static JSBool +date_setYear(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *obj = JS_THIS_OBJECT(cx, vp); + + jsdouble result; + if (!GetUTCTime(cx, obj, vp, &result)) + return false; + + if (argc == 0) { + /* Call this only after GetUTCTime has verified that obj is Date. */ + SetDateToNaN(cx, obj, vp); + return true; + } + + jsdouble year = js_ValueToNumber(cx, &vp[2]); + if (JSVAL_IS_NULL(vp[2])) + return false; + if (!JSDOUBLE_IS_FINITE(year)) { + SetDateToNaN(cx, obj, vp); + return true; + } + year = js_DoubleToInteger(year); + if (year >= 0 && year <= 99) + year += 1900; + + jsdouble t = JSDOUBLE_IS_FINITE(result) ? LocalTime(result) : +0.0; + jsdouble day = MakeDay(year, MonthFromTime(t), DateFromTime(t)); + result = MakeDate(day, TimeWithinDay(t)); + result = UTC(result); + + return SetUTCTime(cx, obj, TIMECLIP(result), vp); +} + +/* constants for toString, toUTCString */ +static char js_NaN_date_str[] = "Invalid Date"; +static const char* days[] = +{ + "Sun","Mon","Tue","Wed","Thu","Fri","Sat" +}; +static const char* months[] = +{ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + + +// Avoid dependence on PRMJ_FormatTimeUSEnglish, because it +// requires a PRMJTime... which only has 16-bit years. Sub-ECMA. +static void +print_gmt_string(char* buf, size_t size, jsdouble utctime) +{ + JS_snprintf(buf, size, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT", + days[WeekDay(utctime)], + DateFromTime(utctime), + months[MonthFromTime(utctime)], + YearFromTime(utctime), + HourFromTime(utctime), + MinFromTime(utctime), + SecFromTime(utctime)); +} + +static void +print_iso_string(char* buf, size_t size, jsdouble utctime) +{ + JS_snprintf(buf, size, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ", + YearFromTime(utctime), + MonthFromTime(utctime) + 1, + DateFromTime(utctime), + HourFromTime(utctime), + MinFromTime(utctime), + SecFromTime(utctime), + msFromTime(utctime)); +} + +static JSBool +date_utc_format(JSContext *cx, jsval *vp, + void (*printFunc)(char*, size_t, jsdouble)) +{ + char buf[100]; + JSString *str; + jsdouble utctime; + + if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime)) + return JS_FALSE; + + if (!JSDOUBLE_IS_FINITE(utctime)) { + JS_snprintf(buf, sizeof buf, js_NaN_date_str); + } else { + (*printFunc)(buf, sizeof buf, utctime); + } + str = JS_NewStringCopyZ(cx, buf); + if (!str) + return JS_FALSE; + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +date_toGMTString(JSContext *cx, uintN argc, jsval *vp) +{ + return date_utc_format(cx, vp, print_gmt_string); +} + +static JSBool +date_toISOString(JSContext *cx, uintN argc, jsval *vp) +{ + return date_utc_format(cx, vp, print_iso_string); +} + +/* for Date.toLocaleString; interface to PRMJTime date struct. + */ +static void +new_explode(jsdouble timeval, PRMJTime *split) +{ + jsint year = YearFromTime(timeval); + + split->tm_usec = (int32) msFromTime(timeval) * 1000; + split->tm_sec = (int8) SecFromTime(timeval); + split->tm_min = (int8) MinFromTime(timeval); + split->tm_hour = (int8) HourFromTime(timeval); + split->tm_mday = (int8) DateFromTime(timeval); + split->tm_mon = (int8) MonthFromTime(timeval); + split->tm_wday = (int8) WeekDay(timeval); + split->tm_year = year; + split->tm_yday = (int16) DayWithinYear(timeval, year); + + /* not sure how this affects things, but it doesn't seem + to matter. */ + split->tm_isdst = (DaylightSavingTA(timeval) != 0); +} + +typedef enum formatspec { + FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME +} formatspec; + +/* helper function */ +static JSBool +date_format(JSContext *cx, jsdouble date, formatspec format, jsval *rval) +{ + char buf[100]; + JSString *str; + char tzbuf[100]; + JSBool usetz; + size_t i, tzlen; + PRMJTime split; + + if (!JSDOUBLE_IS_FINITE(date)) { + JS_snprintf(buf, sizeof buf, js_NaN_date_str); + } else { + jsdouble local = LocalTime(date); + + /* offset from GMT in minutes. The offset includes daylight savings, + if it applies. */ + jsint minutes = (jsint) floor(AdjustTime(date) / msPerMinute); + + /* map 510 minutes to 0830 hours */ + intN offset = (minutes / 60) * 100 + minutes % 60; + + /* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is + * printed as 'GMT-0800' rather than as 'PST' to avoid + * operating-system dependence on strftime (which + * PRMJ_FormatTimeUSEnglish calls, for %Z only.) win32 prints + * PST as 'Pacific Standard Time.' This way we always know + * what we're getting, and can parse it if we produce it. + * The OS TZA string is included as a comment. + */ + + /* get a timezone string from the OS to include as a + comment. */ + new_explode(date, &split); + if (PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &split) != 0) { + + /* Decide whether to use the resulting timezone string. + * + * Reject it if it contains any non-ASCII, non-alphanumeric + * characters. It's then likely in some other character + * encoding, and we probably won't display it correctly. + */ + usetz = JS_TRUE; + tzlen = strlen(tzbuf); + if (tzlen > 100) { + usetz = JS_FALSE; + } else { + for (i = 0; i < tzlen; i++) { + jschar c = tzbuf[i]; + if (c > 127 || + !(isalpha(c) || isdigit(c) || + c == ' ' || c == '(' || c == ')')) { + usetz = JS_FALSE; + } + } + } + + /* Also reject it if it's not parenthesized or if it's '()'. */ + if (tzbuf[0] != '(' || tzbuf[1] == ')') + usetz = JS_FALSE; + } else + usetz = JS_FALSE; + + switch (format) { + case FORMATSPEC_FULL: + /* + * Avoid dependence on PRMJ_FormatTimeUSEnglish, because it + * requires a PRMJTime... which only has 16-bit years. Sub-ECMA. + */ + /* Tue Oct 31 2000 09:41:40 GMT-0800 (PST) */ + JS_snprintf(buf, sizeof buf, + "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s", + days[WeekDay(local)], + months[MonthFromTime(local)], + DateFromTime(local), + YearFromTime(local), + HourFromTime(local), + MinFromTime(local), + SecFromTime(local), + offset, + usetz ? " " : "", + usetz ? tzbuf : ""); + break; + case FORMATSPEC_DATE: + /* Tue Oct 31 2000 */ + JS_snprintf(buf, sizeof buf, + "%s %s %.2d %.4d", + days[WeekDay(local)], + months[MonthFromTime(local)], + DateFromTime(local), + YearFromTime(local)); + break; + case FORMATSPEC_TIME: + /* 09:41:40 GMT-0800 (PST) */ + JS_snprintf(buf, sizeof buf, + "%.2d:%.2d:%.2d GMT%+.4d%s%s", + HourFromTime(local), + MinFromTime(local), + SecFromTime(local), + offset, + usetz ? " " : "", + usetz ? tzbuf : ""); + break; + } + } + + str = JS_NewStringCopyZ(cx, buf); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +date_toLocaleHelper(JSContext *cx, const char *format, jsval *vp) +{ + JSObject *obj; + char buf[100]; + JSString *str; + PRMJTime split; + jsdouble utctime; + + obj = JS_THIS_OBJECT(cx, vp); + if (!GetUTCTime(cx, obj, vp, &utctime)) + return JS_FALSE; + + if (!JSDOUBLE_IS_FINITE(utctime)) { + JS_snprintf(buf, sizeof buf, js_NaN_date_str); + } else { + intN result_len; + jsdouble local = LocalTime(utctime); + new_explode(local, &split); + + /* let PRMJTime format it. */ + result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split); + + /* If it failed, default to toString. */ + if (result_len == 0) + return date_format(cx, utctime, FORMATSPEC_FULL, vp); + + /* Hacked check against undesired 2-digit year 00/00/00 form. */ + if (strcmp(format, "%x") == 0 && result_len >= 6 && + /* Format %x means use OS settings, which may have 2-digit yr, so + hack end of 3/11/22 or 11.03.22 or 11Mar22 to use 4-digit yr...*/ + !isdigit(buf[result_len - 3]) && + isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1]) && + /* ...but not if starts with 4-digit year, like 2022/3/11. */ + !(isdigit(buf[0]) && isdigit(buf[1]) && + isdigit(buf[2]) && isdigit(buf[3]))) { + JS_snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2), + "%d", js_DateGetYear(cx, obj)); + } + + } + + if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode) + return cx->localeCallbacks->localeToUnicode(cx, buf, vp); + + str = JS_NewStringCopyZ(cx, buf); + if (!str) + return JS_FALSE; + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +date_toLocaleString(JSContext *cx, uintN argc, jsval *vp) +{ + /* Use '%#c' for windows, because '%c' is + * backward-compatible and non-y2k with msvc; '%#c' requests that a + * full year be used in the result string. + */ + return date_toLocaleHelper(cx, +#if defined(_WIN32) && !defined(__MWERKS__) + "%#c" +#else + "%c" +#endif + , vp); +} + +static JSBool +date_toLocaleDateString(JSContext *cx, uintN argc, jsval *vp) +{ + /* Use '%#x' for windows, because '%x' is + * backward-compatible and non-y2k with msvc; '%#x' requests that a + * full year be used in the result string. + */ + return date_toLocaleHelper(cx, +#if defined(_WIN32) && !defined(__MWERKS__) + "%#x" +#else + "%x" +#endif + , vp); +} + +static JSBool +date_toLocaleTimeString(JSContext *cx, uintN argc, jsval *vp) +{ + return date_toLocaleHelper(cx, "%X", vp); +} + +static JSBool +date_toLocaleFormat(JSContext *cx, uintN argc, jsval *vp) +{ + JSString *fmt; + const char *fmtbytes; + + if (argc == 0) + return date_toLocaleString(cx, argc, vp); + + fmt = js_ValueToString(cx, vp[2]); + if (!fmt) + return JS_FALSE; + vp[2] = STRING_TO_JSVAL(fmt); + fmtbytes = js_GetStringBytes(cx, fmt); + if (!fmtbytes) + return JS_FALSE; + + return date_toLocaleHelper(cx, fmtbytes, vp); +} + +static JSBool +date_toTimeString(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble utctime; + + if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime)) + return JS_FALSE; + return date_format(cx, utctime, FORMATSPEC_TIME, vp); +} + +static JSBool +date_toDateString(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble utctime; + + if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime)) + return JS_FALSE; + return date_format(cx, utctime, FORMATSPEC_DATE, vp); +} + +#if JS_HAS_TOSOURCE +#include +#include "jsdtoa.h" + +static JSBool +date_toSource(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble utctime; + char buf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr, *bytes; + JSString *str; + + if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime)) + return JS_FALSE; + + numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, utctime); + if (!numStr) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + + bytes = JS_smprintf("(new %s(%s))", js_Date_str, numStr); + if (!bytes) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + + str = JS_NewString(cx, bytes, strlen(bytes)); + if (!str) { + js_free(bytes); + return JS_FALSE; + } + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; +} +#endif + +static JSBool +date_toString(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble utctime; + + if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime)) + return JS_FALSE; + return date_format(cx, utctime, FORMATSPEC_FULL, vp); +} + +#ifdef JS_TRACER +static jsval FASTCALL +date_valueOf_tn(JSContext* cx, JSObject* obj, JSString* str) +{ + JS_ASSERT(JS_InstanceOf(cx, obj, &js_DateClass, NULL)); + jsdouble t = *JSVAL_TO_DOUBLE(obj->fslots[JSSLOT_UTC_TIME]); + + JSString* number_str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]); + jsval v; + if (js_EqualStrings(str, number_str)) { + if (!js_NewNumberInRootedValue(cx, t, &v)) + return JSVAL_ERROR_COOKIE; + } else { + if (!date_format(cx, t, FORMATSPEC_FULL, &v)) + return JSVAL_ERROR_COOKIE; + } + return v; +} +#endif + +static JSBool +date_valueOf(JSContext *cx, uintN argc, jsval *vp) +{ + JSString *str, *number_str; + + /* It is an error to call date_valueOf on a non-date object, but we don't + * need to check for that explicitly here because every path calls + * GetUTCTime, which does the check. + */ + + /* If called directly with no arguments, convert to a time number. */ + if (argc == 0) + return date_getTime(cx, argc, vp); + + /* Convert to number only if the hint was given, otherwise favor string. */ + str = js_ValueToString(cx, vp[2]); + if (!str) + return JS_FALSE; + number_str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]); + if (js_EqualStrings(str, number_str)) + return date_getTime(cx, argc, vp); + return date_toString(cx, argc, vp); +} + +// Don't really need an argument here, but we don't support arg-less builtins +JS_DEFINE_TRCINFO_1(date_now, + (1, (static, DOUBLE, date_now_tn, CONTEXT, 0, 0))) + +static JSFunctionSpec date_static_methods[] = { + JS_FN("UTC", date_UTC, MAXARGS,0), + JS_FN("parse", date_parse, 1,0), + JS_TN("now", date_now, 0,0, &date_now_trcinfo), + JS_FS_END +}; + +JS_DEFINE_TRCINFO_1(date_valueOf, + (3, (static, JSVAL_RETRY, date_valueOf_tn, CONTEXT, THIS, STRING, 0, 0))) + +static JSFunctionSpec date_methods[] = { + JS_FN("getTime", date_getTime, 0,0), + JS_FN("getTimezoneOffset", date_getTimezoneOffset, 0,0), + JS_FN("getYear", date_getYear, 0,0), + JS_FN("getFullYear", date_getFullYear, 0,0), + JS_FN("getUTCFullYear", date_getUTCFullYear, 0,0), + JS_FN("getMonth", date_getMonth, 0,0), + JS_FN("getUTCMonth", date_getUTCMonth, 0,0), + JS_FN("getDate", date_getDate, 0,0), + JS_FN("getUTCDate", date_getUTCDate, 0,0), + JS_FN("getDay", date_getDay, 0,0), + JS_FN("getUTCDay", date_getUTCDay, 0,0), + JS_FN("getHours", date_getHours, 0,0), + JS_FN("getUTCHours", date_getUTCHours, 0,0), + JS_FN("getMinutes", date_getMinutes, 0,0), + JS_FN("getUTCMinutes", date_getUTCMinutes, 0,0), + JS_FN("getSeconds", date_getUTCSeconds, 0,0), + JS_FN("getUTCSeconds", date_getUTCSeconds, 0,0), + JS_FN("getMilliseconds", date_getUTCMilliseconds, 0,0), + JS_FN("getUTCMilliseconds", date_getUTCMilliseconds, 0,0), + JS_FN("setTime", date_setTime, 1,0), + JS_FN("setYear", date_setYear, 1,0), + JS_FN("setFullYear", date_setFullYear, 3,0), + JS_FN("setUTCFullYear", date_setUTCFullYear, 3,0), + JS_FN("setMonth", date_setMonth, 2,0), + JS_FN("setUTCMonth", date_setUTCMonth, 2,0), + JS_FN("setDate", date_setDate, 1,0), + JS_FN("setUTCDate", date_setUTCDate, 1,0), + JS_FN("setHours", date_setHours, 4,0), + JS_FN("setUTCHours", date_setUTCHours, 4,0), + JS_FN("setMinutes", date_setMinutes, 3,0), + JS_FN("setUTCMinutes", date_setUTCMinutes, 3,0), + JS_FN("setSeconds", date_setSeconds, 2,0), + JS_FN("setUTCSeconds", date_setUTCSeconds, 2,0), + JS_FN("setMilliseconds", date_setMilliseconds, 1,0), + JS_FN("setUTCMilliseconds", date_setUTCMilliseconds, 1,0), + JS_FN("toUTCString", date_toGMTString, 0,0), + JS_FN(js_toLocaleString_str, date_toLocaleString, 0,0), + JS_FN("toLocaleDateString", date_toLocaleDateString, 0,0), + JS_FN("toLocaleTimeString", date_toLocaleTimeString, 0,0), + JS_FN("toLocaleFormat", date_toLocaleFormat, 0,0), + JS_FN("toDateString", date_toDateString, 0,0), + JS_FN("toTimeString", date_toTimeString, 0,0), + JS_FN("toISOString", date_toISOString, 0,0), + JS_FN(js_toJSON_str, date_toISOString, 0,0), + +#if JS_HAS_TOSOURCE + JS_FN(js_toSource_str, date_toSource, 0,0), +#endif + JS_FN(js_toString_str, date_toString, 0,0), + JS_TN(js_valueOf_str, date_valueOf, 0,0, &date_valueOf_trcinfo), + JS_FS_END +}; + +JSBool +js_Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + /* Date called as function. */ + if (!JS_IsConstructing(cx)) + return date_format(cx, NowAsMillis(), FORMATSPEC_FULL, rval); + + /* Date called as constructor. */ + jsdouble d; + if (argc == 0) { + d = NowAsMillis(); + } else if (argc == 1) { + if (!JSVAL_IS_STRING(argv[0])) { + /* the argument is a millisecond number */ + d = js_ValueToNumber(cx, &argv[0]); + if (JSVAL_IS_NULL(argv[0])) + return JS_FALSE; + d = TIMECLIP(d); + } else { + /* the argument is a string; parse it. */ + JSString *str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); + + if (!date_parseString(str, &d)) + d = js_NaN; + else + d = TIMECLIP(d); + } + } else { + jsdouble msec_time; + if (!date_msecFromArgs(cx, argc, argv, &msec_time)) + return JS_FALSE; + + if (JSDOUBLE_IS_FINITE(msec_time)) { + msec_time = UTC(msec_time); + msec_time = TIMECLIP(msec_time); + } + d = msec_time; + } + return SetUTCTime(cx, obj, d); +} + +JSObject * +js_InitDateClass(JSContext *cx, JSObject *obj) +{ + JSObject *proto; + + /* set static LocalTZA */ + LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond); + proto = js_InitClass(cx, obj, NULL, &js_DateClass, js_Date, MAXARGS, + NULL, date_methods, NULL, date_static_methods); + if (!proto) + return NULL; + + SetDateToNaN(cx, proto); + + /* Alias toUTCString with toGMTString. (ECMA B.2.6) */ + if (!JS_AliasProperty(cx, proto, "toUTCString", "toGMTString")) + return NULL; + + return proto; +} + +JS_FRIEND_API(JSObject *) +js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time) +{ + JSObject *obj = js_NewObject(cx, &js_DateClass, NULL, NULL); + if (!obj || !SetUTCTime(cx, obj, msec_time)) + return NULL; + return obj; +} + +JS_FRIEND_API(JSObject *) +js_NewDateObject(JSContext* cx, int year, int mon, int mday, + int hour, int min, int sec) +{ + JSObject *obj; + jsdouble msec_time; + + JS_ASSERT(mon < 12); + msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0); + obj = js_NewDateObjectMsec(cx, UTC(msec_time)); + return obj; +} + +JS_FRIEND_API(JSBool) +js_DateIsValid(JSContext *cx, JSObject* obj) +{ + jsdouble utctime; + return GetUTCTime(cx, obj, NULL, &utctime) && !JSDOUBLE_IS_NaN(utctime); +} + +JS_FRIEND_API(int) +js_DateGetYear(JSContext *cx, JSObject* obj) +{ + jsdouble localtime; + + /* Preserve legacy API behavior of returning 0 for invalid dates. */ + if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) || + JSDOUBLE_IS_NaN(localtime)) { + return 0; + } + + return (int) YearFromTime(localtime); +} + +JS_FRIEND_API(int) +js_DateGetMonth(JSContext *cx, JSObject* obj) +{ + jsdouble localtime; + + if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) || + JSDOUBLE_IS_NaN(localtime)) { + return 0; + } + + return (int) MonthFromTime(localtime); +} + +JS_FRIEND_API(int) +js_DateGetDate(JSContext *cx, JSObject* obj) +{ + jsdouble localtime; + + if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) || + JSDOUBLE_IS_NaN(localtime)) { + return 0; + } + + return (int) DateFromTime(localtime); +} + +JS_FRIEND_API(int) +js_DateGetHours(JSContext *cx, JSObject* obj) +{ + jsdouble localtime; + + if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) || + JSDOUBLE_IS_NaN(localtime)) { + return 0; + } + + return (int) HourFromTime(localtime); +} + +JS_FRIEND_API(int) +js_DateGetMinutes(JSContext *cx, JSObject* obj) +{ + jsdouble localtime; + + if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) || + JSDOUBLE_IS_NaN(localtime)) { + return 0; + } + + return (int) MinFromTime(localtime); +} + +JS_FRIEND_API(int) +js_DateGetSeconds(JSContext *cx, JSObject* obj) +{ + jsdouble utctime; + + if (!GetUTCTime(cx, obj, NULL, &utctime) || JSDOUBLE_IS_NaN(utctime)) + return 0; + + return (int) SecFromTime(utctime); +} + +JS_FRIEND_API(void) +js_DateSetYear(JSContext *cx, JSObject *obj, int year) +{ + jsdouble local; + + if (!GetAndCacheLocalTime(cx, obj, NULL, &local)) + return; + + /* reset date if it was NaN */ + if (JSDOUBLE_IS_NaN(local)) + local = 0; + + local = date_msecFromDate(year, + MonthFromTime(local), + DateFromTime(local), + HourFromTime(local), + MinFromTime(local), + SecFromTime(local), + msFromTime(local)); + + /* SetUTCTime also invalidates local time cache. */ + SetUTCTime(cx, obj, UTC(local)); +} + +JS_FRIEND_API(void) +js_DateSetMonth(JSContext *cx, JSObject *obj, int month) +{ + jsdouble local; + + JS_ASSERT(month < 12); + + if (!GetAndCacheLocalTime(cx, obj, NULL, &local)) + return; + + /* bail if date was NaN */ + if (JSDOUBLE_IS_NaN(local)) + return; + + local = date_msecFromDate(YearFromTime(local), + month, + DateFromTime(local), + HourFromTime(local), + MinFromTime(local), + SecFromTime(local), + msFromTime(local)); + SetUTCTime(cx, obj, UTC(local)); +} + +JS_FRIEND_API(void) +js_DateSetDate(JSContext *cx, JSObject *obj, int date) +{ + jsdouble local; + + if (!GetAndCacheLocalTime(cx, obj, NULL, &local)) + return; + + if (JSDOUBLE_IS_NaN(local)) + return; + + local = date_msecFromDate(YearFromTime(local), + MonthFromTime(local), + date, + HourFromTime(local), + MinFromTime(local), + SecFromTime(local), + msFromTime(local)); + SetUTCTime(cx, obj, UTC(local)); +} + +JS_FRIEND_API(void) +js_DateSetHours(JSContext *cx, JSObject *obj, int hours) +{ + jsdouble local; + + if (!GetAndCacheLocalTime(cx, obj, NULL, &local)) + return; + + if (JSDOUBLE_IS_NaN(local)) + return; + local = date_msecFromDate(YearFromTime(local), + MonthFromTime(local), + DateFromTime(local), + hours, + MinFromTime(local), + SecFromTime(local), + msFromTime(local)); + SetUTCTime(cx, obj, UTC(local)); +} + +JS_FRIEND_API(void) +js_DateSetMinutes(JSContext *cx, JSObject *obj, int minutes) +{ + jsdouble local; + + if (!GetAndCacheLocalTime(cx, obj, NULL, &local)) + return; + + if (JSDOUBLE_IS_NaN(local)) + return; + local = date_msecFromDate(YearFromTime(local), + MonthFromTime(local), + DateFromTime(local), + HourFromTime(local), + minutes, + SecFromTime(local), + msFromTime(local)); + SetUTCTime(cx, obj, UTC(local)); +} + +JS_FRIEND_API(void) +js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds) +{ + jsdouble local; + + if (!GetAndCacheLocalTime(cx, obj, NULL, &local)) + return; + + if (JSDOUBLE_IS_NaN(local)) + return; + local = date_msecFromDate(YearFromTime(local), + MonthFromTime(local), + DateFromTime(local), + HourFromTime(local), + MinFromTime(local), + seconds, + msFromTime(local)); + SetUTCTime(cx, obj, UTC(local)); +} + +JS_FRIEND_API(jsdouble) +js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj) +{ + jsdouble utctime; + if (!GetUTCTime(cx, obj, NULL, &utctime)) + return 0; + return utctime; +} + +#ifdef JS_THREADSAFE +#include "prinrval.h" + +JS_FRIEND_API(uint32) +js_IntervalNow() +{ + return uint32(PR_IntervalToMilliseconds(PR_IntervalNow())); +} + +#else /* !JS_THREADSAFE */ + +JS_FRIEND_API(uint32) +js_IntervalNow() +{ + return uint32(PRMJ_Now() / PRMJ_USEC_PER_MSEC); +} +#endif diff --git a/ape-server/deps/js/src/jsdate.h b/ape-server/deps/js/src/jsdate.h new file mode 100755 index 0000000..238994e --- /dev/null +++ b/ape-server/deps/js/src/jsdate.h @@ -0,0 +1,133 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * JS Date class interface. + */ + +#ifndef jsdate_h___ +#define jsdate_h___ + +JS_BEGIN_EXTERN_C + +extern JSClass js_DateClass; + +extern JSObject * +js_InitDateClass(JSContext *cx, JSObject *obj); + +/* + * These functions provide a C interface to the date/time object + */ + +/* + * Construct a new Date Object from a time value given in milliseconds UTC + * since the epoch. + */ +extern JS_FRIEND_API(JSObject*) +js_NewDateObjectMsec(JSContext* cx, jsdouble msec_time); + +/* + * Construct a new Date Object from an exploded local time value. + * + * Assert that mon < 12 to help catch off-by-one user errors, which are common + * due to the 0-based month numbering copied into JS from Java (java.util.Date + * in 1995). js_DateSetMonth (below) likewise asserts month < 12. + */ +extern JS_FRIEND_API(JSObject*) +js_NewDateObject(JSContext* cx, int year, int mon, int mday, + int hour, int min, int sec); + +/* + * Detect whether the internal date value is NaN. (Because failure is + * out-of-band for js_DateGet*) + */ +extern JS_FRIEND_API(JSBool) +js_DateIsValid(JSContext *cx, JSObject* obj); + +extern JS_FRIEND_API(int) +js_DateGetYear(JSContext *cx, JSObject* obj); + +extern JS_FRIEND_API(int) +js_DateGetMonth(JSContext *cx, JSObject* obj); + +extern JS_FRIEND_API(int) +js_DateGetDate(JSContext *cx, JSObject* obj); + +extern JS_FRIEND_API(int) +js_DateGetHours(JSContext *cx, JSObject* obj); + +extern JS_FRIEND_API(int) +js_DateGetMinutes(JSContext *cx, JSObject* obj); + +extern JS_FRIEND_API(int) +js_DateGetSeconds(JSContext *cx, JSObject* obj); + +extern JS_FRIEND_API(void) +js_DateSetYear(JSContext *cx, JSObject *obj, int year); + +extern JS_FRIEND_API(void) +js_DateSetMonth(JSContext *cx, JSObject *obj, int month); + +extern JS_FRIEND_API(void) +js_DateSetDate(JSContext *cx, JSObject *obj, int date); + +extern JS_FRIEND_API(void) +js_DateSetHours(JSContext *cx, JSObject *obj, int hours); + +extern JS_FRIEND_API(void) +js_DateSetMinutes(JSContext *cx, JSObject *obj, int minutes); + +extern JS_FRIEND_API(void) +js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds); + +extern JS_FRIEND_API(jsdouble) +js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj); + +typedef uint32 JSIntervalTime; + +extern JS_FRIEND_API(JSIntervalTime) +js_IntervalNow(); + +/* Date constructor native. Exposed only so the JIT can know its address. */ +JSBool +js_Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); + +JS_END_EXTERN_C + +#endif /* jsdate_h___ */ diff --git a/ape-server/deps/js/src/jsdbgapi.cpp b/ape-server/deps/js/src/jsdbgapi.cpp new file mode 100755 index 0000000..a7f83ee --- /dev/null +++ b/ape-server/deps/js/src/jsdbgapi.cpp @@ -0,0 +1,2539 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * JS debugging API. + */ +#include +#include "jstypes.h" +#include "jsstdint.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jsclist.h" +#include "jsapi.h" +#include "jscntxt.h" +#include "jsversion.h" +#include "jsdbgapi.h" +#include "jsemit.h" +#include "jsfun.h" +#include "jsgc.h" +#include "jsinterp.h" +#include "jslock.h" +#include "jsobj.h" +#include "jsopcode.h" +#include "jsparse.h" +#include "jsscope.h" +#include "jsscript.h" +#include "jsstaticcheck.h" +#include "jsstr.h" + +#include "jsatominlines.h" +#include "jsscopeinlines.h" + +#include "jsautooplen.h" + +typedef struct JSTrap { + JSCList links; + JSScript *script; + jsbytecode *pc; + JSOp op; + JSTrapHandler handler; + void *closure; +} JSTrap; + +#define DBG_LOCK(rt) JS_ACQUIRE_LOCK((rt)->debuggerLock) +#define DBG_UNLOCK(rt) JS_RELEASE_LOCK((rt)->debuggerLock) +#define DBG_LOCK_EVAL(rt,expr) (DBG_LOCK(rt), (expr), DBG_UNLOCK(rt)) + +/* + * NB: FindTrap must be called with rt->debuggerLock acquired. + */ +static JSTrap * +FindTrap(JSRuntime *rt, JSScript *script, jsbytecode *pc) +{ + JSTrap *trap; + + for (trap = (JSTrap *)rt->trapList.next; + &trap->links != &rt->trapList; + trap = (JSTrap *)trap->links.next) { + if (trap->script == script && trap->pc == pc) + return trap; + } + return NULL; +} + +jsbytecode * +js_UntrapScriptCode(JSContext *cx, JSScript *script) +{ + jsbytecode *code; + JSRuntime *rt; + JSTrap *trap; + + code = script->code; + rt = cx->runtime; + DBG_LOCK(rt); + for (trap = (JSTrap *)rt->trapList.next; + &trap->links != + &rt->trapList; + trap = (JSTrap *)trap->links.next) { + if (trap->script == script && + (size_t)(trap->pc - script->code) < script->length) { + if (code == script->code) { + jssrcnote *sn, *notes; + size_t nbytes; + + nbytes = script->length * sizeof(jsbytecode); + notes = script->notes(); + for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) + continue; + nbytes += (sn - notes + 1) * sizeof *sn; + + code = (jsbytecode *) cx->malloc(nbytes); + if (!code) + break; + memcpy(code, script->code, nbytes); + JS_PURGE_GSN_CACHE(cx); + } + code[trap->pc - script->code] = trap->op; + } + } + DBG_UNLOCK(rt); + return code; +} + +JS_PUBLIC_API(JSBool) +JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, + JSTrapHandler handler, void *closure) +{ + JSTrap *junk, *trap, *twin; + JSRuntime *rt; + uint32 sample; + + if (script == JSScript::emptyScript()) { + JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, + NULL, JSMSG_READ_ONLY, "empty script"); + return JS_FALSE; + } + + JS_ASSERT((JSOp) *pc != JSOP_TRAP); + junk = NULL; + rt = cx->runtime; + DBG_LOCK(rt); + trap = FindTrap(rt, script, pc); + if (trap) { + JS_ASSERT(trap->script == script && trap->pc == pc); + JS_ASSERT(*pc == JSOP_TRAP); + } else { + sample = rt->debuggerMutations; + DBG_UNLOCK(rt); + trap = (JSTrap *) cx->malloc(sizeof *trap); + if (!trap) + return JS_FALSE; + trap->closure = NULL; + DBG_LOCK(rt); + twin = (rt->debuggerMutations != sample) + ? FindTrap(rt, script, pc) + : NULL; + if (twin) { + junk = trap; + trap = twin; + } else { + JS_APPEND_LINK(&trap->links, &rt->trapList); + ++rt->debuggerMutations; + trap->script = script; + trap->pc = pc; + trap->op = (JSOp)*pc; + *pc = JSOP_TRAP; + } + } + trap->handler = handler; + trap->closure = closure; + DBG_UNLOCK(rt); + if (junk) + cx->free(junk); + return JS_TRUE; +} + +JS_PUBLIC_API(JSOp) +JS_GetTrapOpcode(JSContext *cx, JSScript *script, jsbytecode *pc) +{ + JSRuntime *rt; + JSTrap *trap; + JSOp op; + + rt = cx->runtime; + DBG_LOCK(rt); + trap = FindTrap(rt, script, pc); + op = trap ? trap->op : (JSOp) *pc; + DBG_UNLOCK(rt); + return op; +} + +static void +DestroyTrapAndUnlock(JSContext *cx, JSTrap *trap) +{ + ++cx->runtime->debuggerMutations; + JS_REMOVE_LINK(&trap->links); + *trap->pc = (jsbytecode)trap->op; + DBG_UNLOCK(cx->runtime); + cx->free(trap); +} + +JS_PUBLIC_API(void) +JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc, + JSTrapHandler *handlerp, void **closurep) +{ + JSTrap *trap; + + DBG_LOCK(cx->runtime); + trap = FindTrap(cx->runtime, script, pc); + if (handlerp) + *handlerp = trap ? trap->handler : NULL; + if (closurep) + *closurep = trap ? trap->closure : NULL; + if (trap) + DestroyTrapAndUnlock(cx, trap); + else + DBG_UNLOCK(cx->runtime); +} + +JS_PUBLIC_API(void) +JS_ClearScriptTraps(JSContext *cx, JSScript *script) +{ + JSRuntime *rt; + JSTrap *trap, *next; + uint32 sample; + + rt = cx->runtime; + DBG_LOCK(rt); + for (trap = (JSTrap *)rt->trapList.next; + &trap->links != &rt->trapList; + trap = next) { + next = (JSTrap *)trap->links.next; + if (trap->script == script) { + sample = rt->debuggerMutations; + DestroyTrapAndUnlock(cx, trap); + DBG_LOCK(rt); + if (rt->debuggerMutations != sample + 1) + next = (JSTrap *)rt->trapList.next; + } + } + DBG_UNLOCK(rt); +} + +JS_PUBLIC_API(void) +JS_ClearAllTraps(JSContext *cx) +{ + JSRuntime *rt; + JSTrap *trap, *next; + uint32 sample; + + rt = cx->runtime; + DBG_LOCK(rt); + for (trap = (JSTrap *)rt->trapList.next; + &trap->links != &rt->trapList; + trap = next) { + next = (JSTrap *)trap->links.next; + sample = rt->debuggerMutations; + DestroyTrapAndUnlock(cx, trap); + DBG_LOCK(rt); + if (rt->debuggerMutations != sample + 1) + next = (JSTrap *)rt->trapList.next; + } + DBG_UNLOCK(rt); +} + +/* + * NB: js_MarkTraps does not acquire cx->runtime->debuggerLock, since the + * debugger should never be racing with the GC (i.e., the debugger must + * respect the request model). + */ +void +js_MarkTraps(JSTracer *trc) +{ + JSRuntime *rt = trc->context->runtime; + + for (JSTrap *trap = (JSTrap *) rt->trapList.next; + &trap->links != &rt->trapList; + trap = (JSTrap *) trap->links.next) { + if (trap->closure) { + JS_SET_TRACING_NAME(trc, "trap->closure"); + js_CallValueTracerIfGCThing(trc, (jsval) trap->closure); + } + } +} + +JS_PUBLIC_API(JSTrapStatus) +JS_HandleTrap(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval) +{ + JSTrap *trap; + jsint op; + JSTrapStatus status; + + DBG_LOCK(cx->runtime); + trap = FindTrap(cx->runtime, script, pc); + JS_ASSERT(!trap || trap->handler); + if (!trap) { + op = (JSOp) *pc; + DBG_UNLOCK(cx->runtime); + + /* Defend against "pc for wrong script" API usage error. */ + JS_ASSERT(op != JSOP_TRAP); + +#ifdef JS_THREADSAFE + /* If the API was abused, we must fail for want of the real op. */ + if (op == JSOP_TRAP) + return JSTRAP_ERROR; + + /* Assume a race with a debugger thread and try to carry on. */ + *rval = INT_TO_JSVAL(op); + return JSTRAP_CONTINUE; +#else + /* Always fail if single-threaded (must be an API usage error). */ + return JSTRAP_ERROR; +#endif + } + DBG_UNLOCK(cx->runtime); + + /* + * It's important that we not use 'trap->' after calling the callback -- + * the callback might remove the trap! + */ + op = (jsint)trap->op; + status = trap->handler(cx, script, pc, rval, trap->closure); + if (status == JSTRAP_CONTINUE) { + /* By convention, return the true op to the interpreter in rval. */ + *rval = INT_TO_JSVAL(op); + } + return status; +} + +#ifdef JS_TRACER +static void +JITInhibitingHookChange(JSRuntime *rt, bool wasInhibited) +{ + if (wasInhibited) { + if (!rt->debuggerInhibitsJIT()) { + for (JSCList *cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next) + js_ContextFromLinkField(cl)->updateJITEnabled(); + } + } else if (rt->debuggerInhibitsJIT()) { + for (JSCList *cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next) + js_ContextFromLinkField(cl)->jitEnabled = false; + } +} + +static void +LeaveTraceRT(JSRuntime *rt) +{ + JSThreadData *data = js_CurrentThreadData(rt); + JSContext *cx = data ? data->traceMonitor.tracecx : NULL; + JS_UNLOCK_GC(rt); + + if (cx) + js_LeaveTrace(cx); +} +#endif + +JS_PUBLIC_API(JSBool) +JS_SetInterrupt(JSRuntime *rt, JSTrapHandler handler, void *closure) +{ +#ifdef JS_TRACER + JS_LOCK_GC(rt); + bool wasInhibited = rt->debuggerInhibitsJIT(); +#endif + rt->globalDebugHooks.interruptHandler = handler; + rt->globalDebugHooks.interruptHandlerData = closure; +#ifdef JS_TRACER + JITInhibitingHookChange(rt, wasInhibited); + JS_UNLOCK_GC(rt); + LeaveTraceRT(rt); +#endif + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_ClearInterrupt(JSRuntime *rt, JSTrapHandler *handlerp, void **closurep) +{ +#ifdef JS_TRACER + JS_LOCK_GC(rt); + bool wasInhibited = rt->debuggerInhibitsJIT(); +#endif + if (handlerp) + *handlerp = rt->globalDebugHooks.interruptHandler; + if (closurep) + *closurep = rt->globalDebugHooks.interruptHandlerData; + rt->globalDebugHooks.interruptHandler = 0; + rt->globalDebugHooks.interruptHandlerData = 0; +#ifdef JS_TRACER + JITInhibitingHookChange(rt, wasInhibited); + JS_UNLOCK_GC(rt); +#endif + return JS_TRUE; +} + +/************************************************************************/ + +typedef struct JSWatchPoint { + JSCList links; + JSObject *object; /* weak link, see js_FinalizeObject */ + JSScopeProperty *sprop; + JSPropertyOp setter; + JSWatchPointHandler handler; + JSObject *closure; + uintN flags; +} JSWatchPoint; + +#define JSWP_LIVE 0x1 /* live because set and not cleared */ +#define JSWP_HELD 0x2 /* held while running handler/setter */ + +static bool +IsWatchedProperty(JSContext *cx, JSScopeProperty *sprop); + +/* + * NB: DropWatchPointAndUnlock releases cx->runtime->debuggerLock in all cases. + */ +static JSBool +DropWatchPointAndUnlock(JSContext *cx, JSWatchPoint *wp, uintN flag) +{ + JSBool ok; + JSScopeProperty *sprop; + JSScope *scope; + JSPropertyOp setter; + + ok = JS_TRUE; + wp->flags &= ~flag; + if (wp->flags != 0) { + DBG_UNLOCK(cx->runtime); + return ok; + } + + /* + * Remove wp from the list, then if there are no other watchpoints for + * wp->sprop in any scope, restore wp->sprop->setter from wp. + */ + ++cx->runtime->debuggerMutations; + JS_REMOVE_LINK(&wp->links); + sprop = wp->sprop; + + /* + * Passing null for the scope parameter tells js_GetWatchedSetter to find + * any watch point for sprop, and not to lock or unlock rt->debuggerLock. + * If js_ChangeNativePropertyAttrs fails, propagate failure after removing + * wp->closure's root and freeing wp. + */ + setter = js_GetWatchedSetter(cx->runtime, NULL, sprop); + DBG_UNLOCK(cx->runtime); + if (!setter) { + JS_LOCK_OBJ(cx, wp->object); + scope = OBJ_SCOPE(wp->object); + + /* + * If the property wasn't found on wp->object, or it isn't still being + * watched, then someone else must have deleted or unwatched it, and we + * don't need to change the property attributes. + */ + JSScopeProperty *wprop = scope->lookup(sprop->id); + if (wprop && + ((wprop->attrs ^ sprop->attrs) & JSPROP_SETTER) == 0 && + IsWatchedProperty(cx, wprop)) { + sprop = scope->changeProperty(cx, wprop, 0, wprop->attrs, + wprop->getter, wp->setter); + if (!sprop) + ok = JS_FALSE; + } + JS_UNLOCK_SCOPE(cx, scope); + } + + cx->free(wp); + return ok; +} + +/* + * NB: js_TraceWatchPoints does not acquire cx->runtime->debuggerLock, since + * the debugger should never be racing with the GC (i.e., the debugger must + * respect the request model). + */ +void +js_TraceWatchPoints(JSTracer *trc, JSObject *obj) +{ + JSRuntime *rt; + JSWatchPoint *wp; + + rt = trc->context->runtime; + + for (wp = (JSWatchPoint *)rt->watchPointList.next; + &wp->links != &rt->watchPointList; + wp = (JSWatchPoint *)wp->links.next) { + if (wp->object == obj) { + wp->sprop->trace(trc); + if ((wp->sprop->attrs & JSPROP_SETTER) && wp->setter) { + JS_CALL_OBJECT_TRACER(trc, js_CastAsObject(wp->setter), + "wp->setter"); + } + JS_SET_TRACING_NAME(trc, "wp->closure"); + js_CallValueTracerIfGCThing(trc, OBJECT_TO_JSVAL(wp->closure)); + } + } +} + +void +js_SweepWatchPoints(JSContext *cx) +{ + JSRuntime *rt; + JSWatchPoint *wp, *next; + uint32 sample; + + rt = cx->runtime; + DBG_LOCK(rt); + for (wp = (JSWatchPoint *)rt->watchPointList.next; + &wp->links != &rt->watchPointList; + wp = next) { + next = (JSWatchPoint *)wp->links.next; + if (js_IsAboutToBeFinalized(cx, wp->object)) { + sample = rt->debuggerMutations; + + /* Ignore failures. */ + DropWatchPointAndUnlock(cx, wp, JSWP_LIVE); + DBG_LOCK(rt); + if (rt->debuggerMutations != sample + 1) + next = (JSWatchPoint *)rt->watchPointList.next; + } + } + DBG_UNLOCK(rt); +} + + + +/* + * NB: FindWatchPoint must be called with rt->debuggerLock acquired. + */ +static JSWatchPoint * +FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id) +{ + JSWatchPoint *wp; + + for (wp = (JSWatchPoint *)rt->watchPointList.next; + &wp->links != &rt->watchPointList; + wp = (JSWatchPoint *)wp->links.next) { + if (OBJ_SCOPE(wp->object) == scope && wp->sprop->id == id) + return wp; + } + return NULL; +} + +JSScopeProperty * +js_FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id) +{ + JSWatchPoint *wp; + JSScopeProperty *sprop; + + DBG_LOCK(rt); + wp = FindWatchPoint(rt, scope, id); + sprop = wp ? wp->sprop : NULL; + DBG_UNLOCK(rt); + return sprop; +} + +/* + * Secret handshake with DropWatchPointAndUnlock: if (!scope), we know our + * caller has acquired rt->debuggerLock, so we don't have to. + */ +JSPropertyOp +js_GetWatchedSetter(JSRuntime *rt, JSScope *scope, + const JSScopeProperty *sprop) +{ + JSPropertyOp setter; + JSWatchPoint *wp; + + setter = NULL; + if (scope) + DBG_LOCK(rt); + for (wp = (JSWatchPoint *)rt->watchPointList.next; + &wp->links != &rt->watchPointList; + wp = (JSWatchPoint *)wp->links.next) { + if ((!scope || OBJ_SCOPE(wp->object) == scope) && wp->sprop == sprop) { + setter = wp->setter; + break; + } + } + if (scope) + DBG_UNLOCK(rt); + return setter; +} + +JSBool +js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSRuntime *rt; + JSWatchPoint *wp; + JSScopeProperty *sprop; + jsval propid, userid; + JSScope *scope; + JSBool ok; + + rt = cx->runtime; + DBG_LOCK(rt); + for (wp = (JSWatchPoint *)rt->watchPointList.next; + &wp->links != &rt->watchPointList; + wp = (JSWatchPoint *)wp->links.next) { + sprop = wp->sprop; + if (wp->object == obj && SPROP_USERID(sprop) == id && + !(wp->flags & JSWP_HELD)) { + wp->flags |= JSWP_HELD; + DBG_UNLOCK(rt); + + JS_LOCK_OBJ(cx, obj); + propid = ID_TO_VALUE(sprop->id); + userid = (sprop->flags & SPROP_HAS_SHORTID) + ? INT_TO_JSVAL(sprop->shortid) + : propid; + scope = OBJ_SCOPE(obj); + JS_UNLOCK_OBJ(cx, obj); + + /* NB: wp is held, so we can safely dereference it still. */ + ok = wp->handler(cx, obj, propid, + SPROP_HAS_VALID_SLOT(sprop, scope) + ? OBJ_GET_SLOT(cx, obj, sprop->slot) + : JSVAL_VOID, + vp, wp->closure); + if (ok) { + /* + * Create a pseudo-frame for the setter invocation so that any + * stack-walking security code under the setter will correctly + * identify the guilty party. So that the watcher appears to + * be active to obj_eval and other such code, point frame.pc + * at the JSOP_STOP at the end of the script. + * + * The pseudo-frame is not created for fast natives as they + * are treated as interpreter frame extensions and always + * trusted. + */ + JSObject *closure; + JSClass *clasp; + JSFunction *fun; + JSScript *script; + JSBool injectFrame; + uintN nslots, slotsStart; + jsval smallv[5]; + jsval *argv; + JSStackFrame frame; + JSFrameRegs regs; + + closure = wp->closure; + clasp = OBJ_GET_CLASS(cx, closure); + if (clasp == &js_FunctionClass) { + fun = GET_FUNCTION_PRIVATE(cx, closure); + script = FUN_SCRIPT(fun); + } else if (clasp == &js_ScriptClass) { + fun = NULL; + script = (JSScript *) closure->getPrivate(); + } else { + fun = NULL; + script = NULL; + } + + slotsStart = nslots = 2; + injectFrame = JS_TRUE; + if (fun) { + nslots += FUN_MINARGS(fun); + if (!FUN_INTERPRETED(fun)) { + nslots += fun->u.n.extra; + injectFrame = !(fun->flags & JSFUN_FAST_NATIVE); + } + + slotsStart = nslots; + } + if (script) + nslots += script->nslots; + + if (injectFrame) { + if (nslots <= JS_ARRAY_LENGTH(smallv)) { + argv = smallv; + } else { + argv = (jsval *) cx->malloc(nslots * sizeof(jsval)); + if (!argv) { + DBG_LOCK(rt); + DropWatchPointAndUnlock(cx, wp, JSWP_HELD); + return JS_FALSE; + } + } + + argv[0] = OBJECT_TO_JSVAL(closure); + argv[1] = JSVAL_NULL; + memset(argv + 2, 0, (nslots - 2) * sizeof(jsval)); + + memset(&frame, 0, sizeof(frame)); + frame.script = script; + frame.regs = NULL; + frame.fun = fun; + frame.argv = argv + 2; + frame.down = js_GetTopStackFrame(cx); + frame.scopeChain = OBJ_GET_PARENT(cx, closure); + if (script && script->nslots) + frame.slots = argv + slotsStart; + if (script) { + JS_ASSERT(script->length >= JSOP_STOP_LENGTH); + regs.pc = script->code + script->length + - JSOP_STOP_LENGTH; + regs.sp = NULL; + frame.regs = ®s; + if (fun && + JSFUN_HEAVYWEIGHT_TEST(fun->flags) && + !js_GetCallObject(cx, &frame)) { + if (argv != smallv) + cx->free(argv); + DBG_LOCK(rt); + DropWatchPointAndUnlock(cx, wp, JSWP_HELD); + return JS_FALSE; + } + } + + cx->fp = &frame; + } +#ifdef __GNUC__ + else + argv = NULL; /* suppress bogus gcc warnings */ +#endif + ok = !wp->setter || + ((sprop->attrs & JSPROP_SETTER) + ? js_InternalCall(cx, obj, + js_CastAsObjectJSVal(wp->setter), + 1, vp, vp) + : wp->setter(cx, obj, userid, vp)); + if (injectFrame) { + /* Evil code can cause us to have an arguments object. */ + frame.putActivationObjects(cx); + cx->fp = frame.down; + if (argv != smallv) + cx->free(argv); + } + } + DBG_LOCK(rt); + return DropWatchPointAndUnlock(cx, wp, JSWP_HELD) && ok; + } + } + DBG_UNLOCK(rt); + return JS_TRUE; +} + +JSBool +js_watch_set_wrapper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSObject *funobj; + JSFunction *wrapper; + jsval userid; + + funobj = JSVAL_TO_OBJECT(argv[-2]); + wrapper = GET_FUNCTION_PRIVATE(cx, funobj); + userid = ATOM_KEY(wrapper->atom); + *rval = argv[0]; + return js_watch_set(cx, obj, userid, rval); +} + +static bool +IsWatchedProperty(JSContext *cx, JSScopeProperty *sprop) +{ + if (sprop->attrs & JSPROP_SETTER) { + JSObject *funobj = js_CastAsObject(sprop->setter); + JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj); + + return FUN_NATIVE(fun) == js_watch_set_wrapper; + } + return sprop->setter == js_watch_set; +} + +JSPropertyOp +js_WrapWatchedSetter(JSContext *cx, jsid id, uintN attrs, JSPropertyOp setter) +{ + JSAtom *atom; + JSFunction *wrapper; + + if (!(attrs & JSPROP_SETTER)) + return &js_watch_set; /* & to silence schoolmarmish MSVC */ + + if (JSID_IS_ATOM(id)) { + atom = JSID_TO_ATOM(id); + } else if (JSID_IS_INT(id)) { + if (!js_ValueToStringId(cx, INT_JSID_TO_JSVAL(id), &id)) + return NULL; + atom = JSID_TO_ATOM(id); + } else { + atom = NULL; + } + wrapper = js_NewFunction(cx, NULL, js_watch_set_wrapper, 1, 0, + OBJ_GET_PARENT(cx, js_CastAsObject(setter)), + atom); + if (!wrapper) + return NULL; + return js_CastAsPropertyOp(FUN_OBJECT(wrapper)); +} + +JS_PUBLIC_API(JSBool) +JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval idval, + JSWatchPointHandler handler, void *closure) +{ + JSObject *origobj; + jsval v; + uintN attrs; + jsid propid; + JSObject *pobj; + JSProperty *prop; + JSScopeProperty *sprop; + JSRuntime *rt; + JSBool ok; + JSWatchPoint *wp; + JSPropertyOp watcher; + + origobj = obj; + obj = js_GetWrappedObject(cx, obj); + OBJ_TO_INNER_OBJECT(cx, obj); + if (!obj) + return JS_FALSE; + + if (JSVAL_IS_INT(idval)) { + propid = INT_JSVAL_TO_JSID(idval); + } else { + if (!js_ValueToStringId(cx, idval, &propid)) + return JS_FALSE; + propid = js_CheckForStringIndex(propid); + } + + /* + * If, by unwrapping and innerizing, we changed the object, check + * again to make sure that we're allowed to set a watch point. + */ + if (origobj != obj && !obj->checkAccess(cx, propid, JSACC_WATCH, &v, &attrs)) + return JS_FALSE; + + if (!OBJ_IS_NATIVE(obj)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_WATCH, + OBJ_GET_CLASS(cx, obj)->name); + return JS_FALSE; + } + + if (!js_LookupProperty(cx, obj, propid, &pobj, &prop)) + return JS_FALSE; + sprop = (JSScopeProperty *) prop; + rt = cx->runtime; + if (!sprop) { + /* Check for a deleted symbol watchpoint, which holds its property. */ + sprop = js_FindWatchPoint(rt, OBJ_SCOPE(obj), propid); + if (!sprop) { + /* Make a new property in obj so we can watch for the first set. */ + if (!js_DefineNativeProperty(cx, obj, propid, JSVAL_VOID, NULL, NULL, + JSPROP_ENUMERATE, 0, 0, &prop)) { + return JS_FALSE; + } + sprop = (JSScopeProperty *) prop; + } + } else if (pobj != obj) { + /* Clone the prototype property so we can watch the right object. */ + jsval value; + JSPropertyOp getter, setter; + uintN attrs, flags; + intN shortid; + + if (OBJ_IS_NATIVE(pobj)) { + value = SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)) + ? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot) + : JSVAL_VOID; + getter = sprop->getter; + setter = sprop->setter; + attrs = sprop->attrs; + flags = sprop->flags; + shortid = sprop->shortid; + } else { + if (!pobj->getProperty(cx, propid, &value) || + !pobj->getAttributes(cx, propid, prop, &attrs)) { + pobj->dropProperty(cx, prop); + return JS_FALSE; + } + getter = setter = NULL; + flags = 0; + shortid = 0; + } + pobj->dropProperty(cx, prop); + + /* Recall that obj is native, whether or not pobj is native. */ + if (!js_DefineNativeProperty(cx, obj, propid, value, getter, setter, + attrs, flags, shortid, &prop)) { + return JS_FALSE; + } + sprop = (JSScopeProperty *) prop; + } + + /* + * At this point, prop/sprop exists in obj, obj is locked, and we must + * obj->dropProperty(cx, prop) before returning. + */ + ok = JS_TRUE; + DBG_LOCK(rt); + wp = FindWatchPoint(rt, OBJ_SCOPE(obj), propid); + if (!wp) { + DBG_UNLOCK(rt); + watcher = js_WrapWatchedSetter(cx, propid, sprop->attrs, sprop->setter); + if (!watcher) { + ok = JS_FALSE; + goto out; + } + + wp = (JSWatchPoint *) cx->malloc(sizeof *wp); + if (!wp) { + ok = JS_FALSE; + goto out; + } + wp->handler = NULL; + wp->closure = NULL; + wp->object = obj; + JS_ASSERT(sprop->setter != js_watch_set || pobj != obj); + wp->setter = sprop->setter; + wp->flags = JSWP_LIVE; + + /* XXXbe nest in obj lock here */ + sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, 0, sprop->attrs, + sprop->getter, watcher); + if (!sprop) { + /* Self-link so DropWatchPointAndUnlock can JS_REMOVE_LINK it. */ + JS_INIT_CLIST(&wp->links); + DBG_LOCK(rt); + DropWatchPointAndUnlock(cx, wp, JSWP_LIVE); + ok = JS_FALSE; + goto out; + } + wp->sprop = sprop; + + /* + * Now that wp is fully initialized, append it to rt's wp list. + * Because obj is locked we know that no other thread could have added + * a watchpoint for (obj, propid). + */ + DBG_LOCK(rt); + JS_ASSERT(!FindWatchPoint(rt, OBJ_SCOPE(obj), propid)); + JS_APPEND_LINK(&wp->links, &rt->watchPointList); + ++rt->debuggerMutations; + } + wp->handler = handler; + wp->closure = reinterpret_cast(closure); + DBG_UNLOCK(rt); + +out: + obj->dropProperty(cx, prop); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsval id, + JSWatchPointHandler *handlerp, void **closurep) +{ + JSRuntime *rt; + JSWatchPoint *wp; + + rt = cx->runtime; + DBG_LOCK(rt); + for (wp = (JSWatchPoint *)rt->watchPointList.next; + &wp->links != &rt->watchPointList; + wp = (JSWatchPoint *)wp->links.next) { + if (wp->object == obj && SPROP_USERID(wp->sprop) == id) { + if (handlerp) + *handlerp = wp->handler; + if (closurep) + *closurep = wp->closure; + return DropWatchPointAndUnlock(cx, wp, JSWP_LIVE); + } + } + DBG_UNLOCK(rt); + if (handlerp) + *handlerp = NULL; + if (closurep) + *closurep = NULL; + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj) +{ + JSRuntime *rt; + JSWatchPoint *wp, *next; + uint32 sample; + + rt = cx->runtime; + DBG_LOCK(rt); + for (wp = (JSWatchPoint *)rt->watchPointList.next; + &wp->links != &rt->watchPointList; + wp = next) { + next = (JSWatchPoint *)wp->links.next; + if (wp->object == obj) { + sample = rt->debuggerMutations; + if (!DropWatchPointAndUnlock(cx, wp, JSWP_LIVE)) + return JS_FALSE; + DBG_LOCK(rt); + if (rt->debuggerMutations != sample + 1) + next = (JSWatchPoint *)rt->watchPointList.next; + } + } + DBG_UNLOCK(rt); + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_ClearAllWatchPoints(JSContext *cx) +{ + JSRuntime *rt; + JSWatchPoint *wp, *next; + uint32 sample; + + rt = cx->runtime; + DBG_LOCK(rt); + for (wp = (JSWatchPoint *)rt->watchPointList.next; + &wp->links != &rt->watchPointList; + wp = next) { + next = (JSWatchPoint *)wp->links.next; + sample = rt->debuggerMutations; + if (!DropWatchPointAndUnlock(cx, wp, JSWP_LIVE)) + return JS_FALSE; + DBG_LOCK(rt); + if (rt->debuggerMutations != sample + 1) + next = (JSWatchPoint *)rt->watchPointList.next; + } + DBG_UNLOCK(rt); + return JS_TRUE; +} + +/************************************************************************/ + +JS_PUBLIC_API(uintN) +JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc) +{ + return js_PCToLineNumber(cx, script, pc); +} + +JS_PUBLIC_API(jsbytecode *) +JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno) +{ + return js_LineNumberToPC(script, lineno); +} + +JS_PUBLIC_API(JSScript *) +JS_GetFunctionScript(JSContext *cx, JSFunction *fun) +{ + return FUN_SCRIPT(fun); +} + +JS_PUBLIC_API(JSNative) +JS_GetFunctionNative(JSContext *cx, JSFunction *fun) +{ + return FUN_NATIVE(fun); +} + +JS_PUBLIC_API(JSFastNative) +JS_GetFunctionFastNative(JSContext *cx, JSFunction *fun) +{ + return FUN_FAST_NATIVE(fun); +} + +JS_PUBLIC_API(JSPrincipals *) +JS_GetScriptPrincipals(JSContext *cx, JSScript *script) +{ + return script->principals; +} + +/************************************************************************/ + +/* + * Stack Frame Iterator + */ +JS_PUBLIC_API(JSStackFrame *) +JS_FrameIterator(JSContext *cx, JSStackFrame **iteratorp) +{ + *iteratorp = (*iteratorp == NULL) ? js_GetTopStackFrame(cx) : (*iteratorp)->down; + return *iteratorp; +} + +JS_PUBLIC_API(JSScript *) +JS_GetFrameScript(JSContext *cx, JSStackFrame *fp) +{ + return fp->script; +} + +JS_PUBLIC_API(jsbytecode *) +JS_GetFramePC(JSContext *cx, JSStackFrame *fp) +{ + return fp->regs ? fp->regs->pc : NULL; +} + +JS_PUBLIC_API(JSStackFrame *) +JS_GetScriptedCaller(JSContext *cx, JSStackFrame *fp) +{ + return js_GetScriptedCaller(cx, fp); +} + +JS_PUBLIC_API(JSPrincipals *) +JS_StackFramePrincipals(JSContext *cx, JSStackFrame *fp) +{ + JSSecurityCallbacks *callbacks; + + if (fp->fun) { + callbacks = JS_GetSecurityCallbacks(cx); + if (callbacks && callbacks->findObjectPrincipals) { + if (FUN_OBJECT(fp->fun) != fp->callee()) + return callbacks->findObjectPrincipals(cx, fp->callee()); + /* FALL THROUGH */ + } + } + if (fp->script) + return fp->script->principals; + return NULL; +} + +JS_PUBLIC_API(JSPrincipals *) +JS_EvalFramePrincipals(JSContext *cx, JSStackFrame *fp, JSStackFrame *caller) +{ + JSPrincipals *principals, *callerPrincipals; + JSSecurityCallbacks *callbacks; + + callbacks = JS_GetSecurityCallbacks(cx); + if (callbacks && callbacks->findObjectPrincipals) { + principals = callbacks->findObjectPrincipals(cx, fp->callee()); + } else { + principals = NULL; + } + if (!caller) + return principals; + callerPrincipals = JS_StackFramePrincipals(cx, caller); + return (callerPrincipals && principals && + callerPrincipals->subsume(callerPrincipals, principals)) + ? principals + : callerPrincipals; +} + +JS_PUBLIC_API(void *) +JS_GetFrameAnnotation(JSContext *cx, JSStackFrame *fp) +{ + if (fp->annotation && fp->script) { + JSPrincipals *principals = JS_StackFramePrincipals(cx, fp); + + if (principals && principals->globalPrivilegesEnabled(cx, principals)) { + /* + * Give out an annotation only if privileges have not been revoked + * or disabled globally. + */ + return fp->annotation; + } + } + + return NULL; +} + +JS_PUBLIC_API(void) +JS_SetFrameAnnotation(JSContext *cx, JSStackFrame *fp, void *annotation) +{ + fp->annotation = annotation; +} + +JS_PUBLIC_API(void *) +JS_GetFramePrincipalArray(JSContext *cx, JSStackFrame *fp) +{ + JSPrincipals *principals; + + principals = JS_StackFramePrincipals(cx, fp); + if (!principals) + return NULL; + return principals->getPrincipalArray(cx, principals); +} + +JS_PUBLIC_API(JSBool) +JS_IsNativeFrame(JSContext *cx, JSStackFrame *fp) +{ + return !fp->script; +} + +/* this is deprecated, use JS_GetFrameScopeChain instead */ +JS_PUBLIC_API(JSObject *) +JS_GetFrameObject(JSContext *cx, JSStackFrame *fp) +{ + return fp->scopeChain; +} + +JS_PUBLIC_API(JSObject *) +JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fp) +{ + /* Force creation of argument and call objects if not yet created */ + (void) JS_GetFrameCallObject(cx, fp); + return js_GetScopeChain(cx, fp); +} + +JS_PUBLIC_API(JSObject *) +JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fp) +{ + if (! fp->fun) + return NULL; + + /* Force creation of argument object if not yet created */ + (void) js_GetArgsObject(cx, fp); + + /* + * XXX ill-defined: null return here means error was reported, unlike a + * null returned above or in the #else + */ + return js_GetCallObject(cx, fp); +} + +JS_PUBLIC_API(JSObject *) +JS_GetFrameThis(JSContext *cx, JSStackFrame *fp) +{ + JSStackFrame *afp; + + if (fp->flags & JSFRAME_COMPUTED_THIS) + return JSVAL_TO_OBJECT(fp->thisv); /* JSVAL_COMPUTED_THIS invariant */ + + /* js_ComputeThis gets confused if fp != cx->fp, so set it aside. */ + if (js_GetTopStackFrame(cx) != fp) { + afp = cx->fp; + if (afp) { + afp->dormantNext = cx->dormantFrameChain; + cx->dormantFrameChain = afp; + cx->fp = fp; + } + } else { + afp = NULL; + } + + if (JSVAL_IS_NULL(fp->thisv) && fp->argv) + fp->thisv = OBJECT_TO_JSVAL(js_ComputeThis(cx, JS_TRUE, fp->argv)); + + if (afp) { + cx->fp = afp; + cx->dormantFrameChain = afp->dormantNext; + afp->dormantNext = NULL; + } + + return JSVAL_TO_OBJECT(fp->thisv); +} + +JS_PUBLIC_API(JSFunction *) +JS_GetFrameFunction(JSContext *cx, JSStackFrame *fp) +{ + return fp->fun; +} + +JS_PUBLIC_API(JSObject *) +JS_GetFrameFunctionObject(JSContext *cx, JSStackFrame *fp) +{ + if (!fp->fun) + return NULL; + + JS_ASSERT(HAS_FUNCTION_CLASS(fp->callee())); + JS_ASSERT(fp->callee()->getPrivate() == fp->fun); + return fp->callee(); +} + +JS_PUBLIC_API(JSBool) +JS_IsConstructorFrame(JSContext *cx, JSStackFrame *fp) +{ + return (fp->flags & JSFRAME_CONSTRUCTING) != 0; +} + +JS_PUBLIC_API(JSObject *) +JS_GetFrameCalleeObject(JSContext *cx, JSStackFrame *fp) +{ + return fp->callee(); +} + +JS_PUBLIC_API(JSBool) +JS_IsDebuggerFrame(JSContext *cx, JSStackFrame *fp) +{ + return (fp->flags & JSFRAME_DEBUGGER) != 0; +} + +JS_PUBLIC_API(jsval) +JS_GetFrameReturnValue(JSContext *cx, JSStackFrame *fp) +{ + return fp->rval; +} + +JS_PUBLIC_API(void) +JS_SetFrameReturnValue(JSContext *cx, JSStackFrame *fp, jsval rval) +{ + fp->rval = rval; +} + +/************************************************************************/ + +JS_PUBLIC_API(const char *) +JS_GetScriptFilename(JSContext *cx, JSScript *script) +{ + return script->filename; +} + +JS_PUBLIC_API(uintN) +JS_GetScriptBaseLineNumber(JSContext *cx, JSScript *script) +{ + return script->lineno; +} + +JS_PUBLIC_API(uintN) +JS_GetScriptLineExtent(JSContext *cx, JSScript *script) +{ + return js_GetScriptLineExtent(script); +} + +JS_PUBLIC_API(JSVersion) +JS_GetScriptVersion(JSContext *cx, JSScript *script) +{ + return (JSVersion) (script->version & JSVERSION_MASK); +} + +/***************************************************************************/ + +JS_PUBLIC_API(void) +JS_SetNewScriptHook(JSRuntime *rt, JSNewScriptHook hook, void *callerdata) +{ + rt->globalDebugHooks.newScriptHook = hook; + rt->globalDebugHooks.newScriptHookData = callerdata; +} + +JS_PUBLIC_API(void) +JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook, + void *callerdata) +{ + rt->globalDebugHooks.destroyScriptHook = hook; + rt->globalDebugHooks.destroyScriptHookData = callerdata; +} + +/***************************************************************************/ + +JS_PUBLIC_API(JSBool) +JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp, + const jschar *chars, uintN length, + const char *filename, uintN lineno, + jsval *rval) +{ + JS_ASSERT_NOT_ON_TRACE(cx); + + JSObject *scobj; + JSScript *script; + JSBool ok; + + scobj = JS_GetFrameScopeChain(cx, fp); + if (!scobj) + return JS_FALSE; + + /* + * NB: This function breaks the assumption that the compiler can see all + * calls and properly compute a static level. In order to get around this, + * we use a static level that will cause us not to attempt to optimize + * variable references made by this frame. + */ + script = JSCompiler::compileScript(cx, scobj, fp, JS_StackFramePrincipals(cx, fp), + TCF_COMPILE_N_GO, chars, length, NULL, + filename, lineno, NULL, JS_DISPLAY_SIZE); + + if (!script) + return JS_FALSE; + + JSStackFrame *displayCopy[JS_DISPLAY_SIZE]; + if (cx->fp != fp) { + memcpy(displayCopy, cx->display, sizeof displayCopy); + + /* + * Set up cx->display as it would have been when fp was active. + * + * NB: To reconstruct cx->display for fp, we have to follow the frame + * chain from oldest to youngest, in the opposite direction to its + * single linkge. To avoid the obvious recursive reversal algorithm, + * which might use too much stack, we reverse in place and reverse + * again as we reconstruct the display. This is safe because cx is + * thread-local and we can't cause GC until the call to js_Execute + * below. + */ + JSStackFrame *fp2 = fp, *last = NULL; + while (fp2) { + JSStackFrame *next = fp2->down; + fp2->down = last; + last = fp2; + fp2 = next; + } + + fp2 = last; + last = NULL; + while (fp2) { + JSStackFrame *next = fp2->down; + fp2->down = last; + last = fp2; + + JSScript *script = fp2->script; + if (script && script->staticLevel < JS_DISPLAY_SIZE) + cx->display[script->staticLevel] = fp2; + fp2 = next; + } + } + + ok = js_Execute(cx, scobj, script, fp, JSFRAME_DEBUGGER | JSFRAME_EVAL, + rval); + + if (cx->fp != fp) + memcpy(cx->display, displayCopy, sizeof cx->display); + js_DestroyScript(cx, script); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp, + const char *bytes, uintN length, + const char *filename, uintN lineno, + jsval *rval) +{ + jschar *chars; + JSBool ok; + size_t len = length; + + chars = js_InflateString(cx, bytes, &len); + if (!chars) + return JS_FALSE; + length = (uintN) len; + ok = JS_EvaluateUCInStackFrame(cx, fp, chars, length, filename, lineno, + rval); + cx->free(chars); + + return ok; +} + +/************************************************************************/ + +/* XXXbe this all needs to be reworked to avoid requiring JSScope types. */ + +JS_PUBLIC_API(JSScopeProperty *) +JS_PropertyIterator(JSObject *obj, JSScopeProperty **iteratorp) +{ + JSScopeProperty *sprop; + JSScope *scope; + + sprop = *iteratorp; + scope = OBJ_SCOPE(obj); + + /* XXXbe minor(?) incompatibility: iterate in reverse definition order */ + sprop = sprop ? sprop->parent : scope->lastProperty(); + *iteratorp = sprop; + return sprop; +} + +JS_PUBLIC_API(JSBool) +JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, + JSPropertyDesc *pd) +{ + pd->id = ID_TO_VALUE(sprop->id); + + bool wasThrowing = cx->throwing; + JSAutoTempValueRooter lastException(cx, cx->exception); + cx->throwing = JS_FALSE; + + if (!js_GetProperty(cx, obj, sprop->id, &pd->value)) { + if (!cx->throwing) { + pd->flags = JSPD_ERROR; + pd->value = JSVAL_VOID; + } else { + pd->flags = JSPD_EXCEPTION; + pd->value = cx->exception; + } + } else { + pd->flags = 0; + } + + cx->throwing = wasThrowing; + if (wasThrowing) + cx->exception = lastException.value(); + + pd->flags |= ((sprop->attrs & JSPROP_ENUMERATE) ? JSPD_ENUMERATE : 0) + | ((sprop->attrs & JSPROP_READONLY) ? JSPD_READONLY : 0) + | ((sprop->attrs & JSPROP_PERMANENT) ? JSPD_PERMANENT : 0); + pd->spare = 0; + if (sprop->getter == js_GetCallArg) { + pd->slot = sprop->shortid; + pd->flags |= JSPD_ARGUMENT; + } else if (sprop->getter == js_GetCallVar) { + pd->slot = sprop->shortid; + pd->flags |= JSPD_VARIABLE; + } else { + pd->slot = 0; + } + pd->alias = JSVAL_VOID; + + JSScope *scope = OBJ_SCOPE(obj); + if (SPROP_HAS_VALID_SLOT(sprop, scope)) { + JSScopeProperty *aprop; + for (aprop = scope->lastProperty(); aprop; aprop = aprop->parent) { + if (aprop != sprop && aprop->slot == sprop->slot) { + pd->alias = ID_TO_VALUE(aprop->id); + break; + } + } + } + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda) +{ + JSClass *clasp; + JSScope *scope; + uint32 i, n; + JSPropertyDesc *pd; + JSScopeProperty *sprop; + + clasp = OBJ_GET_CLASS(cx, obj); + if (!OBJ_IS_NATIVE(obj) || (clasp->flags & JSCLASS_NEW_ENUMERATE)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_CANT_DESCRIBE_PROPS, clasp->name); + return JS_FALSE; + } + if (!clasp->enumerate(cx, obj)) + return JS_FALSE; + + /* have no props, or object's scope has not mutated from that of proto */ + scope = OBJ_SCOPE(obj); + if (scope->entryCount == 0) { + pda->length = 0; + pda->array = NULL; + return JS_TRUE; + } + + n = scope->entryCount; + pd = (JSPropertyDesc *) cx->malloc((size_t)n * sizeof(JSPropertyDesc)); + if (!pd) + return JS_FALSE; + i = 0; + for (sprop = scope->lastProperty(); sprop; sprop = sprop->parent) { + if (!js_AddRoot(cx, &pd[i].id, NULL)) + goto bad; + if (!js_AddRoot(cx, &pd[i].value, NULL)) + goto bad; + if (!JS_GetPropertyDesc(cx, obj, sprop, &pd[i])) + goto bad; + if ((pd[i].flags & JSPD_ALIAS) && !js_AddRoot(cx, &pd[i].alias, NULL)) + goto bad; + if (++i == n) + break; + } + pda->length = i; + pda->array = pd; + return JS_TRUE; + +bad: + pda->length = i + 1; + pda->array = pd; + JS_PutPropertyDescArray(cx, pda); + return JS_FALSE; +} + +JS_PUBLIC_API(void) +JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda) +{ + JSPropertyDesc *pd; + uint32 i; + + pd = pda->array; + for (i = 0; i < pda->length; i++) { + js_RemoveRoot(cx->runtime, &pd[i].id); + js_RemoveRoot(cx->runtime, &pd[i].value); + if (pd[i].flags & JSPD_ALIAS) + js_RemoveRoot(cx->runtime, &pd[i].alias); + } + cx->free(pd); +} + +/************************************************************************/ + +JS_PUBLIC_API(JSBool) +JS_SetDebuggerHandler(JSRuntime *rt, JSTrapHandler handler, void *closure) +{ + rt->globalDebugHooks.debuggerHandler = handler; + rt->globalDebugHooks.debuggerHandlerData = closure; + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_SetSourceHandler(JSRuntime *rt, JSSourceHandler handler, void *closure) +{ + rt->globalDebugHooks.sourceHandler = handler; + rt->globalDebugHooks.sourceHandlerData = closure; + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure) +{ + rt->globalDebugHooks.executeHook = hook; + rt->globalDebugHooks.executeHookData = closure; + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure) +{ +#ifdef JS_TRACER + JS_LOCK_GC(rt); + bool wasInhibited = rt->debuggerInhibitsJIT(); +#endif + rt->globalDebugHooks.callHook = hook; + rt->globalDebugHooks.callHookData = closure; +#ifdef JS_TRACER + JITInhibitingHookChange(rt, wasInhibited); + JS_UNLOCK_GC(rt); + if (hook) + LeaveTraceRT(rt); +#endif + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_SetObjectHook(JSRuntime *rt, JSObjectHook hook, void *closure) +{ +#ifdef JS_TRACER + JS_LOCK_GC(rt); + bool wasInhibited = rt->debuggerInhibitsJIT(); +#endif + rt->globalDebugHooks.objectHook = hook; + rt->globalDebugHooks.objectHookData = closure; +#ifdef JS_TRACER + JITInhibitingHookChange(rt, wasInhibited); + JS_UNLOCK_GC(rt); + if (hook) + LeaveTraceRT(rt); +#endif + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_SetThrowHook(JSRuntime *rt, JSTrapHandler hook, void *closure) +{ + rt->globalDebugHooks.throwHook = hook; + rt->globalDebugHooks.throwHookData = closure; + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure) +{ + rt->globalDebugHooks.debugErrorHook = hook; + rt->globalDebugHooks.debugErrorHookData = closure; + return JS_TRUE; +} + +/************************************************************************/ + +JS_PUBLIC_API(size_t) +JS_GetObjectTotalSize(JSContext *cx, JSObject *obj) +{ + size_t nbytes; + JSScope *scope; + + nbytes = sizeof *obj; + if (obj->dslots) { + nbytes += ((uint32)obj->dslots[-1] - JS_INITIAL_NSLOTS + 1) + * sizeof obj->dslots[0]; + } + if (OBJ_IS_NATIVE(obj)) { + scope = OBJ_SCOPE(obj); + if (scope->owned()) { + nbytes += sizeof *scope; + nbytes += SCOPE_CAPACITY(scope) * sizeof(JSScopeProperty *); + } + } + return nbytes; +} + +static size_t +GetAtomTotalSize(JSContext *cx, JSAtom *atom) +{ + size_t nbytes; + + nbytes = sizeof(JSAtom *) + sizeof(JSDHashEntryStub); + if (ATOM_IS_STRING(atom)) { + nbytes += sizeof(JSString); + nbytes += (ATOM_TO_STRING(atom)->flatLength() + 1) * sizeof(jschar); + } else if (ATOM_IS_DOUBLE(atom)) { + nbytes += sizeof(jsdouble); + } + return nbytes; +} + +JS_PUBLIC_API(size_t) +JS_GetFunctionTotalSize(JSContext *cx, JSFunction *fun) +{ + size_t nbytes; + + nbytes = sizeof *fun; + nbytes += JS_GetObjectTotalSize(cx, FUN_OBJECT(fun)); + if (FUN_INTERPRETED(fun)) + nbytes += JS_GetScriptTotalSize(cx, fun->u.i.script); + if (fun->atom) + nbytes += GetAtomTotalSize(cx, fun->atom); + return nbytes; +} + +#include "jsemit.h" + +JS_PUBLIC_API(size_t) +JS_GetScriptTotalSize(JSContext *cx, JSScript *script) +{ + size_t nbytes, pbytes; + jsatomid i; + jssrcnote *sn, *notes; + JSObjectArray *objarray; + JSPrincipals *principals; + + nbytes = sizeof *script; + if (script->u.object) + nbytes += JS_GetObjectTotalSize(cx, script->u.object); + + nbytes += script->length * sizeof script->code[0]; + nbytes += script->atomMap.length * sizeof script->atomMap.vector[0]; + for (i = 0; i < script->atomMap.length; i++) + nbytes += GetAtomTotalSize(cx, script->atomMap.vector[i]); + + if (script->filename) + nbytes += strlen(script->filename) + 1; + + notes = script->notes(); + for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) + continue; + nbytes += (sn - notes + 1) * sizeof *sn; + + if (script->objectsOffset != 0) { + objarray = script->objects(); + i = objarray->length; + nbytes += sizeof *objarray + i * sizeof objarray->vector[0]; + do { + nbytes += JS_GetObjectTotalSize(cx, objarray->vector[--i]); + } while (i != 0); + } + + if (script->regexpsOffset != 0) { + objarray = script->regexps(); + i = objarray->length; + nbytes += sizeof *objarray + i * sizeof objarray->vector[0]; + do { + nbytes += JS_GetObjectTotalSize(cx, objarray->vector[--i]); + } while (i != 0); + } + + if (script->trynotesOffset != 0) { + nbytes += sizeof(JSTryNoteArray) + + script->trynotes()->length * sizeof(JSTryNote); + } + + principals = script->principals; + if (principals) { + JS_ASSERT(principals->refcount); + pbytes = sizeof *principals; + if (principals->refcount > 1) + pbytes = JS_HOWMANY(pbytes, principals->refcount); + nbytes += pbytes; + } + + return nbytes; +} + +JS_PUBLIC_API(uint32) +JS_GetTopScriptFilenameFlags(JSContext *cx, JSStackFrame *fp) +{ + if (!fp) + fp = js_GetTopStackFrame(cx); + while (fp) { + if (fp->script) + return JS_GetScriptFilenameFlags(fp->script); + fp = fp->down; + } + return 0; + } + +JS_PUBLIC_API(uint32) +JS_GetScriptFilenameFlags(JSScript *script) +{ + JS_ASSERT(script); + if (!script->filename) + return JSFILENAME_NULL; + return js_GetScriptFilenameFlags(script->filename); +} + +JS_PUBLIC_API(JSBool) +JS_FlagScriptFilenamePrefix(JSRuntime *rt, const char *prefix, uint32 flags) +{ + if (!js_SaveScriptFilenameRT(rt, prefix, flags)) + return JS_FALSE; + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_IsSystemObject(JSContext *cx, JSObject *obj) +{ + return obj->isSystem(); +} + +JS_PUBLIC_API(JSObject *) +JS_NewSystemObject(JSContext *cx, JSClass *clasp, JSObject *proto, + JSObject *parent, JSBool system) +{ + JSObject *obj; + + obj = js_NewObject(cx, clasp, proto, parent); + if (obj && system) + obj->setSystem(); + return obj; +} + +/************************************************************************/ + +JS_PUBLIC_API(const JSDebugHooks *) +JS_GetGlobalDebugHooks(JSRuntime *rt) +{ + return &rt->globalDebugHooks; +} + +JS_PUBLIC_API(JSDebugHooks *) +JS_SetContextDebugHooks(JSContext *cx, const JSDebugHooks *hooks) +{ + JS_ASSERT(hooks); + if (hooks != &cx->runtime->globalDebugHooks) + js_LeaveTrace(cx); + +#ifdef JS_TRACER + JS_LOCK_GC(cx->runtime); +#endif + JSDebugHooks *old = const_cast(cx->debugHooks); + cx->debugHooks = hooks; +#ifdef JS_TRACER + cx->updateJITEnabled(); + JS_UNLOCK_GC(cx->runtime); +#endif + return old; +} + +#ifdef MOZ_SHARK + +#include + +JS_PUBLIC_API(JSBool) +JS_StartChudRemote() +{ + if (chudIsRemoteAccessAcquired() && + (chudStartRemotePerfMonitor("Mozilla") == chudSuccess)) { + return JS_TRUE; + } + + return JS_FALSE; +} + +JS_PUBLIC_API(JSBool) +JS_StopChudRemote() +{ + if (chudIsRemoteAccessAcquired() && + (chudStopRemotePerfMonitor() == chudSuccess)) { + return JS_TRUE; + } + + return JS_FALSE; +} + +JS_PUBLIC_API(JSBool) +JS_ConnectShark() +{ + if (!chudIsInitialized() && (chudInitialize() != chudSuccess)) + return JS_FALSE; + + if (chudAcquireRemoteAccess() != chudSuccess) + return JS_FALSE; + + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_DisconnectShark() +{ + if (chudIsRemoteAccessAcquired() && (chudReleaseRemoteAccess() != chudSuccess)) + return JS_FALSE; + + return JS_TRUE; +} + +JS_FRIEND_API(JSBool) +js_StartShark(JSContext *cx, JSObject *obj, + uintN argc, jsval *argv, jsval *rval) +{ + if (!JS_StartChudRemote()) { + JS_ReportError(cx, "Error starting CHUD."); + return JS_FALSE; + } + + return JS_TRUE; +} + +JS_FRIEND_API(JSBool) +js_StopShark(JSContext *cx, JSObject *obj, + uintN argc, jsval *argv, jsval *rval) +{ + if (!JS_StopChudRemote()) { + JS_ReportError(cx, "Error stopping CHUD."); + return JS_FALSE; + } + + return JS_TRUE; +} + +JS_FRIEND_API(JSBool) +js_ConnectShark(JSContext *cx, JSObject *obj, + uintN argc, jsval *argv, jsval *rval) +{ + if (!JS_ConnectShark()) { + JS_ReportError(cx, "Error connecting to Shark."); + return JS_FALSE; + } + + return JS_TRUE; +} + +JS_FRIEND_API(JSBool) +js_DisconnectShark(JSContext *cx, JSObject *obj, + uintN argc, jsval *argv, jsval *rval) +{ + if (!JS_DisconnectShark()) { + JS_ReportError(cx, "Error disconnecting from Shark."); + return JS_FALSE; + } + + return JS_TRUE; +} + +#endif /* MOZ_SHARK */ + +#ifdef MOZ_CALLGRIND + +#include + +JS_FRIEND_API(JSBool) +js_StartCallgrind(JSContext *cx, JSObject *obj, + uintN argc, jsval *argv, jsval *rval) +{ + CALLGRIND_START_INSTRUMENTATION; + CALLGRIND_ZERO_STATS; + return JS_TRUE; +} + +JS_FRIEND_API(JSBool) +js_StopCallgrind(JSContext *cx, JSObject *obj, + uintN argc, jsval *argv, jsval *rval) +{ + CALLGRIND_STOP_INSTRUMENTATION; + return JS_TRUE; +} + +JS_FRIEND_API(JSBool) +js_DumpCallgrind(JSContext *cx, JSObject *obj, + uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + char *cstr; + + if (argc > 0 && JSVAL_IS_STRING(argv[0])) { + str = JSVAL_TO_STRING(argv[0]); + cstr = js_DeflateString(cx, str->chars(), str->length()); + if (cstr) { + CALLGRIND_DUMP_STATS_AT(cstr); + cx->free(cstr); + return JS_TRUE; + } + } + CALLGRIND_DUMP_STATS; + + return JS_TRUE; +} + +#endif /* MOZ_CALLGRIND */ + +#ifdef MOZ_VTUNE +#include + +static const char *vtuneErrorMessages[] = { + "unknown, error #0", + "invalid 'max samples' field", + "invalid 'samples per buffer' field", + "invalid 'sample interval' field", + "invalid path", + "sample file in use", + "invalid 'number of events' field", + "unknown, error #7", + "internal error", + "bad event name", + "VTStopSampling called without calling VTStartSampling", + "no events selected for event-based sampling", + "events selected cannot be run together", + "no sampling parameters", + "sample database already exists", + "sampling already started", + "time-based sampling not supported", + "invalid 'sampling parameters size' field", + "invalid 'event size' field", + "sampling file already bound", + "invalid event path", + "invalid license", + "invalid 'global options' field", + +}; + +JS_FRIEND_API(JSBool) +js_StartVtune(JSContext *cx, JSObject *obj, + uintN argc, jsval *argv, jsval *rval) +{ + VTUNE_EVENT events[] = { + { 1000000, 0, 0, 0, "CPU_CLK_UNHALTED.CORE" }, + { 1000000, 0, 0, 0, "INST_RETIRED.ANY" }, + }; + + U32 n_events = sizeof(events) / sizeof(VTUNE_EVENT); + char *default_filename = "mozilla-vtune.tb5"; + JSString *str; + U32 status; + + VTUNE_SAMPLING_PARAMS params = + sizeof(VTUNE_SAMPLING_PARAMS), + sizeof(VTUNE_EVENT), + 0, 0, /* Reserved fields */ + 1, /* Initialize in "paused" state */ + 0, /* Max samples, or 0 for "continuous" */ + 4096, /* Samples per buffer */ + 0.1, /* Sampling interval in ms */ + 1, /* 1 for event-based sampling, 0 for time-based */ + + n_events, + events, + default_filename, + }; + + if (argc > 0 && JSVAL_IS_STRING(argv[0])) { + str = JSVAL_TO_STRING(argv[0]); + params.tb5Filename = js_DeflateString(cx, str->chars(), str->length()); + } + + status = VTStartSampling(¶ms); + + if (params.tb5Filename != default_filename) + cx->free(params.tb5Filename); + + if (status != 0) { + if (status == VTAPI_MULTIPLE_RUNS) + VTStopSampling(0); + if (status < sizeof(vtuneErrorMessages)) + JS_ReportError(cx, "Vtune setup error: %s", + vtuneErrorMessages[status]); + else + JS_ReportError(cx, "Vtune setup error: %d", + status); + return JS_FALSE; + } + return JS_TRUE; +} + +JS_FRIEND_API(JSBool) +js_StopVtune(JSContext *cx, JSObject *obj, + uintN argc, jsval *argv, jsval *rval) +{ + U32 status = VTStopSampling(1); + if (status) { + if (status < sizeof(vtuneErrorMessages)) + JS_ReportError(cx, "Vtune shutdown error: %s", + vtuneErrorMessages[status]); + else + JS_ReportError(cx, "Vtune shutdown error: %d", + status); + return JS_FALSE; + } + return JS_TRUE; +} + +JS_FRIEND_API(JSBool) +js_PauseVtune(JSContext *cx, JSObject *obj, + uintN argc, jsval *argv, jsval *rval) +{ + VTPause(); + return JS_TRUE; +} + +JS_FRIEND_API(JSBool) +js_ResumeVtune(JSContext *cx, JSObject *obj, + uintN argc, jsval *argv, jsval *rval) +{ + VTResume(); + return JS_TRUE; +} + +#endif /* MOZ_VTUNE */ + +#ifdef MOZ_TRACEVIS +/* + * Ethogram - Javascript wrapper for TraceVis state + * + * ethology: The scientific study of animal behavior, + * especially as it occurs in a natural environment. + * ethogram: A pictorial catalog of the behavioral patterns of + * an organism or a species. + * + */ +#if defined(XP_WIN) +#include +#else +#include +#endif +#include "jstracer.h" + +#define ETHOGRAM_BUF_SIZE 65536 + +static JSBool +ethogram_construct(JSContext *cx, JSObject *obj, + uintN argc, jsval *argv, jsval *rval); +static void +ethogram_finalize(JSContext *cx, JSObject *obj); + +static JSClass ethogram_class = { + "Ethogram", + JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, ethogram_finalize, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +struct EthogramEvent { + TraceVisState s; + TraceVisExitReason r; + int ts; + int tus; + JSString *filename; + int lineno; +}; + +static int +compare_strings(const void *k1, const void *k2) +{ + return strcmp((const char *) k1, (const char *) k2) == 0; +} + +class EthogramEventBuffer { +private: + EthogramEvent mBuf[ETHOGRAM_BUF_SIZE]; + int mReadPos; + int mWritePos; + JSObject *mFilenames; + int mStartSecond; + + struct EthogramScriptEntry { + char *filename; + JSString *jsfilename; + + EthogramScriptEntry *next; + }; + EthogramScriptEntry *mScripts; + +public: + friend JSBool + ethogram_construct(JSContext *cx, JSObject *obj, + uintN argc, jsval *argv, jsval *rval); + + inline void push(TraceVisState s, TraceVisExitReason r, char *filename, int lineno) { + mBuf[mWritePos].s = s; + mBuf[mWritePos].r = r; +#if defined(XP_WIN) + FILETIME now; + GetSystemTimeAsFileTime(&now); + unsigned long long raw_us = 0.1 * + (((unsigned long long) now.dwHighDateTime << 32ULL) | + (unsigned long long) now.dwLowDateTime); + unsigned int sec = raw_us / 1000000L; + unsigned int usec = raw_us % 1000000L; + mBuf[mWritePos].ts = sec - mStartSecond; + mBuf[mWritePos].tus = usec; +#else + struct timeval tv; + gettimeofday(&tv, NULL); + mBuf[mWritePos].ts = tv.tv_sec - mStartSecond; + mBuf[mWritePos].tus = tv.tv_usec; +#endif + + JSString *jsfilename = findScript(filename); + mBuf[mWritePos].filename = jsfilename; + mBuf[mWritePos].lineno = lineno; + + mWritePos = (mWritePos + 1) % ETHOGRAM_BUF_SIZE; + if (mWritePos == mReadPos) { + mReadPos = (mWritePos + 1) % ETHOGRAM_BUF_SIZE; + } + } + + inline EthogramEvent *pop() { + EthogramEvent *e = &mBuf[mReadPos]; + mReadPos = (mReadPos + 1) % ETHOGRAM_BUF_SIZE; + return e; + } + + bool isEmpty() { + return (mReadPos == mWritePos); + } + + EthogramScriptEntry *addScript(JSContext *cx, JSObject *obj, char *filename, JSString *jsfilename) { + JSHashNumber hash = JS_HashString(filename); + JSHashEntry **hep = JS_HashTableRawLookup(traceVisScriptTable, hash, filename); + if (*hep != NULL) + return JS_FALSE; + + JS_HashTableRawAdd(traceVisScriptTable, hep, hash, filename, this); + + EthogramScriptEntry * entry = (EthogramScriptEntry *) JS_malloc(cx, sizeof(EthogramScriptEntry)); + if (entry == NULL) + return NULL; + + entry->next = mScripts; + mScripts = entry; + entry->filename = filename; + entry->jsfilename = jsfilename; + + return mScripts; + } + + void removeScripts(JSContext *cx) { + EthogramScriptEntry *se = mScripts; + while (se != NULL) { + char *filename = se->filename; + + JSHashNumber hash = JS_HashString(filename); + JSHashEntry **hep = JS_HashTableRawLookup(traceVisScriptTable, hash, filename); + JSHashEntry *he = *hep; + if (he) { + /* we hardly knew he */ + JS_HashTableRawRemove(traceVisScriptTable, hep, he); + } + + EthogramScriptEntry *se_head = se; + se = se->next; + JS_free(cx, se_head); + } + } + + JSString *findScript(char *filename) { + EthogramScriptEntry *se = mScripts; + while (se != NULL) { + if (compare_strings(se->filename, filename)) + return (se->jsfilename); + se = se->next; + } + return NULL; + } + + JSObject *filenames() { + return mFilenames; + } + + int length() { + if (mWritePos < mReadPos) + return (mWritePos + ETHOGRAM_BUF_SIZE) - mReadPos; + else + return mWritePos - mReadPos; + } +}; + +static char jstv_empty[] = ""; + +inline char * +jstv_Filename(JSStackFrame *fp) +{ + while (fp && fp->script == NULL) + fp = fp->down; + return (fp && fp->script && fp->script->filename) + ? (char *)fp->script->filename + : jstv_empty; +} +inline uintN +jstv_Lineno(JSContext *cx, JSStackFrame *fp) +{ + while (fp && fp->regs == NULL) + fp = fp->down; + return (fp && fp->regs) ? js_FramePCToLineNumber(cx, fp) : 0; +} + +/* Collect states here and distribute to a matching buffer, if any */ +JS_FRIEND_API(void) +js_StoreTraceVisState(JSContext *cx, TraceVisState s, TraceVisExitReason r) +{ + JSStackFrame *fp = cx->fp; + + char *script_file = jstv_Filename(fp); + JSHashNumber hash = JS_HashString(script_file); + + JSHashEntry **hep = JS_HashTableRawLookup(traceVisScriptTable, hash, script_file); + /* update event buffer, flag if overflowed */ + JSHashEntry *he = *hep; + if (he) { + EthogramEventBuffer *p; + p = (EthogramEventBuffer *) he->value; + + p->push(s, r, script_file, jstv_Lineno(cx, fp)); + } +} + +static JSBool +ethogram_construct(JSContext *cx, JSObject *obj, + uintN argc, jsval *argv, jsval *rval) +{ + EthogramEventBuffer *p; + + p = (EthogramEventBuffer *) JS_malloc(cx, sizeof(EthogramEventBuffer)); + + p->mReadPos = p->mWritePos = 0; + p->mScripts = NULL; + p->mFilenames = JS_NewArrayObject(cx, 0, NULL); + +#if defined(XP_WIN) + FILETIME now; + GetSystemTimeAsFileTime(&now); + unsigned long long raw_us = 0.1 * + (((unsigned long long) now.dwHighDateTime << 32ULL) | + (unsigned long long) now.dwLowDateTime); + unsigned int s = raw_us / 1000000L; + p->mStartSecond = s; +#else + struct timeval tv; + gettimeofday(&tv, NULL); + p->mStartSecond = tv.tv_sec; +#endif + jsval filenames = OBJECT_TO_JSVAL(p->filenames()); + if (!JS_DefineProperty(cx, obj, "filenames", filenames, + NULL, NULL, JSPROP_READONLY|JSPROP_PERMANENT)) + return JS_FALSE; + + if (!JS_IsConstructing(cx)) { + obj = JS_NewObject(cx, ðogram_class, NULL, NULL); + if (!obj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + } + JS_SetPrivate(cx, obj, p); + return JS_TRUE; +} + +static void +ethogram_finalize(JSContext *cx, JSObject *obj) +{ + EthogramEventBuffer *p; + p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, ðogram_class, NULL); + if (!p) + return; + + p->removeScripts(cx); + + JS_free(cx, p); +} + +static JSBool +ethogram_addScript(JSContext *cx, JSObject *obj, + uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + char *filename = NULL; + if (argc > 0 && JSVAL_IS_STRING(argv[0])) { + str = JSVAL_TO_STRING(argv[0]); + filename = js_DeflateString(cx, + str->chars(), + str->length()); + } + + /* silently ignore no args */ + if (!filename) + return JS_TRUE; + + EthogramEventBuffer *p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, ðogram_class, argv); + + p->addScript(cx, obj, filename, str); + JS_CallFunctionName(cx, p->filenames(), "push", 1, argv, rval); + return JS_TRUE; +} + +static JSBool +ethogram_getAllEvents(JSContext *cx, JSObject *obj, + uintN argc, jsval *argv, jsval *rval) +{ + EthogramEventBuffer *p; + + p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, ðogram_class, argv); + if (!p) + return JS_FALSE; + + if (p->isEmpty()) { + *rval = JSVAL_NULL; + return JS_TRUE; + } + + JSObject *rarray = JS_NewArrayObject(cx, 0, NULL); + if (rarray == NULL) { + *rval = JSVAL_NULL; + return JS_TRUE; + } + + *rval = OBJECT_TO_JSVAL(rarray); + + for (int i = 0; !p->isEmpty(); i++) { + + JSObject *x = JS_NewObject(cx, NULL, NULL, NULL); + if (x == NULL) + return JS_FALSE; + + EthogramEvent *e = p->pop(); + + jsval state = INT_TO_JSVAL(e->s); + jsval reason = INT_TO_JSVAL(e->r); + jsval ts = INT_TO_JSVAL(e->ts); + jsval tus = INT_TO_JSVAL(e->tus); + + jsval filename = STRING_TO_JSVAL(e->filename); + jsval lineno = INT_TO_JSVAL(e->lineno); + + if (!JS_SetProperty(cx, x, "state", &state)) + return JS_FALSE; + if (!JS_SetProperty(cx, x, "reason", &reason)) + return JS_FALSE; + if (!JS_SetProperty(cx, x, "ts", &ts)) + return JS_FALSE; + if (!JS_SetProperty(cx, x, "tus", &tus)) + return JS_FALSE; + + if (!JS_SetProperty(cx, x, "filename", &filename)) + return JS_FALSE; + if (!JS_SetProperty(cx, x, "lineno", &lineno)) + return JS_FALSE; + + jsval element = OBJECT_TO_JSVAL(x); + JS_SetElement(cx, rarray, i, &element); + } + + return JS_TRUE; +} + +static JSBool +ethogram_getNextEvent(JSContext *cx, JSObject *obj, + uintN argc, jsval *argv, jsval *rval) +{ + EthogramEventBuffer *p; + + p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, ðogram_class, argv); + if (!p) + return JS_FALSE; + + JSObject *x = JS_NewObject(cx, NULL, NULL, NULL); + if (x == NULL) + return JS_FALSE; + + if (p->isEmpty()) { + *rval = JSVAL_NULL; + return JS_TRUE; + } + + EthogramEvent *e = p->pop(); + jsval state = INT_TO_JSVAL(e->s); + jsval reason = INT_TO_JSVAL(e->r); + jsval ts = INT_TO_JSVAL(e->ts); + jsval tus = INT_TO_JSVAL(e->tus); + + jsval filename = STRING_TO_JSVAL(e->filename); + jsval lineno = INT_TO_JSVAL(e->lineno); + + if (!JS_SetProperty(cx, x, "state", &state)) + return JS_FALSE; + if (!JS_SetProperty(cx, x, "reason", &reason)) + return JS_FALSE; + if (!JS_SetProperty(cx, x, "ts", &ts)) + return JS_FALSE; + if (!JS_SetProperty(cx, x, "tus", &tus)) + return JS_FALSE; + if (!JS_SetProperty(cx, x, "filename", &filename)) + return JS_FALSE; + + if (!JS_SetProperty(cx, x, "lineno", &lineno)) + return JS_FALSE; + + *rval = OBJECT_TO_JSVAL(x); + + return JS_TRUE; +} + +static JSFunctionSpec ethogram_methods[] = { + {"addScript", ethogram_addScript, 1}, + {"getAllEvents", ethogram_getAllEvents, 0}, + {"getNextEvent", ethogram_getNextEvent, 0}, + {0} +}; + +/* + * An |Ethogram| organizes the output of a collection of files that should be + * monitored together. A single object gets events for the group. + */ +JS_FRIEND_API(JSBool) +js_InitEthogram(JSContext *cx, JSObject *obj, + uintN argc, jsval *argv, jsval *rval) +{ + if (!traceVisScriptTable) { + traceVisScriptTable = JS_NewHashTable(8, JS_HashString, compare_strings, + NULL, NULL, NULL); + } + + JS_InitClass(cx, JS_GetGlobalObject(cx), NULL, ðogram_class, + ethogram_construct, 0, NULL, ethogram_methods, + NULL, NULL); + + return JS_TRUE; +} + +JS_FRIEND_API(JSBool) +js_ShutdownEthogram(JSContext *cx, JSObject *obj, + uintN argc, jsval *argv, jsval *rval) +{ + if (traceVisScriptTable) + JS_HashTableDestroy(traceVisScriptTable); + + return JS_TRUE; +} + +#endif /* MOZ_TRACEVIS */ diff --git a/ape-server/deps/js/src/jsdbgapi.h b/ape-server/deps/js/src/jsdbgapi.h new file mode 100755 index 0000000..517f7ed --- /dev/null +++ b/ape-server/deps/js/src/jsdbgapi.h @@ -0,0 +1,509 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsdbgapi_h___ +#define jsdbgapi_h___ +/* + * JS debugger API. + */ +#include "jsapi.h" +#include "jsopcode.h" +#include "jsprvtd.h" + +JS_BEGIN_EXTERN_C + +/* + * Unexported library-private helper used to unpatch all traps in a script. + * Returns script->code if script has no traps, else a JS_malloc'ed copy of + * script->code which the caller must JS_free, or null on JS_malloc OOM. + */ +extern jsbytecode * +js_UntrapScriptCode(JSContext *cx, JSScript *script); + +extern JS_PUBLIC_API(JSBool) +JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, + JSTrapHandler handler, void *closure); + +extern JS_PUBLIC_API(JSOp) +JS_GetTrapOpcode(JSContext *cx, JSScript *script, jsbytecode *pc); + +extern JS_PUBLIC_API(void) +JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc, + JSTrapHandler *handlerp, void **closurep); + +extern JS_PUBLIC_API(void) +JS_ClearScriptTraps(JSContext *cx, JSScript *script); + +extern JS_PUBLIC_API(void) +JS_ClearAllTraps(JSContext *cx); + +extern JS_PUBLIC_API(JSTrapStatus) +JS_HandleTrap(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval); + +extern JS_PUBLIC_API(JSBool) +JS_SetInterrupt(JSRuntime *rt, JSTrapHandler handler, void *closure); + +extern JS_PUBLIC_API(JSBool) +JS_ClearInterrupt(JSRuntime *rt, JSTrapHandler *handlerp, void **closurep); + +/************************************************************************/ + +extern JS_PUBLIC_API(JSBool) +JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id, + JSWatchPointHandler handler, void *closure); + +extern JS_PUBLIC_API(JSBool) +JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsval id, + JSWatchPointHandler *handlerp, void **closurep); + +extern JS_PUBLIC_API(JSBool) +JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj); + +extern JS_PUBLIC_API(JSBool) +JS_ClearAllWatchPoints(JSContext *cx); + +#ifdef JS_HAS_OBJ_WATCHPOINT +/* + * Hide these non-API function prototypes by testing whether the internal + * header file "jsversion.h" has been included. + */ +extern void +js_TraceWatchPoints(JSTracer *trc, JSObject *obj); + +extern void +js_SweepWatchPoints(JSContext *cx); + +extern JSScopeProperty * +js_FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id); + +/* + * NB: callers outside of jsdbgapi.c must pass non-null scope. + */ +extern JSPropertyOp +js_GetWatchedSetter(JSRuntime *rt, JSScope *scope, + const JSScopeProperty *sprop); + +extern JSBool +js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + +extern JSBool +js_watch_set_wrapper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +extern JSPropertyOp +js_WrapWatchedSetter(JSContext *cx, jsid id, uintN attrs, JSPropertyOp setter); + +#endif /* JS_HAS_OBJ_WATCHPOINT */ + +/************************************************************************/ + +extern JS_PUBLIC_API(uintN) +JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc); + +extern JS_PUBLIC_API(jsbytecode *) +JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno); + +extern JS_PUBLIC_API(JSScript *) +JS_GetFunctionScript(JSContext *cx, JSFunction *fun); + +extern JS_PUBLIC_API(JSNative) +JS_GetFunctionNative(JSContext *cx, JSFunction *fun); + +extern JS_PUBLIC_API(JSFastNative) +JS_GetFunctionFastNative(JSContext *cx, JSFunction *fun); + +extern JS_PUBLIC_API(JSPrincipals *) +JS_GetScriptPrincipals(JSContext *cx, JSScript *script); + +/* + * Stack Frame Iterator + * + * Used to iterate through the JS stack frames to extract + * information from the frames. + */ + +extern JS_PUBLIC_API(JSStackFrame *) +JS_FrameIterator(JSContext *cx, JSStackFrame **iteratorp); + +extern JS_PUBLIC_API(JSScript *) +JS_GetFrameScript(JSContext *cx, JSStackFrame *fp); + +extern JS_PUBLIC_API(jsbytecode *) +JS_GetFramePC(JSContext *cx, JSStackFrame *fp); + +/* + * Get the closest scripted frame below fp. If fp is null, start from cx->fp. + */ +extern JS_PUBLIC_API(JSStackFrame *) +JS_GetScriptedCaller(JSContext *cx, JSStackFrame *fp); + +/* + * Return a weak reference to fp's principals. A null return does not denote + * an error, it means there are no principals. + */ +extern JS_PUBLIC_API(JSPrincipals *) +JS_StackFramePrincipals(JSContext *cx, JSStackFrame *fp); + +/* + * This API is like JS_StackFramePrincipals(cx, caller), except that if + * cx->runtime->findObjectPrincipals is non-null, it returns the weaker of + * the caller's principals and the object principals of fp's callee function + * object (fp->argv[-2]), which is eval, Function, or a similar eval-like + * method. The caller parameter should be JS_GetScriptedCaller(cx, fp). + * + * All eval-like methods must use JS_EvalFramePrincipals to acquire a weak + * reference to the correct principals for the eval call to be secure, given + * an embedding that calls JS_SetObjectPrincipalsFinder (see jsapi.h). + */ +extern JS_PUBLIC_API(JSPrincipals *) +JS_EvalFramePrincipals(JSContext *cx, JSStackFrame *fp, JSStackFrame *caller); + +extern JS_PUBLIC_API(void *) +JS_GetFrameAnnotation(JSContext *cx, JSStackFrame *fp); + +extern JS_PUBLIC_API(void) +JS_SetFrameAnnotation(JSContext *cx, JSStackFrame *fp, void *annotation); + +extern JS_PUBLIC_API(void *) +JS_GetFramePrincipalArray(JSContext *cx, JSStackFrame *fp); + +extern JS_PUBLIC_API(JSBool) +JS_IsNativeFrame(JSContext *cx, JSStackFrame *fp); + +/* this is deprecated, use JS_GetFrameScopeChain instead */ +extern JS_PUBLIC_API(JSObject *) +JS_GetFrameObject(JSContext *cx, JSStackFrame *fp); + +extern JS_PUBLIC_API(JSObject *) +JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fp); + +extern JS_PUBLIC_API(JSObject *) +JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fp); + +extern JS_PUBLIC_API(JSObject *) +JS_GetFrameThis(JSContext *cx, JSStackFrame *fp); + +extern JS_PUBLIC_API(JSFunction *) +JS_GetFrameFunction(JSContext *cx, JSStackFrame *fp); + +extern JS_PUBLIC_API(JSObject *) +JS_GetFrameFunctionObject(JSContext *cx, JSStackFrame *fp); + +/* XXXrginda Initially published with typo */ +#define JS_IsContructorFrame JS_IsConstructorFrame +extern JS_PUBLIC_API(JSBool) +JS_IsConstructorFrame(JSContext *cx, JSStackFrame *fp); + +extern JS_PUBLIC_API(JSBool) +JS_IsDebuggerFrame(JSContext *cx, JSStackFrame *fp); + +extern JS_PUBLIC_API(jsval) +JS_GetFrameReturnValue(JSContext *cx, JSStackFrame *fp); + +extern JS_PUBLIC_API(void) +JS_SetFrameReturnValue(JSContext *cx, JSStackFrame *fp, jsval rval); + +/** + * Return fp's callee function object (fp->callee) if it has one. + */ +extern JS_PUBLIC_API(JSObject *) +JS_GetFrameCalleeObject(JSContext *cx, JSStackFrame *fp); + +/************************************************************************/ + +extern JS_PUBLIC_API(const char *) +JS_GetScriptFilename(JSContext *cx, JSScript *script); + +extern JS_PUBLIC_API(uintN) +JS_GetScriptBaseLineNumber(JSContext *cx, JSScript *script); + +extern JS_PUBLIC_API(uintN) +JS_GetScriptLineExtent(JSContext *cx, JSScript *script); + +extern JS_PUBLIC_API(JSVersion) +JS_GetScriptVersion(JSContext *cx, JSScript *script); + +/************************************************************************/ + +/* + * Hook setters for script creation and destruction, see jsprvtd.h for the + * typedefs. These macros provide binary compatibility and newer, shorter + * synonyms. + */ +#define JS_SetNewScriptHook JS_SetNewScriptHookProc +#define JS_SetDestroyScriptHook JS_SetDestroyScriptHookProc + +extern JS_PUBLIC_API(void) +JS_SetNewScriptHook(JSRuntime *rt, JSNewScriptHook hook, void *callerdata); + +extern JS_PUBLIC_API(void) +JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook, + void *callerdata); + +/************************************************************************/ + +extern JS_PUBLIC_API(JSBool) +JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp, + const jschar *chars, uintN length, + const char *filename, uintN lineno, + jsval *rval); + +extern JS_PUBLIC_API(JSBool) +JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp, + const char *bytes, uintN length, + const char *filename, uintN lineno, + jsval *rval); + +/************************************************************************/ + +typedef struct JSPropertyDesc { + jsval id; /* primary id, a string or int */ + jsval value; /* property value */ + uint8 flags; /* flags, see below */ + uint8 spare; /* unused */ + uint16 slot; /* argument/variable slot */ + jsval alias; /* alias id if JSPD_ALIAS flag */ +} JSPropertyDesc; + +#define JSPD_ENUMERATE 0x01 /* visible to for/in loop */ +#define JSPD_READONLY 0x02 /* assignment is error */ +#define JSPD_PERMANENT 0x04 /* property cannot be deleted */ +#define JSPD_ALIAS 0x08 /* property has an alias id */ +#define JSPD_ARGUMENT 0x10 /* argument to function */ +#define JSPD_VARIABLE 0x20 /* local variable in function */ +#define JSPD_EXCEPTION 0x40 /* exception occurred fetching the property, */ + /* value is exception */ +#define JSPD_ERROR 0x80 /* native getter returned JS_FALSE without */ + /* throwing an exception */ + +typedef struct JSPropertyDescArray { + uint32 length; /* number of elements in array */ + JSPropertyDesc *array; /* alloc'd by Get, freed by Put */ +} JSPropertyDescArray; + +extern JS_PUBLIC_API(JSScopeProperty *) +JS_PropertyIterator(JSObject *obj, JSScopeProperty **iteratorp); + +extern JS_PUBLIC_API(JSBool) +JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, + JSPropertyDesc *pd); + +extern JS_PUBLIC_API(JSBool) +JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda); + +extern JS_PUBLIC_API(void) +JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda); + +/************************************************************************/ + +extern JS_PUBLIC_API(JSBool) +JS_SetDebuggerHandler(JSRuntime *rt, JSTrapHandler handler, void *closure); + +extern JS_PUBLIC_API(JSBool) +JS_SetSourceHandler(JSRuntime *rt, JSSourceHandler handler, void *closure); + +extern JS_PUBLIC_API(JSBool) +JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure); + +extern JS_PUBLIC_API(JSBool) +JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure); + +extern JS_PUBLIC_API(JSBool) +JS_SetObjectHook(JSRuntime *rt, JSObjectHook hook, void *closure); + +extern JS_PUBLIC_API(JSBool) +JS_SetThrowHook(JSRuntime *rt, JSTrapHandler hook, void *closure); + +extern JS_PUBLIC_API(JSBool) +JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure); + +/************************************************************************/ + +extern JS_PUBLIC_API(size_t) +JS_GetObjectTotalSize(JSContext *cx, JSObject *obj); + +extern JS_PUBLIC_API(size_t) +JS_GetFunctionTotalSize(JSContext *cx, JSFunction *fun); + +extern JS_PUBLIC_API(size_t) +JS_GetScriptTotalSize(JSContext *cx, JSScript *script); + +/* + * Get the top-most running script on cx starting from fp, or from the top of + * cx's frame stack if fp is null, and return its script filename flags. If + * the script has a null filename member, return JSFILENAME_NULL. + */ +extern JS_PUBLIC_API(uint32) +JS_GetTopScriptFilenameFlags(JSContext *cx, JSStackFrame *fp); + +/* + * Get the script filename flags for the script. If the script doesn't have a + * filename, return JSFILENAME_NULL. + */ +extern JS_PUBLIC_API(uint32) +JS_GetScriptFilenameFlags(JSScript *script); + +/* + * Associate flags with a script filename prefix in rt, so that any subsequent + * script compilation will inherit those flags if the script's filename is the + * same as prefix, or if prefix is a substring of the script's filename. + * + * The API defines only one flag bit, JSFILENAME_SYSTEM, leaving the remaining + * 31 bits up to the API client to define. The union of all 32 bits must not + * be a legal combination, however, in order to preserve JSFILENAME_NULL as a + * unique value. API clients may depend on JSFILENAME_SYSTEM being a set bit + * in JSFILENAME_NULL -- a script with a null filename member is presumed to + * be a "system" script. + */ +extern JS_PUBLIC_API(JSBool) +JS_FlagScriptFilenamePrefix(JSRuntime *rt, const char *prefix, uint32 flags); + +#define JSFILENAME_NULL 0xffffffff /* null script filename */ +#define JSFILENAME_SYSTEM 0x00000001 /* "system" script, see below */ +#define JSFILENAME_PROTECTED 0x00000002 /* scripts need protection */ + +/* + * Return true if obj is a "system" object, that is, one created by + * JS_NewSystemObject with the system flag set and not JS_NewObject. + * + * What "system" means is up to the API client, but it can be used to implement + * access control policies based on script filenames and their prefixes, using + * JS_FlagScriptFilenamePrefix and JS_GetTopScriptFilenameFlags. + */ +extern JS_PUBLIC_API(JSBool) +JS_IsSystemObject(JSContext *cx, JSObject *obj); + +/* + * Call JS_NewObject(cx, clasp, proto, parent) and, if system is true, mark the + * result as a system object, that is an object for which JS_IsSystemObject + * returns true. + */ +extern JS_PUBLIC_API(JSObject *) +JS_NewSystemObject(JSContext *cx, JSClass *clasp, JSObject *proto, + JSObject *parent, JSBool system); + +/************************************************************************/ + +extern JS_PUBLIC_API(const JSDebugHooks *) +JS_GetGlobalDebugHooks(JSRuntime *rt); + +extern JS_PUBLIC_API(JSDebugHooks *) +JS_SetContextDebugHooks(JSContext *cx, const JSDebugHooks *hooks); + +#ifdef MOZ_SHARK + +extern JS_PUBLIC_API(JSBool) +JS_StartChudRemote(); + +extern JS_PUBLIC_API(JSBool) +JS_StopChudRemote(); + +extern JS_PUBLIC_API(JSBool) +JS_ConnectShark(); + +extern JS_PUBLIC_API(JSBool) +JS_DisconnectShark(); + +extern JS_FRIEND_API(JSBool) +js_StopShark(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +extern JS_FRIEND_API(JSBool) +js_StartShark(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +extern JS_FRIEND_API(JSBool) +js_ConnectShark(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +extern JS_FRIEND_API(JSBool) +js_DisconnectShark(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +#endif /* MOZ_SHARK */ + +#ifdef MOZ_CALLGRIND + +extern JS_FRIEND_API(JSBool) +js_StopCallgrind(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +extern JS_FRIEND_API(JSBool) +js_StartCallgrind(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +extern JS_FRIEND_API(JSBool) +js_DumpCallgrind(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +#endif /* MOZ_CALLGRIND */ + +#ifdef MOZ_VTUNE + +extern JS_FRIEND_API(JSBool) +js_StartVtune(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +extern JS_FRIEND_API(JSBool) +js_StopVtune(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +extern JS_FRIEND_API(JSBool) +js_PauseVtune(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +extern JS_FRIEND_API(JSBool) +js_ResumeVtune(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +#endif /* MOZ_VTUNE */ + +#ifdef MOZ_TRACEVIS +extern JS_FRIEND_API(JSBool) +js_InitEthogram(JSContext *cx, JSObject *obj, + uintN argc, jsval *argv, jsval *rval); +extern JS_FRIEND_API(JSBool) +js_ShutdownEthogram(JSContext *cx, JSObject *obj, + uintN argc, jsval *argv, jsval *rval); +#endif /* MOZ_TRACEVIS */ + +JS_END_EXTERN_C + +#endif /* jsdbgapi_h___ */ diff --git a/ape-server/deps/js/src/jsdhash.cpp b/ape-server/deps/js/src/jsdhash.cpp new file mode 100755 index 0000000..c8b9db0 --- /dev/null +++ b/ape-server/deps/js/src/jsdhash.cpp @@ -0,0 +1,903 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla JavaScript code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1999-2001 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Brendan Eich (Original Author) + * Chris Waterson + * L. David Baron , Mozilla Corporation + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Double hashing implementation. + */ +#include +#include +#include +#include "jsstdint.h" +#include "jsbit.h" +#include "jsdhash.h" +#include "jsutil.h" /* for JS_ASSERT */ + +#ifdef JS_DHASHMETER +# if defined MOZILLA_CLIENT && defined DEBUG_XXXbrendan +# include "nsTraceMalloc.h" +# endif +# define METER(x) x +#else +# define METER(x) /* nothing */ +#endif + +/* + * The following DEBUG-only code is used to assert that calls to one of + * table->ops or to an enumerator do not cause re-entry into a call that + * can mutate the table. The recursion level is stored in additional + * space allocated at the end of the entry store to avoid changing + * JSDHashTable, which could cause issues when mixing DEBUG and + * non-DEBUG components. + */ +#ifdef DEBUG + +#define JSDHASH_ONELINE_ASSERT JS_ASSERT +#define RECURSION_LEVEL(table_) (*(uint32*)(table_->entryStore + \ + JS_DHASH_TABLE_SIZE(table_) * \ + table_->entrySize)) +/* + * Most callers that assert about the recursion level don't care about + * this magical value because they are asserting that mutation is + * allowed (and therefore the level is 0 or 1, depending on whether they + * incremented it). + * + * Only PL_DHashTableFinish needs to allow this special value. + */ +#define IMMUTABLE_RECURSION_LEVEL ((uint32)-1) + +#define RECURSION_LEVEL_SAFE_TO_FINISH(table_) \ + (RECURSION_LEVEL(table_) == 0 || \ + RECURSION_LEVEL(table_) == IMMUTABLE_RECURSION_LEVEL) + +#define ENTRY_STORE_EXTRA sizeof(uint32) +#define INCREMENT_RECURSION_LEVEL(table_) \ + JS_BEGIN_MACRO \ + if (RECURSION_LEVEL(table_) != IMMUTABLE_RECURSION_LEVEL) \ + ++RECURSION_LEVEL(table_); \ + JS_END_MACRO +#define DECREMENT_RECURSION_LEVEL(table_) \ + JS_BEGIN_MACRO \ + if (RECURSION_LEVEL(table_) != IMMUTABLE_RECURSION_LEVEL) { \ + JSDHASH_ONELINE_ASSERT(RECURSION_LEVEL(table_) > 0); \ + --RECURSION_LEVEL(table_); \ + } \ + JS_END_MACRO + +#else + +#define ENTRY_STORE_EXTRA 0 +#define INCREMENT_RECURSION_LEVEL(table_) JS_BEGIN_MACRO JS_END_MACRO +#define DECREMENT_RECURSION_LEVEL(table_) JS_BEGIN_MACRO JS_END_MACRO + +#endif /* defined(DEBUG) */ + +JS_PUBLIC_API(void *) +JS_DHashAllocTable(JSDHashTable *table, uint32 nbytes) +{ + return js_malloc(nbytes); +} + +JS_PUBLIC_API(void) +JS_DHashFreeTable(JSDHashTable *table, void *ptr) +{ + js_free(ptr); +} + +JS_PUBLIC_API(JSDHashNumber) +JS_DHashStringKey(JSDHashTable *table, const void *key) +{ + JSDHashNumber h; + const unsigned char *s; + + h = 0; + for (s = (const unsigned char *) key; *s != '\0'; s++) + h = JS_ROTATE_LEFT32(h, 4) ^ *s; + return h; +} + +JS_PUBLIC_API(JSDHashNumber) +JS_DHashVoidPtrKeyStub(JSDHashTable *table, const void *key) +{ + return (JSDHashNumber)(unsigned long)key >> 2; +} + +JS_PUBLIC_API(JSBool) +JS_DHashMatchEntryStub(JSDHashTable *table, + const JSDHashEntryHdr *entry, + const void *key) +{ + const JSDHashEntryStub *stub = (const JSDHashEntryStub *)entry; + + return stub->key == key; +} + +JS_PUBLIC_API(JSBool) +JS_DHashMatchStringKey(JSDHashTable *table, + const JSDHashEntryHdr *entry, + const void *key) +{ + const JSDHashEntryStub *stub = (const JSDHashEntryStub *)entry; + + /* XXX tolerate null keys on account of sloppy Mozilla callers. */ + return stub->key == key || + (stub->key && key && + strcmp((const char *) stub->key, (const char *) key) == 0); +} + +JS_PUBLIC_API(void) +JS_DHashMoveEntryStub(JSDHashTable *table, + const JSDHashEntryHdr *from, + JSDHashEntryHdr *to) +{ + memcpy(to, from, table->entrySize); +} + +JS_PUBLIC_API(void) +JS_DHashClearEntryStub(JSDHashTable *table, JSDHashEntryHdr *entry) +{ + memset(entry, 0, table->entrySize); +} + +JS_PUBLIC_API(void) +JS_DHashFreeStringKey(JSDHashTable *table, JSDHashEntryHdr *entry) +{ + const JSDHashEntryStub *stub = (const JSDHashEntryStub *)entry; + + js_free((void *) stub->key); + memset(entry, 0, table->entrySize); +} + +JS_PUBLIC_API(void) +JS_DHashFinalizeStub(JSDHashTable *table) +{ +} + +static const JSDHashTableOps stub_ops = { + JS_DHashAllocTable, + JS_DHashFreeTable, + JS_DHashVoidPtrKeyStub, + JS_DHashMatchEntryStub, + JS_DHashMoveEntryStub, + JS_DHashClearEntryStub, + JS_DHashFinalizeStub, + NULL +}; + +JS_PUBLIC_API(const JSDHashTableOps *) +JS_DHashGetStubOps(void) +{ + return &stub_ops; +} + +JS_PUBLIC_API(JSDHashTable *) +JS_NewDHashTable(const JSDHashTableOps *ops, void *data, uint32 entrySize, + uint32 capacity) +{ + JSDHashTable *table; + + table = (JSDHashTable *) js_malloc(sizeof *table); + if (!table) + return NULL; + if (!JS_DHashTableInit(table, ops, data, entrySize, capacity)) { + js_free(table); + return NULL; + } + return table; +} + +JS_PUBLIC_API(void) +JS_DHashTableDestroy(JSDHashTable *table) +{ + JS_DHashTableFinish(table); + js_free(table); +} + +JS_PUBLIC_API(JSBool) +JS_DHashTableInit(JSDHashTable *table, const JSDHashTableOps *ops, void *data, + uint32 entrySize, uint32 capacity) +{ + int log2; + uint32 nbytes; + +#ifdef DEBUG + if (entrySize > 10 * sizeof(void *)) { + fprintf(stderr, + "jsdhash: for the table at address %p, the given entrySize" + " of %lu %s favors chaining over double hashing.\n", + (void *) table, + (unsigned long) entrySize, + (entrySize > 16 * sizeof(void*)) ? "definitely" : "probably"); + } +#endif + + table->ops = ops; + table->data = data; + if (capacity < JS_DHASH_MIN_SIZE) + capacity = JS_DHASH_MIN_SIZE; + + JS_CEILING_LOG2(log2, capacity); + + capacity = JS_BIT(log2); + if (capacity >= JS_DHASH_SIZE_LIMIT) + return JS_FALSE; + table->hashShift = JS_DHASH_BITS - log2; + table->maxAlphaFrac = (uint8)(0x100 * JS_DHASH_DEFAULT_MAX_ALPHA); + table->minAlphaFrac = (uint8)(0x100 * JS_DHASH_DEFAULT_MIN_ALPHA); + table->entrySize = entrySize; + table->entryCount = table->removedCount = 0; + table->generation = 0; + nbytes = capacity * entrySize; + + table->entryStore = (char *) ops->allocTable(table, + nbytes + ENTRY_STORE_EXTRA); + if (!table->entryStore) + return JS_FALSE; + memset(table->entryStore, 0, nbytes); + METER(memset(&table->stats, 0, sizeof table->stats)); + +#ifdef DEBUG + RECURSION_LEVEL(table) = 0; +#endif + + return JS_TRUE; +} + +/* + * Compute max and min load numbers (entry counts) from table params. + */ +#define MAX_LOAD(table, size) (((table)->maxAlphaFrac * (size)) >> 8) +#define MIN_LOAD(table, size) (((table)->minAlphaFrac * (size)) >> 8) + +JS_PUBLIC_API(void) +JS_DHashTableSetAlphaBounds(JSDHashTable *table, + float maxAlpha, + float minAlpha) +{ + uint32 size; + + /* + * Reject obviously insane bounds, rather than trying to guess what the + * buggy caller intended. + */ + JS_ASSERT(0.5 <= maxAlpha && maxAlpha < 1 && 0 <= minAlpha); + if (maxAlpha < 0.5 || 1 <= maxAlpha || minAlpha < 0) + return; + + /* + * Ensure that at least one entry will always be free. If maxAlpha at + * minimum size leaves no entries free, reduce maxAlpha based on minimum + * size and the precision limit of maxAlphaFrac's fixed point format. + */ + JS_ASSERT(JS_DHASH_MIN_SIZE - (maxAlpha * JS_DHASH_MIN_SIZE) >= 1); + if (JS_DHASH_MIN_SIZE - (maxAlpha * JS_DHASH_MIN_SIZE) < 1) { + maxAlpha = (float) + (JS_DHASH_MIN_SIZE - JS_MAX(JS_DHASH_MIN_SIZE / 256, 1)) + / JS_DHASH_MIN_SIZE; + } + + /* + * Ensure that minAlpha is strictly less than half maxAlpha. Take care + * not to truncate an entry's worth of alpha when storing in minAlphaFrac + * (8-bit fixed point format). + */ + JS_ASSERT(minAlpha < maxAlpha / 2); + if (minAlpha >= maxAlpha / 2) { + size = JS_DHASH_TABLE_SIZE(table); + minAlpha = (size * maxAlpha - JS_MAX(size / 256, 1)) / (2 * size); + } + + table->maxAlphaFrac = (uint8)(maxAlpha * 256); + table->minAlphaFrac = (uint8)(minAlpha * 256); +} + +/* + * Double hashing needs the second hash code to be relatively prime to table + * size, so we simply make hash2 odd. + */ +#define HASH1(hash0, shift) ((hash0) >> (shift)) +#define HASH2(hash0,log2,shift) ((((hash0) << (log2)) >> (shift)) | 1) + +/* + * Reserve keyHash 0 for free entries and 1 for removed-entry sentinels. Note + * that a removed-entry sentinel need be stored only if the removed entry had + * a colliding entry added after it. Therefore we can use 1 as the collision + * flag in addition to the removed-entry sentinel value. Multiplicative hash + * uses the high order bits of keyHash, so this least-significant reservation + * should not hurt the hash function's effectiveness much. + * + * If you change any of these magic numbers, also update JS_DHASH_ENTRY_IS_LIVE + * in jsdhash.h. It used to be private to jsdhash.c, but then became public to + * assist iterator writers who inspect table->entryStore directly. + */ +#define COLLISION_FLAG ((JSDHashNumber) 1) +#define MARK_ENTRY_FREE(entry) ((entry)->keyHash = 0) +#define MARK_ENTRY_REMOVED(entry) ((entry)->keyHash = 1) +#define ENTRY_IS_REMOVED(entry) ((entry)->keyHash == 1) +#define ENTRY_IS_LIVE(entry) JS_DHASH_ENTRY_IS_LIVE(entry) +#define ENSURE_LIVE_KEYHASH(hash0) if (hash0 < 2) hash0 -= 2; else (void)0 + +/* Match an entry's keyHash against an unstored one computed from a key. */ +#define MATCH_ENTRY_KEYHASH(entry,hash0) \ + (((entry)->keyHash & ~COLLISION_FLAG) == (hash0)) + +/* Compute the address of the indexed entry in table. */ +#define ADDRESS_ENTRY(table, index) \ + ((JSDHashEntryHdr *)((table)->entryStore + (index) * (table)->entrySize)) + +JS_PUBLIC_API(void) +JS_DHashTableFinish(JSDHashTable *table) +{ + char *entryAddr, *entryLimit; + uint32 entrySize; + JSDHashEntryHdr *entry; + +#ifdef DEBUG_XXXbrendan + static FILE *dumpfp = NULL; + if (!dumpfp) dumpfp = fopen("/tmp/jsdhash.bigdump", "w"); + if (dumpfp) { +#ifdef MOZILLA_CLIENT + NS_TraceStack(1, dumpfp); +#endif + JS_DHashTableDumpMeter(table, NULL, dumpfp); + fputc('\n', dumpfp); + } +#endif + + INCREMENT_RECURSION_LEVEL(table); + + /* Call finalize before clearing entries, so it can enumerate them. */ + table->ops->finalize(table); + + /* Clear any remaining live entries. */ + entryAddr = table->entryStore; + entrySize = table->entrySize; + entryLimit = entryAddr + JS_DHASH_TABLE_SIZE(table) * entrySize; + while (entryAddr < entryLimit) { + entry = (JSDHashEntryHdr *)entryAddr; + if (ENTRY_IS_LIVE(entry)) { + METER(table->stats.removeEnums++); + table->ops->clearEntry(table, entry); + } + entryAddr += entrySize; + } + + DECREMENT_RECURSION_LEVEL(table); + JS_ASSERT(RECURSION_LEVEL_SAFE_TO_FINISH(table)); + + /* Free entry storage last. */ + table->ops->freeTable(table, table->entryStore); +} + +static JSDHashEntryHdr * JS_DHASH_FASTCALL +SearchTable(JSDHashTable *table, const void *key, JSDHashNumber keyHash, + JSDHashOperator op) +{ + JSDHashNumber hash1, hash2; + int hashShift, sizeLog2; + JSDHashEntryHdr *entry, *firstRemoved; + JSDHashMatchEntry matchEntry; + uint32 sizeMask; + + METER(table->stats.searches++); + JS_ASSERT(!(keyHash & COLLISION_FLAG)); + + /* Compute the primary hash address. */ + hashShift = table->hashShift; + hash1 = HASH1(keyHash, hashShift); + entry = ADDRESS_ENTRY(table, hash1); + + /* Miss: return space for a new entry. */ + if (JS_DHASH_ENTRY_IS_FREE(entry)) { + METER(table->stats.misses++); + return entry; + } + + /* Hit: return entry. */ + matchEntry = table->ops->matchEntry; + if (MATCH_ENTRY_KEYHASH(entry, keyHash) && matchEntry(table, entry, key)) { + METER(table->stats.hits++); + return entry; + } + + /* Collision: double hash. */ + sizeLog2 = JS_DHASH_BITS - table->hashShift; + hash2 = HASH2(keyHash, sizeLog2, hashShift); + sizeMask = JS_BITMASK(sizeLog2); + + /* Save the first removed entry pointer so JS_DHASH_ADD can recycle it. */ + firstRemoved = NULL; + + for (;;) { + if (JS_UNLIKELY(ENTRY_IS_REMOVED(entry))) { + if (!firstRemoved) + firstRemoved = entry; + } else { + if (op == JS_DHASH_ADD) + entry->keyHash |= COLLISION_FLAG; + } + + METER(table->stats.steps++); + hash1 -= hash2; + hash1 &= sizeMask; + + entry = ADDRESS_ENTRY(table, hash1); + if (JS_DHASH_ENTRY_IS_FREE(entry)) { + METER(table->stats.misses++); + return (firstRemoved && op == JS_DHASH_ADD) ? firstRemoved : entry; + } + + if (MATCH_ENTRY_KEYHASH(entry, keyHash) && + matchEntry(table, entry, key)) { + METER(table->stats.hits++); + return entry; + } + } + + /* NOTREACHED */ + return NULL; +} + +/* + * This is a copy of SearchTable, used by ChangeTable, hardcoded to + * 1. assume |op == PL_DHASH_ADD|, + * 2. assume that |key| will never match an existing entry, and + * 3. assume that no entries have been removed from the current table + * structure. + * Avoiding the need for |key| means we can avoid needing a way to map + * entries to keys, which means callers can use complex key types more + * easily. + */ +static JSDHashEntryHdr * JS_DHASH_FASTCALL +FindFreeEntry(JSDHashTable *table, JSDHashNumber keyHash) +{ + JSDHashNumber hash1, hash2; + int hashShift, sizeLog2; + JSDHashEntryHdr *entry; + uint32 sizeMask; + + METER(table->stats.searches++); + JS_ASSERT(!(keyHash & COLLISION_FLAG)); + + /* Compute the primary hash address. */ + hashShift = table->hashShift; + hash1 = HASH1(keyHash, hashShift); + entry = ADDRESS_ENTRY(table, hash1); + + /* Miss: return space for a new entry. */ + if (JS_DHASH_ENTRY_IS_FREE(entry)) { + METER(table->stats.misses++); + return entry; + } + + /* Collision: double hash. */ + sizeLog2 = JS_DHASH_BITS - table->hashShift; + hash2 = HASH2(keyHash, sizeLog2, hashShift); + sizeMask = JS_BITMASK(sizeLog2); + + for (;;) { + JS_ASSERT(!ENTRY_IS_REMOVED(entry)); + entry->keyHash |= COLLISION_FLAG; + + METER(table->stats.steps++); + hash1 -= hash2; + hash1 &= sizeMask; + + entry = ADDRESS_ENTRY(table, hash1); + if (JS_DHASH_ENTRY_IS_FREE(entry)) { + METER(table->stats.misses++); + return entry; + } + } + + /* NOTREACHED */ + return NULL; +} + +static JSBool +ChangeTable(JSDHashTable *table, int deltaLog2) +{ + int oldLog2, newLog2; + uint32 oldCapacity, newCapacity; + char *newEntryStore, *oldEntryStore, *oldEntryAddr; + uint32 entrySize, i, nbytes; + JSDHashEntryHdr *oldEntry, *newEntry; + JSDHashMoveEntry moveEntry; +#ifdef DEBUG + uint32 recursionLevel; +#endif + + /* Look, but don't touch, until we succeed in getting new entry store. */ + oldLog2 = JS_DHASH_BITS - table->hashShift; + newLog2 = oldLog2 + deltaLog2; + oldCapacity = JS_BIT(oldLog2); + newCapacity = JS_BIT(newLog2); + if (newCapacity >= JS_DHASH_SIZE_LIMIT) + return JS_FALSE; + entrySize = table->entrySize; + nbytes = newCapacity * entrySize; + + newEntryStore = (char *) table->ops->allocTable(table, + nbytes + ENTRY_STORE_EXTRA); + if (!newEntryStore) + return JS_FALSE; + + /* We can't fail from here on, so update table parameters. */ +#ifdef DEBUG + recursionLevel = RECURSION_LEVEL(table); +#endif + table->hashShift = JS_DHASH_BITS - newLog2; + table->removedCount = 0; + table->generation++; + + /* Assign the new entry store to table. */ + memset(newEntryStore, 0, nbytes); + oldEntryAddr = oldEntryStore = table->entryStore; + table->entryStore = newEntryStore; + moveEntry = table->ops->moveEntry; +#ifdef DEBUG + RECURSION_LEVEL(table) = recursionLevel; +#endif + + /* Copy only live entries, leaving removed ones behind. */ + for (i = 0; i < oldCapacity; i++) { + oldEntry = (JSDHashEntryHdr *)oldEntryAddr; + if (ENTRY_IS_LIVE(oldEntry)) { + oldEntry->keyHash &= ~COLLISION_FLAG; + newEntry = FindFreeEntry(table, oldEntry->keyHash); + JS_ASSERT(JS_DHASH_ENTRY_IS_FREE(newEntry)); + moveEntry(table, oldEntry, newEntry); + newEntry->keyHash = oldEntry->keyHash; + } + oldEntryAddr += entrySize; + } + + table->ops->freeTable(table, oldEntryStore); + return JS_TRUE; +} + +JS_PUBLIC_API(JSDHashEntryHdr *) JS_DHASH_FASTCALL +JS_DHashTableOperate(JSDHashTable *table, const void *key, JSDHashOperator op) +{ + JSDHashNumber keyHash; + JSDHashEntryHdr *entry; + uint32 size; + int deltaLog2; + + JS_ASSERT(op == JS_DHASH_LOOKUP || RECURSION_LEVEL(table) == 0); + INCREMENT_RECURSION_LEVEL(table); + + keyHash = table->ops->hashKey(table, key); + keyHash *= JS_DHASH_GOLDEN_RATIO; + + /* Avoid 0 and 1 hash codes, they indicate free and removed entries. */ + ENSURE_LIVE_KEYHASH(keyHash); + keyHash &= ~COLLISION_FLAG; + + switch (op) { + case JS_DHASH_LOOKUP: + METER(table->stats.lookups++); + entry = SearchTable(table, key, keyHash, op); + break; + + case JS_DHASH_ADD: + /* + * If alpha is >= .75, grow or compress the table. If key is already + * in the table, we may grow once more than necessary, but only if we + * are on the edge of being overloaded. + */ + size = JS_DHASH_TABLE_SIZE(table); + if (table->entryCount + table->removedCount >= MAX_LOAD(table, size)) { + /* Compress if a quarter or more of all entries are removed. */ + if (table->removedCount >= size >> 2) { + METER(table->stats.compresses++); + deltaLog2 = 0; + } else { + METER(table->stats.grows++); + deltaLog2 = 1; + } + + /* + * Grow or compress table, returning null if ChangeTable fails and + * falling through might claim the last free entry. + */ + if (!ChangeTable(table, deltaLog2) && + table->entryCount + table->removedCount == size - 1) { + METER(table->stats.addFailures++); + entry = NULL; + break; + } + } + + /* + * Look for entry after possibly growing, so we don't have to add it, + * then skip it while growing the table and re-add it after. + */ + entry = SearchTable(table, key, keyHash, op); + if (!ENTRY_IS_LIVE(entry)) { + /* Initialize the entry, indicating that it's no longer free. */ + METER(table->stats.addMisses++); + if (ENTRY_IS_REMOVED(entry)) { + METER(table->stats.addOverRemoved++); + table->removedCount--; + keyHash |= COLLISION_FLAG; + } + if (table->ops->initEntry && + !table->ops->initEntry(table, entry, key)) { + /* We haven't claimed entry yet; fail with null return. */ + memset(entry + 1, 0, table->entrySize - sizeof *entry); + entry = NULL; + break; + } + entry->keyHash = keyHash; + table->entryCount++; + } + METER(else table->stats.addHits++); + break; + + case JS_DHASH_REMOVE: + entry = SearchTable(table, key, keyHash, op); + if (ENTRY_IS_LIVE(entry)) { + /* Clear this entry and mark it as "removed". */ + METER(table->stats.removeHits++); + JS_DHashTableRawRemove(table, entry); + + /* Shrink if alpha is <= .25 and table isn't too small already. */ + size = JS_DHASH_TABLE_SIZE(table); + if (size > JS_DHASH_MIN_SIZE && + table->entryCount <= MIN_LOAD(table, size)) { + METER(table->stats.shrinks++); + (void) ChangeTable(table, -1); + } + } + METER(else table->stats.removeMisses++); + entry = NULL; + break; + + default: + JS_ASSERT(0); + entry = NULL; + } + + DECREMENT_RECURSION_LEVEL(table); + + return entry; +} + +JS_PUBLIC_API(void) +JS_DHashTableRawRemove(JSDHashTable *table, JSDHashEntryHdr *entry) +{ + JSDHashNumber keyHash; /* load first in case clearEntry goofs it */ + + JS_ASSERT(RECURSION_LEVEL(table) != IMMUTABLE_RECURSION_LEVEL); + + JS_ASSERT(JS_DHASH_ENTRY_IS_LIVE(entry)); + keyHash = entry->keyHash; + table->ops->clearEntry(table, entry); + if (keyHash & COLLISION_FLAG) { + MARK_ENTRY_REMOVED(entry); + table->removedCount++; + } else { + METER(table->stats.removeFrees++); + MARK_ENTRY_FREE(entry); + } + table->entryCount--; +} + +JS_PUBLIC_API(uint32) +JS_DHashTableEnumerate(JSDHashTable *table, JSDHashEnumerator etor, void *arg) +{ + char *entryAddr, *entryLimit; + uint32 i, capacity, entrySize, ceiling; + JSBool didRemove; + JSDHashEntryHdr *entry; + JSDHashOperator op; + + INCREMENT_RECURSION_LEVEL(table); + + entryAddr = table->entryStore; + entrySize = table->entrySize; + capacity = JS_DHASH_TABLE_SIZE(table); + entryLimit = entryAddr + capacity * entrySize; + i = 0; + didRemove = JS_FALSE; + while (entryAddr < entryLimit) { + entry = (JSDHashEntryHdr *)entryAddr; + if (ENTRY_IS_LIVE(entry)) { + op = etor(table, entry, i++, arg); + if (op & JS_DHASH_REMOVE) { + METER(table->stats.removeEnums++); + JS_DHashTableRawRemove(table, entry); + didRemove = JS_TRUE; + } + if (op & JS_DHASH_STOP) + break; + } + entryAddr += entrySize; + } + + JS_ASSERT(!didRemove || RECURSION_LEVEL(table) == 1); + + /* + * Shrink or compress if a quarter or more of all entries are removed, or + * if the table is underloaded according to the configured minimum alpha, + * and is not minimal-size already. Do this only if we removed above, so + * non-removing enumerations can count on stable table->entryStore until + * the next non-lookup-Operate or removing-Enumerate. + */ + if (didRemove && + (table->removedCount >= capacity >> 2 || + (capacity > JS_DHASH_MIN_SIZE && + table->entryCount <= MIN_LOAD(table, capacity)))) { + METER(table->stats.enumShrinks++); + capacity = table->entryCount; + capacity += capacity >> 1; + if (capacity < JS_DHASH_MIN_SIZE) + capacity = JS_DHASH_MIN_SIZE; + + JS_CEILING_LOG2(ceiling, capacity); + ceiling -= JS_DHASH_BITS - table->hashShift; + + (void) ChangeTable(table, ceiling); + } + + DECREMENT_RECURSION_LEVEL(table); + + return i; +} + +#ifdef DEBUG +JS_PUBLIC_API(void) +JS_DHashMarkTableImmutable(JSDHashTable *table) +{ + RECURSION_LEVEL(table) = IMMUTABLE_RECURSION_LEVEL; +} +#endif + +#ifdef JS_DHASHMETER +#include + +JS_PUBLIC_API(void) +JS_DHashTableDumpMeter(JSDHashTable *table, JSDHashEnumerator dump, FILE *fp) +{ + char *entryAddr; + uint32 entrySize, entryCount; + int hashShift, sizeLog2; + uint32 i, tableSize, sizeMask, chainLen, maxChainLen, chainCount; + JSDHashNumber hash1, hash2, saveHash1, maxChainHash1, maxChainHash2; + double sqsum, mean, variance, sigma; + JSDHashEntryHdr *entry, *probe; + + entryAddr = table->entryStore; + entrySize = table->entrySize; + hashShift = table->hashShift; + sizeLog2 = JS_DHASH_BITS - hashShift; + tableSize = JS_DHASH_TABLE_SIZE(table); + sizeMask = JS_BITMASK(sizeLog2); + chainCount = maxChainLen = 0; + hash2 = 0; + sqsum = 0; + + for (i = 0; i < tableSize; i++) { + entry = (JSDHashEntryHdr *)entryAddr; + entryAddr += entrySize; + if (!ENTRY_IS_LIVE(entry)) + continue; + hash1 = HASH1(entry->keyHash & ~COLLISION_FLAG, hashShift); + saveHash1 = hash1; + probe = ADDRESS_ENTRY(table, hash1); + chainLen = 1; + if (probe == entry) { + /* Start of a (possibly unit-length) chain. */ + chainCount++; + } else { + hash2 = HASH2(entry->keyHash & ~COLLISION_FLAG, sizeLog2, + hashShift); + do { + chainLen++; + hash1 -= hash2; + hash1 &= sizeMask; + probe = ADDRESS_ENTRY(table, hash1); + } while (probe != entry); + } + sqsum += chainLen * chainLen; + if (chainLen > maxChainLen) { + maxChainLen = chainLen; + maxChainHash1 = saveHash1; + maxChainHash2 = hash2; + } + } + + entryCount = table->entryCount; + if (entryCount && chainCount) { + mean = (double)entryCount / chainCount; + variance = chainCount * sqsum - entryCount * entryCount; + if (variance < 0 || chainCount == 1) + variance = 0; + else + variance /= chainCount * (chainCount - 1); + sigma = sqrt(variance); + } else { + mean = sigma = 0; + } + + fprintf(fp, "Double hashing statistics:\n"); + fprintf(fp, " table size (in entries): %u\n", tableSize); + fprintf(fp, " number of entries: %u\n", table->entryCount); + fprintf(fp, " number of removed entries: %u\n", table->removedCount); + fprintf(fp, " number of searches: %u\n", table->stats.searches); + fprintf(fp, " number of hits: %u\n", table->stats.hits); + fprintf(fp, " number of misses: %u\n", table->stats.misses); + fprintf(fp, " mean steps per search: %g\n", table->stats.searches ? + (double)table->stats.steps + / table->stats.searches : + 0.); + fprintf(fp, " mean hash chain length: %g\n", mean); + fprintf(fp, " standard deviation: %g\n", sigma); + fprintf(fp, " maximum hash chain length: %u\n", maxChainLen); + fprintf(fp, " number of lookups: %u\n", table->stats.lookups); + fprintf(fp, " adds that made a new entry: %u\n", table->stats.addMisses); + fprintf(fp, "adds that recycled removeds: %u\n", table->stats.addOverRemoved); + fprintf(fp, " adds that found an entry: %u\n", table->stats.addHits); + fprintf(fp, " add failures: %u\n", table->stats.addFailures); + fprintf(fp, " useful removes: %u\n", table->stats.removeHits); + fprintf(fp, " useless removes: %u\n", table->stats.removeMisses); + fprintf(fp, "removes that freed an entry: %u\n", table->stats.removeFrees); + fprintf(fp, " removes while enumerating: %u\n", table->stats.removeEnums); + fprintf(fp, " number of grows: %u\n", table->stats.grows); + fprintf(fp, " number of shrinks: %u\n", table->stats.shrinks); + fprintf(fp, " number of compresses: %u\n", table->stats.compresses); + fprintf(fp, "number of enumerate shrinks: %u\n", table->stats.enumShrinks); + + if (dump && maxChainLen && hash2) { + fputs("Maximum hash chain:\n", fp); + hash1 = maxChainHash1; + hash2 = maxChainHash2; + entry = ADDRESS_ENTRY(table, hash1); + i = 0; + do { + if (dump(table, entry, i++, fp) != JS_DHASH_NEXT) + break; + hash1 -= hash2; + hash1 &= sizeMask; + entry = ADDRESS_ENTRY(table, hash1); + } while (JS_DHASH_ENTRY_IS_BUSY(entry)); + } +} +#endif /* JS_DHASHMETER */ diff --git a/ape-server/deps/js/src/jsdhash.h b/ape-server/deps/js/src/jsdhash.h new file mode 100755 index 0000000..13ab221 --- /dev/null +++ b/ape-server/deps/js/src/jsdhash.h @@ -0,0 +1,607 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla JavaScript code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1999-2001 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Brendan Eich (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsdhash_h___ +#define jsdhash_h___ +/* + * Double hashing, a la Knuth 6. + */ +#include "jstypes.h" + +JS_BEGIN_EXTERN_C + +#if defined(__GNUC__) && defined(__i386__) && (__GNUC__ >= 3) && !defined(XP_OS2) +#define JS_DHASH_FASTCALL __attribute__ ((regparm (3),stdcall)) +#elif defined(XP_WIN) +#define JS_DHASH_FASTCALL __fastcall +#else +#define JS_DHASH_FASTCALL +#endif + +#ifdef DEBUG_XXXbrendan +#define JS_DHASHMETER 1 +#endif + +/* Table size limit, do not equal or exceed (see min&maxAlphaFrac, below). */ +#undef JS_DHASH_SIZE_LIMIT +#define JS_DHASH_SIZE_LIMIT JS_BIT(24) + +/* Minimum table size, or gross entry count (net is at most .75 loaded). */ +#ifndef JS_DHASH_MIN_SIZE +#define JS_DHASH_MIN_SIZE 16 +#elif (JS_DHASH_MIN_SIZE & (JS_DHASH_MIN_SIZE - 1)) != 0 +#error "JS_DHASH_MIN_SIZE must be a power of two!" +#endif + +/* + * Multiplicative hash uses an unsigned 32 bit integer and the golden ratio, + * expressed as a fixed-point 32-bit fraction. + */ +#define JS_DHASH_BITS 32 +#define JS_DHASH_GOLDEN_RATIO 0x9E3779B9U + +/* Primitive and forward-struct typedefs. */ +typedef uint32 JSDHashNumber; +typedef struct JSDHashEntryHdr JSDHashEntryHdr; +typedef struct JSDHashEntryStub JSDHashEntryStub; +typedef struct JSDHashTable JSDHashTable; +typedef struct JSDHashTableOps JSDHashTableOps; + +/* + * Table entry header structure. + * + * In order to allow in-line allocation of key and value, we do not declare + * either here. Instead, the API uses const void *key as a formal parameter. + * The key need not be stored in the entry; it may be part of the value, but + * need not be stored at all. + * + * Callback types are defined below and grouped into the JSDHashTableOps + * structure, for single static initialization per hash table sub-type. + * + * Each hash table sub-type should nest the JSDHashEntryHdr structure at the + * front of its particular entry type. The keyHash member contains the result + * of multiplying the hash code returned from the hashKey callback (see below) + * by JS_DHASH_GOLDEN_RATIO, then constraining the result to avoid the magic 0 + * and 1 values. The stored keyHash value is table size invariant, and it is + * maintained automatically by JS_DHashTableOperate -- users should never set + * it, and its only uses should be via the entry macros below. + * + * The JS_DHASH_ENTRY_IS_LIVE macro tests whether entry is neither free nor + * removed. An entry may be either busy or free; if busy, it may be live or + * removed. Consumers of this API should not access members of entries that + * are not live. + * + * However, use JS_DHASH_ENTRY_IS_BUSY for faster liveness testing of entries + * returned by JS_DHashTableOperate, as JS_DHashTableOperate never returns a + * non-live, busy (i.e., removed) entry pointer to its caller. See below for + * more details on JS_DHashTableOperate's calling rules. + */ +struct JSDHashEntryHdr { + JSDHashNumber keyHash; /* every entry must begin like this */ +}; + +#define JS_DHASH_ENTRY_IS_FREE(entry) ((entry)->keyHash == 0) +#define JS_DHASH_ENTRY_IS_BUSY(entry) (!JS_DHASH_ENTRY_IS_FREE(entry)) +#define JS_DHASH_ENTRY_IS_LIVE(entry) ((entry)->keyHash >= 2) + +/* + * A JSDHashTable is currently 8 words (without the JS_DHASHMETER overhead) + * on most architectures, and may be allocated on the stack or within another + * structure or class (see below for the Init and Finish functions to use). + * + * To decide whether to use double hashing vs. chaining, we need to develop a + * trade-off relation, as follows: + * + * Let alpha be the load factor, esize the entry size in words, count the + * entry count, and pow2 the power-of-two table size in entries. + * + * (JSDHashTable overhead) > (JSHashTable overhead) + * (unused table entry space) > (malloc and .next overhead per entry) + + * (buckets overhead) + * (1 - alpha) * esize * pow2 > 2 * count + pow2 + * + * Notice that alpha is by definition (count / pow2): + * + * (1 - alpha) * esize * pow2 > 2 * alpha * pow2 + pow2 + * (1 - alpha) * esize > 2 * alpha + 1 + * + * esize > (1 + 2 * alpha) / (1 - alpha) + * + * This assumes both tables must keep keyHash, key, and value for each entry, + * where key and value point to separately allocated strings or structures. + * If key and value can be combined into one pointer, then the trade-off is: + * + * esize > (1 + 3 * alpha) / (1 - alpha) + * + * If the entry value can be a subtype of JSDHashEntryHdr, rather than a type + * that must be allocated separately and referenced by an entry.value pointer + * member, and provided key's allocation can be fused with its entry's, then + * k (the words wasted per entry with chaining) is 4. + * + * To see these curves, feed gnuplot input like so: + * + * gnuplot> f(x,k) = (1 + k * x) / (1 - x) + * gnuplot> plot [0:.75] f(x,2), f(x,3), f(x,4) + * + * For k of 2 and a well-loaded table (alpha > .5), esize must be more than 4 + * words for chaining to be more space-efficient than double hashing. + * + * Solving for alpha helps us decide when to shrink an underloaded table: + * + * esize > (1 + k * alpha) / (1 - alpha) + * esize - alpha * esize > 1 + k * alpha + * esize - 1 > (k + esize) * alpha + * (esize - 1) / (k + esize) > alpha + * + * alpha < (esize - 1) / (esize + k) + * + * Therefore double hashing should keep alpha >= (esize - 1) / (esize + k), + * assuming esize is not too large (in which case, chaining should probably be + * used for any alpha). For esize=2 and k=3, we want alpha >= .2; for esize=3 + * and k=2, we want alpha >= .4. For k=4, esize could be 6, and alpha >= .5 + * would still obtain. See the JS_DHASH_MIN_ALPHA macro further below. + * + * The current implementation uses a configurable lower bound on alpha, which + * defaults to .25, when deciding to shrink the table (while still respecting + * JS_DHASH_MIN_SIZE). + * + * Note a qualitative difference between chaining and double hashing: under + * chaining, entry addresses are stable across table shrinks and grows. With + * double hashing, you can't safely hold an entry pointer and use it after an + * ADD or REMOVE operation, unless you sample table->generation before adding + * or removing, and compare the sample after, dereferencing the entry pointer + * only if table->generation has not changed. + * + * The moral of this story: there is no one-size-fits-all hash table scheme, + * but for small table entry size, and assuming entry address stability is not + * required, double hashing wins. + */ +struct JSDHashTable { + const JSDHashTableOps *ops; /* virtual operations, see below */ + void *data; /* ops- and instance-specific data */ + int16 hashShift; /* multiplicative hash shift */ + uint8 maxAlphaFrac; /* 8-bit fixed point max alpha */ + uint8 minAlphaFrac; /* 8-bit fixed point min alpha */ + uint32 entrySize; /* number of bytes in an entry */ + uint32 entryCount; /* number of entries in table */ + uint32 removedCount; /* removed entry sentinels in table */ + uint32 generation; /* entry storage generation number */ + char *entryStore; /* entry storage */ +#ifdef JS_DHASHMETER + struct JSDHashStats { + uint32 searches; /* total number of table searches */ + uint32 steps; /* hash chain links traversed */ + uint32 hits; /* searches that found key */ + uint32 misses; /* searches that didn't find key */ + uint32 lookups; /* number of JS_DHASH_LOOKUPs */ + uint32 addMisses; /* adds that miss, and do work */ + uint32 addOverRemoved; /* adds that recycled a removed entry */ + uint32 addHits; /* adds that hit an existing entry */ + uint32 addFailures; /* out-of-memory during add growth */ + uint32 removeHits; /* removes that hit, and do work */ + uint32 removeMisses; /* useless removes that miss */ + uint32 removeFrees; /* removes that freed entry directly */ + uint32 removeEnums; /* removes done by Enumerate */ + uint32 grows; /* table expansions */ + uint32 shrinks; /* table contractions */ + uint32 compresses; /* table compressions */ + uint32 enumShrinks; /* contractions after Enumerate */ + } stats; +#endif +}; + +/* + * Size in entries (gross, not net of free and removed sentinels) for table. + * We store hashShift rather than sizeLog2 to optimize the collision-free case + * in SearchTable. + */ +#define JS_DHASH_TABLE_SIZE(table) JS_BIT(JS_DHASH_BITS - (table)->hashShift) + +/* + * Table space at entryStore is allocated and freed using these callbacks. + * The allocator should return null on error only (not if called with nbytes + * equal to 0; but note that jsdhash.c code will never call with 0 nbytes). + */ +typedef void * +(* JSDHashAllocTable)(JSDHashTable *table, uint32 nbytes); + +typedef void +(* JSDHashFreeTable) (JSDHashTable *table, void *ptr); + +/* + * Compute the hash code for a given key to be looked up, added, or removed + * from table. A hash code may have any JSDHashNumber value. + */ +typedef JSDHashNumber +(* JSDHashHashKey) (JSDHashTable *table, const void *key); + +/* + * Compare the key identifying entry in table with the provided key parameter. + * Return JS_TRUE if keys match, JS_FALSE otherwise. + */ +typedef JSBool +(* JSDHashMatchEntry)(JSDHashTable *table, const JSDHashEntryHdr *entry, + const void *key); + +/* + * Copy the data starting at from to the new entry storage at to. Do not add + * reference counts for any strong references in the entry, however, as this + * is a "move" operation: the old entry storage at from will be freed without + * any reference-decrementing callback shortly. + */ +typedef void +(* JSDHashMoveEntry)(JSDHashTable *table, const JSDHashEntryHdr *from, + JSDHashEntryHdr *to); + +/* + * Clear the entry and drop any strong references it holds. This callback is + * invoked during a JS_DHASH_REMOVE operation (see below for operation codes), + * but only if the given key is found in the table. + */ +typedef void +(* JSDHashClearEntry)(JSDHashTable *table, JSDHashEntryHdr *entry); + +/* + * Called when a table (whether allocated dynamically by itself, or nested in + * a larger structure, or allocated on the stack) is finished. This callback + * allows table->ops-specific code to finalize table->data. + */ +typedef void +(* JSDHashFinalize) (JSDHashTable *table); + +/* + * Initialize a new entry, apart from keyHash. This function is called when + * JS_DHashTableOperate's JS_DHASH_ADD case finds no existing entry for the + * given key, and must add a new one. At that point, entry->keyHash is not + * set yet, to avoid claiming the last free entry in a severely overloaded + * table. + */ +typedef JSBool +(* JSDHashInitEntry)(JSDHashTable *table, JSDHashEntryHdr *entry, + const void *key); + +/* + * Finally, the "vtable" structure for JSDHashTable. The first eight hooks + * must be provided by implementations; they're called unconditionally by the + * generic jsdhash.c code. Hooks after these may be null. + * + * Summary of allocation-related hook usage with C++ placement new emphasis: + * allocTable Allocate raw bytes with malloc, no ctors run. + * freeTable Free raw bytes with free, no dtors run. + * initEntry Call placement new using default key-based ctor. + * Return JS_TRUE on success, JS_FALSE on error. + * moveEntry Call placement new using copy ctor, run dtor on old + * entry storage. + * clearEntry Run dtor on entry. + * finalize Stub unless table->data was initialized and needs to + * be finalized. + * + * Note the reason why initEntry is optional: the default hooks (stubs) clear + * entry storage: On successful JS_DHashTableOperate(tbl, key, JS_DHASH_ADD), + * the returned entry pointer addresses an entry struct whose keyHash member + * has been set non-zero, but all other entry members are still clear (null). + * JS_DHASH_ADD callers can test such members to see whether the entry was + * newly created by the JS_DHASH_ADD call that just succeeded. If placement + * new or similar initialization is required, define an initEntry hook. Of + * course, the clearEntry hook must zero or null appropriately. + * + * XXX assumes 0 is null for pointer types. + */ +struct JSDHashTableOps { + /* Mandatory hooks. All implementations must provide these. */ + JSDHashAllocTable allocTable; + JSDHashFreeTable freeTable; + JSDHashHashKey hashKey; + JSDHashMatchEntry matchEntry; + JSDHashMoveEntry moveEntry; + JSDHashClearEntry clearEntry; + JSDHashFinalize finalize; + + /* Optional hooks start here. If null, these are not called. */ + JSDHashInitEntry initEntry; +}; + +/* + * Default implementations for the above ops. + */ +extern JS_PUBLIC_API(void *) +JS_DHashAllocTable(JSDHashTable *table, uint32 nbytes); + +extern JS_PUBLIC_API(void) +JS_DHashFreeTable(JSDHashTable *table, void *ptr); + +extern JS_PUBLIC_API(JSDHashNumber) +JS_DHashStringKey(JSDHashTable *table, const void *key); + +/* A minimal entry contains a keyHash header and a void key pointer. */ +struct JSDHashEntryStub { + JSDHashEntryHdr hdr; + const void *key; +}; + +extern JS_PUBLIC_API(JSDHashNumber) +JS_DHashVoidPtrKeyStub(JSDHashTable *table, const void *key); + +extern JS_PUBLIC_API(JSBool) +JS_DHashMatchEntryStub(JSDHashTable *table, + const JSDHashEntryHdr *entry, + const void *key); + +extern JS_PUBLIC_API(JSBool) +JS_DHashMatchStringKey(JSDHashTable *table, + const JSDHashEntryHdr *entry, + const void *key); + +extern JS_PUBLIC_API(void) +JS_DHashMoveEntryStub(JSDHashTable *table, + const JSDHashEntryHdr *from, + JSDHashEntryHdr *to); + +extern JS_PUBLIC_API(void) +JS_DHashClearEntryStub(JSDHashTable *table, JSDHashEntryHdr *entry); + +extern JS_PUBLIC_API(void) +JS_DHashFreeStringKey(JSDHashTable *table, JSDHashEntryHdr *entry); + +extern JS_PUBLIC_API(void) +JS_DHashFinalizeStub(JSDHashTable *table); + +/* + * If you use JSDHashEntryStub or a subclass of it as your entry struct, and + * if your entries move via memcpy and clear via memset(0), you can use these + * stub operations. + */ +extern JS_PUBLIC_API(const JSDHashTableOps *) +JS_DHashGetStubOps(void); + +/* + * Dynamically allocate a new JSDHashTable using malloc, initialize it using + * JS_DHashTableInit, and return its address. Return null on malloc failure. + * Note that the entry storage at table->entryStore will be allocated using + * the ops->allocTable callback. + */ +extern JS_PUBLIC_API(JSDHashTable *) +JS_NewDHashTable(const JSDHashTableOps *ops, void *data, uint32 entrySize, + uint32 capacity); + +/* + * Finalize table's data, free its entry storage (via table->ops->freeTable), + * and return the memory starting at table to the malloc heap. + */ +extern JS_PUBLIC_API(void) +JS_DHashTableDestroy(JSDHashTable *table); + +/* + * Initialize table with ops, data, entrySize, and capacity. Capacity is a + * guess for the smallest table size at which the table will usually be less + * than 75% loaded (the table will grow or shrink as needed; capacity serves + * only to avoid inevitable early growth from JS_DHASH_MIN_SIZE). + */ +extern JS_PUBLIC_API(JSBool) +JS_DHashTableInit(JSDHashTable *table, const JSDHashTableOps *ops, void *data, + uint32 entrySize, uint32 capacity); + +/* + * Set maximum and minimum alpha for table. The defaults are 0.75 and .25. + * maxAlpha must be in [0.5, 0.9375] for the default JS_DHASH_MIN_SIZE; or if + * MinSize=JS_DHASH_MIN_SIZE <= 256, in [0.5, (float)(MinSize-1)/MinSize]; or + * else in [0.5, 255.0/256]. minAlpha must be in [0, maxAlpha / 2), so that + * we don't shrink on the very next remove after growing a table upon adding + * an entry that brings entryCount past maxAlpha * tableSize. + */ +extern JS_PUBLIC_API(void) +JS_DHashTableSetAlphaBounds(JSDHashTable *table, + float maxAlpha, + float minAlpha); + +/* + * Call this macro with k, the number of pointer-sized words wasted per entry + * under chaining, to compute the minimum alpha at which double hashing still + * beats chaining. + */ +#define JS_DHASH_MIN_ALPHA(table, k) \ + ((float)((table)->entrySize / sizeof(void *) - 1) \ + / ((table)->entrySize / sizeof(void *) + (k))) + +/* + * Default max/min alpha, and macros to compute the value for the |capacity| + * parameter to JS_NewDHashTable and JS_DHashTableInit, given default or any + * max alpha, such that adding entryCount entries right after initializing the + * table will not require a reallocation (so JS_DHASH_ADD can't fail for those + * JS_DHashTableOperate calls). + * + * NB: JS_DHASH_CAP is a helper macro meant for use only in JS_DHASH_CAPACITY. + * Don't use it directly! + */ +#define JS_DHASH_DEFAULT_MAX_ALPHA 0.75 +#define JS_DHASH_DEFAULT_MIN_ALPHA 0.25 + +#define JS_DHASH_CAP(entryCount, maxAlpha) \ + ((uint32)((double)(entryCount) / (maxAlpha))) + +#define JS_DHASH_CAPACITY(entryCount, maxAlpha) \ + (JS_DHASH_CAP(entryCount, maxAlpha) + \ + (((JS_DHASH_CAP(entryCount, maxAlpha) * (uint8)(0x100 * (maxAlpha))) \ + >> 8) < (entryCount))) + +#define JS_DHASH_DEFAULT_CAPACITY(entryCount) \ + JS_DHASH_CAPACITY(entryCount, JS_DHASH_DEFAULT_MAX_ALPHA) + +/* + * Finalize table's data, free its entry storage using table->ops->freeTable, + * and leave its members unchanged from their last live values (which leaves + * pointers dangling). If you want to burn cycles clearing table, it's up to + * your code to call memset. + */ +extern JS_PUBLIC_API(void) +JS_DHashTableFinish(JSDHashTable *table); + +/* + * To consolidate keyHash computation and table grow/shrink code, we use a + * single entry point for lookup, add, and remove operations. The operation + * codes are declared here, along with codes returned by JSDHashEnumerator + * functions, which control JS_DHashTableEnumerate's behavior. + */ +typedef enum JSDHashOperator { + JS_DHASH_LOOKUP = 0, /* lookup entry */ + JS_DHASH_ADD = 1, /* add entry */ + JS_DHASH_REMOVE = 2, /* remove entry, or enumerator says remove */ + JS_DHASH_NEXT = 0, /* enumerator says continue */ + JS_DHASH_STOP = 1 /* enumerator says stop */ +} JSDHashOperator; + +/* + * To lookup a key in table, call: + * + * entry = JS_DHashTableOperate(table, key, JS_DHASH_LOOKUP); + * + * If JS_DHASH_ENTRY_IS_BUSY(entry) is true, key was found and it identifies + * entry. If JS_DHASH_ENTRY_IS_FREE(entry) is true, key was not found. + * + * To add an entry identified by key to table, call: + * + * entry = JS_DHashTableOperate(table, key, JS_DHASH_ADD); + * + * If entry is null upon return, then either the table is severely overloaded, + * and memory can't be allocated for entry storage via table->ops->allocTable; + * Or if table->ops->initEntry is non-null, the table->ops->initEntry op may + * have returned false. + * + * Otherwise, entry->keyHash has been set so that JS_DHASH_ENTRY_IS_BUSY(entry) + * is true, and it is up to the caller to initialize the key and value parts + * of the entry sub-type, if they have not been set already (i.e. if entry was + * not already in the table, and if the optional initEntry hook was not used). + * + * To remove an entry identified by key from table, call: + * + * (void) JS_DHashTableOperate(table, key, JS_DHASH_REMOVE); + * + * If key's entry is found, it is cleared (via table->ops->clearEntry) and + * the entry is marked so that JS_DHASH_ENTRY_IS_FREE(entry). This operation + * returns null unconditionally; you should ignore its return value. + */ +extern JS_PUBLIC_API(JSDHashEntryHdr *) JS_DHASH_FASTCALL +JS_DHashTableOperate(JSDHashTable *table, const void *key, JSDHashOperator op); + +/* + * Remove an entry already accessed via LOOKUP or ADD. + * + * NB: this is a "raw" or low-level routine, intended to be used only where + * the inefficiency of a full JS_DHashTableOperate (which rehashes in order + * to find the entry given its key) is not tolerable. This function does not + * shrink the table if it is underloaded. It does not update stats #ifdef + * JS_DHASHMETER, either. + */ +extern JS_PUBLIC_API(void) +JS_DHashTableRawRemove(JSDHashTable *table, JSDHashEntryHdr *entry); + +/* + * Enumerate entries in table using etor: + * + * count = JS_DHashTableEnumerate(table, etor, arg); + * + * JS_DHashTableEnumerate calls etor like so: + * + * op = etor(table, entry, number, arg); + * + * where number is a zero-based ordinal assigned to live entries according to + * their order in table->entryStore. + * + * The return value, op, is treated as a set of flags. If op is JS_DHASH_NEXT, + * then continue enumerating. If op contains JS_DHASH_REMOVE, then clear (via + * table->ops->clearEntry) and free entry. Then we check whether op contains + * JS_DHASH_STOP; if so, stop enumerating and return the number of live entries + * that were enumerated so far. Return the total number of live entries when + * enumeration completes normally. + * + * If etor calls JS_DHashTableOperate on table with op != JS_DHASH_LOOKUP, it + * must return JS_DHASH_STOP; otherwise undefined behavior results. + * + * If any enumerator returns JS_DHASH_REMOVE, table->entryStore may be shrunk + * or compressed after enumeration, but before JS_DHashTableEnumerate returns. + * Such an enumerator therefore can't safely set aside entry pointers, but an + * enumerator that never returns JS_DHASH_REMOVE can set pointers to entries + * aside, e.g., to avoid copying live entries into an array of the entry type. + * Copying entry pointers is cheaper, and safe so long as the caller of such a + * "stable" Enumerate doesn't use the set-aside pointers after any call either + * to PL_DHashTableOperate, or to an "unstable" form of Enumerate, which might + * grow or shrink entryStore. + * + * If your enumerator wants to remove certain entries, but set aside pointers + * to other entries that it retains, it can use JS_DHashTableRawRemove on the + * entries to be removed, returning JS_DHASH_NEXT to skip them. Likewise, if + * you want to remove entries, but for some reason you do not want entryStore + * to be shrunk or compressed, you can call JS_DHashTableRawRemove safely on + * the entry being enumerated, rather than returning JS_DHASH_REMOVE. + */ +typedef JSDHashOperator +(* JSDHashEnumerator)(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, + void *arg); + +extern JS_PUBLIC_API(uint32) +JS_DHashTableEnumerate(JSDHashTable *table, JSDHashEnumerator etor, void *arg); + +#ifdef DEBUG +/** + * Mark a table as immutable for the remainder of its lifetime. This + * changes the implementation from ASSERTing one set of invariants to + * ASSERTing a different set. + * + * When a table is NOT marked as immutable, the table implementation + * asserts that the table is not mutated from its own callbacks. It + * assumes the caller protects the table from being accessed on multiple + * threads simultaneously. + * + * When the table is marked as immutable, the re-entry assertions will + * no longer trigger erroneously due to multi-threaded access. Instead, + * mutations will cause assertions. + */ +extern JS_PUBLIC_API(void) +JS_DHashMarkTableImmutable(JSDHashTable *table); +#endif + +#ifdef JS_DHASHMETER +#include + +extern JS_PUBLIC_API(void) +JS_DHashTableDumpMeter(JSDHashTable *table, JSDHashEnumerator dump, FILE *fp); +#endif + +JS_END_EXTERN_C + +#endif /* jsdhash_h___ */ diff --git a/ape-server/deps/js/src/jsdtoa.cpp b/ape-server/deps/js/src/jsdtoa.cpp new file mode 100755 index 0000000..111cb5d --- /dev/null +++ b/ape-server/deps/js/src/jsdtoa.cpp @@ -0,0 +1,572 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Portable double to alphanumeric string and back converters. + */ +#include "jstypes.h" +#include "jsstdint.h" +#include "jsdtoa.h" +#include "jsprf.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jsprvtd.h" +#include "jsnum.h" +#include "jsbit.h" +#include "jslibmath.h" + +#ifdef JS_THREADSAFE +#include "jslock.h" +#endif + +#ifdef IS_LITTLE_ENDIAN +#define IEEE_8087 +#else +#define IEEE_MC68k +#endif + +#ifndef Long +#define Long int32 +#endif + +#ifndef ULong +#define ULong uint32 +#endif + +/* +#ifndef Llong +#define Llong JSInt64 +#endif + +#ifndef ULlong +#define ULlong JSUint64 +#endif +*/ + +#ifdef JS_THREADSAFE +static PRLock *dtoalock; +static JSBool _dtoainited = JS_FALSE; + +#define LOCK_DTOA() PR_Lock(dtoalock); +#define UNLOCK_DTOA() PR_Unlock(dtoalock) +#else +#define LOCK_DTOA() +#define UNLOCK_DTOA() +#endif +#include "dtoa.c" + +JS_FRIEND_API(JSBool) +js_InitDtoa() +{ +#ifdef JS_THREADSAFE + if (!_dtoainited) { + dtoalock = PR_NewLock(); + JS_ASSERT(dtoalock); + _dtoainited = JS_TRUE; + } + + return (dtoalock != 0); +#else + return JS_TRUE; +#endif +} + +JS_FRIEND_API(void) +js_FinishDtoa() +{ +#ifdef JS_THREADSAFE + if (_dtoainited) { + PR_DestroyLock(dtoalock); + dtoalock = NULL; + _dtoainited = JS_FALSE; + } +#endif +} + +/* Mapping of JSDToStrMode -> js_dtoa mode */ +static const uint8 dtoaModes[] = { + 0, /* DTOSTR_STANDARD */ + 0, /* DTOSTR_STANDARD_EXPONENTIAL, */ + 3, /* DTOSTR_FIXED, */ + 2, /* DTOSTR_EXPONENTIAL, */ + 2}; /* DTOSTR_PRECISION */ + +JS_FRIEND_API(double) +JS_strtod(const char *s00, char **se, int *err) +{ + double retval; + if (err) + *err = 0; + LOCK_DTOA(); + retval = _strtod(s00, se); + UNLOCK_DTOA(); + return retval; +} + +JS_FRIEND_API(char *) +JS_dtostr(char *buffer, size_t bufferSize, JSDToStrMode mode, int precision, double dinput) +{ + U d; + int decPt; /* Offset of decimal point from first digit */ + int sign; /* Nonzero if the sign bit was set in d */ + int nDigits; /* Number of significand digits returned by js_dtoa */ + char *numBegin; /* Pointer to the digits returned by js_dtoa */ + char *numEnd = 0; /* Pointer past the digits returned by js_dtoa */ + + JS_ASSERT(bufferSize >= (size_t)(mode <= DTOSTR_STANDARD_EXPONENTIAL + ? DTOSTR_STANDARD_BUFFER_SIZE + : DTOSTR_VARIABLE_BUFFER_SIZE(precision))); + + /* + * Change mode here rather than below because the buffer may not be large + * enough to hold a large integer. + */ + if (mode == DTOSTR_FIXED && (dinput >= 1e21 || dinput <= -1e21)) + mode = DTOSTR_STANDARD; + + LOCK_DTOA(); + dval(d) = dinput; + numBegin = dtoa(d, dtoaModes[mode], precision, &decPt, &sign, &numEnd); + if (!numBegin) { + UNLOCK_DTOA(); + return NULL; + } + + nDigits = numEnd - numBegin; + JS_ASSERT((size_t) nDigits <= bufferSize - 2); + if ((size_t) nDigits > bufferSize - 2) { + UNLOCK_DTOA(); + return NULL; + } + + memcpy(buffer + 2, numBegin, nDigits); + freedtoa(numBegin); + UNLOCK_DTOA(); + numBegin = buffer + 2; /* +2 leaves space for sign and/or decimal point */ + numEnd = numBegin + nDigits; + *numEnd = '\0'; + + /* If Infinity, -Infinity, or NaN, return the string regardless of mode. */ + if (decPt != 9999) { + JSBool exponentialNotation = JS_FALSE; + int minNDigits = 0; /* Min number of significant digits required */ + char *p; + char *q; + + switch (mode) { + case DTOSTR_STANDARD: + if (decPt < -5 || decPt > 21) + exponentialNotation = JS_TRUE; + else + minNDigits = decPt; + break; + + case DTOSTR_FIXED: + if (precision >= 0) + minNDigits = decPt + precision; + else + minNDigits = decPt; + break; + + case DTOSTR_EXPONENTIAL: + JS_ASSERT(precision > 0); + minNDigits = precision; + /* Fall through */ + case DTOSTR_STANDARD_EXPONENTIAL: + exponentialNotation = JS_TRUE; + break; + + case DTOSTR_PRECISION: + JS_ASSERT(precision > 0); + minNDigits = precision; + if (decPt < -5 || decPt > precision) + exponentialNotation = JS_TRUE; + break; + } + + /* If the number has fewer than minNDigits, end-pad it with zeros. */ + if (nDigits < minNDigits) { + p = numBegin + minNDigits; + nDigits = minNDigits; + do { + *numEnd++ = '0'; + } while (numEnd != p); + *numEnd = '\0'; + } + + if (exponentialNotation) { + /* Insert a decimal point if more than one significand digit */ + if (nDigits != 1) { + numBegin--; + numBegin[0] = numBegin[1]; + numBegin[1] = '.'; + } + JS_snprintf(numEnd, bufferSize - (numEnd - buffer), "e%+d", decPt-1); + } else if (decPt != nDigits) { + /* Some kind of a fraction in fixed notation */ + JS_ASSERT(decPt <= nDigits); + if (decPt > 0) { + /* dd...dd . dd...dd */ + p = --numBegin; + do { + *p = p[1]; + p++; + } while (--decPt); + *p = '.'; + } else { + /* 0 . 00...00dd...dd */ + p = numEnd; + numEnd += 1 - decPt; + q = numEnd; + JS_ASSERT(numEnd < buffer + bufferSize); + *numEnd = '\0'; + while (p != numBegin) + *--q = *--p; + for (p = numBegin + 1; p != q; p++) + *p = '0'; + *numBegin = '.'; + *--numBegin = '0'; + } + } + } + + /* If negative and neither -0.0 nor NaN, output a leading '-'. */ + if (sign && + !(word0(d) == Sign_bit && word1(d) == 0) && + !((word0(d) & Exp_mask) == Exp_mask && + (word1(d) || (word0(d) & Frac_mask)))) { + *--numBegin = '-'; + } + return numBegin; +} + + +/* Let b = floor(b / divisor), and return the remainder. b must be nonnegative. + * divisor must be between 1 and 65536. + * This function cannot run out of memory. */ +static uint32 +divrem(Bigint *b, uint32 divisor) +{ + int32 n = b->wds; + uint32 remainder = 0; + ULong *bx; + ULong *bp; + + JS_ASSERT(divisor > 0 && divisor <= 65536); + + if (!n) + return 0; /* b is zero */ + bx = b->x; + bp = bx + n; + do { + ULong a = *--bp; + ULong dividend = remainder << 16 | a >> 16; + ULong quotientHi = dividend / divisor; + ULong quotientLo; + + remainder = dividend - quotientHi*divisor; + JS_ASSERT(quotientHi <= 0xFFFF && remainder < divisor); + dividend = remainder << 16 | (a & 0xFFFF); + quotientLo = dividend / divisor; + remainder = dividend - quotientLo*divisor; + JS_ASSERT(quotientLo <= 0xFFFF && remainder < divisor); + *bp = quotientHi << 16 | quotientLo; + } while (bp != bx); + /* Decrease the size of the number if its most significant word is now zero. */ + if (bx[n-1] == 0) + b->wds--; + return remainder; +} + +/* Return floor(b/2^k) and set b to be the remainder. The returned quotient must be less than 2^32. */ +static uint32 quorem2(Bigint *b, int32 k) +{ + ULong mask; + ULong result; + ULong *bx, *bxe; + int32 w; + int32 n = k >> 5; + k &= 0x1F; + mask = (1<wds - n; + if (w <= 0) + return 0; + JS_ASSERT(w <= 2); + bx = b->x; + bxe = bx + n; + result = *bxe >> k; + *bxe &= mask; + if (w == 2) { + JS_ASSERT(!(bxe[1] & ~mask)); + if (k) + result |= bxe[1] << (32 - k); + } + n++; + while (!*bxe && bxe != bx) { + n--; + bxe--; + } + b->wds = n; + return result; +} + + +/* "-0.0000...(1073 zeros after decimal point)...0001\0" is the longest string that we could produce, + * which occurs when printing -5e-324 in binary. We could compute a better estimate of the size of + * the output string and malloc fewer bytes depending on d and base, but why bother? */ +#define DTOBASESTR_BUFFER_SIZE 1078 +#define BASEDIGIT(digit) ((char)(((digit) >= 10) ? 'a' - 10 + (digit) : '0' + (digit))) + +JS_FRIEND_API(char *) +JS_dtobasestr(int base, double dinput) +{ + U d; + char *buffer; /* The output string */ + char *p; /* Pointer to current position in the buffer */ + char *pInt; /* Pointer to the beginning of the integer part of the string */ + char *q; + uint32 digit; + U di; /* d truncated to an integer */ + U df; /* The fractional part of d */ + + JS_ASSERT(base >= 2 && base <= 36); + + dval(d) = dinput; + buffer = (char*) js_malloc(DTOBASESTR_BUFFER_SIZE); + if (buffer) { + p = buffer; + if (dval(d) < 0.0 +#if defined(XP_WIN) || defined(XP_OS2) + && !((word0(d) & Exp_mask) == Exp_mask && ((word0(d) & Frac_mask) || word1(d))) /* Visual C++ doesn't know how to compare against NaN */ +#endif + ) { + *p++ = '-'; + dval(d) = -dval(d); + } + + /* Check for Infinity and NaN */ + if ((word0(d) & Exp_mask) == Exp_mask) { + strcpy(p, !word1(d) && !(word0(d) & Frac_mask) ? "Infinity" : "NaN"); + return buffer; + } + + LOCK_DTOA(); + /* Output the integer part of d with the digits in reverse order. */ + pInt = p; + dval(di) = floor(dval(d)); + if (dval(di) <= 4294967295.0) { + uint32 n = (uint32)dval(di); + if (n) + do { + uint32 m = n / base; + digit = n - m*base; + n = m; + JS_ASSERT(digit < (uint32)base); + *p++ = BASEDIGIT(digit); + } while (n); + else *p++ = '0'; + } else { + int e; + int bits; /* Number of significant bits in di; not used. */ + Bigint *b = d2b(di, &e, &bits); + if (!b) + goto nomem1; + b = lshift(b, e); + if (!b) { + nomem1: + Bfree(b); + UNLOCK_DTOA(); + js_free(buffer); + return NULL; + } + do { + digit = divrem(b, base); + JS_ASSERT(digit < (uint32)base); + *p++ = BASEDIGIT(digit); + } while (b->wds); + Bfree(b); + } + /* Reverse the digits of the integer part of d. */ + q = p-1; + while (q > pInt) { + char ch = *pInt; + *pInt++ = *q; + *q-- = ch; + } + + dval(df) = dval(d) - dval(di); + if (dval(df) != 0.0) { + /* We have a fraction. */ + int e, bbits; + int32 s2, done; + Bigint *b, *s, *mlo, *mhi; + + b = s = mlo = mhi = NULL; + + *p++ = '.'; + b = d2b(df, &e, &bbits); + if (!b) { + nomem2: + Bfree(b); + Bfree(s); + if (mlo != mhi) + Bfree(mlo); + Bfree(mhi); + UNLOCK_DTOA(); + js_free(buffer); + return NULL; + } + JS_ASSERT(e < 0); + /* At this point df = b * 2^e. e must be less than zero because 0 < df < 1. */ + + s2 = -(int32)(word0(d) >> Exp_shift1 & Exp_mask>>Exp_shift1); +#ifndef Sudden_Underflow + if (!s2) + s2 = -1; +#endif + s2 += Bias + P; + /* 1/2^s2 = (nextDouble(d) - d)/2 */ + JS_ASSERT(-s2 < e); + mlo = i2b(1); + if (!mlo) + goto nomem2; + mhi = mlo; + if (!word1(d) && !(word0(d) & Bndry_mask) +#ifndef Sudden_Underflow + && word0(d) & (Exp_mask & Exp_mask << 1) +#endif + ) { + /* The special case. Here we want to be within a quarter of the last input + significant digit instead of one half of it when the output string's value is less than d. */ + s2 += Log2P; + mhi = i2b(1< df = b/2^s2 > 0; + * (d - prevDouble(d))/2 = mlo/2^s2; + * (nextDouble(d) - d)/2 = mhi/2^s2. */ + + done = JS_FALSE; + do { + int32 j, j1; + Bigint *delta; + + b = multadd(b, base, 0); + if (!b) + goto nomem2; + digit = quorem2(b, s2); + if (mlo == mhi) { + mlo = mhi = multadd(mlo, base, 0); + if (!mhi) + goto nomem2; + } + else { + mlo = multadd(mlo, base, 0); + if (!mlo) + goto nomem2; + mhi = multadd(mhi, base, 0); + if (!mhi) + goto nomem2; + } + + /* Do we yet have the shortest string that will round to d? */ + j = cmp(b, mlo); + /* j is b/2^s2 compared with mlo/2^s2. */ + delta = diff(s, mhi); + if (!delta) + goto nomem2; + j1 = delta->sign ? 1 : cmp(b, delta); + Bfree(delta); + /* j1 is b/2^s2 compared with 1 - mhi/2^s2. */ + +#ifndef ROUND_BIASED + if (j1 == 0 && !(word1(d) & 1)) { + if (j > 0) + digit++; + done = JS_TRUE; + } else +#endif + if (j < 0 || (j == 0 +#ifndef ROUND_BIASED + && !(word1(d) & 1) +#endif + )) { + if (j1 > 0) { + /* Either dig or dig+1 would work here as the least significant digit. + Use whichever would produce an output value closer to d. */ + b = lshift(b, 1); + if (!b) + goto nomem2; + j1 = cmp(b, s); + if (j1 > 0) /* The even test (|| (j1 == 0 && (digit & 1))) is not here because it messes up odd base output + * such as 3.5 in base 3. */ + digit++; + } + done = JS_TRUE; + } else if (j1 > 0) { + digit++; + done = JS_TRUE; + } + JS_ASSERT(digit < (uint32)base); + *p++ = BASEDIGIT(digit); + } while (!done); + Bfree(b); + Bfree(s); + if (mlo != mhi) + Bfree(mlo); + Bfree(mhi); + } + JS_ASSERT(p < buffer + DTOBASESTR_BUFFER_SIZE); + *p = '\0'; + UNLOCK_DTOA(); + } + return buffer; +} diff --git a/ape-server/deps/js/src/jsdtoa.h b/ape-server/deps/js/src/jsdtoa.h new file mode 100755 index 0000000..b074c9a --- /dev/null +++ b/ape-server/deps/js/src/jsdtoa.h @@ -0,0 +1,131 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsdtoa_h___ +#define jsdtoa_h___ +/* + * Public interface to portable double-precision floating point to string + * and back conversion package. + */ + +#include "jscompat.h" + +JS_BEGIN_EXTERN_C + +/* + * JS_strtod() returns as a double-precision floating-point number + * the value represented by the character string pointed to by + * s00. The string is scanned up to the first unrecognized + * character. + * If the value of se is not (char **)NULL, a pointer to + * the character terminating the scan is returned in the location pointed + * to by se. If no number can be formed, se is set to s00r, and + * zero is returned. + * + * *err is set to zero on success; it's set to JS_DTOA_ERANGE on range + * errors and JS_DTOA_ENOMEM on memory failure. + */ +#define JS_DTOA_ERANGE 1 +#define JS_DTOA_ENOMEM 2 +JS_FRIEND_API(double) +JS_strtod(const char *s00, char **se, int *err); + +/* + * Modes for converting floating-point numbers to strings. + * + * Some of the modes can round-trip; this means that if the number is converted to + * a string using one of these mode and then converted back to a number, the result + * will be identical to the original number (except that, due to ECMA, -0 will get converted + * to +0). These round-trip modes return the minimum number of significand digits that + * permit the round trip. + * + * Some of the modes take an integer parameter . + */ +/* NB: Keep this in sync with number_constants[]. */ +typedef enum JSDToStrMode { + DTOSTR_STANDARD, /* Either fixed or exponential format; round-trip */ + DTOSTR_STANDARD_EXPONENTIAL, /* Always exponential format; round-trip */ + DTOSTR_FIXED, /* Round to digits after the decimal point; exponential if number is large */ + DTOSTR_EXPONENTIAL, /* Always exponential format; significant digits */ + DTOSTR_PRECISION /* Either fixed or exponential format; significant digits */ +} JSDToStrMode; + + +/* Maximum number of characters (including trailing null) that a DTOSTR_STANDARD or DTOSTR_STANDARD_EXPONENTIAL + * conversion can produce. This maximum is reached for a number like -0.0000012345678901234567. */ +#define DTOSTR_STANDARD_BUFFER_SIZE 26 + +/* Maximum number of characters (including trailing null) that one of the other conversions + * can produce. This maximum is reached for TO_FIXED, which can generate up to 21 digits before the decimal point. */ +#define DTOSTR_VARIABLE_BUFFER_SIZE(precision) ((precision)+24 > DTOSTR_STANDARD_BUFFER_SIZE ? (precision)+24 : DTOSTR_STANDARD_BUFFER_SIZE) + +/* + * Convert dval according to the given mode and return a pointer to the resulting ASCII string. + * The result is held somewhere in buffer, but not necessarily at the beginning. The size of + * buffer is given in bufferSize, and must be at least as large as given by the above macros. + * + * Return NULL if out of memory. + */ +JS_FRIEND_API(char *) +JS_dtostr(char *buffer, size_t bufferSize, JSDToStrMode mode, int precision, double dval); + +/* + * Convert d to a string in the given base. The integral part of d will be printed exactly + * in that base, regardless of how large it is, because there is no exponential notation for non-base-ten + * numbers. The fractional part will be rounded to as few digits as possible while still preserving + * the round-trip property (analogous to that of printing decimal numbers). In other words, if one were + * to read the resulting string in via a hypothetical base-number-reading routine that rounds to the nearest + * IEEE double (and to an even significand if there are two equally near doubles), then the result would + * equal d (except for -0.0, which converts to "0", and NaN, which is not equal to itself). + * + * Return NULL if out of memory. If the result is not NULL, it must be released via free(). + */ +JS_FRIEND_API(char *) +JS_dtobasestr(int base, double d); + +/* + * Clean up any persistent RAM allocated during the execution of DtoA + * routines, and remove any locks that might have been created. + */ +JS_FRIEND_API(JSBool) js_InitDtoa(void); +JS_FRIEND_API(void) js_FinishDtoa(void); + +JS_END_EXTERN_C + +#endif /* jsdtoa_h___ */ diff --git a/ape-server/deps/js/src/jsdtracef.cpp b/ape-server/deps/js/src/jsdtracef.cpp new file mode 100755 index 0000000..940aea0 --- /dev/null +++ b/ape-server/deps/js/src/jsdtracef.cpp @@ -0,0 +1,281 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=80: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Copyright (C) 2007 Sun Microsystems, Inc. All Rights Reserved. + * + * Contributor(s): + * Brendan Eich + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "jsapi.h" +#include "jsutil.h" +#include "jsatom.h" +#include "jscntxt.h" +#include "jsdbgapi.h" +#include "jsfun.h" +#include "jsinterp.h" +#include "jsobj.h" +#include "jsscript.h" +#include "jsstr.h" + +#include "jsdtracef.h" +#include + +#define TYPEOF(cx,v) (JSVAL_IS_NULL(v) ? JSTYPE_NULL : JS_TypeOfValue(cx,v)) + +static char dempty[] = ""; + +static char * +jsdtrace_fun_classname(JSFunction *fun) +{ + return (fun && + !FUN_INTERPRETED(fun) && + !(fun->flags & JSFUN_TRCINFO) && + FUN_CLASP(fun)) + ? (char *)FUN_CLASP(fun)->name + : dempty; +} + +static char * +jsdtrace_filename(JSStackFrame *fp) +{ + return (fp && fp->script && fp->script->filename) + ? (char *)fp->script->filename + : dempty; +} + +static int +jsdtrace_fun_linenumber(JSContext *cx, JSFunction *fun) +{ + if (fun && FUN_INTERPRETED(fun)) + return (int) JS_GetScriptBaseLineNumber(cx, FUN_SCRIPT(fun)); + + return 0; +} + +int +jsdtrace_frame_linenumber(JSContext *cx, JSStackFrame *fp) +{ + if (fp && fp->regs) + return (int) js_FramePCToLineNumber(cx, fp); + + return 0; +} + +/* + * This function is used to convert function arguments and return value (jsval) + * into the following based on each value's type tag: + * + * jsval returned + * ------------------- + * STRING -> char * + * INT -> int + * DOUBLE -> double * + * BOOLEAN -> int + * OBJECT -> void * + * + * All are presented as void * for DTrace consumers to use, after shifting or + * masking out the JavaScript type bits. This allows D scripts to use ints and + * booleans directly and copyinstr() for string arguments, when types are known + * beforehand. + * + * This is used by the function-args and function-rval probes, which also + * provide raw (unmasked) jsvals should type info be useful from D scripts. + */ +static void * +jsdtrace_jsvaltovoid(JSContext *cx, jsval argval) +{ + JSType type = TYPEOF(cx, argval); + + switch (type) { + case JSTYPE_NULL: + case JSTYPE_VOID: + return (void *)JS_TYPE_STR(type); + + case JSTYPE_BOOLEAN: + return (void *)JSVAL_TO_BOOLEAN(argval); + + case JSTYPE_STRING: + return (void *)js_GetStringBytes(cx, JSVAL_TO_STRING(argval)); + + case JSTYPE_NUMBER: + if (JSVAL_IS_INT(argval)) + return (void *)JSVAL_TO_INT(argval); + return JSVAL_TO_DOUBLE(argval); + + default: + return JSVAL_TO_GCTHING(argval); + } + /* NOTREACHED */ +} + +static char * +jsdtrace_fun_name(JSContext *cx, JSFunction *fun) +{ + JSAtom *atom; + char *name; + + if (!fun) + return dempty; + + atom = fun->atom; + if (!atom) { + /* + * TODO: maybe do more work here to figure out the name of the property + * or variable that held the anonymous function that we're calling, if anyone + * cares; an easy workaround is to just give your anonymous functions names. + */ + return dempty; + } + + name = (char *)js_GetStringBytes(cx, ATOM_TO_STRING(atom)); + return name ? name : dempty; +} + +/* + * These functions call the DTrace macros for the JavaScript USDT probes. + * Originally this code was inlined in the JavaScript code; however since + * a number of operations are called, these have been placed into functions + * to reduce any negative compiler optimization effect that the addition of + * a number of usually unused lines of code would cause. + */ +void +jsdtrace_function_entry(JSContext *cx, JSStackFrame *fp, JSFunction *fun) +{ + JAVASCRIPT_FUNCTION_ENTRY( + jsdtrace_filename(fp), + jsdtrace_fun_classname(fun), + jsdtrace_fun_name(cx, fun) + ); +} + +void +jsdtrace_function_info(JSContext *cx, JSStackFrame *fp, JSStackFrame *dfp, + JSFunction *fun) +{ + JAVASCRIPT_FUNCTION_INFO( + jsdtrace_filename(fp), + jsdtrace_fun_classname(fun), + jsdtrace_fun_name(cx, fun), + jsdtrace_fun_linenumber(cx, fun), + jsdtrace_filename(dfp), + jsdtrace_frame_linenumber(cx, dfp) + ); +} + +void +jsdtrace_function_args(JSContext *cx, JSStackFrame *fp, JSFunction *fun, jsuint argc, jsval *argv) +{ + JAVASCRIPT_FUNCTION_ARGS( + jsdtrace_filename(fp), + jsdtrace_fun_classname(fun), + jsdtrace_fun_name(cx, fun), + argc, (void *)argv, + (argc > 0) ? jsdtrace_jsvaltovoid(cx, argv[0]) : 0, + (argc > 1) ? jsdtrace_jsvaltovoid(cx, argv[1]) : 0, + (argc > 2) ? jsdtrace_jsvaltovoid(cx, argv[2]) : 0, + (argc > 3) ? jsdtrace_jsvaltovoid(cx, argv[3]) : 0, + (argc > 4) ? jsdtrace_jsvaltovoid(cx, argv[4]) : 0 + ); +} + +void +jsdtrace_function_rval(JSContext *cx, JSStackFrame *fp, JSFunction *fun, jsval *rval) +{ + JAVASCRIPT_FUNCTION_RVAL( + jsdtrace_filename(fp), + jsdtrace_fun_classname(fun), + jsdtrace_fun_name(cx, fun), + jsdtrace_fun_linenumber(cx, fun), + (void *)rval, + jsdtrace_jsvaltovoid(cx, *rval) + ); +} + +void +jsdtrace_function_return(JSContext *cx, JSStackFrame *fp, JSFunction *fun) +{ + JAVASCRIPT_FUNCTION_RETURN( + jsdtrace_filename(fp), + jsdtrace_fun_classname(fun), + jsdtrace_fun_name(cx, fun) + ); +} + +void +jsdtrace_object_create_start(JSStackFrame *fp, JSClass *clasp) +{ + JAVASCRIPT_OBJECT_CREATE_START(jsdtrace_filename(fp), (char *)clasp->name); +} + +void +jsdtrace_object_create_done(JSStackFrame *fp, JSClass *clasp) +{ + JAVASCRIPT_OBJECT_CREATE_DONE(jsdtrace_filename(fp), (char *)clasp->name); +} + +void +jsdtrace_object_create(JSContext *cx, JSClass *clasp, JSObject *obj) +{ + JAVASCRIPT_OBJECT_CREATE( + jsdtrace_filename(cx->fp), + (char *)clasp->name, + (uintptr_t)obj, + jsdtrace_frame_linenumber(cx, cx->fp) + ); +} + +void +jsdtrace_object_finalize(JSObject *obj) +{ + JSClass *clasp; + + clasp = obj->getClass(); + + /* the first arg is NULL - reserved for future use (filename?) */ + JAVASCRIPT_OBJECT_FINALIZE(NULL, (char *)clasp->name, (uintptr_t)obj); +} + +void +jsdtrace_execute_start(JSScript *script) +{ + JAVASCRIPT_EXECUTE_START( + script->filename ? (char *)script->filename : dempty, + script->lineno + ); +} + +void +jsdtrace_execute_done(JSScript *script) +{ + JAVASCRIPT_EXECUTE_DONE( + script->filename ? (char *)script->filename : dempty, + script->lineno + ); +} diff --git a/ape-server/deps/js/src/jsdtracef.h b/ape-server/deps/js/src/jsdtracef.h new file mode 100755 index 0000000..e5e2b46 --- /dev/null +++ b/ape-server/deps/js/src/jsdtracef.h @@ -0,0 +1,81 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=80: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Copyright (C) 2007 Sun Microsystems, Inc. All Rights Reserved. + * + * Contributor(s): + * Brendan Eich + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "javascript-trace.h" +#include "jspubtd.h" +#include "jsprvtd.h" + +#ifndef _JSDTRACEF_H +#define _JSDTRACEF_H + +JS_BEGIN_EXTERN_C + +extern void +jsdtrace_function_entry(JSContext *cx, JSStackFrame *fp, JSFunction *fun); + +extern void +jsdtrace_function_info(JSContext *cx, JSStackFrame *fp, JSStackFrame *dfp, + JSFunction *fun); + +extern void +jsdtrace_function_args(JSContext *cx, JSStackFrame *fp, JSFunction *fun, jsuint argc, jsval *argv); + +extern void +jsdtrace_function_rval(JSContext *cx, JSStackFrame *fp, JSFunction *fun, jsval *rval); + +extern void +jsdtrace_function_return(JSContext *cx, JSStackFrame *fp, JSFunction *fun); + +extern void +jsdtrace_object_create_start(JSStackFrame *fp, JSClass *clasp); + +extern void +jsdtrace_object_create_done(JSStackFrame *fp, JSClass *clasp); + +extern void +jsdtrace_object_create(JSContext *cx, JSClass *clasp, JSObject *obj); + +extern void +jsdtrace_object_finalize(JSObject *obj); + +extern void +jsdtrace_execute_start(JSScript *script); + +extern void +jsdtrace_execute_done(JSScript *script); + +JS_END_EXTERN_C + +#endif /* _JSDTRACE_H */ diff --git a/ape-server/deps/js/src/jsemit.cpp b/ape-server/deps/js/src/jsemit.cpp new file mode 100755 index 0000000..f8b482f --- /dev/null +++ b/ape-server/deps/js/src/jsemit.cpp @@ -0,0 +1,7382 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=99: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * JS bytecode generation. + */ +#ifdef HAVE_MEMORY_H +#include +#endif +#include +#include +#include "jstypes.h" +#include "jsstdint.h" +#include "jsarena.h" /* Added by JSIFY */ +#include "jsutil.h" /* Added by JSIFY */ +#include "jsbit.h" +#include "jsprf.h" +#include "jsapi.h" +#include "jsatom.h" +#include "jsbool.h" +#include "jscntxt.h" +#include "jsversion.h" +#include "jsemit.h" +#include "jsfun.h" +#include "jsnum.h" +#include "jsopcode.h" +#include "jsparse.h" +#include "jsregexp.h" +#include "jsscan.h" +#include "jsscope.h" +#include "jsscript.h" +#include "jsautooplen.h" +#include "jsstaticcheck.h" + +/* Allocation chunk counts, must be powers of two in general. */ +#define BYTECODE_CHUNK 256 /* code allocation increment */ +#define SRCNOTE_CHUNK 64 /* initial srcnote allocation increment */ +#define TRYNOTE_CHUNK 64 /* trynote allocation increment */ + +/* Macros to compute byte sizes from typed element counts. */ +#define BYTECODE_SIZE(n) ((n) * sizeof(jsbytecode)) +#define SRCNOTE_SIZE(n) ((n) * sizeof(jssrcnote)) +#define TRYNOTE_SIZE(n) ((n) * sizeof(JSTryNote)) + +static JSBool +NewTryNote(JSContext *cx, JSCodeGenerator *cg, JSTryNoteKind kind, + uintN stackDepth, size_t start, size_t end); + +JSCodeGenerator::JSCodeGenerator(JSCompiler *jsc, + JSArenaPool *cpool, JSArenaPool *npool, + uintN lineno) + : JSTreeContext(jsc), + codePool(cpool), notePool(npool), + codeMark(JS_ARENA_MARK(cpool)), noteMark(JS_ARENA_MARK(npool)), + stackDepth(0), maxStackDepth(0), + ntrynotes(0), lastTryNode(NULL), + spanDeps(NULL), jumpTargets(NULL), jtFreeList(NULL), + numSpanDeps(0), numJumpTargets(0), spanDepTodo(0), + arrayCompDepth(0), + emitLevel(0) +{ + flags = TCF_COMPILING; + memset(&prolog, 0, sizeof prolog); + memset(&main, 0, sizeof main); + current = &main; + firstLine = prolog.currentLine = main.currentLine = lineno; + prolog.noteMask = main.noteMask = SRCNOTE_CHUNK - 1; + memset(&upvarMap, 0, sizeof upvarMap); +} + +JSCodeGenerator::~JSCodeGenerator() +{ + JS_ARENA_RELEASE(codePool, codeMark); + JS_ARENA_RELEASE(notePool, noteMark); + + /* NB: non-null only after OOM. */ + if (spanDeps) + compiler->context->free(spanDeps); + + if (upvarMap.vector) + compiler->context->free(upvarMap.vector); +} + +static ptrdiff_t +EmitCheck(JSContext *cx, JSCodeGenerator *cg, JSOp op, ptrdiff_t delta) +{ + jsbytecode *base, *limit, *next; + ptrdiff_t offset, length; + size_t incr, size; + + base = CG_BASE(cg); + next = CG_NEXT(cg); + limit = CG_LIMIT(cg); + offset = next - base; + if (next + delta > limit) { + length = offset + delta; + length = (length <= BYTECODE_CHUNK) + ? BYTECODE_CHUNK + : JS_BIT(JS_CeilingLog2(length)); + incr = BYTECODE_SIZE(length); + if (!base) { + JS_ARENA_ALLOCATE_CAST(base, jsbytecode *, cg->codePool, incr); + } else { + size = BYTECODE_SIZE(limit - base); + incr -= size; + JS_ARENA_GROW_CAST(base, jsbytecode *, cg->codePool, size, incr); + } + if (!base) { + js_ReportOutOfScriptQuota(cx); + return -1; + } + CG_BASE(cg) = base; + CG_LIMIT(cg) = base + length; + CG_NEXT(cg) = base + offset; + } + return offset; +} + +static void +UpdateDepth(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t target) +{ + jsbytecode *pc; + JSOp op; + const JSCodeSpec *cs; + uintN extra, depth, nuses; + intN ndefs; + + pc = CG_CODE(cg, target); + op = (JSOp) *pc; + cs = &js_CodeSpec[op]; +#ifdef JS_TRACER + extern uint8 js_opcode2extra[]; + extra = js_opcode2extra[op]; +#else + extra = 0; +#endif + if ((cs->format & JOF_TMPSLOT_MASK) || extra) { + depth = (uintN) cg->stackDepth + + ((cs->format & JOF_TMPSLOT_MASK) >> JOF_TMPSLOT_SHIFT) + + extra; + if (depth > cg->maxStackDepth) + cg->maxStackDepth = depth; + } + + nuses = js_GetStackUses(cs, op, pc); + cg->stackDepth -= nuses; + JS_ASSERT(cg->stackDepth >= 0); + if (cg->stackDepth < 0) { + char numBuf[12]; + JSTokenStream *ts; + + JS_snprintf(numBuf, sizeof numBuf, "%d", target); + ts = &cg->compiler->tokenStream; + JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, + js_GetErrorMessage, NULL, + JSMSG_STACK_UNDERFLOW, + ts->filename ? ts->filename : "stdin", + numBuf); + } + ndefs = cs->ndefs; + if (ndefs < 0) { + JSObject *blockObj; + + /* We just executed IndexParsedObject */ + JS_ASSERT(op == JSOP_ENTERBLOCK); + JS_ASSERT(nuses == 0); + blockObj = cg->objectList.lastbox->object; + JS_ASSERT(STOBJ_GET_CLASS(blockObj) == &js_BlockClass); + JS_ASSERT(JSVAL_IS_VOID(blockObj->fslots[JSSLOT_BLOCK_DEPTH])); + + OBJ_SET_BLOCK_DEPTH(cx, blockObj, cg->stackDepth); + ndefs = OBJ_BLOCK_COUNT(cx, blockObj); + } + cg->stackDepth += ndefs; + if ((uintN)cg->stackDepth > cg->maxStackDepth) + cg->maxStackDepth = cg->stackDepth; +} + +ptrdiff_t +js_Emit1(JSContext *cx, JSCodeGenerator *cg, JSOp op) +{ + ptrdiff_t offset = EmitCheck(cx, cg, op, 1); + + if (offset >= 0) { + *CG_NEXT(cg)++ = (jsbytecode)op; + UpdateDepth(cx, cg, offset); + } + return offset; +} + +ptrdiff_t +js_Emit2(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1) +{ + ptrdiff_t offset = EmitCheck(cx, cg, op, 2); + + if (offset >= 0) { + jsbytecode *next = CG_NEXT(cg); + next[0] = (jsbytecode)op; + next[1] = op1; + CG_NEXT(cg) = next + 2; + UpdateDepth(cx, cg, offset); + } + return offset; +} + +ptrdiff_t +js_Emit3(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1, + jsbytecode op2) +{ + ptrdiff_t offset = EmitCheck(cx, cg, op, 3); + + if (offset >= 0) { + jsbytecode *next = CG_NEXT(cg); + next[0] = (jsbytecode)op; + next[1] = op1; + next[2] = op2; + CG_NEXT(cg) = next + 3; + UpdateDepth(cx, cg, offset); + } + return offset; +} + +ptrdiff_t +js_EmitN(JSContext *cx, JSCodeGenerator *cg, JSOp op, size_t extra) +{ + ptrdiff_t length = 1 + (ptrdiff_t)extra; + ptrdiff_t offset = EmitCheck(cx, cg, op, length); + + if (offset >= 0) { + jsbytecode *next = CG_NEXT(cg); + *next = (jsbytecode)op; + memset(next + 1, 0, BYTECODE_SIZE(extra)); + CG_NEXT(cg) = next + length; + + /* + * Don't UpdateDepth if op's use-count comes from the immediate + * operand yet to be stored in the extra bytes after op. + */ + if (js_CodeSpec[op].nuses >= 0) + UpdateDepth(cx, cg, offset); + } + return offset; +} + +/* XXX too many "... statement" L10N gaffes below -- fix via js.msg! */ +const char js_with_statement_str[] = "with statement"; +const char js_finally_block_str[] = "finally block"; +const char js_script_str[] = "script"; + +static const char *statementName[] = { + "label statement", /* LABEL */ + "if statement", /* IF */ + "else statement", /* ELSE */ + "destructuring body", /* BODY */ + "switch statement", /* SWITCH */ + "block", /* BLOCK */ + js_with_statement_str, /* WITH */ + "catch block", /* CATCH */ + "try block", /* TRY */ + js_finally_block_str, /* FINALLY */ + js_finally_block_str, /* SUBROUTINE */ + "do loop", /* DO_LOOP */ + "for loop", /* FOR_LOOP */ + "for/in loop", /* FOR_IN_LOOP */ + "while loop", /* WHILE_LOOP */ +}; + +JS_STATIC_ASSERT(JS_ARRAY_LENGTH(statementName) == STMT_LIMIT); + +static const char * +StatementName(JSCodeGenerator *cg) +{ + if (!cg->topStmt) + return js_script_str; + return statementName[cg->topStmt->type]; +} + +static void +ReportStatementTooLarge(JSContext *cx, JSCodeGenerator *cg) +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEED_DIET, + StatementName(cg)); +} + +/** + Span-dependent instructions in JS bytecode consist of the jump (JOF_JUMP) + and switch (JOF_LOOKUPSWITCH, JOF_TABLESWITCH) format opcodes, subdivided + into unconditional (gotos and gosubs), and conditional jumps or branches + (which pop a value, test it, and jump depending on its value). Most jumps + have just one immediate operand, a signed offset from the jump opcode's pc + to the target bytecode. The lookup and table switch opcodes may contain + many jump offsets. + + Mozilla bug #80981 (http://bugzilla.mozilla.org/show_bug.cgi?id=80981) was + fixed by adding extended "X" counterparts to the opcodes/formats (NB: X is + suffixed to prefer JSOP_ORX thereby avoiding a JSOP_XOR name collision for + the extended form of the JSOP_OR branch opcode). The unextended or short + formats have 16-bit signed immediate offset operands, the extended or long + formats have 32-bit signed immediates. The span-dependency problem consists + of selecting as few long instructions as possible, or about as few -- since + jumps can span other jumps, extending one jump may cause another to need to + be extended. + + Most JS scripts are short, so need no extended jumps. We optimize for this + case by generating short jumps until we know a long jump is needed. After + that point, we keep generating short jumps, but each jump's 16-bit immediate + offset operand is actually an unsigned index into cg->spanDeps, an array of + JSSpanDep structs. Each struct tells the top offset in the script of the + opcode, the "before" offset of the jump (which will be the same as top for + simplex jumps, but which will index further into the bytecode array for a + non-initial jump offset in a lookup or table switch), the after "offset" + adjusted during span-dependent instruction selection (initially the same + value as the "before" offset), and the jump target (more below). + + Since we generate cg->spanDeps lazily, from within js_SetJumpOffset, we must + ensure that all bytecode generated so far can be inspected to discover where + the jump offset immediate operands lie within CG_CODE(cg). But the bonus is + that we generate span-dependency records sorted by their offsets, so we can + binary-search when trying to find a JSSpanDep for a given bytecode offset, + or the nearest JSSpanDep at or above a given pc. + + To avoid limiting scripts to 64K jumps, if the cg->spanDeps index overflows + 65534, we store SPANDEP_INDEX_HUGE in the jump's immediate operand. This + tells us that we need to binary-search for the cg->spanDeps entry by the + jump opcode's bytecode offset (sd->before). + + Jump targets need to be maintained in a data structure that lets us look + up an already-known target by its address (jumps may have a common target), + and that also lets us update the addresses (script-relative, a.k.a. absolute + offsets) of targets that come after a jump target (for when a jump below + that target needs to be extended). We use an AVL tree, implemented using + recursion, but with some tricky optimizations to its height-balancing code + (see http://www.cmcrossroads.com/bradapp/ftp/src/libs/C++/AvlTrees.html). + + A final wrinkle: backpatch chains are linked by jump-to-jump offsets with + positive sign, even though they link "backward" (i.e., toward lower bytecode + address). We don't want to waste space and search time in the AVL tree for + such temporary backpatch deltas, so we use a single-bit wildcard scheme to + tag true JSJumpTarget pointers and encode untagged, signed (positive) deltas + in JSSpanDep.target pointers, depending on whether the JSSpanDep has a known + target, or is still awaiting backpatching. + + Note that backpatch chains would present a problem for BuildSpanDepTable, + which inspects bytecode to build cg->spanDeps on demand, when the first + short jump offset overflows. To solve this temporary problem, we emit a + proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_POP for branch ops) whose + nuses/ndefs counts help keep the stack balanced, but whose opcode format + distinguishes its backpatch delta immediate operand from a normal jump + offset. + */ +static int +BalanceJumpTargets(JSJumpTarget **jtp) +{ + JSJumpTarget *jt, *jt2, *root; + int dir, otherDir, heightChanged; + JSBool doubleRotate; + + jt = *jtp; + JS_ASSERT(jt->balance != 0); + + if (jt->balance < -1) { + dir = JT_RIGHT; + doubleRotate = (jt->kids[JT_LEFT]->balance > 0); + } else if (jt->balance > 1) { + dir = JT_LEFT; + doubleRotate = (jt->kids[JT_RIGHT]->balance < 0); + } else { + return 0; + } + + otherDir = JT_OTHER_DIR(dir); + if (doubleRotate) { + jt2 = jt->kids[otherDir]; + *jtp = root = jt2->kids[dir]; + + jt->kids[otherDir] = root->kids[dir]; + root->kids[dir] = jt; + + jt2->kids[dir] = root->kids[otherDir]; + root->kids[otherDir] = jt2; + + heightChanged = 1; + root->kids[JT_LEFT]->balance = -JS_MAX(root->balance, 0); + root->kids[JT_RIGHT]->balance = -JS_MIN(root->balance, 0); + root->balance = 0; + } else { + *jtp = root = jt->kids[otherDir]; + jt->kids[otherDir] = root->kids[dir]; + root->kids[dir] = jt; + + heightChanged = (root->balance != 0); + jt->balance = -((dir == JT_LEFT) ? --root->balance : ++root->balance); + } + + return heightChanged; +} + +typedef struct AddJumpTargetArgs { + JSContext *cx; + JSCodeGenerator *cg; + ptrdiff_t offset; + JSJumpTarget *node; +} AddJumpTargetArgs; + +static int +AddJumpTarget(AddJumpTargetArgs *args, JSJumpTarget **jtp) +{ + JSJumpTarget *jt; + int balanceDelta; + + jt = *jtp; + if (!jt) { + JSCodeGenerator *cg = args->cg; + + jt = cg->jtFreeList; + if (jt) { + cg->jtFreeList = jt->kids[JT_LEFT]; + } else { + JS_ARENA_ALLOCATE_CAST(jt, JSJumpTarget *, &args->cx->tempPool, + sizeof *jt); + if (!jt) { + js_ReportOutOfScriptQuota(args->cx); + return 0; + } + } + jt->offset = args->offset; + jt->balance = 0; + jt->kids[JT_LEFT] = jt->kids[JT_RIGHT] = NULL; + cg->numJumpTargets++; + args->node = jt; + *jtp = jt; + return 1; + } + + if (jt->offset == args->offset) { + args->node = jt; + return 0; + } + + if (args->offset < jt->offset) + balanceDelta = -AddJumpTarget(args, &jt->kids[JT_LEFT]); + else + balanceDelta = AddJumpTarget(args, &jt->kids[JT_RIGHT]); + if (!args->node) + return 0; + + jt->balance += balanceDelta; + return (balanceDelta && jt->balance) + ? 1 - BalanceJumpTargets(jtp) + : 0; +} + +#ifdef DEBUG_brendan +static int AVLCheck(JSJumpTarget *jt) +{ + int lh, rh; + + if (!jt) return 0; + JS_ASSERT(-1 <= jt->balance && jt->balance <= 1); + lh = AVLCheck(jt->kids[JT_LEFT]); + rh = AVLCheck(jt->kids[JT_RIGHT]); + JS_ASSERT(jt->balance == rh - lh); + return 1 + JS_MAX(lh, rh); +} +#endif + +static JSBool +SetSpanDepTarget(JSContext *cx, JSCodeGenerator *cg, JSSpanDep *sd, + ptrdiff_t off) +{ + AddJumpTargetArgs args; + + if (off < JUMPX_OFFSET_MIN || JUMPX_OFFSET_MAX < off) { + ReportStatementTooLarge(cx, cg); + return JS_FALSE; + } + + args.cx = cx; + args.cg = cg; + args.offset = sd->top + off; + args.node = NULL; + AddJumpTarget(&args, &cg->jumpTargets); + if (!args.node) + return JS_FALSE; + +#ifdef DEBUG_brendan + AVLCheck(cg->jumpTargets); +#endif + + SD_SET_TARGET(sd, args.node); + return JS_TRUE; +} + +#define SPANDEPS_MIN 256 +#define SPANDEPS_SIZE(n) ((n) * sizeof(JSSpanDep)) +#define SPANDEPS_SIZE_MIN SPANDEPS_SIZE(SPANDEPS_MIN) + +static JSBool +AddSpanDep(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc, jsbytecode *pc2, + ptrdiff_t off) +{ + uintN index; + JSSpanDep *sdbase, *sd; + size_t size; + + index = cg->numSpanDeps; + if (index + 1 == 0) { + ReportStatementTooLarge(cx, cg); + return JS_FALSE; + } + + if ((index & (index - 1)) == 0 && + (!(sdbase = cg->spanDeps) || index >= SPANDEPS_MIN)) { + size = sdbase ? SPANDEPS_SIZE(index) : SPANDEPS_SIZE_MIN / 2; + sdbase = (JSSpanDep *) cx->realloc(sdbase, size + size); + if (!sdbase) + return JS_FALSE; + cg->spanDeps = sdbase; + } + + cg->numSpanDeps = index + 1; + sd = cg->spanDeps + index; + sd->top = pc - CG_BASE(cg); + sd->offset = sd->before = pc2 - CG_BASE(cg); + + if (js_CodeSpec[*pc].format & JOF_BACKPATCH) { + /* Jump offset will be backpatched if off is a non-zero "bpdelta". */ + if (off != 0) { + JS_ASSERT(off >= 1 + JUMP_OFFSET_LEN); + if (off > BPDELTA_MAX) { + ReportStatementTooLarge(cx, cg); + return JS_FALSE; + } + } + SD_SET_BPDELTA(sd, off); + } else if (off == 0) { + /* Jump offset will be patched directly, without backpatch chaining. */ + SD_SET_TARGET(sd, 0); + } else { + /* The jump offset in off is non-zero, therefore it's already known. */ + if (!SetSpanDepTarget(cx, cg, sd, off)) + return JS_FALSE; + } + + if (index > SPANDEP_INDEX_MAX) + index = SPANDEP_INDEX_HUGE; + SET_SPANDEP_INDEX(pc2, index); + return JS_TRUE; +} + +static jsbytecode * +AddSwitchSpanDeps(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc) +{ + JSOp op; + jsbytecode *pc2; + ptrdiff_t off; + jsint low, high; + uintN njumps, indexlen; + + op = (JSOp) *pc; + JS_ASSERT(op == JSOP_TABLESWITCH || op == JSOP_LOOKUPSWITCH); + pc2 = pc; + off = GET_JUMP_OFFSET(pc2); + if (!AddSpanDep(cx, cg, pc, pc2, off)) + return NULL; + pc2 += JUMP_OFFSET_LEN; + if (op == JSOP_TABLESWITCH) { + low = GET_JUMP_OFFSET(pc2); + pc2 += JUMP_OFFSET_LEN; + high = GET_JUMP_OFFSET(pc2); + pc2 += JUMP_OFFSET_LEN; + njumps = (uintN) (high - low + 1); + indexlen = 0; + } else { + njumps = GET_UINT16(pc2); + pc2 += UINT16_LEN; + indexlen = INDEX_LEN; + } + while (njumps) { + --njumps; + pc2 += indexlen; + off = GET_JUMP_OFFSET(pc2); + if (!AddSpanDep(cx, cg, pc, pc2, off)) + return NULL; + pc2 += JUMP_OFFSET_LEN; + } + return 1 + pc2; +} + +static JSBool +BuildSpanDepTable(JSContext *cx, JSCodeGenerator *cg) +{ + jsbytecode *pc, *end; + JSOp op; + const JSCodeSpec *cs; + ptrdiff_t off; + + pc = CG_BASE(cg) + cg->spanDepTodo; + end = CG_NEXT(cg); + while (pc != end) { + JS_ASSERT(pc < end); + op = (JSOp)*pc; + cs = &js_CodeSpec[op]; + + switch (JOF_TYPE(cs->format)) { + case JOF_TABLESWITCH: + case JOF_LOOKUPSWITCH: + pc = AddSwitchSpanDeps(cx, cg, pc); + if (!pc) + return JS_FALSE; + break; + + case JOF_JUMP: + off = GET_JUMP_OFFSET(pc); + if (!AddSpanDep(cx, cg, pc, pc, off)) + return JS_FALSE; + /* FALL THROUGH */ + default: + pc += cs->length; + break; + } + } + + return JS_TRUE; +} + +static JSSpanDep * +GetSpanDep(JSCodeGenerator *cg, jsbytecode *pc) +{ + uintN index; + ptrdiff_t offset; + int lo, hi, mid; + JSSpanDep *sd; + + index = GET_SPANDEP_INDEX(pc); + if (index != SPANDEP_INDEX_HUGE) + return cg->spanDeps + index; + + offset = pc - CG_BASE(cg); + lo = 0; + hi = cg->numSpanDeps - 1; + while (lo <= hi) { + mid = (lo + hi) / 2; + sd = cg->spanDeps + mid; + if (sd->before == offset) + return sd; + if (sd->before < offset) + lo = mid + 1; + else + hi = mid - 1; + } + + JS_ASSERT(0); + return NULL; +} + +static JSBool +SetBackPatchDelta(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc, + ptrdiff_t delta) +{ + JSSpanDep *sd; + + JS_ASSERT(delta >= 1 + JUMP_OFFSET_LEN); + if (!cg->spanDeps && delta < JUMP_OFFSET_MAX) { + SET_JUMP_OFFSET(pc, delta); + return JS_TRUE; + } + + if (delta > BPDELTA_MAX) { + ReportStatementTooLarge(cx, cg); + return JS_FALSE; + } + + if (!cg->spanDeps && !BuildSpanDepTable(cx, cg)) + return JS_FALSE; + + sd = GetSpanDep(cg, pc); + JS_ASSERT(SD_GET_BPDELTA(sd) == 0); + SD_SET_BPDELTA(sd, delta); + return JS_TRUE; +} + +static void +UpdateJumpTargets(JSJumpTarget *jt, ptrdiff_t pivot, ptrdiff_t delta) +{ + if (jt->offset > pivot) { + jt->offset += delta; + if (jt->kids[JT_LEFT]) + UpdateJumpTargets(jt->kids[JT_LEFT], pivot, delta); + } + if (jt->kids[JT_RIGHT]) + UpdateJumpTargets(jt->kids[JT_RIGHT], pivot, delta); +} + +static JSSpanDep * +FindNearestSpanDep(JSCodeGenerator *cg, ptrdiff_t offset, int lo, + JSSpanDep *guard) +{ + int num, hi, mid; + JSSpanDep *sdbase, *sd; + + num = cg->numSpanDeps; + JS_ASSERT(num > 0); + hi = num - 1; + sdbase = cg->spanDeps; + while (lo <= hi) { + mid = (lo + hi) / 2; + sd = sdbase + mid; + if (sd->before == offset) + return sd; + if (sd->before < offset) + lo = mid + 1; + else + hi = mid - 1; + } + if (lo == num) + return guard; + sd = sdbase + lo; + JS_ASSERT(sd->before >= offset && (lo == 0 || sd[-1].before < offset)); + return sd; +} + +static void +FreeJumpTargets(JSCodeGenerator *cg, JSJumpTarget *jt) +{ + if (jt->kids[JT_LEFT]) + FreeJumpTargets(cg, jt->kids[JT_LEFT]); + if (jt->kids[JT_RIGHT]) + FreeJumpTargets(cg, jt->kids[JT_RIGHT]); + jt->kids[JT_LEFT] = cg->jtFreeList; + cg->jtFreeList = jt; +} + +static JSBool +OptimizeSpanDeps(JSContext *cx, JSCodeGenerator *cg) +{ + jsbytecode *pc, *oldpc, *base, *limit, *next; + JSSpanDep *sd, *sd2, *sdbase, *sdlimit, *sdtop, guard; + ptrdiff_t offset, growth, delta, top, pivot, span, length, target; + JSBool done; + JSOp op; + uint32 type; + size_t size, incr; + jssrcnote *sn, *snlimit; + JSSrcNoteSpec *spec; + uintN i, n, noteIndex; + JSTryNode *tryNode; +#ifdef DEBUG_brendan + int passes = 0; +#endif + + base = CG_BASE(cg); + sdbase = cg->spanDeps; + sdlimit = sdbase + cg->numSpanDeps; + offset = CG_OFFSET(cg); + growth = 0; + + do { + done = JS_TRUE; + delta = 0; + top = pivot = -1; + sdtop = NULL; + pc = NULL; + op = JSOP_NOP; + type = 0; +#ifdef DEBUG_brendan + passes++; +#endif + + for (sd = sdbase; sd < sdlimit; sd++) { + JS_ASSERT(JT_HAS_TAG(sd->target)); + sd->offset += delta; + + if (sd->top != top) { + sdtop = sd; + top = sd->top; + JS_ASSERT(top == sd->before); + pivot = sd->offset; + pc = base + top; + op = (JSOp) *pc; + type = JOF_OPTYPE(op); + if (JOF_TYPE_IS_EXTENDED_JUMP(type)) { + /* + * We already extended all the jump offset operands for + * the opcode at sd->top. Jumps and branches have only + * one jump offset operand, but switches have many, all + * of which are adjacent in cg->spanDeps. + */ + continue; + } + + JS_ASSERT(type == JOF_JUMP || + type == JOF_TABLESWITCH || + type == JOF_LOOKUPSWITCH); + } + + if (!JOF_TYPE_IS_EXTENDED_JUMP(type)) { + span = SD_SPAN(sd, pivot); + if (span < JUMP_OFFSET_MIN || JUMP_OFFSET_MAX < span) { + ptrdiff_t deltaFromTop = 0; + + done = JS_FALSE; + + switch (op) { + case JSOP_GOTO: op = JSOP_GOTOX; break; + case JSOP_IFEQ: op = JSOP_IFEQX; break; + case JSOP_IFNE: op = JSOP_IFNEX; break; + case JSOP_OR: op = JSOP_ORX; break; + case JSOP_AND: op = JSOP_ANDX; break; + case JSOP_GOSUB: op = JSOP_GOSUBX; break; + case JSOP_CASE: op = JSOP_CASEX; break; + case JSOP_DEFAULT: op = JSOP_DEFAULTX; break; + case JSOP_TABLESWITCH: op = JSOP_TABLESWITCHX; break; + case JSOP_LOOKUPSWITCH: op = JSOP_LOOKUPSWITCHX; break; + default: + ReportStatementTooLarge(cx, cg); + return JS_FALSE; + } + *pc = (jsbytecode) op; + + for (sd2 = sdtop; sd2 < sdlimit && sd2->top == top; sd2++) { + if (sd2 <= sd) { + /* + * sd2->offset already includes delta as it stood + * before we entered this loop, but it must also + * include the delta relative to top due to all the + * extended jump offset immediates for the opcode + * starting at top, which we extend in this loop. + * + * If there is only one extended jump offset, then + * sd2->offset won't change and this for loop will + * iterate once only. + */ + sd2->offset += deltaFromTop; + deltaFromTop += JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN; + } else { + /* + * sd2 comes after sd, and won't be revisited by + * the outer for loop, so we have to increase its + * offset by delta, not merely by deltaFromTop. + */ + sd2->offset += delta; + } + + delta += JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN; + UpdateJumpTargets(cg->jumpTargets, sd2->offset, + JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN); + } + sd = sd2 - 1; + } + } + } + + growth += delta; + } while (!done); + + if (growth) { +#ifdef DEBUG_brendan + JSTokenStream *ts = &cg->compiler->tokenStream; + + printf("%s:%u: %u/%u jumps extended in %d passes (%d=%d+%d)\n", + ts->filename ? ts->filename : "stdin", cg->firstLine, + growth / (JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN), cg->numSpanDeps, + passes, offset + growth, offset, growth); +#endif + + /* + * Ensure that we have room for the extended jumps, but don't round up + * to a power of two -- we're done generating code, so we cut to fit. + */ + limit = CG_LIMIT(cg); + length = offset + growth; + next = base + length; + if (next > limit) { + JS_ASSERT(length > BYTECODE_CHUNK); + size = BYTECODE_SIZE(limit - base); + incr = BYTECODE_SIZE(length) - size; + JS_ARENA_GROW_CAST(base, jsbytecode *, cg->codePool, size, incr); + if (!base) { + js_ReportOutOfScriptQuota(cx); + return JS_FALSE; + } + CG_BASE(cg) = base; + CG_LIMIT(cg) = next = base + length; + } + CG_NEXT(cg) = next; + + /* + * Set up a fake span dependency record to guard the end of the code + * being generated. This guard record is returned as a fencepost by + * FindNearestSpanDep if there is no real spandep at or above a given + * unextended code offset. + */ + guard.top = -1; + guard.offset = offset + growth; + guard.before = offset; + guard.target = NULL; + } + + /* + * Now work backwards through the span dependencies, copying chunks of + * bytecode between each extended jump toward the end of the grown code + * space, and restoring immediate offset operands for all jump bytecodes. + * The first chunk of bytecodes, starting at base and ending at the first + * extended jump offset (NB: this chunk includes the operation bytecode + * just before that immediate jump offset), doesn't need to be copied. + */ + JS_ASSERT(sd == sdlimit); + top = -1; + while (--sd >= sdbase) { + if (sd->top != top) { + top = sd->top; + op = (JSOp) base[top]; + type = JOF_OPTYPE(op); + + for (sd2 = sd - 1; sd2 >= sdbase && sd2->top == top; sd2--) + continue; + sd2++; + pivot = sd2->offset; + JS_ASSERT(top == sd2->before); + } + + oldpc = base + sd->before; + span = SD_SPAN(sd, pivot); + + /* + * If this jump didn't need to be extended, restore its span immediate + * offset operand now, overwriting the index of sd within cg->spanDeps + * that was stored temporarily after *pc when BuildSpanDepTable ran. + * + * Note that span might fit in 16 bits even for an extended jump op, + * if the op has multiple span operands, not all of which overflowed + * (e.g. JSOP_LOOKUPSWITCH or JSOP_TABLESWITCH where some cases are in + * range for a short jump, but others are not). + */ + if (!JOF_TYPE_IS_EXTENDED_JUMP(type)) { + JS_ASSERT(JUMP_OFFSET_MIN <= span && span <= JUMP_OFFSET_MAX); + SET_JUMP_OFFSET(oldpc, span); + continue; + } + + /* + * Set up parameters needed to copy the next run of bytecode starting + * at offset (which is a cursor into the unextended, original bytecode + * vector), down to sd->before (a cursor of the same scale as offset, + * it's the index of the original jump pc). Reuse delta to count the + * nominal number of bytes to copy. + */ + pc = base + sd->offset; + delta = offset - sd->before; + JS_ASSERT(delta >= 1 + JUMP_OFFSET_LEN); + + /* + * Don't bother copying the jump offset we're about to reset, but do + * copy the bytecode at oldpc (which comes just before its immediate + * jump offset operand), on the next iteration through the loop, by + * including it in offset's new value. + */ + offset = sd->before + 1; + size = BYTECODE_SIZE(delta - (1 + JUMP_OFFSET_LEN)); + if (size) { + memmove(pc + 1 + JUMPX_OFFSET_LEN, + oldpc + 1 + JUMP_OFFSET_LEN, + size); + } + + SET_JUMPX_OFFSET(pc, span); + } + + if (growth) { + /* + * Fix source note deltas. Don't hardwire the delta fixup adjustment, + * even though currently it must be JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN + * at each sd that moved. The future may bring different offset sizes + * for span-dependent instruction operands. However, we fix only main + * notes here, not prolog notes -- we know that prolog opcodes are not + * span-dependent, and aren't likely ever to be. + */ + offset = growth = 0; + sd = sdbase; + for (sn = cg->main.notes, snlimit = sn + cg->main.noteCount; + sn < snlimit; + sn = SN_NEXT(sn)) { + /* + * Recall that the offset of a given note includes its delta, and + * tells the offset of the annotated bytecode from the main entry + * point of the script. + */ + offset += SN_DELTA(sn); + while (sd < sdlimit && sd->before < offset) { + /* + * To compute the delta to add to sn, we need to look at the + * spandep after sd, whose offset - (before + growth) tells by + * how many bytes sd's instruction grew. + */ + sd2 = sd + 1; + if (sd2 == sdlimit) + sd2 = &guard; + delta = sd2->offset - (sd2->before + growth); + if (delta > 0) { + JS_ASSERT(delta == JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN); + sn = js_AddToSrcNoteDelta(cx, cg, sn, delta); + if (!sn) + return JS_FALSE; + snlimit = cg->main.notes + cg->main.noteCount; + growth += delta; + } + sd++; + } + + /* + * If sn has span-dependent offset operands, check whether each + * covers further span-dependencies, and increase those operands + * accordingly. Some source notes measure offset not from the + * annotated pc, but from that pc plus some small bias. NB: we + * assume that spec->offsetBias can't itself span span-dependent + * instructions! + */ + spec = &js_SrcNoteSpec[SN_TYPE(sn)]; + if (spec->isSpanDep) { + pivot = offset + spec->offsetBias; + n = spec->arity; + for (i = 0; i < n; i++) { + span = js_GetSrcNoteOffset(sn, i); + if (span == 0) + continue; + target = pivot + span * spec->isSpanDep; + sd2 = FindNearestSpanDep(cg, target, + (target >= pivot) + ? sd - sdbase + : 0, + &guard); + + /* + * Increase target by sd2's before-vs-after offset delta, + * which is absolute (i.e., relative to start of script, + * as is target). Recompute the span by subtracting its + * adjusted pivot from target. + */ + target += sd2->offset - sd2->before; + span = target - (pivot + growth); + span *= spec->isSpanDep; + noteIndex = sn - cg->main.notes; + if (!js_SetSrcNoteOffset(cx, cg, noteIndex, i, span)) + return JS_FALSE; + sn = cg->main.notes + noteIndex; + snlimit = cg->main.notes + cg->main.noteCount; + } + } + } + cg->main.lastNoteOffset += growth; + + /* + * Fix try/catch notes (O(numTryNotes * log2(numSpanDeps)), but it's + * not clear how we can beat that). + */ + for (tryNode = cg->lastTryNode; tryNode; tryNode = tryNode->prev) { + /* + * First, look for the nearest span dependency at/above tn->start. + * There may not be any such spandep, in which case the guard will + * be returned. + */ + offset = tryNode->note.start; + sd = FindNearestSpanDep(cg, offset, 0, &guard); + delta = sd->offset - sd->before; + tryNode->note.start = offset + delta; + + /* + * Next, find the nearest spandep at/above tn->start + tn->length. + * Use its delta minus tn->start's delta to increase tn->length. + */ + length = tryNode->note.length; + sd2 = FindNearestSpanDep(cg, offset + length, sd - sdbase, &guard); + if (sd2 != sd) { + tryNode->note.length = + length + sd2->offset - sd2->before - delta; + } + } + } + +#ifdef DEBUG_brendan + { + uintN bigspans = 0; + top = -1; + for (sd = sdbase; sd < sdlimit; sd++) { + offset = sd->offset; + + /* NB: sd->top cursors into the original, unextended bytecode vector. */ + if (sd->top != top) { + JS_ASSERT(top == -1 || + !JOF_TYPE_IS_EXTENDED_JUMP(type) || + bigspans != 0); + bigspans = 0; + top = sd->top; + JS_ASSERT(top == sd->before); + op = (JSOp) base[offset]; + type = JOF_OPTYPE(op); + JS_ASSERT(type == JOF_JUMP || + type == JOF_JUMPX || + type == JOF_TABLESWITCH || + type == JOF_TABLESWITCHX || + type == JOF_LOOKUPSWITCH || + type == JOF_LOOKUPSWITCHX); + pivot = offset; + } + + pc = base + offset; + if (JOF_TYPE_IS_EXTENDED_JUMP(type)) { + span = GET_JUMPX_OFFSET(pc); + if (span < JUMP_OFFSET_MIN || JUMP_OFFSET_MAX < span) { + bigspans++; + } else { + JS_ASSERT(type == JOF_TABLESWITCHX || + type == JOF_LOOKUPSWITCHX); + } + } else { + span = GET_JUMP_OFFSET(pc); + } + JS_ASSERT(SD_SPAN(sd, pivot) == span); + } + JS_ASSERT(!JOF_TYPE_IS_EXTENDED_JUMP(type) || bigspans != 0); + } +#endif + + /* + * Reset so we optimize at most once -- cg may be used for further code + * generation of successive, independent, top-level statements. No jump + * can span top-level statements, because JS lacks goto. + */ + size = SPANDEPS_SIZE(JS_BIT(JS_CeilingLog2(cg->numSpanDeps))); + cx->free(cg->spanDeps); + cg->spanDeps = NULL; + FreeJumpTargets(cg, cg->jumpTargets); + cg->jumpTargets = NULL; + cg->numSpanDeps = cg->numJumpTargets = 0; + cg->spanDepTodo = CG_OFFSET(cg); + return JS_TRUE; +} + +static ptrdiff_t +EmitJump(JSContext *cx, JSCodeGenerator *cg, JSOp op, ptrdiff_t off) +{ + JSBool extend; + ptrdiff_t jmp; + jsbytecode *pc; + + extend = off < JUMP_OFFSET_MIN || JUMP_OFFSET_MAX < off; + if (extend && !cg->spanDeps && !BuildSpanDepTable(cx, cg)) + return -1; + + jmp = js_Emit3(cx, cg, op, JUMP_OFFSET_HI(off), JUMP_OFFSET_LO(off)); + if (jmp >= 0 && (extend || cg->spanDeps)) { + pc = CG_CODE(cg, jmp); + if (!AddSpanDep(cx, cg, pc, pc, off)) + return -1; + } + return jmp; +} + +static ptrdiff_t +GetJumpOffset(JSCodeGenerator *cg, jsbytecode *pc) +{ + JSSpanDep *sd; + JSJumpTarget *jt; + ptrdiff_t top; + + if (!cg->spanDeps) + return GET_JUMP_OFFSET(pc); + + sd = GetSpanDep(cg, pc); + jt = sd->target; + if (!JT_HAS_TAG(jt)) + return JT_TO_BPDELTA(jt); + + top = sd->top; + while (--sd >= cg->spanDeps && sd->top == top) + continue; + sd++; + return JT_CLR_TAG(jt)->offset - sd->offset; +} + +JSBool +js_SetJumpOffset(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc, + ptrdiff_t off) +{ + if (!cg->spanDeps) { + if (JUMP_OFFSET_MIN <= off && off <= JUMP_OFFSET_MAX) { + SET_JUMP_OFFSET(pc, off); + return JS_TRUE; + } + + if (!BuildSpanDepTable(cx, cg)) + return JS_FALSE; + } + + return SetSpanDepTarget(cx, cg, GetSpanDep(cg, pc), off); +} + +bool +JSTreeContext::inStatement(JSStmtType type) +{ + for (JSStmtInfo *stmt = topStmt; stmt; stmt = stmt->down) { + if (stmt->type == type) + return true; + } + return false; +} + +bool +JSTreeContext::ensureSharpSlots() +{ +#if JS_HAS_SHARP_VARS + JS_STATIC_ASSERT(SHARP_NSLOTS == 2); + + if (sharpSlotBase >= 0) { + JS_ASSERT(flags & TCF_HAS_SHARPS); + return true; + } + + JS_ASSERT(!(flags & TCF_HAS_SHARPS)); + if (flags & TCF_IN_FUNCTION) { + JSContext *cx = compiler->context; + JSAtom *sharpArrayAtom = js_Atomize(cx, "#array", 6, 0); + JSAtom *sharpDepthAtom = js_Atomize(cx, "#depth", 6, 0); + if (!sharpArrayAtom || !sharpDepthAtom) + return false; + + sharpSlotBase = fun->u.i.nvars; + if (!js_AddLocal(cx, fun, sharpArrayAtom, JSLOCAL_VAR)) + return false; + if (!js_AddLocal(cx, fun, sharpDepthAtom, JSLOCAL_VAR)) + return false; + } else { + /* + * JSCompiler::compileScript will rebase immediate operands indexing + * the sharp slots to come at the end of the global script's |nfixed| + * slots storage, after gvars and regexps. + */ + sharpSlotBase = 0; + } + flags |= TCF_HAS_SHARPS; +#endif + return true; +} + +void +js_PushStatement(JSTreeContext *tc, JSStmtInfo *stmt, JSStmtType type, + ptrdiff_t top) +{ + stmt->type = type; + stmt->flags = 0; + stmt->blockid = tc->blockid(); + SET_STATEMENT_TOP(stmt, top); + stmt->label = NULL; + JS_ASSERT(!stmt->blockObj); + stmt->down = tc->topStmt; + tc->topStmt = stmt; + if (STMT_LINKS_SCOPE(stmt)) { + stmt->downScope = tc->topScopeStmt; + tc->topScopeStmt = stmt; + } else { + stmt->downScope = NULL; + } +} + +void +js_PushBlockScope(JSTreeContext *tc, JSStmtInfo *stmt, JSObject *blockObj, + ptrdiff_t top) +{ + js_PushStatement(tc, stmt, STMT_BLOCK, top); + stmt->flags |= SIF_SCOPE; + STOBJ_SET_PARENT(blockObj, tc->blockChain); + stmt->downScope = tc->topScopeStmt; + tc->topScopeStmt = stmt; + tc->blockChain = blockObj; + stmt->blockObj = blockObj; +} + +/* + * Emit a backpatch op with offset pointing to the previous jump of this type, + * so that we can walk back up the chain fixing up the op and jump offset. + */ +static ptrdiff_t +EmitBackPatchOp(JSContext *cx, JSCodeGenerator *cg, JSOp op, ptrdiff_t *lastp) +{ + ptrdiff_t offset, delta; + + offset = CG_OFFSET(cg); + delta = offset - *lastp; + *lastp = offset; + JS_ASSERT(delta > 0); + return EmitJump(cx, cg, op, delta); +} + +/* + * Macro to emit a bytecode followed by a uint16 immediate operand stored in + * big-endian order, used for arg and var numbers as well as for atomIndexes. + * NB: We use cx and cg from our caller's lexical environment, and return + * false on error. + */ +#define EMIT_UINT16_IMM_OP(op, i) \ + JS_BEGIN_MACRO \ + if (js_Emit3(cx, cg, op, UINT16_HI(i), UINT16_LO(i)) < 0) \ + return JS_FALSE; \ + JS_END_MACRO + +#define EMIT_UINT16PAIR_IMM_OP(op, i, j) \ + JS_BEGIN_MACRO \ + ptrdiff_t off_ = js_EmitN(cx, cg, op, 2 * UINT16_LEN); \ + if (off_ < 0) \ + return JS_FALSE; \ + jsbytecode *pc_ = CG_CODE(cg, off_); \ + SET_UINT16(pc_, i); \ + pc_ += UINT16_LEN; \ + SET_UINT16(pc_, j); \ + JS_END_MACRO + +static JSBool +FlushPops(JSContext *cx, JSCodeGenerator *cg, intN *npops) +{ + JS_ASSERT(*npops != 0); + if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) + return JS_FALSE; + EMIT_UINT16_IMM_OP(JSOP_POPN, *npops); + *npops = 0; + return JS_TRUE; +} + +/* + * Emit additional bytecode(s) for non-local jumps. + */ +static JSBool +EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt) +{ + intN depth, npops; + JSStmtInfo *stmt; + + /* + * The non-local jump fixup we emit will unbalance cg->stackDepth, because + * the fixup replicates balanced code such as JSOP_LEAVEWITH emitted at the + * end of a with statement, so we save cg->stackDepth here and restore it + * just before a successful return. + */ + depth = cg->stackDepth; + npops = 0; + +#define FLUSH_POPS() if (npops && !FlushPops(cx, cg, &npops)) return JS_FALSE + + for (stmt = cg->topStmt; stmt != toStmt; stmt = stmt->down) { + switch (stmt->type) { + case STMT_FINALLY: + FLUSH_POPS(); + if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) + return JS_FALSE; + if (EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &GOSUBS(*stmt)) < 0) + return JS_FALSE; + break; + + case STMT_WITH: + /* There's a With object on the stack that we need to pop. */ + FLUSH_POPS(); + if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) + return JS_FALSE; + break; + + case STMT_FOR_IN_LOOP: + /* + * The iterator and the object being iterated need to be popped. + */ + FLUSH_POPS(); + if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_ENDITER) < 0) + return JS_FALSE; + break; + + case STMT_SUBROUTINE: + /* + * There's a [exception or hole, retsub pc-index] pair on the + * stack that we need to pop. + */ + npops += 2; + break; + + default:; + } + + if (stmt->flags & SIF_SCOPE) { + uintN i; + + /* There is a Block object with locals on the stack to pop. */ + FLUSH_POPS(); + if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) + return JS_FALSE; + i = OBJ_BLOCK_COUNT(cx, stmt->blockObj); + EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, i); + } + } + + FLUSH_POPS(); + cg->stackDepth = depth; + return JS_TRUE; + +#undef FLUSH_POPS +} + +static ptrdiff_t +EmitGoto(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt, + ptrdiff_t *lastp, JSAtomListElement *label, JSSrcNoteType noteType) +{ + intN index; + + if (!EmitNonLocalJumpFixup(cx, cg, toStmt)) + return -1; + + if (label) + index = js_NewSrcNote2(cx, cg, noteType, (ptrdiff_t) ALE_INDEX(label)); + else if (noteType != SRC_NULL) + index = js_NewSrcNote(cx, cg, noteType); + else + index = 0; + if (index < 0) + return -1; + + return EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, lastp); +} + +static JSBool +BackPatch(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t last, + jsbytecode *target, jsbytecode op) +{ + jsbytecode *pc, *stop; + ptrdiff_t delta, span; + + pc = CG_CODE(cg, last); + stop = CG_CODE(cg, -1); + while (pc != stop) { + delta = GetJumpOffset(cg, pc); + span = target - pc; + CHECK_AND_SET_JUMP_OFFSET(cx, cg, pc, span); + + /* + * Set *pc after jump offset in case bpdelta didn't overflow, but span + * does (if so, CHECK_AND_SET_JUMP_OFFSET might call BuildSpanDepTable + * and need to see the JSOP_BACKPATCH* op at *pc). + */ + *pc = op; + pc -= delta; + } + return JS_TRUE; +} + +void +js_PopStatement(JSTreeContext *tc) +{ + JSStmtInfo *stmt; + + stmt = tc->topStmt; + tc->topStmt = stmt->down; + if (STMT_LINKS_SCOPE(stmt)) { + tc->topScopeStmt = stmt->downScope; + if (stmt->flags & SIF_SCOPE) { + tc->blockChain = STOBJ_GET_PARENT(stmt->blockObj); + JS_SCOPE_DEPTH_METERING(--tc->scopeDepth); + } + } +} + +JSBool +js_PopStatementCG(JSContext *cx, JSCodeGenerator *cg) +{ + JSStmtInfo *stmt; + + stmt = cg->topStmt; + if (!STMT_IS_TRYING(stmt) && + (!BackPatch(cx, cg, stmt->breaks, CG_NEXT(cg), JSOP_GOTO) || + !BackPatch(cx, cg, stmt->continues, CG_CODE(cg, stmt->update), + JSOP_GOTO))) { + return JS_FALSE; + } + js_PopStatement(cg); + return JS_TRUE; +} + +JSBool +js_DefineCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, + JSParseNode *pn) +{ + jsdouble dval; + jsint ival; + JSAtom *valueAtom; + jsval v; + JSAtomListElement *ale; + + /* XXX just do numbers for now */ + if (pn->pn_type == TOK_NUMBER) { + dval = pn->pn_dval; + if (JSDOUBLE_IS_INT(dval, ival) && INT_FITS_IN_JSVAL(ival)) { + v = INT_TO_JSVAL(ival); + } else { + /* + * We atomize double to root a jsdouble instance that we wrap as + * jsval and store in cg->constList. This works because atoms are + * protected from GC during compilation. + */ + valueAtom = js_AtomizeDouble(cx, dval); + if (!valueAtom) + return JS_FALSE; + v = ATOM_KEY(valueAtom); + } + ale = cg->constList.add(cg->compiler, atom); + if (!ale) + return JS_FALSE; + ALE_SET_VALUE(ale, v); + } + return JS_TRUE; +} + +JSStmtInfo * +js_LexicalLookup(JSTreeContext *tc, JSAtom *atom, jsint *slotp, JSStmtInfo *stmt) +{ + JSObject *obj; + JSScope *scope; + JSScopeProperty *sprop; + + if (!stmt) + stmt = tc->topScopeStmt; + for (; stmt; stmt = stmt->downScope) { + if (stmt->type == STMT_WITH) + break; + + /* Skip "maybe scope" statements that don't contain let bindings. */ + if (!(stmt->flags & SIF_SCOPE)) + continue; + + obj = stmt->blockObj; + JS_ASSERT(obj->getClass() == &js_BlockClass); + scope = OBJ_SCOPE(obj); + sprop = scope->lookup(ATOM_TO_JSID(atom)); + if (sprop) { + JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID); + + if (slotp) { + JS_ASSERT(JSVAL_IS_INT(obj->fslots[JSSLOT_BLOCK_DEPTH])); + *slotp = JSVAL_TO_INT(obj->fslots[JSSLOT_BLOCK_DEPTH]) + + sprop->shortid; + } + return stmt; + } + } + + if (slotp) + *slotp = -1; + return stmt; +} + +/* + * Check if the attributes describe a property holding a compile-time constant + * or a permanent, read-only property without a getter. + */ +#define IS_CONSTANT_PROPERTY(attrs) \ + (((attrs) & (JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_GETTER)) == \ + (JSPROP_READONLY | JSPROP_PERMANENT)) + +/* + * The function sets vp to JSVAL_HOLE when the atom does not corresponds to a + * name defining a constant. + */ +static JSBool +LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, + jsval *vp) +{ + JSBool ok; + JSStmtInfo *stmt; + JSAtomListElement *ale; + JSObject *obj, *objbox; + JSProperty *prop; + uintN attrs; + + /* + * Chase down the cg stack, but only until we reach the outermost cg. + * This enables propagating consts from top-level into switch cases in a + * function compiled along with the top-level script. + */ + *vp = JSVAL_HOLE; + do { + if (cg->flags & (TCF_IN_FUNCTION | TCF_COMPILE_N_GO)) { + /* XXX this will need revising if 'const' becomes block-scoped. */ + stmt = js_LexicalLookup(cg, atom, NULL); + if (stmt) + return JS_TRUE; + + ale = cg->constList.lookup(atom); + if (ale) { + JS_ASSERT(ALE_VALUE(ale) != JSVAL_HOLE); + *vp = ALE_VALUE(ale); + return JS_TRUE; + } + + /* + * Try looking in the variable object for a direct property that + * is readonly and permanent. We know such a property can't be + * shadowed by another property on obj's prototype chain, or a + * with object or catch variable; nor can prop's value be changed, + * nor can prop be deleted. + */ + if (cg->flags & TCF_IN_FUNCTION) { + if (js_LookupLocal(cx, cg->fun, atom, NULL) != JSLOCAL_NONE) + break; + } else { + JS_ASSERT(cg->flags & TCF_COMPILE_N_GO); + obj = cg->scopeChain; + ok = obj->lookupProperty(cx, ATOM_TO_JSID(atom), &objbox, &prop); + if (!ok) + return JS_FALSE; + if (objbox == obj) { + /* + * We're compiling code that will be executed immediately, + * not re-executed against a different scope chain and/or + * variable object. Therefore we can get constant values + * from our variable object here. + */ + ok = obj->getAttributes(cx, ATOM_TO_JSID(atom), prop, &attrs); + if (ok && IS_CONSTANT_PROPERTY(attrs)) { + ok = obj->getProperty(cx, ATOM_TO_JSID(atom), vp); + JS_ASSERT_IF(ok, *vp != JSVAL_HOLE); + } + } + if (prop) + objbox->dropProperty(cx, prop); + if (!ok) + return JS_FALSE; + if (prop) + break; + } + } + } while ((cg = (JSCodeGenerator *) cg->parent) != NULL); + return JS_TRUE; +} + +/* + * Return JSOP_NOP to indicate that index fits 2 bytes and no index segment + * reset instruction is necessary, JSOP_FALSE to indicate an error or either + * JSOP_RESETBASE0 or JSOP_RESETBASE1 to indicate the reset bytecode to issue + * after the main bytecode sequence. + */ +static JSOp +EmitBigIndexPrefix(JSContext *cx, JSCodeGenerator *cg, uintN index) +{ + uintN indexBase; + + /* + * We have max 3 bytes for indexes and check for INDEX_LIMIT overflow only + * for big indexes. + */ + JS_STATIC_ASSERT(INDEX_LIMIT <= JS_BIT(24)); + JS_STATIC_ASSERT(INDEX_LIMIT >= + (JSOP_INDEXBASE3 - JSOP_INDEXBASE1 + 2) << 16); + + if (index < JS_BIT(16)) + return JSOP_NOP; + indexBase = index >> 16; + if (indexBase <= JSOP_INDEXBASE3 - JSOP_INDEXBASE1 + 1) { + if (js_Emit1(cx, cg, (JSOp)(JSOP_INDEXBASE1 + indexBase - 1)) < 0) + return JSOP_FALSE; + return JSOP_RESETBASE0; + } + + if (index >= INDEX_LIMIT) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_TOO_MANY_LITERALS); + return JSOP_FALSE; + } + + if (js_Emit2(cx, cg, JSOP_INDEXBASE, (JSOp)indexBase) < 0) + return JSOP_FALSE; + return JSOP_RESETBASE; +} + +/* + * Emit a bytecode and its 2-byte constant index immediate operand. If the + * index requires more than 2 bytes, emit a prefix op whose 8-bit immediate + * operand effectively extends the 16-bit immediate of the prefixed opcode, + * by changing index "segment" (see jsinterp.c). We optimize segments 1-3 + * with single-byte JSOP_INDEXBASE[123] codes. + * + * Such prefixing currently requires a suffix to restore the "zero segment" + * register setting, but this could be optimized further. + */ +static JSBool +EmitIndexOp(JSContext *cx, JSOp op, uintN index, JSCodeGenerator *cg) +{ + JSOp bigSuffix; + + bigSuffix = EmitBigIndexPrefix(cx, cg, index); + if (bigSuffix == JSOP_FALSE) + return JS_FALSE; + EMIT_UINT16_IMM_OP(op, index); + return bigSuffix == JSOP_NOP || js_Emit1(cx, cg, bigSuffix) >= 0; +} + +/* + * Slight sugar for EmitIndexOp, again accessing cx and cg from the macro + * caller's lexical environment, and embedding a false return on error. + */ +#define EMIT_INDEX_OP(op, index) \ + JS_BEGIN_MACRO \ + if (!EmitIndexOp(cx, op, index, cg)) \ + return JS_FALSE; \ + JS_END_MACRO + +static JSBool +EmitAtomOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) +{ + JSAtomListElement *ale; + + JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM); + if (op == JSOP_GETPROP && + pn->pn_atom == cx->runtime->atomState.lengthAtom) { + return js_Emit1(cx, cg, JSOP_LENGTH) >= 0; + } + ale = cg->atomList.add(cg->compiler, pn->pn_atom); + if (!ale) + return JS_FALSE; + return EmitIndexOp(cx, op, ALE_INDEX(ale), cg); +} + +static JSBool +EmitObjectOp(JSContext *cx, JSObjectBox *objbox, JSOp op, + JSCodeGenerator *cg) +{ + JS_ASSERT(JOF_OPTYPE(op) == JOF_OBJECT); + return EmitIndexOp(cx, op, cg->objectList.index(objbox), cg); +} + +/* + * What good are ARGNO_LEN and SLOTNO_LEN, you ask? The answer is that, apart + * from EmitSlotIndexOp, they abstract out the detail that both are 2, and in + * other parts of the code there's no necessary relationship between the two. + * The abstraction cracks here in order to share EmitSlotIndexOp code among + * the JSOP_DEFLOCALFUN and JSOP_GET{ARG,VAR,LOCAL}PROP cases. + */ +JS_STATIC_ASSERT(ARGNO_LEN == 2); +JS_STATIC_ASSERT(SLOTNO_LEN == 2); + +static JSBool +EmitSlotIndexOp(JSContext *cx, JSOp op, uintN slot, uintN index, + JSCodeGenerator *cg) +{ + JSOp bigSuffix; + ptrdiff_t off; + jsbytecode *pc; + + JS_ASSERT(JOF_OPTYPE(op) == JOF_SLOTATOM || + JOF_OPTYPE(op) == JOF_SLOTOBJECT); + bigSuffix = EmitBigIndexPrefix(cx, cg, index); + if (bigSuffix == JSOP_FALSE) + return JS_FALSE; + + /* Emit [op, slot, index]. */ + off = js_EmitN(cx, cg, op, 2 + INDEX_LEN); + if (off < 0) + return JS_FALSE; + pc = CG_CODE(cg, off); + SET_UINT16(pc, slot); + pc += 2; + SET_INDEX(pc, index); + return bigSuffix == JSOP_NOP || js_Emit1(cx, cg, bigSuffix) >= 0; +} + +/* + * Adjust the slot for a block local to account for the number of variables + * that share the same index space with locals. Due to the incremental code + * generation for top-level script, we do the adjustment via code patching in + * JSCompiler::compileScript; see comments there. + * + * The function returns -1 on failures. + */ +static jsint +AdjustBlockSlot(JSContext *cx, JSCodeGenerator *cg, jsint slot) +{ + JS_ASSERT((jsuint) slot < cg->maxStackDepth); + if (cg->flags & TCF_IN_FUNCTION) { + slot += cg->fun->u.i.nvars; + if ((uintN) slot >= SLOTNO_LIMIT) { + js_ReportCompileErrorNumber(cx, CG_TS(cg), NULL, + JSREPORT_ERROR, + JSMSG_TOO_MANY_LOCALS); + slot = -1; + } + } + return slot; +} + +static bool +EmitEnterBlock(JSContext *cx, JSParseNode *pn, JSCodeGenerator *cg) +{ + JS_ASSERT(PN_TYPE(pn) == TOK_LEXICALSCOPE); + if (!EmitObjectOp(cx, pn->pn_objbox, JSOP_ENTERBLOCK, cg)) + return false; + + JSObject *blockObj = pn->pn_objbox->object; + jsint depth = AdjustBlockSlot(cx, cg, OBJ_BLOCK_DEPTH(cx, blockObj)); + if (depth < 0) + return false; + + for (uintN slot = JSSLOT_FREE(&js_BlockClass), + limit = slot + OBJ_BLOCK_COUNT(cx, blockObj); + slot < limit; slot++) { + jsval v = STOBJ_GET_SLOT(blockObj, slot); + + /* Beware the empty destructuring dummy. */ + if (JSVAL_IS_VOID(v)) { + JS_ASSERT(slot + 1 <= limit); + continue; + } + + JSDefinition *dn = (JSDefinition *) JSVAL_TO_PRIVATE(v); + JS_ASSERT(dn->pn_defn); + JS_ASSERT(uintN(dn->frameSlot() + depth) < JS_BIT(16)); + dn->pn_cookie += depth; +#ifdef DEBUG + for (JSParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) { + JS_ASSERT(pnu->pn_lexdef == dn); + JS_ASSERT(!(pnu->pn_dflags & PND_BOUND)); + JS_ASSERT(pnu->pn_cookie == FREE_UPVAR_COOKIE); + } +#endif + } + + OBJ_SCOPE(blockObj)->freeslot = JSSLOT_FREE(&js_BlockClass); + return js_GrowSlots(cx, blockObj, JSSLOT_FREE(&js_BlockClass)); +} + +/* + * When eval is called from a function, the eval code or function code it + * compiles may reference upvars that live in the eval-calling function. The + * eval-invoked compiler does not have explicit definitions for these upvars + * and we do not attempt to create them a-priori (by inspecting the function's + * args and vars) -- we could, but we'd take an avoidable penalty for each + * function local not referenced by any upvar. Instead, we map such upvars + * lazily, growing upvarMap.vector by powers of two. + * + * This function knows that it is called with pn pointing to a PN_NAME-arity + * node, and cg->compiler->callerFrame having a non-null fun member, and the + * static level of cg at least one greater than the eval-calling function's + * static level. + */ +static bool +MakeUpvarForEval(JSParseNode *pn, JSCodeGenerator *cg) +{ + JSContext *cx = cg->compiler->context; + JSFunction *fun = cg->compiler->callerFrame->fun; + uintN upvarLevel = fun->u.i.script->staticLevel; + + JSFunctionBox *funbox = cg->funbox; + if (funbox) { + /* + * Treat top-level function definitions as escaping (i.e., as funargs), + * required since we compile each such top level function or statement + * and throw away the AST, so we can't yet see all funarg uses of this + * function being compiled (cg->funbox->object). See bug 493177. + */ + if (funbox->level == fun->u.i.script->staticLevel + 1U && + !(((JSFunction *) funbox->object)->flags & JSFUN_LAMBDA)) { + JS_ASSERT_IF(cx->options & JSOPTION_ANONFUNFIX, + ((JSFunction *) funbox->object)->atom); + return true; + } + + while (funbox->level >= upvarLevel) { + if (funbox->node->pn_dflags & PND_FUNARG) + return true; + funbox = funbox->parent; + if (!funbox) + break; + } + } + + JSAtom *atom = pn->pn_atom; + + uintN index; + JSLocalKind localKind = js_LookupLocal(cx, fun, atom, &index); + if (localKind == JSLOCAL_NONE) + return true; + + JS_ASSERT(cg->staticLevel > upvarLevel); + if (cg->staticLevel >= JS_DISPLAY_SIZE || upvarLevel >= JS_DISPLAY_SIZE) + return true; + + JSAtomListElement *ale = cg->upvarList.lookup(atom); + if (!ale) { + if ((cg->flags & TCF_IN_FUNCTION) && + !js_AddLocal(cx, cg->fun, atom, JSLOCAL_UPVAR)) { + return false; + } + + ale = cg->upvarList.add(cg->compiler, atom); + if (!ale) + return false; + JS_ASSERT(ALE_INDEX(ale) == cg->upvarList.count - 1); + + uint32 *vector = cg->upvarMap.vector; + uint32 length = cg->upvarMap.length; + + JS_ASSERT(ALE_INDEX(ale) <= length); + if (ALE_INDEX(ale) == length) { + length = 2 * JS_MAX(2, length); + vector = (uint32 *) cx->realloc(vector, length * sizeof *vector); + if (!vector) + return false; + cg->upvarMap.vector = vector; + cg->upvarMap.length = length; + } + + if (localKind != JSLOCAL_ARG) + index += fun->nargs; + JS_ASSERT(index < JS_BIT(16)); + + uintN skip = cg->staticLevel - upvarLevel; + vector[ALE_INDEX(ale)] = MAKE_UPVAR_COOKIE(skip, index); + } + + pn->pn_op = JSOP_GETUPVAR; + pn->pn_cookie = MAKE_UPVAR_COOKIE(cg->staticLevel, ALE_INDEX(ale)); + pn->pn_dflags |= PND_BOUND; + return true; +} + +/* + * BindNameToSlot attempts to optimize name gets and sets to stack slot loads + * and stores, given the compile-time information in cg and a TOK_NAME node pn. + * It returns false on error, true on success. + * + * The caller can inspect pn->pn_cookie for FREE_UPVAR_COOKIE to tell whether + * optimization occurred, in which case BindNameToSlot also updated pn->pn_op. + * If pn->pn_cookie is still FREE_UPVAR_COOKIE on return, pn->pn_op still may + * have been optimized, e.g., from JSOP_NAME to JSOP_CALLEE. Whether or not + * pn->pn_op was modified, if this function finds an argument or local variable + * name, PND_CONST will be set in pn_dflags for read-only properties after a + * successful return. + * + * NB: if you add more opcodes specialized from JSOP_NAME, etc., don't forget + * to update the TOK_FOR (for-in) and TOK_ASSIGN (op=, e.g. +=) special cases + * in js_EmitTree. + */ +static JSBool +BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) +{ + JSDefinition *dn; + JSOp op; + JSAtom *atom; + uint32 cookie; + JSDefinition::Kind dn_kind; + JSAtomListElement *ale; + uintN index; + + JS_ASSERT(pn->pn_type == TOK_NAME); + + /* Idempotency tests come first, since we may be called more than once. */ + if (pn->pn_dflags & PND_BOUND) + return JS_TRUE; + + /* No cookie initialized for these two, they're pre-bound by definition. */ + JS_ASSERT(pn->pn_op != JSOP_ARGUMENTS && pn->pn_op != JSOP_CALLEE); + + /* + * The parser linked all uses (including forward references) to their + * definitions, unless a with statement or direct eval intervened. + */ + if (pn->pn_used) { + JS_ASSERT(pn->pn_cookie == FREE_UPVAR_COOKIE); + dn = pn->pn_lexdef; + JS_ASSERT(dn->pn_defn); + pn->pn_dflags |= (dn->pn_dflags & PND_CONST); + } else { + if (!pn->pn_defn) + return JS_TRUE; + dn = (JSDefinition *) pn; + } + + op = PN_OP(pn); + if (op == JSOP_NOP) + return JS_TRUE; + + JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM); + atom = pn->pn_atom; + cookie = dn->pn_cookie; + dn_kind = dn->kind(); + + /* + * Turn attempts to mutate const-declared bindings into get ops (for + * pre-increment and pre-decrement ops, our caller will have to emit + * JSOP_POS, JSOP_ONE, and JSOP_ADD as well). + * + * Turn JSOP_DELNAME into JSOP_FALSE if dn is known, as all declared + * bindings visible to the compiler are permanent in JS unless the + * declaration originates in eval code. We detect eval code by testing + * cg->compiler->callerFrame, which is set only by eval or a debugger + * equivalent. + * + * Note that this callerFrame non-null test must be qualified by testing + * !cg->funbox to exclude function code nested in eval code, which is not + * subject to the deletable binding exception. + */ + switch (op) { + case JSOP_NAME: + case JSOP_SETCONST: + break; + case JSOP_DELNAME: + if (dn_kind != JSDefinition::UNKNOWN) { + if (cg->compiler->callerFrame && !cg->funbox) + JS_ASSERT(cg->flags & TCF_COMPILE_N_GO); + else + pn->pn_op = JSOP_FALSE; + pn->pn_dflags |= PND_BOUND; + return JS_TRUE; + } + break; + default: + if (pn->isConst()) + pn->pn_op = op = JSOP_NAME; + } + + if (cookie == FREE_UPVAR_COOKIE) { + JSStackFrame *caller = cg->compiler->callerFrame; + if (caller) { + JS_ASSERT(cg->flags & TCF_COMPILE_N_GO); + + /* + * Don't generate upvars on the left side of a for loop. See + * bug 470758. + */ + if (cg->flags & TCF_IN_FOR_INIT) + return JS_TRUE; + + JS_ASSERT(caller->script); + if (!caller->fun) + return JS_TRUE; + + /* + * Make sure the variable object used by the compiler to initialize + * parent links matches the caller's varobj. Compile-n-go compiler- + * created function objects have the top-level cg's scopeChain set + * as their parent by JSCompiler::newFunction. + */ + JSObject *scopeobj = (cg->flags & TCF_IN_FUNCTION) + ? STOBJ_GET_PARENT(FUN_OBJECT(cg->fun)) + : cg->scopeChain; + if (scopeobj != caller->varobj) + return JS_TRUE; + + /* + * We are compiling eval or debug script inside a function frame + * and the scope chain matches the function's variable object. + * Optimize access to function's arguments and variable and the + * arguments object. + */ + if (op != JSOP_NAME) + return JS_TRUE; + + return MakeUpvarForEval(pn, cg); + } + return JS_TRUE; + } + + if (dn->pn_dflags & PND_GVAR) { + /* + * If this is a global reference from within a function, leave pn_op as + * JSOP_NAME, etc. We could emit JSOP_*GVAR ops within function code if + * only we could depend on the global frame's slots being valid for all + * calls to the function. + */ + if (cg->flags & TCF_IN_FUNCTION) + return JS_TRUE; + + /* + * We are optimizing global variables and there may be no pre-existing + * global property named atom when this global script runs. If atom was + * declared via const or var, optimize pn to access fp->vars using the + * appropriate JSOP_*GVAR op. + * + * FIXME: should be able to optimize global function access too. + */ + JS_ASSERT(dn_kind == JSDefinition::VAR || dn_kind == JSDefinition::CONST); + + switch (op) { + case JSOP_NAME: op = JSOP_GETGVAR; break; + case JSOP_SETNAME: op = JSOP_SETGVAR; break; + case JSOP_SETCONST: /* NB: no change */ break; + case JSOP_INCNAME: op = JSOP_INCGVAR; break; + case JSOP_NAMEINC: op = JSOP_GVARINC; break; + case JSOP_DECNAME: op = JSOP_DECGVAR; break; + case JSOP_NAMEDEC: op = JSOP_GVARDEC; break; + case JSOP_FORNAME: /* NB: no change */ break; + case JSOP_DELNAME: /* NB: no change */ break; + default: JS_NOT_REACHED("gvar"); + } + pn->pn_op = op; + pn->pn_cookie = cookie; + pn->pn_dflags |= PND_BOUND; + return JS_TRUE; + } + + uintN level = UPVAR_FRAME_SKIP(cookie); + JS_ASSERT(cg->staticLevel >= level); + + /* + * A JSDefinition witnessed as a declaration by the parser cannot be an + * upvar, unless it is the degenerate kind of upvar selected above (in the + * code before the PND_GVAR test) for the special case of compile-and-go + * code generated from eval called from a function, where the eval code + * uses local vars defined in the function. We detect this upvar-for-eval + * case by checking dn's op. + */ + if (PN_OP(dn) == JSOP_GETUPVAR) { + JS_ASSERT(cg->staticLevel >= level); + if (op != JSOP_NAME) + return JS_TRUE; + +#ifdef DEBUG + JSStackFrame *caller = cg->compiler->callerFrame; +#endif + JS_ASSERT(caller); + JS_ASSERT(caller->script); + + JSTreeContext *tc = cg; + while (tc->staticLevel != level) + tc = tc->parent; + JS_ASSERT(tc->flags & TCF_COMPILING); + + JSCodeGenerator *evalcg = (JSCodeGenerator *) tc; + JS_ASSERT(evalcg->flags & TCF_COMPILE_N_GO); + JS_ASSERT(caller->fun && caller->varobj == evalcg->scopeChain); + + /* + * Don't generate upvars on the left side of a for loop. See + * bug 470758 and bug 520513. + */ + if (evalcg->flags & TCF_IN_FOR_INIT) + return JS_TRUE; + + if (cg->staticLevel == level) { + pn->pn_op = JSOP_GETUPVAR; + pn->pn_cookie = cookie; + pn->pn_dflags |= PND_BOUND; + return JS_TRUE; + } + + return MakeUpvarForEval(pn, cg); + } + + uintN skip = cg->staticLevel - level; + if (skip != 0) { + JS_ASSERT(cg->flags & TCF_IN_FUNCTION); + JS_ASSERT_IF(UPVAR_FRAME_SLOT(cookie) != CALLEE_UPVAR_SLOT, + cg->lexdeps.lookup(atom)); + JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM); + JS_ASSERT(cg->fun->u.i.skipmin <= skip); + + /* + * If op is a mutating opcode, this upvar's static level is too big to + * index into the display, or the function is heavyweight, we fall back + * on JSOP_*NAME*. + */ + if (op != JSOP_NAME) + return JS_TRUE; + if (level >= JS_DISPLAY_SIZE) + return JS_TRUE; + if (cg->flags & TCF_FUN_HEAVYWEIGHT) + return JS_TRUE; + + if (FUN_FLAT_CLOSURE(cg->fun)) { + op = JSOP_GETDSLOT; + } else { + /* + * The function we're compiling may not be heavyweight, but if it + * escapes as a funarg, we can't use JSOP_GETUPVAR/JSOP_CALLUPVAR. + * JSCompiler::analyzeFunctions has arranged for this function's + * enclosing functions to be heavyweight, so we can safely stick + * with JSOP_NAME/JSOP_CALLNAME. + */ + if (cg->funbox->node->pn_dflags & PND_FUNARG) + return JS_TRUE; + + /* + * Generator functions may be resumed from any call stack, which + * defeats the display optimization to static link searching used + * by JSOP_{GET,CALL}UPVAR. + */ + if (cg->flags & TCF_FUN_IS_GENERATOR) + return JS_TRUE; + + op = JSOP_GETUPVAR; + } + + ale = cg->upvarList.lookup(atom); + if (ale) { + index = ALE_INDEX(ale); + } else { + if (!js_AddLocal(cx, cg->fun, atom, JSLOCAL_UPVAR)) + return JS_FALSE; + + ale = cg->upvarList.add(cg->compiler, atom); + if (!ale) + return JS_FALSE; + index = ALE_INDEX(ale); + JS_ASSERT(index == cg->upvarList.count - 1); + + uint32 *vector = cg->upvarMap.vector; + if (!vector) { + uint32 length = cg->lexdeps.count; + + vector = (uint32 *) js_calloc(length * sizeof *vector); + if (!vector) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + cg->upvarMap.vector = vector; + cg->upvarMap.length = length; + } + + uintN slot = UPVAR_FRAME_SLOT(cookie); + if (slot != CALLEE_UPVAR_SLOT && dn_kind != JSDefinition::ARG) { + JSTreeContext *tc = cg; + do { + tc = tc->parent; + } while (tc->staticLevel != level); + if (tc->flags & TCF_IN_FUNCTION) + slot += tc->fun->nargs; + } + + vector[index] = MAKE_UPVAR_COOKIE(skip, slot); + } + + pn->pn_op = op; + pn->pn_cookie = index; + pn->pn_dflags |= PND_BOUND; + return JS_TRUE; + } + + /* + * We are compiling a function body and may be able to optimize name + * to stack slot. Look for an argument or variable in the function and + * rewrite pn_op and update pn accordingly. + */ + switch (dn_kind) { + case JSDefinition::UNKNOWN: + return JS_TRUE; + + case JSDefinition::LET: + switch (op) { + case JSOP_NAME: op = JSOP_GETLOCAL; break; + case JSOP_SETNAME: op = JSOP_SETLOCAL; break; + case JSOP_INCNAME: op = JSOP_INCLOCAL; break; + case JSOP_NAMEINC: op = JSOP_LOCALINC; break; + case JSOP_DECNAME: op = JSOP_DECLOCAL; break; + case JSOP_NAMEDEC: op = JSOP_LOCALDEC; break; + case JSOP_FORNAME: op = JSOP_FORLOCAL; break; + default: JS_NOT_REACHED("let"); + } + break; + + case JSDefinition::ARG: + switch (op) { + case JSOP_NAME: op = JSOP_GETARG; break; + case JSOP_SETNAME: op = JSOP_SETARG; break; + case JSOP_INCNAME: op = JSOP_INCARG; break; + case JSOP_NAMEINC: op = JSOP_ARGINC; break; + case JSOP_DECNAME: op = JSOP_DECARG; break; + case JSOP_NAMEDEC: op = JSOP_ARGDEC; break; + case JSOP_FORNAME: op = JSOP_FORARG; break; + default: JS_NOT_REACHED("arg"); + } + JS_ASSERT(!pn->isConst()); + break; + + case JSDefinition::VAR: + if (PN_OP(dn) == JSOP_CALLEE) { + JS_ASSERT(op != JSOP_CALLEE); + JS_ASSERT((cg->fun->flags & JSFUN_LAMBDA) && atom == cg->fun->atom); + + /* + * Leave pn->pn_op == JSOP_NAME if cg->fun is heavyweight, as we + * cannot be sure cg->fun is not something of the form: + * + * var ff = (function f(s) { eval(s); return f; }); + * + * where a caller invokes ff("var f = 42"). The result returned for + * such an invocation must be 42, since the callee name is + * lexically bound in an outer declarative environment from the + * function's activation. See jsfun.cpp:call_resolve. + */ + JS_ASSERT(op != JSOP_DELNAME); + if (!(cg->flags & TCF_FUN_HEAVYWEIGHT)) { + op = JSOP_CALLEE; + pn->pn_dflags |= PND_CONST; + } + + pn->pn_op = op; + pn->pn_dflags |= PND_BOUND; + return JS_TRUE; + } + /* FALL THROUGH */ + + default: + JS_ASSERT_IF(dn_kind != JSDefinition::FUNCTION, + dn_kind == JSDefinition::VAR || + dn_kind == JSDefinition::CONST); + switch (op) { + case JSOP_NAME: op = JSOP_GETLOCAL; break; + case JSOP_SETNAME: op = JSOP_SETLOCAL; break; + case JSOP_SETCONST: op = JSOP_SETLOCAL; break; + case JSOP_INCNAME: op = JSOP_INCLOCAL; break; + case JSOP_NAMEINC: op = JSOP_LOCALINC; break; + case JSOP_DECNAME: op = JSOP_DECLOCAL; break; + case JSOP_NAMEDEC: op = JSOP_LOCALDEC; break; + case JSOP_FORNAME: op = JSOP_FORLOCAL; break; + default: JS_NOT_REACHED("local"); + } + JS_ASSERT_IF(dn_kind == JSDefinition::CONST, pn->pn_dflags & PND_CONST); + break; + } + + JS_ASSERT(op != PN_OP(pn)); + pn->pn_op = op; + pn->pn_cookie = UPVAR_FRAME_SLOT(cookie); + pn->pn_dflags |= PND_BOUND; + return JS_TRUE; +} + +/* + * If pn contains a useful expression, return true with *answer set to true. + * If pn contains a useless expression, return true with *answer set to false. + * Return false on error. + * + * The caller should initialize *answer to false and invoke this function on + * an expression statement or similar subtree to decide whether the tree could + * produce code that has any side effects. For an expression statement, we + * define useless code as code with no side effects, because the main effect, + * the value left on the stack after the code executes, will be discarded by a + * pop bytecode. + */ +static JSBool +CheckSideEffects(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, + JSBool *answer) +{ + JSBool ok; + JSParseNode *pn2; + + ok = JS_TRUE; + if (!pn || *answer) + return ok; + + switch (pn->pn_arity) { + case PN_FUNC: + /* + * A named function, contrary to ES3, is no longer useful, because we + * bind its name lexically (using JSOP_CALLEE) instead of creating an + * Object instance and binding a readonly, permanent property in it + * (the object and binding can be detected and hijacked or captured). + * This is a bug fix to ES3; it is fixed in ES3.1 drafts. + */ + *answer = JS_FALSE; + break; + + case PN_LIST: + if (pn->pn_op == JSOP_NOP || + pn->pn_op == JSOP_OR || pn->pn_op == JSOP_AND || + pn->pn_op == JSOP_STRICTEQ || pn->pn_op == JSOP_STRICTNE) { + /* + * Non-operators along with ||, &&, ===, and !== never invoke + * toString or valueOf. + */ + for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) + ok &= CheckSideEffects(cx, cg, pn2, answer); + } else { + /* + * All invocation operations (construct: TOK_NEW, call: TOK_LP) + * are presumed to be useful, because they may have side effects + * even if their main effect (their return value) is discarded. + * + * TOK_LB binary trees of 3 or more nodes are flattened into lists + * to avoid too much recursion. All such lists must be presumed + * to be useful because each index operation could invoke a getter + * (the JSOP_ARGUMENTS special case below, in the PN_BINARY case, + * does not apply here: arguments[i][j] might invoke a getter). + * + * Likewise, array and object initialisers may call prototype + * setters (the __defineSetter__ built-in, and writable __proto__ + * on Array.prototype create this hazard). Initialiser list nodes + * have JSOP_NEWINIT in their pn_op. + */ + *answer = JS_TRUE; + } + break; + + case PN_TERNARY: + ok = CheckSideEffects(cx, cg, pn->pn_kid1, answer) && + CheckSideEffects(cx, cg, pn->pn_kid2, answer) && + CheckSideEffects(cx, cg, pn->pn_kid3, answer); + break; + + case PN_BINARY: + if (pn->pn_type == TOK_ASSIGN) { + /* + * Assignment is presumed to be useful, even if the next operation + * is another assignment overwriting this one's ostensible effect, + * because the left operand may be a property with a setter that + * has side effects. + * + * The only exception is assignment of a useless value to a const + * declared in the function currently being compiled. + */ + pn2 = pn->pn_left; + if (pn2->pn_type != TOK_NAME) { + *answer = JS_TRUE; + } else { + if (!BindNameToSlot(cx, cg, pn2)) + return JS_FALSE; + if (!CheckSideEffects(cx, cg, pn->pn_right, answer)) + return JS_FALSE; + if (!*answer && (pn->pn_op != JSOP_NOP || !pn2->isConst())) + *answer = JS_TRUE; + } + } else { + if (pn->pn_op == JSOP_OR || pn->pn_op == JSOP_AND || + pn->pn_op == JSOP_STRICTEQ || pn->pn_op == JSOP_STRICTNE) { + /* + * ||, &&, ===, and !== do not convert their operands via + * toString or valueOf method calls. + */ + ok = CheckSideEffects(cx, cg, pn->pn_left, answer) && + CheckSideEffects(cx, cg, pn->pn_right, answer); + } else { + /* + * We can't easily prove that neither operand ever denotes an + * object with a toString or valueOf method. + */ + *answer = JS_TRUE; + } + } + break; + + case PN_UNARY: + switch (pn->pn_type) { + case TOK_DELETE: + pn2 = pn->pn_kid; + switch (pn2->pn_type) { + case TOK_NAME: + if (!BindNameToSlot(cx, cg, pn2)) + return JS_FALSE; + if (pn2->isConst()) { + *answer = JS_FALSE; + break; + } + /* FALL THROUGH */ + case TOK_DOT: +#if JS_HAS_XML_SUPPORT + case TOK_DBLDOT: +#endif + case TOK_LP: + case TOK_LB: + /* All these delete addressing modes have effects too. */ + *answer = JS_TRUE; + break; + default: + ok = CheckSideEffects(cx, cg, pn2, answer); + break; + } + break; + + case TOK_UNARYOP: + if (pn->pn_op == JSOP_NOT) { + /* ! does not convert its operand via toString or valueOf. */ + ok = CheckSideEffects(cx, cg, pn->pn_kid, answer); + break; + } + /* FALL THROUGH */ + + default: + /* + * All of TOK_INC, TOK_DEC, TOK_THROW, TOK_YIELD, and TOK_DEFSHARP + * have direct effects. Of the remaining unary-arity node types, + * we can't easily prove that the operand never denotes an object + * with a toString or valueOf method. + */ + *answer = JS_TRUE; + break; + } + break; + + case PN_NAME: + /* + * Take care to avoid trying to bind a label name (labels, both for + * statements and property values in object initialisers, have pn_op + * defaulted to JSOP_NOP). + */ + if (pn->pn_type == TOK_NAME && pn->pn_op != JSOP_NOP) { + if (!BindNameToSlot(cx, cg, pn)) + return JS_FALSE; + if (pn->pn_op != JSOP_ARGUMENTS && pn->pn_op != JSOP_CALLEE && + pn->pn_cookie == FREE_UPVAR_COOKIE) { + /* + * Not an argument or local variable use, and not a use of a + * unshadowed named function expression's given name, so this + * expression could invoke a getter that has side effects. + */ + *answer = JS_TRUE; + } + } + pn2 = pn->maybeExpr(); + if (pn->pn_type == TOK_DOT) { + if (pn2->pn_type == TOK_NAME && !BindNameToSlot(cx, cg, pn2)) + return JS_FALSE; + if (!(pn2->pn_op == JSOP_ARGUMENTS && + pn->pn_atom == cx->runtime->atomState.lengthAtom)) { + /* + * Any dotted property reference could call a getter, except + * for arguments.length where arguments is unambiguous. + */ + *answer = JS_TRUE; + } + } + ok = CheckSideEffects(cx, cg, pn2, answer); + break; + + case PN_NAMESET: + ok = CheckSideEffects(cx, cg, pn->pn_tree, answer); + break; + + case PN_NULLARY: + if (pn->pn_type == TOK_DEBUGGER) + *answer = JS_TRUE; + break; + } + return ok; +} + +static JSBool +EmitNameOp(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, + JSBool callContext) +{ + JSOp op; + + if (!BindNameToSlot(cx, cg, pn)) + return JS_FALSE; + op = PN_OP(pn); + + if (callContext) { + switch (op) { + case JSOP_NAME: + op = JSOP_CALLNAME; + break; + case JSOP_GETGVAR: + op = JSOP_CALLGVAR; + break; + case JSOP_GETARG: + op = JSOP_CALLARG; + break; + case JSOP_GETLOCAL: + op = JSOP_CALLLOCAL; + break; + case JSOP_GETUPVAR: + op = JSOP_CALLUPVAR; + break; + case JSOP_GETDSLOT: + op = JSOP_CALLDSLOT; + break; + default: + JS_ASSERT(op == JSOP_ARGUMENTS || op == JSOP_CALLEE); + break; + } + } + + if (op == JSOP_ARGUMENTS || op == JSOP_CALLEE) { + if (js_Emit1(cx, cg, op) < 0) + return JS_FALSE; + if (callContext && js_Emit1(cx, cg, JSOP_NULL) < 0) + return JS_FALSE; + } else { + if (pn->pn_cookie != FREE_UPVAR_COOKIE) { + EMIT_UINT16_IMM_OP(op, pn->pn_cookie); + } else { + if (!EmitAtomOp(cx, pn, op, cg)) + return JS_FALSE; + } + } + + return JS_TRUE; +} + +#if JS_HAS_XML_SUPPORT +static JSBool +EmitXMLName(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) +{ + JSParseNode *pn2; + uintN oldflags; + + JS_ASSERT(pn->pn_type == TOK_UNARYOP); + JS_ASSERT(pn->pn_op == JSOP_XMLNAME); + JS_ASSERT(op == JSOP_XMLNAME || op == JSOP_CALLXMLNAME); + + pn2 = pn->pn_kid; + oldflags = cg->flags; + cg->flags &= ~TCF_IN_FOR_INIT; + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + cg->flags |= oldflags & TCF_IN_FOR_INIT; + if (js_NewSrcNote2(cx, cg, SRC_PCBASE, + CG_OFFSET(cg) - pn2->pn_offset) < 0) { + return JS_FALSE; + } + + return js_Emit1(cx, cg, op) >= 0; +} +#endif + +static JSBool +EmitSpecialPropOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) +{ + /* + * Special case for obj.__proto__, obj.__parent__, obj.__count__ to + * deoptimize away from fast paths in the interpreter and trace recorder, + * which skip dense array instances by going up to Array.prototype before + * looking up the property name. + */ + JSAtomListElement *ale = cg->atomList.add(cg->compiler, pn->pn_atom); + if (!ale) + return JS_FALSE; + if (!EmitIndexOp(cx, JSOP_QNAMEPART, ALE_INDEX(ale), cg)) + return JS_FALSE; + if (js_Emit1(cx, cg, op) < 0) + return JS_FALSE; + return JS_TRUE; +} + +static JSBool +EmitPropOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg, + JSBool callContext) +{ + JSParseNode *pn2, *pndot, *pnup, *pndown; + ptrdiff_t top; + + JS_ASSERT(pn->pn_arity == PN_NAME); + pn2 = pn->maybeExpr(); + + /* Special case deoptimization on __proto__, __count__ and __parent__. */ + if ((op == JSOP_GETPROP || op == JSOP_CALLPROP) && + (pn->pn_atom == cx->runtime->atomState.protoAtom || + pn->pn_atom == cx->runtime->atomState.parentAtom || + pn->pn_atom == cx->runtime->atomState.countAtom)) { + if (pn2 && !js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + return EmitSpecialPropOp(cx, pn, callContext ? JSOP_CALLELEM : JSOP_GETELEM, cg); + } + + if (callContext) { + JS_ASSERT(pn->pn_type == TOK_DOT); + JS_ASSERT(op == JSOP_GETPROP); + op = JSOP_CALLPROP; + } else if (op == JSOP_GETPROP && pn->pn_type == TOK_DOT) { + if (pn2->pn_op == JSOP_THIS) { + if (pn->pn_atom != cx->runtime->atomState.lengthAtom) { + /* Fast path for gets of |this.foo|. */ + return EmitAtomOp(cx, pn, JSOP_GETTHISPROP, cg); + } + } else if (pn2->pn_type == TOK_NAME) { + /* + * Try to optimize: + * - arguments.length into JSOP_ARGCNT + * - argname.prop into JSOP_GETARGPROP + * - localname.prop into JSOP_GETLOCALPROP + * but don't do this if the property is 'length' -- prefer to emit + * JSOP_GETARG, etc., and then JSOP_LENGTH. + */ + if (!BindNameToSlot(cx, cg, pn2)) + return JS_FALSE; + if (pn->pn_atom == cx->runtime->atomState.lengthAtom) { + if (pn2->pn_op == JSOP_ARGUMENTS) + return js_Emit1(cx, cg, JSOP_ARGCNT) >= 0; + } else { + switch (pn2->pn_op) { + case JSOP_GETARG: + op = JSOP_GETARGPROP; + goto do_indexconst; + case JSOP_GETLOCAL: + op = JSOP_GETLOCALPROP; + do_indexconst: { + JSAtomListElement *ale; + jsatomid atomIndex; + + ale = cg->atomList.add(cg->compiler, pn->pn_atom); + if (!ale) + return JS_FALSE; + atomIndex = ALE_INDEX(ale); + return EmitSlotIndexOp(cx, op, pn2->pn_cookie, atomIndex, cg); + } + + default:; + } + } + } + } + + /* + * If the object operand is also a dotted property reference, reverse the + * list linked via pn_expr temporarily so we can iterate over it from the + * bottom up (reversing again as we go), to avoid excessive recursion. + */ + if (pn2->pn_type == TOK_DOT) { + pndot = pn2; + pnup = NULL; + top = CG_OFFSET(cg); + for (;;) { + /* Reverse pndot->pn_expr to point up, not down. */ + pndot->pn_offset = top; + JS_ASSERT(!pndot->pn_used); + pndown = pndot->pn_expr; + pndot->pn_expr = pnup; + if (pndown->pn_type != TOK_DOT) + break; + pnup = pndot; + pndot = pndown; + } + + /* pndown is a primary expression, not a dotted property reference. */ + if (!js_EmitTree(cx, cg, pndown)) + return JS_FALSE; + + do { + /* Walk back up the list, emitting annotated name ops. */ + if (js_NewSrcNote2(cx, cg, SRC_PCBASE, + CG_OFFSET(cg) - pndown->pn_offset) < 0) { + return JS_FALSE; + } + + /* + * Special case deoptimization on __proto__, __count__ and + * __parent__, as above. + */ + if (pndot->pn_arity == PN_NAME && + (pndot->pn_atom == cx->runtime->atomState.protoAtom || + pndot->pn_atom == cx->runtime->atomState.parentAtom || + pndot->pn_atom == cx->runtime->atomState.countAtom)) { + if (!EmitSpecialPropOp(cx, pndot, JSOP_GETELEM, cg)) + return JS_FALSE; + } else if (!EmitAtomOp(cx, pndot, PN_OP(pndot), cg)) { + return JS_FALSE; + } + + /* Reverse the pn_expr link again. */ + pnup = pndot->pn_expr; + pndot->pn_expr = pndown; + pndown = pndot; + } while ((pndot = pnup) != NULL); + } else { + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + } + + if (js_NewSrcNote2(cx, cg, SRC_PCBASE, + CG_OFFSET(cg) - pn2->pn_offset) < 0) { + return JS_FALSE; + } + + return EmitAtomOp(cx, pn, op, cg); +} + +static JSBool +EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) +{ + ptrdiff_t top; + JSParseNode *left, *right, *next, ltmp, rtmp; + jsint slot; + + top = CG_OFFSET(cg); + if (pn->pn_arity == PN_LIST) { + /* Left-associative operator chain to avoid too much recursion. */ + JS_ASSERT(pn->pn_op == JSOP_GETELEM); + JS_ASSERT(pn->pn_count >= 3); + left = pn->pn_head; + right = pn->last(); + next = left->pn_next; + JS_ASSERT(next != right); + + /* + * Try to optimize arguments[0][j]... into JSOP_ARGSUB<0> followed by + * one or more index expression and JSOP_GETELEM op pairs. + */ + if (left->pn_type == TOK_NAME && next->pn_type == TOK_NUMBER) { + if (!BindNameToSlot(cx, cg, left)) + return JS_FALSE; + if (left->pn_op == JSOP_ARGUMENTS && + JSDOUBLE_IS_INT(next->pn_dval, slot) && + (jsuint)slot < JS_BIT(16)) { + /* + * arguments[i]() requires arguments object as "this". + * Check that we never generates list for that usage. + */ + JS_ASSERT(op != JSOP_CALLELEM || next->pn_next); + left->pn_offset = next->pn_offset = top; + EMIT_UINT16_IMM_OP(JSOP_ARGSUB, (jsatomid)slot); + left = next; + next = left->pn_next; + } + } + + /* + * Check whether we generated JSOP_ARGSUB, just above, and have only + * one more index expression to emit. Given arguments[0][j], we must + * skip the while loop altogether, falling through to emit code for j + * (in the subtree referenced by right), followed by the annotated op, + * at the bottom of this function. + */ + JS_ASSERT(next != right || pn->pn_count == 3); + if (left == pn->pn_head) { + if (!js_EmitTree(cx, cg, left)) + return JS_FALSE; + } + while (next != right) { + if (!js_EmitTree(cx, cg, next)) + return JS_FALSE; + if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_GETELEM) < 0) + return JS_FALSE; + next = next->pn_next; + } + } else { + if (pn->pn_arity == PN_NAME) { + /* + * Set left and right so pn appears to be a TOK_LB node, instead + * of a TOK_DOT node. See the TOK_FOR/IN case in js_EmitTree, and + * EmitDestructuringOps nearer below. In the destructuring case, + * the base expression (pn_expr) of the name may be null, which + * means we have to emit a JSOP_BINDNAME. + */ + left = pn->maybeExpr(); + if (!left) { + left = <mp; + left->pn_type = TOK_STRING; + left->pn_op = JSOP_BINDNAME; + left->pn_arity = PN_NULLARY; + left->pn_pos = pn->pn_pos; + left->pn_atom = pn->pn_atom; + } + right = &rtmp; + right->pn_type = TOK_STRING; + JS_ASSERT(ATOM_IS_STRING(pn->pn_atom)); + right->pn_op = js_IsIdentifier(ATOM_TO_STRING(pn->pn_atom)) + ? JSOP_QNAMEPART + : JSOP_STRING; + right->pn_arity = PN_NULLARY; + right->pn_pos = pn->pn_pos; + right->pn_atom = pn->pn_atom; + } else { + JS_ASSERT(pn->pn_arity == PN_BINARY); + left = pn->pn_left; + right = pn->pn_right; + } + + /* Try to optimize arguments[0] (e.g.) into JSOP_ARGSUB<0>. */ + if (op == JSOP_GETELEM && + left->pn_type == TOK_NAME && + right->pn_type == TOK_NUMBER) { + if (!BindNameToSlot(cx, cg, left)) + return JS_FALSE; + if (left->pn_op == JSOP_ARGUMENTS && + JSDOUBLE_IS_INT(right->pn_dval, slot) && + (jsuint)slot < JS_BIT(16)) { + left->pn_offset = right->pn_offset = top; + EMIT_UINT16_IMM_OP(JSOP_ARGSUB, (jsatomid)slot); + return JS_TRUE; + } + } + + if (!js_EmitTree(cx, cg, left)) + return JS_FALSE; + } + + /* The right side of the descendant operator is implicitly quoted. */ + JS_ASSERT(op != JSOP_DESCENDANTS || right->pn_type != TOK_STRING || + right->pn_op == JSOP_QNAMEPART); + if (!js_EmitTree(cx, cg, right)) + return JS_FALSE; + if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0) + return JS_FALSE; + return js_Emit1(cx, cg, op) >= 0; +} + +static JSBool +EmitNumberOp(JSContext *cx, jsdouble dval, JSCodeGenerator *cg) +{ + jsint ival; + uint32 u; + ptrdiff_t off; + jsbytecode *pc; + JSAtom *atom; + JSAtomListElement *ale; + + if (JSDOUBLE_IS_INT(dval, ival) && INT_FITS_IN_JSVAL(ival)) { + if (ival == 0) + return js_Emit1(cx, cg, JSOP_ZERO) >= 0; + if (ival == 1) + return js_Emit1(cx, cg, JSOP_ONE) >= 0; + if ((jsint)(int8)ival == ival) + return js_Emit2(cx, cg, JSOP_INT8, (jsbytecode)(int8)ival) >= 0; + + u = (uint32)ival; + if (u < JS_BIT(16)) { + EMIT_UINT16_IMM_OP(JSOP_UINT16, u); + } else if (u < JS_BIT(24)) { + off = js_EmitN(cx, cg, JSOP_UINT24, 3); + if (off < 0) + return JS_FALSE; + pc = CG_CODE(cg, off); + SET_UINT24(pc, u); + } else { + off = js_EmitN(cx, cg, JSOP_INT32, 4); + if (off < 0) + return JS_FALSE; + pc = CG_CODE(cg, off); + SET_INT32(pc, ival); + } + return JS_TRUE; + } + + atom = js_AtomizeDouble(cx, dval); + if (!atom) + return JS_FALSE; + + ale = cg->atomList.add(cg->compiler, atom); + if (!ale) + return JS_FALSE; + return EmitIndexOp(cx, JSOP_DOUBLE, ALE_INDEX(ale), cg); +} + +static JSBool +EmitSwitch(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, + JSStmtInfo *stmtInfo) +{ + JSOp switchOp; + JSBool ok, hasDefault, constPropagated; + ptrdiff_t top, off, defaultOffset; + JSParseNode *pn2, *pn3, *pn4; + uint32 caseCount, tableLength; + JSParseNode **table; + jsdouble d; + jsint i, low, high; + jsval v; + JSAtom *atom; + JSAtomListElement *ale; + intN noteIndex; + size_t switchSize, tableSize; + jsbytecode *pc, *savepc; +#if JS_HAS_BLOCK_SCOPE + jsint count; +#endif + + /* Try for most optimal, fall back if not dense ints, and per ECMAv2. */ + switchOp = JSOP_TABLESWITCH; + ok = JS_TRUE; + hasDefault = constPropagated = JS_FALSE; + defaultOffset = -1; + + /* + * If the switch contains let variables scoped by its body, model the + * resulting block on the stack first, before emitting the discriminant's + * bytecode (in case the discriminant contains a stack-model dependency + * such as a let expression). + */ + pn2 = pn->pn_right; +#if JS_HAS_BLOCK_SCOPE + if (pn2->pn_type == TOK_LEXICALSCOPE) { + /* + * Push the body's block scope before discriminant code-gen for proper + * static block scope linkage in case the discriminant contains a let + * expression. The block's locals must lie under the discriminant on + * the stack so that case-dispatch bytecodes can find the discriminant + * on top of stack. + */ + count = OBJ_BLOCK_COUNT(cx, pn2->pn_objbox->object); + js_PushBlockScope(cg, stmtInfo, pn2->pn_objbox->object, -1); + stmtInfo->type = STMT_SWITCH; + + /* Emit JSOP_ENTERBLOCK before code to evaluate the discriminant. */ + if (!EmitEnterBlock(cx, pn2, cg)) + return JS_FALSE; + + /* + * Pop the switch's statement info around discriminant code-gen. Note + * how this leaves cg->blockChain referencing the switch's + * block scope object, which is necessary for correct block parenting + * in the case where the discriminant contains a let expression. + */ + cg->topStmt = stmtInfo->down; + cg->topScopeStmt = stmtInfo->downScope; + } +#ifdef __GNUC__ + else { + count = 0; + } +#endif +#endif + + /* + * Emit code for the discriminant first (or nearly first, in the case of a + * switch whose body is a block scope). + */ + if (!js_EmitTree(cx, cg, pn->pn_left)) + return JS_FALSE; + + /* Switch bytecodes run from here till end of final case. */ + top = CG_OFFSET(cg); +#if !JS_HAS_BLOCK_SCOPE + js_PushStatement(cg, stmtInfo, STMT_SWITCH, top); +#else + if (pn2->pn_type == TOK_LC) { + js_PushStatement(cg, stmtInfo, STMT_SWITCH, top); + } else { + /* Re-push the switch's statement info record. */ + cg->topStmt = cg->topScopeStmt = stmtInfo; + + /* Set the statement info record's idea of top. */ + stmtInfo->update = top; + + /* Advance pn2 to refer to the switch case list. */ + pn2 = pn2->expr(); + } +#endif + + caseCount = pn2->pn_count; + tableLength = 0; + table = NULL; + + if (caseCount == 0 || + (caseCount == 1 && + (hasDefault = (pn2->pn_head->pn_type == TOK_DEFAULT)))) { + caseCount = 0; + low = 0; + high = -1; + } else { +#define INTMAP_LENGTH 256 + jsbitmap intmap_space[INTMAP_LENGTH]; + jsbitmap *intmap = NULL; + int32 intmap_bitlen = 0; + + low = JSVAL_INT_MAX; + high = JSVAL_INT_MIN; + + for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { + if (pn3->pn_type == TOK_DEFAULT) { + hasDefault = JS_TRUE; + caseCount--; /* one of the "cases" was the default */ + continue; + } + + JS_ASSERT(pn3->pn_type == TOK_CASE); + if (switchOp == JSOP_CONDSWITCH) + continue; + + pn4 = pn3->pn_left; + while (pn4->pn_type == TOK_RP) + pn4 = pn4->pn_kid; + switch (pn4->pn_type) { + case TOK_NUMBER: + d = pn4->pn_dval; + if (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i)) { + pn3->pn_val = INT_TO_JSVAL(i); + } else { + atom = js_AtomizeDouble(cx, d); + if (!atom) { + ok = JS_FALSE; + goto release; + } + pn3->pn_val = ATOM_KEY(atom); + } + break; + case TOK_STRING: + pn3->pn_val = ATOM_KEY(pn4->pn_atom); + break; + case TOK_NAME: + if (!pn4->maybeExpr()) { + ok = LookupCompileTimeConstant(cx, cg, pn4->pn_atom, &v); + if (!ok) + goto release; + if (v != JSVAL_HOLE) { + if (!JSVAL_IS_PRIMITIVE(v)) { + /* + * XXX JSOP_LOOKUPSWITCH does not support const- + * propagated object values, see bug 407186. + */ + switchOp = JSOP_CONDSWITCH; + continue; + } + pn3->pn_val = v; + constPropagated = JS_TRUE; + break; + } + } + /* FALL THROUGH */ + case TOK_PRIMARY: + if (pn4->pn_op == JSOP_TRUE) { + pn3->pn_val = JSVAL_TRUE; + break; + } + if (pn4->pn_op == JSOP_FALSE) { + pn3->pn_val = JSVAL_FALSE; + break; + } + /* FALL THROUGH */ + default: + switchOp = JSOP_CONDSWITCH; + continue; + } + + JS_ASSERT(JSVAL_IS_PRIMITIVE(pn3->pn_val)); + + if (switchOp != JSOP_TABLESWITCH) + continue; + if (!JSVAL_IS_INT(pn3->pn_val)) { + switchOp = JSOP_LOOKUPSWITCH; + continue; + } + i = JSVAL_TO_INT(pn3->pn_val); + if ((jsuint)(i + (jsint)JS_BIT(15)) >= (jsuint)JS_BIT(16)) { + switchOp = JSOP_LOOKUPSWITCH; + continue; + } + if (i < low) + low = i; + if (high < i) + high = i; + + /* + * Check for duplicates, which require a JSOP_LOOKUPSWITCH. + * We bias i by 65536 if it's negative, and hope that's a rare + * case (because it requires a malloc'd bitmap). + */ + if (i < 0) + i += JS_BIT(16); + if (i >= intmap_bitlen) { + if (!intmap && + i < (INTMAP_LENGTH << JS_BITS_PER_WORD_LOG2)) { + intmap = intmap_space; + intmap_bitlen = INTMAP_LENGTH << JS_BITS_PER_WORD_LOG2; + } else { + /* Just grab 8K for the worst-case bitmap. */ + intmap_bitlen = JS_BIT(16); + intmap = (jsbitmap *) + cx->malloc((JS_BIT(16) >> JS_BITS_PER_WORD_LOG2) + * sizeof(jsbitmap)); + if (!intmap) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + } + memset(intmap, 0, intmap_bitlen >> JS_BITS_PER_BYTE_LOG2); + } + if (JS_TEST_BIT(intmap, i)) { + switchOp = JSOP_LOOKUPSWITCH; + continue; + } + JS_SET_BIT(intmap, i); + } + + release: + if (intmap && intmap != intmap_space) + cx->free(intmap); + if (!ok) + return JS_FALSE; + + /* + * Compute table length and select lookup instead if overlarge or + * more than half-sparse. + */ + if (switchOp == JSOP_TABLESWITCH) { + tableLength = (uint32)(high - low + 1); + if (tableLength >= JS_BIT(16) || tableLength > 2 * caseCount) + switchOp = JSOP_LOOKUPSWITCH; + } else if (switchOp == JSOP_LOOKUPSWITCH) { + /* + * Lookup switch supports only atom indexes below 64K limit. + * Conservatively estimate the maximum possible index during + * switch generation and use conditional switch if it exceeds + * the limit. + */ + if (caseCount + cg->atomList.count > JS_BIT(16)) + switchOp = JSOP_CONDSWITCH; + } + } + + /* + * Emit a note with two offsets: first tells total switch code length, + * second tells offset to first JSOP_CASE if condswitch. + */ + noteIndex = js_NewSrcNote3(cx, cg, SRC_SWITCH, 0, 0); + if (noteIndex < 0) + return JS_FALSE; + + if (switchOp == JSOP_CONDSWITCH) { + /* + * 0 bytes of immediate for unoptimized ECMAv2 switch. + */ + switchSize = 0; + } else if (switchOp == JSOP_TABLESWITCH) { + /* + * 3 offsets (len, low, high) before the table, 1 per entry. + */ + switchSize = (size_t)(JUMP_OFFSET_LEN * (3 + tableLength)); + } else { + /* + * JSOP_LOOKUPSWITCH: + * 1 offset (len) and 1 atom index (npairs) before the table, + * 1 atom index and 1 jump offset per entry. + */ + switchSize = (size_t)(JUMP_OFFSET_LEN + INDEX_LEN + + (INDEX_LEN + JUMP_OFFSET_LEN) * caseCount); + } + + /* + * Emit switchOp followed by switchSize bytes of jump or lookup table. + * + * If switchOp is JSOP_LOOKUPSWITCH or JSOP_TABLESWITCH, it is crucial + * to emit the immediate operand(s) by which bytecode readers such as + * BuildSpanDepTable discover the length of the switch opcode *before* + * calling js_SetJumpOffset (which may call BuildSpanDepTable). It's + * also important to zero all unknown jump offset immediate operands, + * so they can be converted to span dependencies with null targets to + * be computed later (js_EmitN zeros switchSize bytes after switchOp). + */ + if (js_EmitN(cx, cg, switchOp, switchSize) < 0) + return JS_FALSE; + + off = -1; + if (switchOp == JSOP_CONDSWITCH) { + intN caseNoteIndex = -1; + JSBool beforeCases = JS_TRUE; + + /* Emit code for evaluating cases and jumping to case statements. */ + for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { + pn4 = pn3->pn_left; + if (pn4 && !js_EmitTree(cx, cg, pn4)) + return JS_FALSE; + if (caseNoteIndex >= 0) { + /* off is the previous JSOP_CASE's bytecode offset. */ + if (!js_SetSrcNoteOffset(cx, cg, (uintN)caseNoteIndex, 0, + CG_OFFSET(cg) - off)) { + return JS_FALSE; + } + } + if (!pn4) { + JS_ASSERT(pn3->pn_type == TOK_DEFAULT); + continue; + } + caseNoteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0); + if (caseNoteIndex < 0) + return JS_FALSE; + off = EmitJump(cx, cg, JSOP_CASE, 0); + if (off < 0) + return JS_FALSE; + pn3->pn_offset = off; + if (beforeCases) { + uintN noteCount, noteCountDelta; + + /* Switch note's second offset is to first JSOP_CASE. */ + noteCount = CG_NOTE_COUNT(cg); + if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1, + off - top)) { + return JS_FALSE; + } + noteCountDelta = CG_NOTE_COUNT(cg) - noteCount; + if (noteCountDelta != 0) + caseNoteIndex += noteCountDelta; + beforeCases = JS_FALSE; + } + } + + /* + * If we didn't have an explicit default (which could fall in between + * cases, preventing us from fusing this js_SetSrcNoteOffset with the + * call in the loop above), link the last case to the implicit default + * for the decompiler. + */ + if (!hasDefault && + caseNoteIndex >= 0 && + !js_SetSrcNoteOffset(cx, cg, (uintN)caseNoteIndex, 0, + CG_OFFSET(cg) - off)) { + return JS_FALSE; + } + + /* Emit default even if no explicit default statement. */ + defaultOffset = EmitJump(cx, cg, JSOP_DEFAULT, 0); + if (defaultOffset < 0) + return JS_FALSE; + } else { + pc = CG_CODE(cg, top + JUMP_OFFSET_LEN); + + if (switchOp == JSOP_TABLESWITCH) { + /* Fill in switch bounds, which we know fit in 16-bit offsets. */ + SET_JUMP_OFFSET(pc, low); + pc += JUMP_OFFSET_LEN; + SET_JUMP_OFFSET(pc, high); + pc += JUMP_OFFSET_LEN; + + /* + * Use malloc to avoid arena bloat for programs with many switches. + * We free table if non-null at label out, so all control flow must + * exit this function through goto out or goto bad. + */ + if (tableLength != 0) { + tableSize = (size_t)tableLength * sizeof *table; + table = (JSParseNode **) cx->malloc(tableSize); + if (!table) + return JS_FALSE; + memset(table, 0, tableSize); + for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { + if (pn3->pn_type == TOK_DEFAULT) + continue; + i = JSVAL_TO_INT(pn3->pn_val); + i -= low; + JS_ASSERT((uint32)i < tableLength); + table[i] = pn3; + } + } + } else { + JS_ASSERT(switchOp == JSOP_LOOKUPSWITCH); + + /* Fill in the number of cases. */ + SET_INDEX(pc, caseCount); + pc += INDEX_LEN; + } + + /* + * After this point, all control flow involving JSOP_TABLESWITCH + * must set ok and goto out to exit this function. To keep things + * simple, all switchOp cases exit that way. + */ + MUST_FLOW_THROUGH("out"); + if (cg->spanDeps) { + /* + * We have already generated at least one big jump so we must + * explicitly add span dependencies for the switch jumps. When + * called below, js_SetJumpOffset can only do it when patching + * the first big jump or when cg->spanDeps is null. + */ + if (!AddSwitchSpanDeps(cx, cg, CG_CODE(cg, top))) + goto bad; + } + + if (constPropagated) { + /* + * Skip switchOp, as we are not setting jump offsets in the two + * for loops below. We'll restore CG_NEXT(cg) from savepc after, + * unless there was an error. + */ + savepc = CG_NEXT(cg); + CG_NEXT(cg) = pc + 1; + if (switchOp == JSOP_TABLESWITCH) { + for (i = 0; i < (jsint)tableLength; i++) { + pn3 = table[i]; + if (pn3 && + (pn4 = pn3->pn_left) != NULL && + pn4->pn_type == TOK_NAME) { + /* Note a propagated constant with the const's name. */ + JS_ASSERT(!pn4->maybeExpr()); + ale = cg->atomList.add(cg->compiler, pn4->pn_atom); + if (!ale) + goto bad; + CG_NEXT(cg) = pc; + if (js_NewSrcNote2(cx, cg, SRC_LABEL, (ptrdiff_t) + ALE_INDEX(ale)) < 0) { + goto bad; + } + } + pc += JUMP_OFFSET_LEN; + } + } else { + for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { + pn4 = pn3->pn_left; + if (pn4 && pn4->pn_type == TOK_NAME) { + /* Note a propagated constant with the const's name. */ + JS_ASSERT(!pn4->maybeExpr()); + ale = cg->atomList.add(cg->compiler, pn4->pn_atom); + if (!ale) + goto bad; + CG_NEXT(cg) = pc; + if (js_NewSrcNote2(cx, cg, SRC_LABEL, (ptrdiff_t) + ALE_INDEX(ale)) < 0) { + goto bad; + } + } + pc += INDEX_LEN + JUMP_OFFSET_LEN; + } + } + CG_NEXT(cg) = savepc; + } + } + + /* Emit code for each case's statements, copying pn_offset up to pn3. */ + for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { + if (switchOp == JSOP_CONDSWITCH && pn3->pn_type != TOK_DEFAULT) + CHECK_AND_SET_JUMP_OFFSET_AT_CUSTOM(cx, cg, pn3->pn_offset, goto bad); + pn4 = pn3->pn_right; + ok = js_EmitTree(cx, cg, pn4); + if (!ok) + goto out; + pn3->pn_offset = pn4->pn_offset; + if (pn3->pn_type == TOK_DEFAULT) + off = pn3->pn_offset - top; + } + + if (!hasDefault) { + /* If no default case, offset for default is to end of switch. */ + off = CG_OFFSET(cg) - top; + } + + /* We better have set "off" by now. */ + JS_ASSERT(off != -1); + + /* Set the default offset (to end of switch if no default). */ + if (switchOp == JSOP_CONDSWITCH) { + pc = NULL; + JS_ASSERT(defaultOffset != -1); + ok = js_SetJumpOffset(cx, cg, CG_CODE(cg, defaultOffset), + off - (defaultOffset - top)); + if (!ok) + goto out; + } else { + pc = CG_CODE(cg, top); + ok = js_SetJumpOffset(cx, cg, pc, off); + if (!ok) + goto out; + pc += JUMP_OFFSET_LEN; + } + + /* Set the SRC_SWITCH note's offset operand to tell end of switch. */ + off = CG_OFFSET(cg) - top; + ok = js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, off); + if (!ok) + goto out; + + if (switchOp == JSOP_TABLESWITCH) { + /* Skip over the already-initialized switch bounds. */ + pc += 2 * JUMP_OFFSET_LEN; + + /* Fill in the jump table, if there is one. */ + for (i = 0; i < (jsint)tableLength; i++) { + pn3 = table[i]; + off = pn3 ? pn3->pn_offset - top : 0; + ok = js_SetJumpOffset(cx, cg, pc, off); + if (!ok) + goto out; + pc += JUMP_OFFSET_LEN; + } + } else if (switchOp == JSOP_LOOKUPSWITCH) { + /* Skip over the already-initialized number of cases. */ + pc += INDEX_LEN; + + for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { + if (pn3->pn_type == TOK_DEFAULT) + continue; + if (!js_AtomizePrimitiveValue(cx, pn3->pn_val, &atom)) + goto bad; + ale = cg->atomList.add(cg->compiler, atom); + if (!ale) + goto bad; + SET_INDEX(pc, ALE_INDEX(ale)); + pc += INDEX_LEN; + + off = pn3->pn_offset - top; + ok = js_SetJumpOffset(cx, cg, pc, off); + if (!ok) + goto out; + pc += JUMP_OFFSET_LEN; + } + } + +out: + if (table) + cx->free(table); + if (ok) { + ok = js_PopStatementCG(cx, cg); + +#if JS_HAS_BLOCK_SCOPE + if (ok && pn->pn_right->pn_type == TOK_LEXICALSCOPE) + EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, count); +#endif + } + return ok; + +bad: + ok = JS_FALSE; + goto out; +} + +JSBool +js_EmitFunctionScript(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body) +{ + if (cg->flags & TCF_FUN_IS_GENERATOR) { + /* JSOP_GENERATOR must be the first instruction. */ + CG_SWITCH_TO_PROLOG(cg); + JS_ASSERT(CG_NEXT(cg) == CG_BASE(cg)); + if (js_Emit1(cx, cg, JSOP_GENERATOR) < 0) + return JS_FALSE; + CG_SWITCH_TO_MAIN(cg); + } else { + /* + * Emit a trace hint opcode only if not in a generator, since generators + * are not yet traced and both want to be the first instruction. + */ + if (js_Emit1(cx, cg, JSOP_TRACE) < 0) + return JS_FALSE; + } + + return js_EmitTree(cx, cg, body) && + js_Emit1(cx, cg, JSOP_STOP) >= 0 && + js_NewScriptFromCG(cx, cg); +} + +/* A macro for inlining at the top of js_EmitTree (whence it came). */ +#define UPDATE_LINE_NUMBER_NOTES(cx, cg, line) \ + JS_BEGIN_MACRO \ + uintN line_ = (line); \ + uintN delta_ = line_ - CG_CURRENT_LINE(cg); \ + if (delta_ != 0) { \ + /* \ + * Encode any change in the current source line number by using \ + * either several SRC_NEWLINE notes or just one SRC_SETLINE note, \ + * whichever consumes less space. \ + * \ + * NB: We handle backward line number deltas (possible with for \ + * loops where the update part is emitted after the body, but its \ + * line number is <= any line number in the body) here by letting \ + * unsigned delta_ wrap to a very large number, which triggers a \ + * SRC_SETLINE. \ + */ \ + CG_CURRENT_LINE(cg) = line_; \ + if (delta_ >= (uintN)(2 + ((line_ > SN_3BYTE_OFFSET_MASK)<<1))) { \ + if (js_NewSrcNote2(cx, cg, SRC_SETLINE, (ptrdiff_t)line_) < 0)\ + return JS_FALSE; \ + } else { \ + do { \ + if (js_NewSrcNote(cx, cg, SRC_NEWLINE) < 0) \ + return JS_FALSE; \ + } while (--delta_ != 0); \ + } \ + } \ + JS_END_MACRO + +/* A function, so that we avoid macro-bloating all the other callsites. */ +static JSBool +UpdateLineNumberNotes(JSContext *cx, JSCodeGenerator *cg, uintN line) +{ + UPDATE_LINE_NUMBER_NOTES(cx, cg, line); + return JS_TRUE; +} + +static JSBool +MaybeEmitVarDecl(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, + JSParseNode *pn, jsatomid *result) +{ + jsatomid atomIndex; + JSAtomListElement *ale; + + if (pn->pn_cookie != FREE_UPVAR_COOKIE) { + atomIndex = (jsatomid) UPVAR_FRAME_SLOT(pn->pn_cookie); + } else { + ale = cg->atomList.add(cg->compiler, pn->pn_atom); + if (!ale) + return JS_FALSE; + atomIndex = ALE_INDEX(ale); + } + + if (JOF_OPTYPE(pn->pn_op) == JOF_ATOM && + (!(cg->flags & TCF_IN_FUNCTION) || (cg->flags & TCF_FUN_HEAVYWEIGHT))) { + CG_SWITCH_TO_PROLOG(cg); + if (!UpdateLineNumberNotes(cx, cg, pn->pn_pos.begin.lineno)) + return JS_FALSE; + EMIT_INDEX_OP(prologOp, atomIndex); + CG_SWITCH_TO_MAIN(cg); + } + + if (result) + *result = atomIndex; + return JS_TRUE; +} + +#if JS_HAS_DESTRUCTURING + +typedef JSBool +(*DestructuringDeclEmitter)(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, + JSParseNode *pn); + +static JSBool +EmitDestructuringDecl(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, + JSParseNode *pn) +{ + JS_ASSERT(pn->pn_type == TOK_NAME); + if (!BindNameToSlot(cx, cg, pn)) + return JS_FALSE; + + JS_ASSERT(PN_OP(pn) != JSOP_ARGUMENTS && PN_OP(pn) != JSOP_CALLEE); + return MaybeEmitVarDecl(cx, cg, prologOp, pn, NULL); +} + +static JSBool +EmitDestructuringDecls(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, + JSParseNode *pn) +{ + JSParseNode *pn2, *pn3; + DestructuringDeclEmitter emitter; + + if (pn->pn_type == TOK_RB) { + for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { + if (pn2->pn_type == TOK_COMMA) + continue; + emitter = (pn2->pn_type == TOK_NAME) + ? EmitDestructuringDecl + : EmitDestructuringDecls; + if (!emitter(cx, cg, prologOp, pn2)) + return JS_FALSE; + } + } else { + JS_ASSERT(pn->pn_type == TOK_RC); + for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { + pn3 = pn2->pn_right; + emitter = (pn3->pn_type == TOK_NAME) + ? EmitDestructuringDecl + : EmitDestructuringDecls; + if (!emitter(cx, cg, prologOp, pn3)) + return JS_FALSE; + } + } + return JS_TRUE; +} + +static JSBool +EmitDestructuringOpsHelper(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn); + +static JSBool +EmitDestructuringLHS(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) +{ + jsuint slot; + + /* + * Now emit the lvalue opcode sequence. If the lvalue is a nested + * destructuring initialiser-form, call ourselves to handle it, then + * pop the matched value. Otherwise emit an lvalue bytecode sequence + * ending with a JSOP_ENUMELEM or equivalent op. + */ + if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) { + if (!EmitDestructuringOpsHelper(cx, cg, pn)) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_POP) < 0) + return JS_FALSE; + } else { + if (pn->pn_type == TOK_NAME) { + if (!BindNameToSlot(cx, cg, pn)) + return JS_FALSE; + if (pn->isConst() && !pn->isInitialized()) + return js_Emit1(cx, cg, JSOP_POP) >= 0; + } + + switch (pn->pn_op) { + case JSOP_SETNAME: + /* + * NB: pn is a PN_NAME node, not a PN_BINARY. Nevertheless, + * we want to emit JSOP_ENUMELEM, which has format JOF_ELEM. + * So here and for JSOP_ENUMCONSTELEM, we use EmitElemOp. + */ + if (!EmitElemOp(cx, pn, JSOP_ENUMELEM, cg)) + return JS_FALSE; + break; + + case JSOP_SETCONST: + if (!EmitElemOp(cx, pn, JSOP_ENUMCONSTELEM, cg)) + return JS_FALSE; + break; + + case JSOP_SETLOCAL: + slot = (jsuint) pn->pn_cookie; + EMIT_UINT16_IMM_OP(JSOP_SETLOCALPOP, slot); + break; + + case JSOP_SETARG: + case JSOP_SETGVAR: + slot = (jsuint) pn->pn_cookie; + EMIT_UINT16_IMM_OP(PN_OP(pn), slot); + if (js_Emit1(cx, cg, JSOP_POP) < 0) + return JS_FALSE; + break; + + default: + { + ptrdiff_t top; + + top = CG_OFFSET(cg); + if (!js_EmitTree(cx, cg, pn)) + return JS_FALSE; + if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_ENUMELEM) < 0) + return JS_FALSE; + break; + } + + case JSOP_ENUMELEM: + JS_ASSERT(0); + } + } + + return JS_TRUE; +} + +/* + * Recursive helper for EmitDestructuringOps. + * + * Given a value to destructure on the stack, walk over an object or array + * initialiser at pn, emitting bytecodes to match property values and store + * them in the lvalues identified by the matched property names. + */ +static JSBool +EmitDestructuringOpsHelper(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) +{ + jsuint index; + JSParseNode *pn2, *pn3; + JSBool doElemOp; + +#ifdef DEBUG + intN stackDepth = cg->stackDepth; + JS_ASSERT(stackDepth != 0); + JS_ASSERT(pn->pn_arity == PN_LIST); + JS_ASSERT(pn->pn_type == TOK_RB || pn->pn_type == TOK_RC); +#endif + + if (pn->pn_count == 0) { + /* Emit a DUP;POP sequence for the decompiler. */ + return js_Emit1(cx, cg, JSOP_DUP) >= 0 && + js_Emit1(cx, cg, JSOP_POP) >= 0; + } + + index = 0; + for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { + /* + * Duplicate the value being destructured to use as a reference base. + * If dup is not the first one, annotate it for the decompiler. + */ + if (pn2 != pn->pn_head && js_NewSrcNote(cx, cg, SRC_CONTINUE) < 0) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_DUP) < 0) + return JS_FALSE; + + /* + * Now push the property name currently being matched, which is either + * the array initialiser's current index, or the current property name + * "label" on the left of a colon in the object initialiser. Set pn3 + * to the lvalue node, which is in the value-initializing position. + */ + doElemOp = JS_TRUE; + if (pn->pn_type == TOK_RB) { + if (!EmitNumberOp(cx, index, cg)) + return JS_FALSE; + pn3 = pn2; + } else { + JS_ASSERT(pn->pn_type == TOK_RC); + JS_ASSERT(pn2->pn_type == TOK_COLON); + pn3 = pn2->pn_left; + if (pn3->pn_type == TOK_NUMBER) { + /* + * If we are emitting an object destructuring initialiser, + * annotate the index op with SRC_INITPROP so we know we are + * not decompiling an array initialiser. + */ + if (js_NewSrcNote(cx, cg, SRC_INITPROP) < 0) + return JS_FALSE; + if (!EmitNumberOp(cx, pn3->pn_dval, cg)) + return JS_FALSE; + } else { + JS_ASSERT(pn3->pn_type == TOK_STRING || + pn3->pn_type == TOK_NAME); + if (!EmitAtomOp(cx, pn3, JSOP_GETPROP, cg)) + return JS_FALSE; + doElemOp = JS_FALSE; + } + pn3 = pn2->pn_right; + } + + if (doElemOp) { + /* + * Ok, get the value of the matching property name. This leaves + * that value on top of the value being destructured, so the stack + * is one deeper than when we started. + */ + if (js_Emit1(cx, cg, JSOP_GETELEM) < 0) + return JS_FALSE; + JS_ASSERT(cg->stackDepth == stackDepth + 1); + } + + /* Nullary comma node makes a hole in the array destructurer. */ + if (pn3->pn_type == TOK_COMMA && pn3->pn_arity == PN_NULLARY) { + JS_ASSERT(pn->pn_type == TOK_RB); + JS_ASSERT(pn2 == pn3); + if (js_Emit1(cx, cg, JSOP_POP) < 0) + return JS_FALSE; + } else { + if (!EmitDestructuringLHS(cx, cg, pn3)) + return JS_FALSE; + } + + JS_ASSERT(cg->stackDepth == stackDepth); + ++index; + } + + return JS_TRUE; +} + +static ptrdiff_t +OpToDeclType(JSOp op) +{ + switch (op) { + case JSOP_NOP: + return SRC_DECL_LET; + case JSOP_DEFCONST: + return SRC_DECL_CONST; + case JSOP_DEFVAR: + return SRC_DECL_VAR; + default: + return SRC_DECL_NONE; + } +} + +static JSBool +EmitDestructuringOps(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, + JSParseNode *pn) +{ + /* + * If we're called from a variable declaration, help the decompiler by + * annotating the first JSOP_DUP that EmitDestructuringOpsHelper emits. + * If the destructuring initialiser is empty, our helper will emit a + * JSOP_DUP followed by a JSOP_POP for the decompiler. + */ + if (js_NewSrcNote2(cx, cg, SRC_DESTRUCT, OpToDeclType(prologOp)) < 0) + return JS_FALSE; + + /* + * Call our recursive helper to emit the destructuring assignments and + * related stack manipulations. + */ + return EmitDestructuringOpsHelper(cx, cg, pn); +} + +static JSBool +EmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, + JSParseNode *lhs, JSParseNode *rhs) +{ + jsuint depth, limit, i, nslots; + JSParseNode *pn; + + depth = limit = (uintN) cg->stackDepth; + for (pn = rhs->pn_head; pn; pn = pn->pn_next) { + if (limit == JS_BIT(16)) { + js_ReportCompileErrorNumber(cx, CG_TS(cg), rhs, JSREPORT_ERROR, + JSMSG_ARRAY_INIT_TOO_BIG); + return JS_FALSE; + } + + /* MaybeEmitGroupAssignment won't call us if rhs is holey. */ + JS_ASSERT(!(pn->pn_type == TOK_COMMA && pn->pn_arity == PN_NULLARY)); + if (!js_EmitTree(cx, cg, pn)) + return JS_FALSE; + ++limit; + } + + if (js_NewSrcNote2(cx, cg, SRC_GROUPASSIGN, OpToDeclType(prologOp)) < 0) + return JS_FALSE; + + i = depth; + for (pn = lhs->pn_head; pn; pn = pn->pn_next, ++i) { + /* MaybeEmitGroupAssignment requires lhs->pn_count <= rhs->pn_count. */ + JS_ASSERT(i < limit); + jsint slot = AdjustBlockSlot(cx, cg, i); + if (slot < 0) + return JS_FALSE; + EMIT_UINT16_IMM_OP(JSOP_GETLOCAL, slot); + + if (pn->pn_type == TOK_COMMA && pn->pn_arity == PN_NULLARY) { + if (js_Emit1(cx, cg, JSOP_POP) < 0) + return JS_FALSE; + } else { + if (!EmitDestructuringLHS(cx, cg, pn)) + return JS_FALSE; + } + } + + nslots = limit - depth; + EMIT_UINT16_IMM_OP(JSOP_POPN, nslots); + cg->stackDepth = (uintN) depth; + return JS_TRUE; +} + +/* + * Helper called with pop out param initialized to a JSOP_POP* opcode. If we + * can emit a group assignment sequence, which results in 0 stack depth delta, + * we set *pop to JSOP_NOP so callers can veto emitting pn followed by a pop. + */ +static JSBool +MaybeEmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, + JSParseNode *pn, JSOp *pop) +{ + JSParseNode *lhs, *rhs; + + JS_ASSERT(pn->pn_type == TOK_ASSIGN); + JS_ASSERT(*pop == JSOP_POP || *pop == JSOP_POPV); + lhs = pn->pn_left; + rhs = pn->pn_right; + if (lhs->pn_type == TOK_RB && rhs->pn_type == TOK_RB && + !(rhs->pn_xflags & PNX_HOLEY) && + lhs->pn_count <= rhs->pn_count) { + if (!EmitGroupAssignment(cx, cg, prologOp, lhs, rhs)) + return JS_FALSE; + *pop = JSOP_NOP; + } + return JS_TRUE; +} + +#endif /* JS_HAS_DESTRUCTURING */ + +static JSBool +EmitVariables(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, + JSBool inLetHead, ptrdiff_t *headNoteIndex) +{ + bool let, forInVar, first; +#if JS_HAS_BLOCK_SCOPE + bool forInLet, popScope; + JSStmtInfo *stmt, *scopeStmt; +#endif + ptrdiff_t off, noteIndex, tmp; + JSParseNode *pn2, *pn3, *next; + JSOp op; + jsatomid atomIndex; + uintN oldflags; + + /* Default in case of JS_HAS_BLOCK_SCOPE early return, below. */ + *headNoteIndex = -1; + + /* + * Let blocks and expressions have a parenthesized head in which the new + * scope is not yet open. Initializer evaluation uses the parent node's + * lexical scope. If popScope is true below, then we hide the top lexical + * block from any calls to BindNameToSlot hiding in pn2->pn_expr so that + * it won't find any names in the new let block. + * + * The same goes for let declarations in the head of any kind of for loop. + * Unlike a let declaration 'let x = i' within a block, where x is hoisted + * to the start of the block, a 'for (let x = i...) ...' loop evaluates i + * in the containing scope, and puts x in the loop body's scope. + */ + let = (pn->pn_op == JSOP_NOP); + forInVar = (pn->pn_xflags & PNX_FORINVAR) != 0; +#if JS_HAS_BLOCK_SCOPE + forInLet = let && forInVar; + popScope = (inLetHead || (let && (cg->flags & TCF_IN_FOR_INIT))); + if (popScope) { + stmt = cg->topStmt; + scopeStmt = cg->topScopeStmt; + } +# ifdef __GNUC__ + else stmt = scopeStmt = NULL; /* quell GCC overwarning */ +# endif + JS_ASSERT(!popScope || let); +#endif + + off = noteIndex = -1; + for (pn2 = pn->pn_head; ; pn2 = next) { + first = pn2 == pn->pn_head; + next = pn2->pn_next; + + if (pn2->pn_type != TOK_NAME) { +#if JS_HAS_DESTRUCTURING + if (pn2->pn_type == TOK_RB || pn2->pn_type == TOK_RC) { + /* + * Emit variable binding ops, but not destructuring ops. + * The parser (see Variables, jsparse.c) has ensured that + * our caller will be the TOK_FOR/TOK_IN case in js_EmitTree, + * and that case will emit the destructuring code only after + * emitting an enumerating opcode and a branch that tests + * whether the enumeration ended. + */ + JS_ASSERT(forInVar); + JS_ASSERT(pn->pn_count == 1); + if (!EmitDestructuringDecls(cx, cg, PN_OP(pn), pn2)) + return JS_FALSE; + break; + } +#endif + + /* + * A destructuring initialiser assignment preceded by var will + * never occur to the left of 'in' in a for-in loop. As with 'for + * (var x = i in o)...', this will cause the entire 'var [a, b] = + * i' to be hoisted out of the loop. + */ + JS_ASSERT(pn2->pn_type == TOK_ASSIGN); + JS_ASSERT(!forInVar); + + /* + * To allow the front end to rewrite var f = x; as f = x; when a + * function f(){} precedes the var, detect simple name assignment + * here and initialize the name. + */ +#if !JS_HAS_DESTRUCTURING + JS_ASSERT(pn2->pn_left->pn_type == TOK_NAME); +#else + if (pn2->pn_left->pn_type == TOK_NAME) +#endif + { + pn3 = pn2->pn_right; + pn2 = pn2->pn_left; + goto do_name; + } + +#if JS_HAS_DESTRUCTURING + if (pn->pn_count == 1) { + /* + * If this is the only destructuring assignment in the list, + * try to optimize to a group assignment. If we're in a let + * head, pass JSOP_POP rather than the pseudo-prolog JSOP_NOP + * in pn->pn_op, to suppress a second (and misplaced) 'let'. + */ + JS_ASSERT(noteIndex < 0 && !pn2->pn_next); + op = JSOP_POP; + if (!MaybeEmitGroupAssignment(cx, cg, + inLetHead ? JSOP_POP : PN_OP(pn), + pn2, &op)) { + return JS_FALSE; + } + if (op == JSOP_NOP) { + pn->pn_xflags = (pn->pn_xflags & ~PNX_POPVAR) | PNX_GROUPINIT; + break; + } + } + + pn3 = pn2->pn_left; + if (!EmitDestructuringDecls(cx, cg, PN_OP(pn), pn3)) + return JS_FALSE; + + if (!js_EmitTree(cx, cg, pn2->pn_right)) + return JS_FALSE; + + /* + * Veto pn->pn_op if inLetHead to avoid emitting a SRC_DESTRUCT + * that's redundant with respect to the SRC_DECL/SRC_DECL_LET that + * we will emit at the bottom of this function. + */ + if (!EmitDestructuringOps(cx, cg, + inLetHead ? JSOP_POP : PN_OP(pn), + pn3)) { + return JS_FALSE; + } + goto emit_note_pop; +#endif + } + + /* + * Load initializer early to share code above that jumps to do_name. + * NB: if this var redeclares an existing binding, then pn2 is linked + * on its definition's use-chain and pn_expr has been overlayed with + * pn_lexdef. + */ + pn3 = pn2->maybeExpr(); + + do_name: + if (!BindNameToSlot(cx, cg, pn2)) + return JS_FALSE; + + op = PN_OP(pn2); + if (op == JSOP_ARGUMENTS) { + /* JSOP_ARGUMENTS => no initializer */ + JS_ASSERT(!pn3 && !let); + pn3 = NULL; +#ifdef __GNUC__ + atomIndex = 0; /* quell GCC overwarning */ +#endif + } else { + JS_ASSERT(op != JSOP_CALLEE); + JS_ASSERT(pn2->pn_cookie != FREE_UPVAR_COOKIE || !let); + if (!MaybeEmitVarDecl(cx, cg, PN_OP(pn), pn2, &atomIndex)) + return JS_FALSE; + + if (pn3) { + JS_ASSERT(!forInVar); + if (op == JSOP_SETNAME) { + JS_ASSERT(!let); + EMIT_INDEX_OP(JSOP_BINDNAME, atomIndex); + } + if (pn->pn_op == JSOP_DEFCONST && + !js_DefineCompileTimeConstant(cx, cg, pn2->pn_atom, pn3)) { + return JS_FALSE; + } + +#if JS_HAS_BLOCK_SCOPE + /* Evaluate expr in the outer lexical scope if requested. */ + if (popScope) { + cg->topStmt = stmt->down; + cg->topScopeStmt = scopeStmt->downScope; + } +#endif + + oldflags = cg->flags; + cg->flags &= ~TCF_IN_FOR_INIT; + if (!js_EmitTree(cx, cg, pn3)) + return JS_FALSE; + cg->flags |= oldflags & TCF_IN_FOR_INIT; + +#if JS_HAS_BLOCK_SCOPE + if (popScope) { + cg->topStmt = stmt; + cg->topScopeStmt = scopeStmt; + } +#endif + } + } + + /* + * The parser rewrites 'for (var x = i in o)' to hoist 'var x = i' -- + * likewise 'for (let x = i in o)' becomes 'i; for (let x in o)' using + * a TOK_SEQ node to make the two statements appear as one. Therefore + * if this declaration is part of a for-in loop head, we do not need to + * emit op or any source note. Our caller, the TOK_FOR/TOK_IN case in + * js_EmitTree, will annotate appropriately. + */ + JS_ASSERT_IF(pn2->pn_defn, pn3 == pn2->pn_expr); + if (forInVar) { + JS_ASSERT(pn->pn_count == 1); + JS_ASSERT(!pn3); + break; + } + + if (first && + !inLetHead && + js_NewSrcNote2(cx, cg, SRC_DECL, + (pn->pn_op == JSOP_DEFCONST) + ? SRC_DECL_CONST + : (pn->pn_op == JSOP_DEFVAR) + ? SRC_DECL_VAR + : SRC_DECL_LET) < 0) { + return JS_FALSE; + } + if (op == JSOP_ARGUMENTS) { + if (js_Emit1(cx, cg, op) < 0) + return JS_FALSE; + } else if (pn2->pn_cookie != FREE_UPVAR_COOKIE) { + EMIT_UINT16_IMM_OP(op, atomIndex); + } else { + EMIT_INDEX_OP(op, atomIndex); + } + +#if JS_HAS_DESTRUCTURING + emit_note_pop: +#endif + tmp = CG_OFFSET(cg); + if (noteIndex >= 0) { + if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off)) + return JS_FALSE; + } + if (!next) + break; + off = tmp; + noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0); + if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_POP) < 0) + return JS_FALSE; + } + + /* If this is a let head, emit and return a srcnote on the pop. */ + if (inLetHead) { + *headNoteIndex = js_NewSrcNote(cx, cg, SRC_DECL); + if (*headNoteIndex < 0) + return JS_FALSE; + if (!(pn->pn_xflags & PNX_POPVAR)) + return js_Emit1(cx, cg, JSOP_NOP) >= 0; + } + + return !(pn->pn_xflags & PNX_POPVAR) || js_Emit1(cx, cg, JSOP_POP) >= 0; +} + +#if defined DEBUG_brendan || defined DEBUG_mrbkap +static JSBool +GettableNoteForNextOp(JSCodeGenerator *cg) +{ + ptrdiff_t offset, target; + jssrcnote *sn, *end; + + offset = 0; + target = CG_OFFSET(cg); + for (sn = CG_NOTES(cg), end = sn + CG_NOTE_COUNT(cg); sn < end; + sn = SN_NEXT(sn)) { + if (offset == target && SN_IS_GETTABLE(sn)) + return JS_TRUE; + offset += SN_DELTA(sn); + } + return JS_FALSE; +} +#endif + +/* Top-level named functions need a nop for decompilation. */ +static JSBool +EmitFunctionDefNop(JSContext *cx, JSCodeGenerator *cg, uintN index) +{ + return js_NewSrcNote2(cx, cg, SRC_FUNCDEF, (ptrdiff_t)index) >= 0 && + js_Emit1(cx, cg, JSOP_NOP) >= 0; +} + +static bool +EmitNewInit(JSContext *cx, JSCodeGenerator *cg, JSProtoKey key, JSParseNode *pn, int sharpnum) +{ + if (js_Emit2(cx, cg, JSOP_NEWINIT, (jsbytecode) key) < 0) + return false; +#if JS_HAS_SHARP_VARS + if (cg->hasSharps()) { + if (pn->pn_count != 0) + EMIT_UINT16_IMM_OP(JSOP_SHARPINIT, cg->sharpSlotBase); + if (sharpnum >= 0) + EMIT_UINT16PAIR_IMM_OP(JSOP_DEFSHARP, cg->sharpSlotBase, sharpnum); + } else { + JS_ASSERT(sharpnum < 0); + } +#endif + return true; +} + +static bool +EmitEndInit(JSContext *cx, JSCodeGenerator *cg, uint32 count) +{ +#if JS_HAS_SHARP_VARS + /* Emit an op for sharp array cleanup and decompilation. */ + if (cg->hasSharps() && count != 0) + EMIT_UINT16_IMM_OP(JSOP_SHARPINIT, cg->sharpSlotBase); +#endif + return js_Emit1(cx, cg, JSOP_ENDINIT) >= 0; +} + +/* See the SRC_FOR source note offsetBias comments later in this file. */ +JS_STATIC_ASSERT(JSOP_NOP_LENGTH == 1); +JS_STATIC_ASSERT(JSOP_POP_LENGTH == 1); + +JSBool +js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) +{ + JSBool ok, useful, wantval; + JSStmtInfo *stmt, stmtInfo; + ptrdiff_t top, off, tmp, beq, jmp; + JSParseNode *pn2, *pn3; + JSAtom *atom; + JSAtomListElement *ale; + jsatomid atomIndex; + uintN index; + ptrdiff_t noteIndex; + JSSrcNoteType noteType; + jsbytecode *pc; + JSOp op; + JSTokenType type; + uint32 argc; +#if JS_HAS_SHARP_VARS + jsint sharpnum; +#endif + + JS_CHECK_RECURSION(cx, return JS_FALSE); + + ok = JS_TRUE; + cg->emitLevel++; + pn->pn_offset = top = CG_OFFSET(cg); + + /* Emit notes to tell the current bytecode's source line number. */ + UPDATE_LINE_NUMBER_NOTES(cx, cg, pn->pn_pos.begin.lineno); + + switch (pn->pn_type) { + case TOK_FUNCTION: + { + JSFunction *fun; + uintN slot; + +#if JS_HAS_XML_SUPPORT + if (pn->pn_arity == PN_NULLARY) { + if (js_Emit1(cx, cg, JSOP_GETFUNNS) < 0) + return JS_FALSE; + break; + } +#endif + + fun = (JSFunction *) pn->pn_funbox->object; + JS_ASSERT(FUN_INTERPRETED(fun)); + if (fun->u.i.script) { + /* + * This second pass is needed to emit JSOP_NOP with a source note + * for the already-emitted function definition prolog opcode. See + * comments in the TOK_LC case. + */ + JS_ASSERT(pn->pn_op == JSOP_NOP); + JS_ASSERT(cg->flags & TCF_IN_FUNCTION); + if (!EmitFunctionDefNop(cx, cg, pn->pn_index)) + return JS_FALSE; + break; + } + + JS_ASSERT_IF(cx->options & JSOPTION_ANONFUNFIX, + pn->pn_defn || + (!pn->pn_used && !pn->isTopLevel()) || + (fun->flags & JSFUN_LAMBDA)); + + JS_ASSERT_IF(pn->pn_funbox->tcflags & TCF_FUN_HEAVYWEIGHT, + FUN_KIND(fun) == JSFUN_INTERPRETED); + + /* Generate code for the function's body. */ + void *cg2mark = JS_ARENA_MARK(cg->codePool); + void *cg2space; + JS_ARENA_ALLOCATE_TYPE(cg2space, JSCodeGenerator, cg->codePool); + if (!cg2space) { + js_ReportOutOfScriptQuota(cx); + return JS_FALSE; + } + JSCodeGenerator *cg2 = + new (cg2space) JSCodeGenerator(cg->compiler, + cg->codePool, cg->notePool, + pn->pn_pos.begin.lineno); + cg2->flags = pn->pn_funbox->tcflags | TCF_IN_FUNCTION; +#if JS_HAS_SHARP_VARS + if (cg2->flags & TCF_HAS_SHARPS) { + cg2->sharpSlotBase = fun->sharpSlotBase(cx); + if (cg2->sharpSlotBase < 0) + return JS_FALSE; + } +#endif + cg2->fun = fun; + cg2->funbox = pn->pn_funbox; + cg2->parent = cg; + + /* + * jsparse.cpp:SetStaticLevel limited static nesting depth to fit in 16 + * bits and to reserve the all-ones value, thereby reserving the magic + * FREE_UPVAR_COOKIE value. Note the cg2->staticLevel assignment below. + */ + JS_ASSERT(cg->staticLevel < JS_BITMASK(16) - 1); + cg2->staticLevel = cg->staticLevel + 1; + + /* We measured the max scope depth when we parsed the function. */ + JS_SCOPE_DEPTH_METERING(cg2->maxScopeDepth = (uintN) -1); + if (!js_EmitFunctionScript(cx, cg2, pn->pn_body)) + pn = NULL; + + cg2->~JSCodeGenerator(); + JS_ARENA_RELEASE(cg->codePool, cg2mark); + cg2 = NULL; + if (!pn) + return JS_FALSE; + + /* Make the function object a literal in the outer script's pool. */ + index = cg->objectList.index(pn->pn_funbox); + + /* Emit a bytecode pointing to the closure object in its immediate. */ + op = PN_OP(pn); + if (op != JSOP_NOP) { + if ((pn->pn_funbox->tcflags & TCF_GENEXP_LAMBDA) && + js_NewSrcNote(cx, cg, SRC_GENEXP) < 0) { + return JS_FALSE; + } + EMIT_INDEX_OP(op, index); + break; + } + + /* + * For a script we emit the code as we parse. Thus the bytecode for + * top-level functions should go in the prolog to predefine their + * names in the variable object before the already-generated main code + * is executed. This extra work for top-level scripts is not necessary + * when we emit the code for a function. It is fully parsed prior to + * invocation of the emitter and calls to js_EmitTree for function + * definitions can be scheduled before generating the rest of code. + */ + if (!(cg->flags & TCF_IN_FUNCTION)) { + JS_ASSERT(!cg->topStmt); + CG_SWITCH_TO_PROLOG(cg); + op = FUN_FLAT_CLOSURE(fun) ? JSOP_DEFFUN_FC : JSOP_DEFFUN; + EMIT_INDEX_OP(op, index); + CG_SWITCH_TO_MAIN(cg); + + /* Emit NOP for the decompiler. */ + if (!EmitFunctionDefNop(cx, cg, index)) + return JS_FALSE; + } else { +#ifdef DEBUG + JSLocalKind localKind = +#endif + js_LookupLocal(cx, cg->fun, fun->atom, &slot); + JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST); + JS_ASSERT(index < JS_BIT(20)); + pn->pn_index = index; + op = FUN_FLAT_CLOSURE(fun) ? JSOP_DEFLOCALFUN_FC : JSOP_DEFLOCALFUN; + if (!EmitSlotIndexOp(cx, op, slot, index, cg)) + return JS_FALSE; + } + break; + } + + case TOK_ARGSBODY: + ok = js_EmitTree(cx, cg, pn->last()); + break; + + case TOK_UPVARS: + JS_ASSERT(cg->lexdeps.count == 0); + JS_ASSERT(pn->pn_names.count != 0); + cg->lexdeps = pn->pn_names; + ok = js_EmitTree(cx, cg, pn->pn_tree); + break; + + case TOK_IF: + /* Initialize so we can detect else-if chains and avoid recursion. */ + stmtInfo.type = STMT_IF; + beq = jmp = -1; + noteIndex = -1; + + if_again: + /* Emit code for the condition before pushing stmtInfo. */ + if (!js_EmitTree(cx, cg, pn->pn_kid1)) + return JS_FALSE; + top = CG_OFFSET(cg); + if (stmtInfo.type == STMT_IF) { + js_PushStatement(cg, &stmtInfo, STMT_IF, top); + } else { + /* + * We came here from the goto further below that detects else-if + * chains, so we must mutate stmtInfo back into a STMT_IF record. + * Also (see below for why) we need a note offset for SRC_IF_ELSE + * to help the decompiler. Actually, we need two offsets, one for + * decompiling any else clause and the second for decompiling an + * else-if chain without bracing, overindenting, or incorrectly + * scoping let declarations. + */ + JS_ASSERT(stmtInfo.type == STMT_ELSE); + stmtInfo.type = STMT_IF; + stmtInfo.update = top; + if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq)) + return JS_FALSE; + if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 1, top - jmp)) + return JS_FALSE; + } + + /* Emit an annotated branch-if-false around the then part. */ + pn3 = pn->pn_kid3; + noteIndex = js_NewSrcNote(cx, cg, pn3 ? SRC_IF_ELSE : SRC_IF); + if (noteIndex < 0) + return JS_FALSE; + beq = EmitJump(cx, cg, JSOP_IFEQ, 0); + if (beq < 0) + return JS_FALSE; + + /* Emit code for the then and optional else parts. */ + if (!js_EmitTree(cx, cg, pn->pn_kid2)) + return JS_FALSE; + if (pn3) { + /* Modify stmtInfo so we know we're in the else part. */ + stmtInfo.type = STMT_ELSE; + + /* + * Emit a JSOP_BACKPATCH op to jump from the end of our then part + * around the else part. The js_PopStatementCG call at the bottom + * of this switch case will fix up the backpatch chain linked from + * stmtInfo.breaks. + */ + jmp = EmitGoto(cx, cg, &stmtInfo, &stmtInfo.breaks, NULL, SRC_NULL); + if (jmp < 0) + return JS_FALSE; + + /* Ensure the branch-if-false comes here, then emit the else. */ + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); + if (pn3->pn_type == TOK_IF) { + pn = pn3; + goto if_again; + } + + if (!js_EmitTree(cx, cg, pn3)) + return JS_FALSE; + + /* + * Annotate SRC_IF_ELSE with the offset from branch to jump, for + * the decompiler's benefit. We can't just "back up" from the pc + * of the else clause, because we don't know whether an extended + * jump was required to leap from the end of the then clause over + * the else clause. + */ + if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq)) + return JS_FALSE; + } else { + /* No else part, fixup the branch-if-false to come here. */ + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); + } + ok = js_PopStatementCG(cx, cg); + break; + + case TOK_SWITCH: + /* Out of line to avoid bloating js_EmitTree's stack frame size. */ + ok = EmitSwitch(cx, cg, pn, &stmtInfo); + break; + + case TOK_WHILE: + /* + * Minimize bytecodes issued for one or more iterations by jumping to + * the condition below the body and closing the loop if the condition + * is true with a backward branch. For iteration count i: + * + * i test at the top test at the bottom + * = =============== ================== + * 0 ifeq-pass goto; ifne-fail + * 1 ifeq-fail; goto; ifne-pass goto; ifne-pass; ifne-fail + * 2 2*(ifeq-fail; goto); ifeq-pass goto; 2*ifne-pass; ifne-fail + * . . . + * N N*(ifeq-fail; goto); ifeq-pass goto; N*ifne-pass; ifne-fail + * + * SpiderMonkey, pre-mozilla.org, emitted while parsing and so used + * test at the top. When JSParseNode trees were added during the ES3 + * work (1998-9), the code generation scheme was not optimized, and + * the decompiler continued to take advantage of the branch and jump + * that bracketed the body. But given the SRC_WHILE note, it is easy + * to support the more efficient scheme. + */ + js_PushStatement(cg, &stmtInfo, STMT_WHILE_LOOP, top); + noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE); + if (noteIndex < 0) + return JS_FALSE; + jmp = EmitJump(cx, cg, JSOP_GOTO, 0); + if (jmp < 0) + return JS_FALSE; + top = js_Emit1(cx, cg, JSOP_TRACE); + if (top < 0) + return JS_FALSE; + if (!js_EmitTree(cx, cg, pn->pn_right)) + return JS_FALSE; + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); + if (!js_EmitTree(cx, cg, pn->pn_left)) + return JS_FALSE; + beq = EmitJump(cx, cg, JSOP_IFNE, top - CG_OFFSET(cg)); + if (beq < 0) + return JS_FALSE; + if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, beq - jmp)) + return JS_FALSE; + ok = js_PopStatementCG(cx, cg); + break; + + case TOK_DO: + /* Emit an annotated nop so we know to decompile a 'do' keyword. */ + noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE); + if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_NOP) < 0) + return JS_FALSE; + + /* Compile the loop body. */ + top = js_Emit1(cx, cg, JSOP_TRACE); + if (top < 0) + return JS_FALSE; + js_PushStatement(cg, &stmtInfo, STMT_DO_LOOP, top); + if (!js_EmitTree(cx, cg, pn->pn_left)) + return JS_FALSE; + + /* Set loop and enclosing label update offsets, for continue. */ + stmt = &stmtInfo; + do { + stmt->update = CG_OFFSET(cg); + } while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL); + + /* Compile the loop condition, now that continues know where to go. */ + if (!js_EmitTree(cx, cg, pn->pn_right)) + return JS_FALSE; + + /* + * Since we use JSOP_IFNE for other purposes as well as for do-while + * loops, we must store 1 + (beq - top) in the SRC_WHILE note offset, + * and the decompiler must get that delta and decompile recursively. + */ + beq = EmitJump(cx, cg, JSOP_IFNE, top - CG_OFFSET(cg)); + if (beq < 0) + return JS_FALSE; + if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, 1 + (beq - top))) + return JS_FALSE; + ok = js_PopStatementCG(cx, cg); + break; + + case TOK_FOR: + beq = 0; /* suppress gcc warnings */ + jmp = -1; + pn2 = pn->pn_left; + js_PushStatement(cg, &stmtInfo, STMT_FOR_LOOP, top); + + if (pn2->pn_type == TOK_IN) { + /* Set stmtInfo type for later testing. */ + stmtInfo.type = STMT_FOR_IN_LOOP; + + /* + * If the left part is 'var x', emit code to define x if necessary + * using a prolog opcode, but do not emit a pop. If the left part + * is 'var x = i', emit prolog code to define x if necessary; then + * emit code to evaluate i, assign the result to x, and pop the + * result off the stack. + * + * All the logic to do this is implemented in the outer switch's + * TOK_VAR case, conditioned on pn_xflags flags set by the parser. + * + * In the 'for (var x = i in o) ...' case, the js_EmitTree(...pn3) + * called here will generate the proper note for the assignment + * op that sets x = i, hoisting the initialized var declaration + * out of the loop: 'var x = i; for (x in o) ...'. + * + * In the 'for (var x in o) ...' case, nothing but the prolog op + * (if needed) should be generated here, we must emit the note + * just before the JSOP_FOR* opcode in the switch on pn3->pn_type + * a bit below, so nothing is hoisted: 'for (var x in o) ...'. + * + * A 'for (let x = i in o)' loop must not be hoisted, since in + * this form the let variable is scoped by the loop body (but not + * the head). The initializer expression i must be evaluated for + * any side effects. So we hoist only i in the let case. + */ + pn3 = pn2->pn_left; + type = PN_TYPE(pn3); + cg->flags |= TCF_IN_FOR_INIT; + if (TOKEN_TYPE_IS_DECL(type) && !js_EmitTree(cx, cg, pn3)) + return JS_FALSE; + cg->flags &= ~TCF_IN_FOR_INIT; + + /* Compile the object expression to the right of 'in'. */ + if (!js_EmitTree(cx, cg, pn2->pn_right)) + return JS_FALSE; + + /* + * Emit a bytecode to convert top of stack value to the iterator + * object depending on the loop variant (for-in, for-each-in, or + * destructuring for-in). + */ + JS_ASSERT(pn->pn_op == JSOP_ITER); + if (js_Emit2(cx, cg, JSOP_ITER, (uint8) pn->pn_iflags) < 0) + return JS_FALSE; + + /* Annotate so the decompiler can find the loop-closing jump. */ + noteIndex = js_NewSrcNote(cx, cg, SRC_FOR_IN); + if (noteIndex < 0) + return JS_FALSE; + + /* + * Jump down to the loop condition to minimize overhead assuming at + * least one iteration, as the other loop forms do. + */ + jmp = EmitJump(cx, cg, JSOP_GOTO, 0); + if (jmp < 0) + return JS_FALSE; + + top = CG_OFFSET(cg); + SET_STATEMENT_TOP(&stmtInfo, top); + if (js_Emit1(cx, cg, JSOP_TRACE) < 0) + return JS_FALSE; + +#ifdef DEBUG + intN loopDepth = cg->stackDepth; +#endif + + /* + * Compile a JSOP_FOR* bytecode based on the left hand side. + * + * Initialize op to JSOP_SETNAME in case of |for ([a, b] in o)...| + * or similar, to signify assignment, rather than declaration, to + * the decompiler. EmitDestructuringOps takes a prolog bytecode + * parameter and emits the appropriate source note, defaulting to + * assignment, so JSOP_SETNAME is not critical here; many similar + * ops could be used -- just not JSOP_NOP (which means 'let'). + */ + op = JSOP_SETNAME; + switch (type) { +#if JS_HAS_BLOCK_SCOPE + case TOK_LET: +#endif + case TOK_VAR: + JS_ASSERT(pn3->pn_arity == PN_LIST && pn3->pn_count == 1); + pn3 = pn3->pn_head; +#if JS_HAS_DESTRUCTURING + if (pn3->pn_type == TOK_ASSIGN) { + pn3 = pn3->pn_left; + JS_ASSERT(pn3->pn_type == TOK_RB || pn3->pn_type == TOK_RC); + } + if (pn3->pn_type == TOK_RB || pn3->pn_type == TOK_RC) { + op = PN_OP(pn2->pn_left); + goto destructuring_for; + } +#else + JS_ASSERT(pn3->pn_type == TOK_NAME); +#endif + /* FALL THROUGH */ + + case TOK_NAME: + /* + * Always annotate JSOP_FORLOCAL if given input of the form + * 'for (let x in * o)' -- the decompiler must not hoist the + * 'let x' out of the loop head, or x will be bound in the + * wrong scope. Likewise, but in this case only for the sake + * of higher decompilation fidelity only, do not hoist 'var x' + * when given 'for (var x in o)'. + */ + if (( +#if JS_HAS_BLOCK_SCOPE + type == TOK_LET || +#endif + (type == TOK_VAR && !pn3->maybeExpr())) && + js_NewSrcNote2(cx, cg, SRC_DECL, + (type == TOK_VAR) + ? SRC_DECL_VAR + : SRC_DECL_LET) < 0) { + return JS_FALSE; + } + if (pn3->pn_cookie != FREE_UPVAR_COOKIE) { + op = PN_OP(pn3); + switch (op) { + case JSOP_GETARG: /* FALL THROUGH */ + case JSOP_SETARG: op = JSOP_FORARG; break; + case JSOP_GETGVAR: /* FALL THROUGH */ + case JSOP_SETGVAR: op = JSOP_FORNAME; break; + case JSOP_GETLOCAL: /* FALL THROUGH */ + case JSOP_SETLOCAL: op = JSOP_FORLOCAL; break; + default: JS_ASSERT(0); + } + } else { + pn3->pn_op = JSOP_FORNAME; + if (!BindNameToSlot(cx, cg, pn3)) + return JS_FALSE; + op = PN_OP(pn3); + } + if (pn3->isConst()) { + js_ReportCompileErrorNumber(cx, CG_TS(cg), pn3, JSREPORT_ERROR, + JSMSG_BAD_FOR_LEFTSIDE); + return JS_FALSE; + } + if (pn3->pn_cookie != FREE_UPVAR_COOKIE) { + atomIndex = (jsatomid) pn3->pn_cookie; + EMIT_UINT16_IMM_OP(op, atomIndex); + } else { + if (!EmitAtomOp(cx, pn3, op, cg)) + return JS_FALSE; + } + break; + + case TOK_DOT: + /* + * 'for (o.p in q)' can use JSOP_FORPROP only if evaluating 'o' + * has no side effects. + */ + useful = JS_FALSE; + if (!CheckSideEffects(cx, cg, pn3->expr(), &useful)) + return JS_FALSE; + if (!useful) { + if (!EmitPropOp(cx, pn3, JSOP_FORPROP, cg, JS_FALSE)) + return JS_FALSE; + break; + } + /* FALL THROUGH */ + +#if JS_HAS_DESTRUCTURING + destructuring_for: +#endif + default: + if (js_Emit1(cx, cg, JSOP_FORELEM) < 0) + return JS_FALSE; + JS_ASSERT(cg->stackDepth >= 3); + +#if JS_HAS_DESTRUCTURING + if (pn3->pn_type == TOK_RB || pn3->pn_type == TOK_RC) { + if (!EmitDestructuringOps(cx, cg, op, pn3)) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_POP) < 0) + return JS_FALSE; + } else +#endif + if (pn3->pn_type == TOK_LP) { + JS_ASSERT(pn3->pn_op == JSOP_SETCALL); + if (!js_EmitTree(cx, cg, pn3)) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_ENUMELEM) < 0) + return JS_FALSE; + } else +#if JS_HAS_XML_SUPPORT + if (pn3->pn_type == TOK_UNARYOP) { + JS_ASSERT(pn3->pn_op == JSOP_BINDXMLNAME); + if (!js_EmitTree(cx, cg, pn3)) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_ENUMELEM) < 0) + return JS_FALSE; + } else +#endif + if (!EmitElemOp(cx, pn3, JSOP_ENUMELEM, cg)) + return JS_FALSE; + break; + } + + /* The stack should be balanced around the JSOP_FOR* opcode sequence. */ + JS_ASSERT(cg->stackDepth == loopDepth); + + /* Set the first srcnote offset so we can find the start of the loop body. */ + if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, CG_OFFSET(cg) - jmp)) + return JS_FALSE; + + /* Emit code for the loop body. */ + if (!js_EmitTree(cx, cg, pn->pn_right)) + return JS_FALSE; + + /* Set loop and enclosing "update" offsets, for continue. */ + stmt = &stmtInfo; + do { + stmt->update = CG_OFFSET(cg); + } while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL); + + /* + * Fixup the goto that starts the loop to jump down to JSOP_NEXTITER. + */ + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); + if (js_Emit1(cx, cg, JSOP_NEXTITER) < 0) + return JS_FALSE; + beq = EmitJump(cx, cg, JSOP_IFNE, top - CG_OFFSET(cg)); + if (beq < 0) + return JS_FALSE; + + /* Set the second srcnote offset so we can find the closing jump. */ + if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1, beq - jmp)) + return JS_FALSE; + } else { + /* C-style for (init; cond; update) ... loop. */ + op = JSOP_POP; + pn3 = pn2->pn_kid1; + if (!pn3) { + /* No initializer: emit an annotated nop for the decompiler. */ + op = JSOP_NOP; + } else { + cg->flags |= TCF_IN_FOR_INIT; +#if JS_HAS_DESTRUCTURING + if (pn3->pn_type == TOK_ASSIGN && + !MaybeEmitGroupAssignment(cx, cg, op, pn3, &op)) { + return JS_FALSE; + } +#endif + if (op == JSOP_POP) { + if (!js_EmitTree(cx, cg, pn3)) + return JS_FALSE; + if (TOKEN_TYPE_IS_DECL(pn3->pn_type)) { + /* + * Check whether a destructuring-initialized var decl + * was optimized to a group assignment. If so, we do + * not need to emit a pop below, so switch to a nop, + * just for the decompiler. + */ + JS_ASSERT(pn3->pn_arity == PN_LIST); + if (pn3->pn_xflags & PNX_GROUPINIT) + op = JSOP_NOP; + } + } + cg->flags &= ~TCF_IN_FOR_INIT; + } + + /* + * NB: the SRC_FOR note has offsetBias 1 (JSOP_{NOP,POP}_LENGTH). + * Use tmp to hold the biased srcnote "top" offset, which differs + * from the top local variable by the length of the JSOP_GOTO{,X} + * emitted in between tmp and top if this loop has a condition. + */ + noteIndex = js_NewSrcNote(cx, cg, SRC_FOR); + if (noteIndex < 0 || js_Emit1(cx, cg, op) < 0) + return JS_FALSE; + tmp = CG_OFFSET(cg); + + if (pn2->pn_kid2) { + /* Goto the loop condition, which branches back to iterate. */ + jmp = EmitJump(cx, cg, JSOP_GOTO, 0); + if (jmp < 0) + return JS_FALSE; + } + + top = CG_OFFSET(cg); + SET_STATEMENT_TOP(&stmtInfo, top); + + /* Emit code for the loop body. */ + if (js_Emit1(cx, cg, JSOP_TRACE) < 0) + return JS_FALSE; + if (!js_EmitTree(cx, cg, pn->pn_right)) + return JS_FALSE; + + /* Set the second note offset so we can find the update part. */ + JS_ASSERT(noteIndex != -1); + if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1, + CG_OFFSET(cg) - tmp)) { + return JS_FALSE; + } + + /* Set loop and enclosing "update" offsets, for continue. */ + stmt = &stmtInfo; + do { + stmt->update = CG_OFFSET(cg); + } while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL); + + /* Check for update code to do before the condition (if any). */ + pn3 = pn2->pn_kid3; + if (pn3) { + op = JSOP_POP; +#if JS_HAS_DESTRUCTURING + if (pn3->pn_type == TOK_ASSIGN && + !MaybeEmitGroupAssignment(cx, cg, op, pn3, &op)) { + return JS_FALSE; + } +#endif + if (op == JSOP_POP && !js_EmitTree(cx, cg, pn3)) + return JS_FALSE; + + /* Always emit the POP or NOP, to help the decompiler. */ + if (js_Emit1(cx, cg, op) < 0) + return JS_FALSE; + + /* Restore the absolute line number for source note readers. */ + off = (ptrdiff_t) pn->pn_pos.end.lineno; + if (CG_CURRENT_LINE(cg) != (uintN) off) { + if (js_NewSrcNote2(cx, cg, SRC_SETLINE, off) < 0) + return JS_FALSE; + CG_CURRENT_LINE(cg) = (uintN) off; + } + } + + /* Set the first note offset so we can find the loop condition. */ + if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, + CG_OFFSET(cg) - tmp)) { + return JS_FALSE; + } + + if (pn2->pn_kid2) { + /* Fix up the goto from top to target the loop condition. */ + JS_ASSERT(jmp >= 0); + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); + + if (!js_EmitTree(cx, cg, pn2->pn_kid2)) + return JS_FALSE; + } + + /* The third note offset helps us find the loop-closing jump. */ + if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 2, + CG_OFFSET(cg) - tmp)) { + return JS_FALSE; + } + + if (pn2->pn_kid2) { + beq = EmitJump(cx, cg, JSOP_IFNE, top - CG_OFFSET(cg)); + if (beq < 0) + return JS_FALSE; + } else { + /* No loop condition -- emit the loop-closing jump. */ + jmp = EmitJump(cx, cg, JSOP_GOTO, top - CG_OFFSET(cg)); + if (jmp < 0) + return JS_FALSE; + } + } + + /* Now fixup all breaks and continues (before for/in's JSOP_ENDITER). */ + if (!js_PopStatementCG(cx, cg)) + return JS_FALSE; + + if (pn2->pn_type == TOK_IN) { + /* + * JSOP_ENDITER must have a slot to save an exception thrown from + * the body of for-in loop when closing the iterator object, and + * fortunately it does: the slot that was set by JSOP_NEXTITER to + * the return value of iterator.next(). + */ + JS_ASSERT(js_CodeSpec[JSOP_ENDITER].nuses == 2); + if (!NewTryNote(cx, cg, JSTRY_ITER, cg->stackDepth, top, CG_OFFSET(cg)) || + js_Emit1(cx, cg, JSOP_ENDITER) < 0) { + return JS_FALSE; + } + } + break; + + case TOK_BREAK: + stmt = cg->topStmt; + atom = pn->pn_atom; + if (atom) { + ale = cg->atomList.add(cg->compiler, atom); + if (!ale) + return JS_FALSE; + while (stmt->type != STMT_LABEL || stmt->label != atom) + stmt = stmt->down; + noteType = SRC_BREAK2LABEL; + } else { + ale = NULL; + while (!STMT_IS_LOOP(stmt) && stmt->type != STMT_SWITCH) + stmt = stmt->down; + noteType = (stmt->type == STMT_SWITCH) ? SRC_NULL : SRC_BREAK; + } + + if (EmitGoto(cx, cg, stmt, &stmt->breaks, ale, noteType) < 0) + return JS_FALSE; + break; + + case TOK_CONTINUE: + stmt = cg->topStmt; + atom = pn->pn_atom; + if (atom) { + /* Find the loop statement enclosed by the matching label. */ + JSStmtInfo *loop = NULL; + ale = cg->atomList.add(cg->compiler, atom); + if (!ale) + return JS_FALSE; + while (stmt->type != STMT_LABEL || stmt->label != atom) { + if (STMT_IS_LOOP(stmt)) + loop = stmt; + stmt = stmt->down; + } + stmt = loop; + noteType = SRC_CONT2LABEL; + } else { + ale = NULL; + while (!STMT_IS_LOOP(stmt)) + stmt = stmt->down; + noteType = SRC_CONTINUE; + } + + if (EmitGoto(cx, cg, stmt, &stmt->continues, ale, noteType) < 0) + return JS_FALSE; + break; + + case TOK_WITH: + if (!js_EmitTree(cx, cg, pn->pn_left)) + return JS_FALSE; + js_PushStatement(cg, &stmtInfo, STMT_WITH, CG_OFFSET(cg)); + if (js_Emit1(cx, cg, JSOP_ENTERWITH) < 0) + return JS_FALSE; + if (!js_EmitTree(cx, cg, pn->pn_right)) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) + return JS_FALSE; + ok = js_PopStatementCG(cx, cg); + break; + + case TOK_TRY: + { + ptrdiff_t tryStart, tryEnd, catchJump, finallyStart; + intN depth; + JSParseNode *lastCatch; + + catchJump = -1; + + /* + * Push stmtInfo to track jumps-over-catches and gosubs-to-finally + * for later fixup. + * + * When a finally block is active (STMT_FINALLY in our tree context), + * non-local jumps (including jumps-over-catches) result in a GOSUB + * being written into the bytecode stream and fixed-up later (c.f. + * EmitBackPatchOp and BackPatch). + */ + js_PushStatement(cg, &stmtInfo, + pn->pn_kid3 ? STMT_FINALLY : STMT_TRY, + CG_OFFSET(cg)); + + /* + * Since an exception can be thrown at any place inside the try block, + * we need to restore the stack and the scope chain before we transfer + * the control to the exception handler. + * + * For that we store in a try note associated with the catch or + * finally block the stack depth upon the try entry. The interpreter + * uses this depth to properly unwind the stack and the scope chain. + */ + depth = cg->stackDepth; + + /* Mark try location for decompilation, then emit try block. */ + if (js_Emit1(cx, cg, JSOP_TRY) < 0) + return JS_FALSE; + tryStart = CG_OFFSET(cg); + if (!js_EmitTree(cx, cg, pn->pn_kid1)) + return JS_FALSE; + JS_ASSERT(depth == cg->stackDepth); + + /* GOSUB to finally, if present. */ + if (pn->pn_kid3) { + if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) + return JS_FALSE; + jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &GOSUBS(stmtInfo)); + if (jmp < 0) + return JS_FALSE; + } + + /* Emit (hidden) jump over catch and/or finally. */ + if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) + return JS_FALSE; + jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &catchJump); + if (jmp < 0) + return JS_FALSE; + + tryEnd = CG_OFFSET(cg); + + /* If this try has a catch block, emit it. */ + pn2 = pn->pn_kid2; + lastCatch = NULL; + if (pn2) { + jsint count = 0; /* previous catch block's population */ + + /* + * The emitted code for a catch block looks like: + * + * [throwing] only if 2nd+ catch block + * [leaveblock] only if 2nd+ catch block + * enterblock with SRC_CATCH + * exception + * [dup] only if catchguard + * setlocalpop or destructuring code + * [< catchguard code >] if there's a catchguard + * [ifeq ] " " + * [pop] only if catchguard + * < catch block contents > + * leaveblock + * goto non-local; finally applies + * + * If there's no catch block without a catchguard, the last + * points to rethrow code. This + * code will [gosub] to the finally code if appropriate, and is + * also used for the catch-all trynote for capturing exceptions + * thrown from catch{} blocks. + */ + for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { + ptrdiff_t guardJump, catchNote; + + JS_ASSERT(cg->stackDepth == depth); + guardJump = GUARDJUMP(stmtInfo); + if (guardJump != -1) { + /* Fix up and clean up previous catch block. */ + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, guardJump); + + /* + * Account for JSOP_ENTERBLOCK (whose block object count + * is saved below) and pushed exception object that we + * still have after the jumping from the previous guard. + */ + cg->stackDepth = depth + count + 1; + + /* + * Move exception back to cx->exception to prepare for + * the next catch. We hide [throwing] from the decompiler + * since it compensates for the hidden JSOP_DUP at the + * start of the previous guarded catch. + */ + if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || + js_Emit1(cx, cg, JSOP_THROWING) < 0) { + return JS_FALSE; + } + if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) + return JS_FALSE; + EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, count); + JS_ASSERT(cg->stackDepth == depth); + } + + /* + * Annotate the JSOP_ENTERBLOCK that's about to be generated + * by the call to js_EmitTree immediately below. Save this + * source note's index in stmtInfo for use by the TOK_CATCH: + * case, where the length of the catch guard is set as the + * note's offset. + */ + catchNote = js_NewSrcNote2(cx, cg, SRC_CATCH, 0); + if (catchNote < 0) + return JS_FALSE; + CATCHNOTE(stmtInfo) = catchNote; + + /* + * Emit the lexical scope and catch body. Save the catch's + * block object population via count, for use when targeting + * guardJump at the next catch (the guard mismatch case). + */ + JS_ASSERT(pn3->pn_type == TOK_LEXICALSCOPE); + count = OBJ_BLOCK_COUNT(cx, pn3->pn_objbox->object); + if (!js_EmitTree(cx, cg, pn3)) + return JS_FALSE; + + /* gosub , if required */ + if (pn->pn_kid3) { + jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, + &GOSUBS(stmtInfo)); + if (jmp < 0) + return JS_FALSE; + JS_ASSERT(cg->stackDepth == depth); + } + + /* + * Jump over the remaining catch blocks. This will get fixed + * up to jump to after catch/finally. + */ + if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) + return JS_FALSE; + jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &catchJump); + if (jmp < 0) + return JS_FALSE; + + /* + * Save a pointer to the last catch node to handle try-finally + * and try-catch(guard)-finally special cases. + */ + lastCatch = pn3->expr(); + } + } + + /* + * Last catch guard jumps to the rethrow code sequence if none of the + * guards match. Target guardJump at the beginning of the rethrow + * sequence, just in case a guard expression throws and leaves the + * stack unbalanced. + */ + if (lastCatch && lastCatch->pn_kid2) { + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, GUARDJUMP(stmtInfo)); + + /* Sync the stack to take into account pushed exception. */ + JS_ASSERT(cg->stackDepth == depth); + cg->stackDepth = depth + 1; + + /* + * Rethrow the exception, delegating executing of finally if any + * to the exception handler. + */ + if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || + js_Emit1(cx, cg, JSOP_THROW) < 0) { + return JS_FALSE; + } + } + + JS_ASSERT(cg->stackDepth == depth); + + /* Emit finally handler if any. */ + finallyStart = 0; /* to quell GCC uninitialized warnings */ + if (pn->pn_kid3) { + /* + * Fix up the gosubs that might have been emitted before non-local + * jumps to the finally code. + */ + if (!BackPatch(cx, cg, GOSUBS(stmtInfo), CG_NEXT(cg), JSOP_GOSUB)) + return JS_FALSE; + + finallyStart = CG_OFFSET(cg); + + /* Indicate that we're emitting a subroutine body. */ + stmtInfo.type = STMT_SUBROUTINE; + if (!UpdateLineNumberNotes(cx, cg, pn->pn_kid3->pn_pos.begin.lineno)) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_FINALLY) < 0 || + !js_EmitTree(cx, cg, pn->pn_kid3) || + js_Emit1(cx, cg, JSOP_RETSUB) < 0) { + return JS_FALSE; + } + JS_ASSERT(cg->stackDepth == depth); + } + if (!js_PopStatementCG(cx, cg)) + return JS_FALSE; + + if (js_NewSrcNote(cx, cg, SRC_ENDBRACE) < 0 || + js_Emit1(cx, cg, JSOP_NOP) < 0) { + return JS_FALSE; + } + + /* Fix up the end-of-try/catch jumps to come here. */ + if (!BackPatch(cx, cg, catchJump, CG_NEXT(cg), JSOP_GOTO)) + return JS_FALSE; + + /* + * Add the try note last, to let post-order give us the right ordering + * (first to last for a given nesting level, inner to outer by level). + */ + if (pn->pn_kid2 && + !NewTryNote(cx, cg, JSTRY_CATCH, depth, tryStart, tryEnd)) { + return JS_FALSE; + } + + /* + * If we've got a finally, mark try+catch region with additional + * trynote to catch exceptions (re)thrown from a catch block or + * for the try{}finally{} case. + */ + if (pn->pn_kid3 && + !NewTryNote(cx, cg, JSTRY_FINALLY, depth, tryStart, finallyStart)) { + return JS_FALSE; + } + break; + } + + case TOK_CATCH: + { + ptrdiff_t catchStart, guardJump; + JSObject *blockObj; + + /* + * Morph STMT_BLOCK to STMT_CATCH, note the block entry code offset, + * and save the block object atom. + */ + stmt = cg->topStmt; + JS_ASSERT(stmt->type == STMT_BLOCK && (stmt->flags & SIF_SCOPE)); + stmt->type = STMT_CATCH; + catchStart = stmt->update; + blockObj = stmt->blockObj; + + /* Go up one statement info record to the TRY or FINALLY record. */ + stmt = stmt->down; + JS_ASSERT(stmt->type == STMT_TRY || stmt->type == STMT_FINALLY); + + /* Pick up the pending exception and bind it to the catch variable. */ + if (js_Emit1(cx, cg, JSOP_EXCEPTION) < 0) + return JS_FALSE; + + /* + * Dup the exception object if there is a guard for rethrowing to use + * it later when rethrowing or in other catches. + */ + if (pn->pn_kid2 && js_Emit1(cx, cg, JSOP_DUP) < 0) + return JS_FALSE; + + pn2 = pn->pn_kid1; + switch (pn2->pn_type) { +#if JS_HAS_DESTRUCTURING + case TOK_RB: + case TOK_RC: + if (!EmitDestructuringOps(cx, cg, JSOP_NOP, pn2)) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_POP) < 0) + return JS_FALSE; + break; +#endif + + case TOK_NAME: + /* Inline and specialize BindNameToSlot for pn2. */ + JS_ASSERT(pn2->pn_cookie != FREE_UPVAR_COOKIE); + EMIT_UINT16_IMM_OP(JSOP_SETLOCALPOP, pn2->pn_cookie); + break; + + default: + JS_ASSERT(0); + } + + /* Emit the guard expression, if there is one. */ + if (pn->pn_kid2) { + if (!js_EmitTree(cx, cg, pn->pn_kid2)) + return JS_FALSE; + if (!js_SetSrcNoteOffset(cx, cg, CATCHNOTE(*stmt), 0, + CG_OFFSET(cg) - catchStart)) { + return JS_FALSE; + } + /* ifeq */ + guardJump = EmitJump(cx, cg, JSOP_IFEQ, 0); + if (guardJump < 0) + return JS_FALSE; + GUARDJUMP(*stmt) = guardJump; + + /* Pop duplicated exception object as we no longer need it. */ + if (js_Emit1(cx, cg, JSOP_POP) < 0) + return JS_FALSE; + } + + /* Emit the catch body. */ + if (!js_EmitTree(cx, cg, pn->pn_kid3)) + return JS_FALSE; + + /* + * Annotate the JSOP_LEAVEBLOCK that will be emitted as we unwind via + * our TOK_LEXICALSCOPE parent, so the decompiler knows to pop. + */ + off = cg->stackDepth; + if (js_NewSrcNote2(cx, cg, SRC_CATCH, off) < 0) + return JS_FALSE; + break; + } + + case TOK_VAR: + if (!EmitVariables(cx, cg, pn, JS_FALSE, ¬eIndex)) + return JS_FALSE; + break; + + case TOK_RETURN: + /* Push a return value */ + pn2 = pn->pn_kid; + if (pn2) { + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + } else { + if (js_Emit1(cx, cg, JSOP_PUSH) < 0) + return JS_FALSE; + } + + /* + * EmitNonLocalJumpFixup may add fixup bytecode to close open try + * blocks having finally clauses and to exit intermingled let blocks. + * We can't simply transfer control flow to our caller in that case, + * because we must gosub to those finally clauses from inner to outer, + * with the correct stack pointer (i.e., after popping any with, + * for/in, etc., slots nested inside the finally's try). + * + * In this case we mutate JSOP_RETURN into JSOP_SETRVAL and add an + * extra JSOP_RETRVAL after the fixups. + */ + top = CG_OFFSET(cg); + if (js_Emit1(cx, cg, JSOP_RETURN) < 0) + return JS_FALSE; + if (!EmitNonLocalJumpFixup(cx, cg, NULL)) + return JS_FALSE; + if (top + JSOP_RETURN_LENGTH != CG_OFFSET(cg)) { + CG_BASE(cg)[top] = JSOP_SETRVAL; + if (js_Emit1(cx, cg, JSOP_RETRVAL) < 0) + return JS_FALSE; + } + break; + +#if JS_HAS_GENERATORS + case TOK_YIELD: + if (!(cg->flags & TCF_IN_FUNCTION)) { + js_ReportCompileErrorNumber(cx, CG_TS(cg), pn, JSREPORT_ERROR, + JSMSG_BAD_RETURN_OR_YIELD, + js_yield_str); + return JS_FALSE; + } + if (pn->pn_kid) { + if (!js_EmitTree(cx, cg, pn->pn_kid)) + return JS_FALSE; + } else { + if (js_Emit1(cx, cg, JSOP_PUSH) < 0) + return JS_FALSE; + } + if (pn->pn_hidden && js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_YIELD) < 0) + return JS_FALSE; + break; +#endif + + case TOK_LC: + { +#if JS_HAS_XML_SUPPORT + if (pn->pn_arity == PN_UNARY) { + if (!js_EmitTree(cx, cg, pn->pn_kid)) + return JS_FALSE; + if (js_Emit1(cx, cg, PN_OP(pn)) < 0) + return JS_FALSE; + break; + } +#endif + + JS_ASSERT(pn->pn_arity == PN_LIST); + + noteIndex = -1; + tmp = CG_OFFSET(cg); + if (pn->pn_xflags & PNX_NEEDBRACES) { + noteIndex = js_NewSrcNote2(cx, cg, SRC_BRACE, 0); + if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_NOP) < 0) + return JS_FALSE; + } + + js_PushStatement(cg, &stmtInfo, STMT_BLOCK, top); + + JSParseNode *pnchild = pn->pn_head; + if (pn->pn_xflags & PNX_FUNCDEFS) { + /* + * This block contains top-level function definitions. To ensure + * that we emit the bytecode defining them before the rest of code + * in the block we use a separate pass over functions. During the + * main pass later the emitter will add JSOP_NOP with source notes + * for the function to preserve the original functions position + * when decompiling. + * + * Currently this is used only for functions, as compile-as-we go + * mode for scripts does not allow separate emitter passes. + */ + JS_ASSERT(cg->flags & TCF_IN_FUNCTION); + if (pn->pn_xflags & PNX_DESTRUCT) { + /* + * Assign the destructuring arguments before defining any + * functions, see bug 419662. + */ + JS_ASSERT(pnchild->pn_type == TOK_SEMI); + JS_ASSERT(pnchild->pn_kid->pn_type == TOK_COMMA); + if (!js_EmitTree(cx, cg, pnchild)) + return JS_FALSE; + pnchild = pnchild->pn_next; + } + + for (pn2 = pnchild; pn2; pn2 = pn2->pn_next) { + if (pn2->pn_type == TOK_FUNCTION) { + if (pn2->pn_op == JSOP_NOP) { + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + } else { + /* + * JSOP_DEFFUN in a top-level block with function + * definitions appears, for example, when "if (true)" + * is optimized away from "if (true) function x() {}". + * See bug 428424. + */ + JS_ASSERT(pn2->pn_op == JSOP_DEFFUN); + } + } + } + } + for (pn2 = pnchild; pn2; pn2 = pn2->pn_next) { + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + } + + if (noteIndex >= 0 && + !js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, + CG_OFFSET(cg) - tmp)) { + return JS_FALSE; + } + + ok = js_PopStatementCG(cx, cg); + break; + } + + case TOK_SEQ: + JS_ASSERT(pn->pn_arity == PN_LIST); + js_PushStatement(cg, &stmtInfo, STMT_SEQ, top); + for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + } + ok = js_PopStatementCG(cx, cg); + break; + + case TOK_SEMI: + pn2 = pn->pn_kid; + if (pn2) { + /* + * Top-level or called-from-a-native JS_Execute/EvaluateScript, + * debugger, and eval frames may need the value of the ultimate + * expression statement as the script's result, despite the fact + * that it appears useless to the compiler. + * + * API users may also set the JSOPTION_NO_SCRIPT_RVAL option when + * calling JS_Compile* to suppress JSOP_POPV. + */ + useful = wantval = !(cg->flags & (TCF_IN_FUNCTION | TCF_NO_SCRIPT_RVAL)); + if (!useful) { + if (!CheckSideEffects(cx, cg, pn2, &useful)) + return JS_FALSE; + } + + /* + * Don't eliminate apparently useless expressions if they are + * labeled expression statements. The tc->topStmt->update test + * catches the case where we are nesting in js_EmitTree for a + * labeled compound statement. + */ + if (!useful && + (!cg->topStmt || + cg->topStmt->type != STMT_LABEL || + cg->topStmt->update < CG_OFFSET(cg))) { + CG_CURRENT_LINE(cg) = pn2->pn_pos.begin.lineno; + if (!js_ReportCompileErrorNumber(cx, CG_TS(cg), pn2, + JSREPORT_WARNING | + JSREPORT_STRICT, + JSMSG_USELESS_EXPR)) { + return JS_FALSE; + } + } else { + op = wantval ? JSOP_POPV : JSOP_POP; +#if JS_HAS_DESTRUCTURING + if (!wantval && + pn2->pn_type == TOK_ASSIGN && + !MaybeEmitGroupAssignment(cx, cg, op, pn2, &op)) { + return JS_FALSE; + } +#endif + if (op != JSOP_NOP) { + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + if (js_Emit1(cx, cg, op) < 0) + return JS_FALSE; + } + } + } + break; + + case TOK_COLON: + /* Emit an annotated nop so we know to decompile a label. */ + atom = pn->pn_atom; + ale = cg->atomList.add(cg->compiler, atom); + if (!ale) + return JS_FALSE; + pn2 = pn->expr(); + noteType = (pn2->pn_type == TOK_LC || + (pn2->pn_type == TOK_LEXICALSCOPE && + pn2->expr()->pn_type == TOK_LC)) + ? SRC_LABELBRACE + : SRC_LABEL; + noteIndex = js_NewSrcNote2(cx, cg, noteType, + (ptrdiff_t) ALE_INDEX(ale)); + if (noteIndex < 0 || + js_Emit1(cx, cg, JSOP_NOP) < 0) { + return JS_FALSE; + } + + /* Emit code for the labeled statement. */ + js_PushStatement(cg, &stmtInfo, STMT_LABEL, CG_OFFSET(cg)); + stmtInfo.label = atom; + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + if (!js_PopStatementCG(cx, cg)) + return JS_FALSE; + + /* If the statement was compound, emit a note for the end brace. */ + if (noteType == SRC_LABELBRACE) { + if (js_NewSrcNote(cx, cg, SRC_ENDBRACE) < 0 || + js_Emit1(cx, cg, JSOP_NOP) < 0) { + return JS_FALSE; + } + } + break; + + case TOK_COMMA: + /* + * Emit SRC_PCDELTA notes on each JSOP_POP between comma operands. + * These notes help the decompiler bracket the bytecodes generated + * from each sub-expression that follows a comma. + */ + off = noteIndex = -1; + for (pn2 = pn->pn_head; ; pn2 = pn2->pn_next) { + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + tmp = CG_OFFSET(cg); + if (noteIndex >= 0) { + if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off)) + return JS_FALSE; + } + if (!pn2->pn_next) + break; + off = tmp; + noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0); + if (noteIndex < 0 || + js_Emit1(cx, cg, JSOP_POP) < 0) { + return JS_FALSE; + } + } + break; + + case TOK_ASSIGN: + /* + * Check left operand type and generate specialized code for it. + * Specialize to avoid ECMA "reference type" values on the operand + * stack, which impose pervasive runtime "GetValue" costs. + */ + pn2 = pn->pn_left; + atomIndex = (jsatomid) -1; /* quell GCC overwarning */ + switch (pn2->pn_type) { + case TOK_NAME: + if (!BindNameToSlot(cx, cg, pn2)) + return JS_FALSE; + if (pn2->pn_cookie != FREE_UPVAR_COOKIE) { + atomIndex = (jsatomid) pn2->pn_cookie; + } else { + ale = cg->atomList.add(cg->compiler, pn2->pn_atom); + if (!ale) + return JS_FALSE; + atomIndex = ALE_INDEX(ale); + if (!pn2->isConst()) + EMIT_INDEX_OP(JSOP_BINDNAME, atomIndex); + } + break; + case TOK_DOT: + if (!js_EmitTree(cx, cg, pn2->expr())) + return JS_FALSE; + ale = cg->atomList.add(cg->compiler, pn2->pn_atom); + if (!ale) + return JS_FALSE; + atomIndex = ALE_INDEX(ale); + break; + case TOK_LB: + JS_ASSERT(pn2->pn_arity == PN_BINARY); + if (!js_EmitTree(cx, cg, pn2->pn_left)) + return JS_FALSE; + if (!js_EmitTree(cx, cg, pn2->pn_right)) + return JS_FALSE; + break; +#if JS_HAS_DESTRUCTURING + case TOK_RB: + case TOK_RC: + break; +#endif + case TOK_LP: + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + break; +#if JS_HAS_XML_SUPPORT + case TOK_UNARYOP: + JS_ASSERT(pn2->pn_op == JSOP_SETXMLNAME); + if (!js_EmitTree(cx, cg, pn2->pn_kid)) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_BINDXMLNAME) < 0) + return JS_FALSE; + break; +#endif + default: + JS_ASSERT(0); + } + + op = PN_OP(pn); +#if JS_HAS_GETTER_SETTER + if (op == JSOP_GETTER || op == JSOP_SETTER) { + if (pn2->pn_type == TOK_NAME && PN_OP(pn2) != JSOP_SETNAME) { + /* + * x getter = y where x is a local or let variable is not + * supported. + */ + js_ReportCompileErrorNumber(cx, + TS(cg->compiler), + pn2, JSREPORT_ERROR, + JSMSG_BAD_GETTER_OR_SETTER, + (op == JSOP_GETTER) + ? js_getter_str + : js_setter_str); + return JS_FALSE; + } + + /* We'll emit these prefix bytecodes after emitting the r.h.s. */ + } else +#endif + /* If += or similar, dup the left operand and get its value. */ + if (op != JSOP_NOP) { + switch (pn2->pn_type) { + case TOK_NAME: + if (pn2->isConst()) { + if (PN_OP(pn2) == JSOP_CALLEE) { + if (js_Emit1(cx, cg, JSOP_CALLEE) < 0) + return JS_FALSE; + } else { + EMIT_INDEX_OP(PN_OP(pn2), atomIndex); + } + } else if (PN_OP(pn2) == JSOP_SETNAME) { + if (js_Emit1(cx, cg, JSOP_DUP) < 0) + return JS_FALSE; + EMIT_INDEX_OP(JSOP_GETXPROP, atomIndex); + } else { + EMIT_UINT16_IMM_OP((PN_OP(pn2) == JSOP_SETGVAR) + ? JSOP_GETGVAR + : (PN_OP(pn2) == JSOP_GETUPVAR) + ? JSOP_GETUPVAR + : (PN_OP(pn2) == JSOP_SETARG) + ? JSOP_GETARG + : JSOP_GETLOCAL, + atomIndex); + } + break; + case TOK_DOT: + if (js_Emit1(cx, cg, JSOP_DUP) < 0) + return JS_FALSE; + if (pn2->pn_atom == cx->runtime->atomState.lengthAtom) { + if (js_Emit1(cx, cg, JSOP_LENGTH) < 0) + return JS_FALSE; + } else if (pn2->pn_atom == cx->runtime->atomState.protoAtom) { + if (!EmitIndexOp(cx, JSOP_QNAMEPART, atomIndex, cg)) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_GETELEM) < 0) + return JS_FALSE; + } else { + EMIT_INDEX_OP(JSOP_GETPROP, atomIndex); + } + break; + case TOK_LB: + case TOK_LP: +#if JS_HAS_XML_SUPPORT + case TOK_UNARYOP: +#endif + if (js_Emit1(cx, cg, JSOP_DUP2) < 0) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_GETELEM) < 0) + return JS_FALSE; + break; + default:; + } + } + + /* Now emit the right operand (it may affect the namespace). */ + if (!js_EmitTree(cx, cg, pn->pn_right)) + return JS_FALSE; + + /* If += etc., emit the binary operator with a decompiler note. */ + if (op != JSOP_NOP) { + /* + * Take care to avoid SRC_ASSIGNOP if the left-hand side is a const + * declared in the current compilation unit, as in this case (just + * a bit further below) we will avoid emitting the assignment op. + */ + if (pn2->pn_type != TOK_NAME || !pn2->isConst()) { + if (js_NewSrcNote(cx, cg, SRC_ASSIGNOP) < 0) + return JS_FALSE; + } + if (js_Emit1(cx, cg, op) < 0) + return JS_FALSE; + } + + /* Left parts such as a.b.c and a[b].c need a decompiler note. */ + if (pn2->pn_type != TOK_NAME && +#if JS_HAS_DESTRUCTURING + pn2->pn_type != TOK_RB && + pn2->pn_type != TOK_RC && +#endif + js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0) { + return JS_FALSE; + } + + /* Finally, emit the specialized assignment bytecode. */ + switch (pn2->pn_type) { + case TOK_NAME: + if (pn2->isConst()) + break; + /* FALL THROUGH */ + case TOK_DOT: + EMIT_INDEX_OP(PN_OP(pn2), atomIndex); + break; + case TOK_LB: + case TOK_LP: + if (js_Emit1(cx, cg, JSOP_SETELEM) < 0) + return JS_FALSE; + break; +#if JS_HAS_DESTRUCTURING + case TOK_RB: + case TOK_RC: + if (!EmitDestructuringOps(cx, cg, JSOP_SETNAME, pn2)) + return JS_FALSE; + break; +#endif +#if JS_HAS_XML_SUPPORT + case TOK_UNARYOP: + if (js_Emit1(cx, cg, JSOP_SETXMLNAME) < 0) + return JS_FALSE; + break; +#endif + default: + JS_ASSERT(0); + } + break; + + case TOK_HOOK: + /* Emit the condition, then branch if false to the else part. */ + if (!js_EmitTree(cx, cg, pn->pn_kid1)) + return JS_FALSE; + noteIndex = js_NewSrcNote(cx, cg, SRC_COND); + if (noteIndex < 0) + return JS_FALSE; + beq = EmitJump(cx, cg, JSOP_IFEQ, 0); + if (beq < 0 || !js_EmitTree(cx, cg, pn->pn_kid2)) + return JS_FALSE; + + /* Jump around else, fixup the branch, emit else, fixup jump. */ + jmp = EmitJump(cx, cg, JSOP_GOTO, 0); + if (jmp < 0) + return JS_FALSE; + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); + + /* + * Because each branch pushes a single value, but our stack budgeting + * analysis ignores branches, we now have to adjust cg->stackDepth to + * ignore the value pushed by the first branch. Execution will follow + * only one path, so we must decrement cg->stackDepth. + * + * Failing to do this will foil code, such as the try/catch/finally + * exception handling code generator, that samples cg->stackDepth for + * use at runtime (JSOP_SETSP), or in let expression and block code + * generation, which must use the stack depth to compute local stack + * indexes correctly. + */ + JS_ASSERT(cg->stackDepth > 0); + cg->stackDepth--; + if (!js_EmitTree(cx, cg, pn->pn_kid3)) + return JS_FALSE; + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); + if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq)) + return JS_FALSE; + break; + + case TOK_OR: + case TOK_AND: + /* + * JSOP_OR converts the operand on the stack to boolean, and if true, + * leaves the original operand value on the stack and jumps; otherwise + * it pops and falls into the next bytecode, which evaluates the right + * operand. The jump goes around the right operand evaluation. + * + * JSOP_AND converts the operand on the stack to boolean, and if false, + * leaves the original operand value on the stack and jumps; otherwise + * it pops and falls into the right operand's bytecode. + */ + if (pn->pn_arity == PN_BINARY) { + if (!js_EmitTree(cx, cg, pn->pn_left)) + return JS_FALSE; + top = EmitJump(cx, cg, JSOP_BACKPATCH_POP, 0); + if (top < 0) + return JS_FALSE; + if (!js_EmitTree(cx, cg, pn->pn_right)) + return JS_FALSE; + off = CG_OFFSET(cg); + pc = CG_CODE(cg, top); + CHECK_AND_SET_JUMP_OFFSET(cx, cg, pc, off - top); + *pc = pn->pn_op; + } else { + JS_ASSERT(pn->pn_arity == PN_LIST); + JS_ASSERT(pn->pn_head->pn_next->pn_next); + + /* Left-associative operator chain: avoid too much recursion. */ + pn2 = pn->pn_head; + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + top = EmitJump(cx, cg, JSOP_BACKPATCH_POP, 0); + if (top < 0) + return JS_FALSE; + + /* Emit nodes between the head and the tail. */ + jmp = top; + while ((pn2 = pn2->pn_next)->pn_next) { + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + off = EmitJump(cx, cg, JSOP_BACKPATCH_POP, 0); + if (off < 0) + return JS_FALSE; + if (!SetBackPatchDelta(cx, cg, CG_CODE(cg, jmp), off - jmp)) + return JS_FALSE; + jmp = off; + + } + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + + pn2 = pn->pn_head; + off = CG_OFFSET(cg); + do { + pc = CG_CODE(cg, top); + tmp = GetJumpOffset(cg, pc); + CHECK_AND_SET_JUMP_OFFSET(cx, cg, pc, off - top); + *pc = pn->pn_op; + top += tmp; + } while ((pn2 = pn2->pn_next)->pn_next); + } + break; + + case TOK_PLUS: + /* For TCF_IN_FUNCTION test, see TOK_RB concerning JSOP_NEWARRAY. */ + if (pn->pn_arity == PN_LIST && pn->pn_count < JS_BIT(16) && + (cg->flags & TCF_IN_FUNCTION)) { + /* Emit up to the first string literal conventionally. */ + for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { + if (pn2->pn_type == TOK_STRING) + break; + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + if (pn2 != pn->pn_head && js_Emit1(cx, cg, JSOP_ADD) < 0) + return JS_FALSE; + } + + /* Emit remainder as a single JSOP_CONCATN. */ + for (index = 0; pn2; pn2 = pn2->pn_next, index++) { + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + } + + if (index != 0) { + EMIT_UINT16_IMM_OP(JSOP_CONCATN, index); + + /* If we had a prefix, we need to be added to it now. */ + if (pn->pn_head->pn_type != TOK_STRING && + js_Emit1(cx, cg, JSOP_ADD) < 0) { + return JS_FALSE; + } + } + break; + } + case TOK_BITOR: + case TOK_BITXOR: + case TOK_BITAND: + case TOK_EQOP: + case TOK_RELOP: + case TOK_IN: + case TOK_INSTANCEOF: + case TOK_SHOP: + case TOK_MINUS: + case TOK_STAR: + case TOK_DIVOP: + if (pn->pn_arity == PN_LIST) { + /* Left-associative operator chain: avoid too much recursion. */ + pn2 = pn->pn_head; + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + op = PN_OP(pn); + while ((pn2 = pn2->pn_next) != NULL) { + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + if (js_Emit1(cx, cg, op) < 0) + return JS_FALSE; + } + } else { +#if JS_HAS_XML_SUPPORT + uintN oldflags; + + case TOK_DBLCOLON: + if (pn->pn_arity == PN_NAME) { + if (!js_EmitTree(cx, cg, pn->expr())) + return JS_FALSE; + if (!EmitAtomOp(cx, pn, PN_OP(pn), cg)) + return JS_FALSE; + break; + } + + /* + * Binary :: has a right operand that brackets arbitrary code, + * possibly including a let (a = b) ... expression. We must clear + * TCF_IN_FOR_INIT to avoid mis-compiling such beasts. + */ + oldflags = cg->flags; + cg->flags &= ~TCF_IN_FOR_INIT; +#endif + + /* Binary operators that evaluate both operands unconditionally. */ + if (!js_EmitTree(cx, cg, pn->pn_left)) + return JS_FALSE; + if (!js_EmitTree(cx, cg, pn->pn_right)) + return JS_FALSE; +#if JS_HAS_XML_SUPPORT + cg->flags |= oldflags & TCF_IN_FOR_INIT; +#endif + if (js_Emit1(cx, cg, PN_OP(pn)) < 0) + return JS_FALSE; + } + break; + + case TOK_THROW: +#if JS_HAS_XML_SUPPORT + case TOK_AT: + case TOK_DEFAULT: + JS_ASSERT(pn->pn_arity == PN_UNARY); + /* FALL THROUGH */ +#endif + case TOK_UNARYOP: + { + uintN oldflags; + + /* Unary op, including unary +/-. */ + op = PN_OP(pn); +#if JS_HAS_XML_SUPPORT + if (op == JSOP_XMLNAME) { + if (!EmitXMLName(cx, pn, op, cg)) + return JS_FALSE; + break; + } +#endif + pn2 = pn->pn_kid; + + /* See js_FoldConstants for why this assertion holds true. */ + JS_ASSERT_IF(op == JSOP_TYPEOF, pn2->pn_type == TOK_NAME); + + oldflags = cg->flags; + cg->flags &= ~TCF_IN_FOR_INIT; + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + cg->flags |= oldflags & TCF_IN_FOR_INIT; + if (js_Emit1(cx, cg, op) < 0) + return JS_FALSE; + break; + } + + case TOK_INC: + case TOK_DEC: + /* Emit lvalue-specialized code for ++/-- operators. */ + pn2 = pn->pn_kid; + JS_ASSERT(pn2->pn_type != TOK_RP); + op = PN_OP(pn); + switch (pn2->pn_type) { + default: + JS_ASSERT(pn2->pn_type == TOK_NAME); + pn2->pn_op = op; + if (!BindNameToSlot(cx, cg, pn2)) + return JS_FALSE; + op = PN_OP(pn2); + if (op == JSOP_CALLEE) { + if (js_Emit1(cx, cg, op) < 0) + return JS_FALSE; + } else if (pn2->pn_cookie != FREE_UPVAR_COOKIE) { + atomIndex = (jsatomid) pn2->pn_cookie; + EMIT_UINT16_IMM_OP(op, atomIndex); + } else { + JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM); + if (!EmitAtomOp(cx, pn2, op, cg)) + return JS_FALSE; + break; + } + if (pn2->isConst()) { + if (js_Emit1(cx, cg, JSOP_POS) < 0) + return JS_FALSE; + op = PN_OP(pn); + if (!(js_CodeSpec[op].format & JOF_POST)) { + if (js_Emit1(cx, cg, JSOP_ONE) < 0) + return JS_FALSE; + op = (js_CodeSpec[op].format & JOF_INC) ? JSOP_ADD : JSOP_SUB; + if (js_Emit1(cx, cg, op) < 0) + return JS_FALSE; + } + } + break; + case TOK_DOT: + if (!EmitPropOp(cx, pn2, op, cg, JS_FALSE)) + return JS_FALSE; + break; + case TOK_LB: + if (!EmitElemOp(cx, pn2, op, cg)) + return JS_FALSE; + break; + case TOK_LP: + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + if (js_NewSrcNote2(cx, cg, SRC_PCBASE, + CG_OFFSET(cg) - pn2->pn_offset) < 0) { + return JS_FALSE; + } + if (js_Emit1(cx, cg, op) < 0) + return JS_FALSE; + break; +#if JS_HAS_XML_SUPPORT + case TOK_UNARYOP: + JS_ASSERT(pn2->pn_op == JSOP_SETXMLNAME); + if (!js_EmitTree(cx, cg, pn2->pn_kid)) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_BINDXMLNAME) < 0) + return JS_FALSE; + if (js_Emit1(cx, cg, op) < 0) + return JS_FALSE; + break; +#endif + } + break; + + case TOK_DELETE: + /* + * Under ECMA 3, deleting a non-reference returns true -- but alas we + * must evaluate the operand if it appears it might have side effects. + */ + pn2 = pn->pn_kid; + switch (pn2->pn_type) { + case TOK_NAME: + if (!BindNameToSlot(cx, cg, pn2)) + return JS_FALSE; + op = PN_OP(pn2); + if (op == JSOP_FALSE) { + if (js_Emit1(cx, cg, op) < 0) + return JS_FALSE; + } else { + if (!EmitAtomOp(cx, pn2, op, cg)) + return JS_FALSE; + } + break; + case TOK_DOT: + if (!EmitPropOp(cx, pn2, JSOP_DELPROP, cg, JS_FALSE)) + return JS_FALSE; + break; +#if JS_HAS_XML_SUPPORT + case TOK_DBLDOT: + if (!EmitElemOp(cx, pn2, JSOP_DELDESC, cg)) + return JS_FALSE; + break; +#endif + case TOK_LB: + if (!EmitElemOp(cx, pn2, JSOP_DELELEM, cg)) + return JS_FALSE; + break; + default: + /* + * If useless, just emit JSOP_TRUE; otherwise convert delete foo() + * to foo(), true (a comma expression, requiring SRC_PCDELTA). + */ + useful = JS_FALSE; + if (!CheckSideEffects(cx, cg, pn2, &useful)) + return JS_FALSE; + if (!useful) { + off = noteIndex = -1; + } else { + if (pn2->pn_op == JSOP_SETCALL) + pn2->pn_op = JSOP_CALL; + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + off = CG_OFFSET(cg); + noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0); + if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_POP) < 0) + return JS_FALSE; + } + if (js_Emit1(cx, cg, JSOP_TRUE) < 0) + return JS_FALSE; + if (noteIndex >= 0) { + tmp = CG_OFFSET(cg); + if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off)) + return JS_FALSE; + } + } + break; + +#if JS_HAS_XML_SUPPORT + case TOK_FILTER: + if (!js_EmitTree(cx, cg, pn->pn_left)) + return JS_FALSE; + jmp = js_Emit3(cx, cg, JSOP_FILTER, 0, 0); + if (jmp < 0) + return JS_FALSE; + top = js_Emit1(cx, cg, JSOP_TRACE); + if (top < 0) + return JS_FALSE; + if (!js_EmitTree(cx, cg, pn->pn_right)) + return JS_FALSE; + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); + if (EmitJump(cx, cg, JSOP_ENDFILTER, top - CG_OFFSET(cg)) < 0) + return JS_FALSE; + break; +#endif + + case TOK_DOT: + /* + * Pop a stack operand, convert it to object, get a property named by + * this bytecode's immediate-indexed atom operand, and push its value + * (not a reference to it). + */ + ok = EmitPropOp(cx, pn, PN_OP(pn), cg, JS_FALSE); + break; + + case TOK_LB: +#if JS_HAS_XML_SUPPORT + case TOK_DBLDOT: +#endif + /* + * Pop two operands, convert the left one to object and the right one + * to property name (atom or tagged int), get the named property, and + * push its value. Set the "obj" register to the result of ToObject + * on the left operand. + */ + ok = EmitElemOp(cx, pn, PN_OP(pn), cg); + break; + + case TOK_NEW: + case TOK_LP: + { + bool callop = (PN_TYPE(pn) == TOK_LP); + uintN oldflags; + + /* + * Emit callable invocation or operator new (constructor call) code. + * First, emit code for the left operand to evaluate the callable or + * constructable object expression. + * + * For operator new applied to other expressions than E4X ones, we emit + * JSOP_GETPROP instead of JSOP_CALLPROP, etc. This is necessary to + * interpose the lambda-initialized method read barrier -- see the code + * in jsops.cpp for JSOP_LAMBDA followed by JSOP_{SET,INIT}PROP. + * + * Then (or in a call case that has no explicit reference-base object) + * we emit JSOP_NULL as a placeholder local GC root to hold the |this| + * parameter: in the operator new case, the newborn instance; in the + * base-less call case, a cookie meaning "use the global object as the + * |this| value" (or in ES5 strict mode, "use undefined", so we should + * use JSOP_PUSH instead of JSOP_NULL -- see bug 514570). + */ + pn2 = pn->pn_head; + switch (pn2->pn_type) { + case TOK_NAME: + if (!EmitNameOp(cx, cg, pn2, callop)) + return JS_FALSE; + break; + case TOK_DOT: + if (!EmitPropOp(cx, pn2, PN_OP(pn2), cg, callop)) + return JS_FALSE; + break; + case TOK_LB: + JS_ASSERT(pn2->pn_op == JSOP_GETELEM); + if (!EmitElemOp(cx, pn2, callop ? JSOP_CALLELEM : JSOP_GETELEM, cg)) + return JS_FALSE; + break; + case TOK_UNARYOP: +#if JS_HAS_XML_SUPPORT + if (pn2->pn_op == JSOP_XMLNAME) { + if (!EmitXMLName(cx, pn2, JSOP_CALLXMLNAME, cg)) + return JS_FALSE; + callop = true; /* suppress JSOP_NULL after */ + break; + } +#endif + /* FALL THROUGH */ + default: + /* + * Push null as a placeholder for the global object, per ECMA-262 + * 11.2.3 step 6. + */ + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + callop = false; /* trigger JSOP_NULL after */ + break; + } + if (!callop && js_Emit1(cx, cg, JSOP_NULL) < 0) + return JS_FALSE; + + /* Remember start of callable-object bytecode for decompilation hint. */ + off = top; + + /* + * Emit code for each argument in order, then emit the JSOP_*CALL or + * JSOP_NEW bytecode with a two-byte immediate telling how many args + * were pushed on the operand stack. + */ + oldflags = cg->flags; + cg->flags &= ~TCF_IN_FOR_INIT; + for (pn3 = pn2->pn_next; pn3; pn3 = pn3->pn_next) { + if (!js_EmitTree(cx, cg, pn3)) + return JS_FALSE; + } + cg->flags |= oldflags & TCF_IN_FOR_INIT; + if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - off) < 0) + return JS_FALSE; + + argc = pn->pn_count - 1; + if (js_Emit3(cx, cg, PN_OP(pn), ARGC_HI(argc), ARGC_LO(argc)) < 0) + return JS_FALSE; + if (PN_OP(pn) == JSOP_CALL) { + /* Add a trace hint opcode for recursion. */ + if (js_Emit1(cx, cg, JSOP_TRACE) < 0) + return JS_FALSE; + } + if (PN_OP(pn) == JSOP_EVAL) + EMIT_UINT16_IMM_OP(JSOP_LINENO, pn->pn_pos.begin.lineno); + break; + } + + case TOK_LEXICALSCOPE: + { + JSObjectBox *objbox; + uintN count; + + objbox = pn->pn_objbox; + js_PushBlockScope(cg, &stmtInfo, objbox->object, CG_OFFSET(cg)); + + /* + * If this lexical scope is not for a catch block, let block or let + * expression, or any kind of for loop (where the scope starts in the + * head after the first part if for (;;), else in the body if for-in); + * and if our container is top-level but not a function body, or else + * a block statement; then emit a SRC_BRACE note. All other container + * statements get braces by default from the decompiler. + */ + noteIndex = -1; + type = PN_TYPE(pn->expr()); + if (type != TOK_CATCH && type != TOK_LET && type != TOK_FOR && + (!(stmt = stmtInfo.down) + ? !(cg->flags & TCF_IN_FUNCTION) + : stmt->type == STMT_BLOCK)) { +#if defined DEBUG_brendan || defined DEBUG_mrbkap + /* There must be no source note already output for the next op. */ + JS_ASSERT(CG_NOTE_COUNT(cg) == 0 || + CG_LAST_NOTE_OFFSET(cg) != CG_OFFSET(cg) || + !GettableNoteForNextOp(cg)); +#endif + noteIndex = js_NewSrcNote2(cx, cg, SRC_BRACE, 0); + if (noteIndex < 0) + return JS_FALSE; + } + + JS_ASSERT(CG_OFFSET(cg) == top); + if (!EmitEnterBlock(cx, pn, cg)) + return JS_FALSE; + + if (!js_EmitTree(cx, cg, pn->pn_expr)) + return JS_FALSE; + + op = PN_OP(pn); + if (op == JSOP_LEAVEBLOCKEXPR) { + if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0) + return JS_FALSE; + } else { + if (noteIndex >= 0 && + !js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, + CG_OFFSET(cg) - top)) { + return JS_FALSE; + } + } + + /* Emit the JSOP_LEAVEBLOCK or JSOP_LEAVEBLOCKEXPR opcode. */ + count = OBJ_BLOCK_COUNT(cx, objbox->object); + EMIT_UINT16_IMM_OP(op, count); + + ok = js_PopStatementCG(cx, cg); + break; + } + +#if JS_HAS_BLOCK_SCOPE + case TOK_LET: + /* Let statements have their variable declarations on the left. */ + if (pn->pn_arity == PN_BINARY) { + pn2 = pn->pn_right; + pn = pn->pn_left; + } else { + pn2 = NULL; + } + + /* Non-null pn2 means that pn is the variable list from a let head. */ + JS_ASSERT(pn->pn_arity == PN_LIST); + if (!EmitVariables(cx, cg, pn, pn2 != NULL, ¬eIndex)) + return JS_FALSE; + + /* Thus non-null pn2 is the body of the let block or expression. */ + tmp = CG_OFFSET(cg); + if (pn2 && !js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + + if (noteIndex >= 0 && + !js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, + CG_OFFSET(cg) - tmp)) { + return JS_FALSE; + } + break; +#endif /* JS_HAS_BLOCK_SCOPE */ + +#if JS_HAS_GENERATORS + case TOK_ARRAYPUSH: { + jsint slot; + + /* + * The array object's stack index is in cg->arrayCompDepth. See below + * under the array initialiser code generator for array comprehension + * special casing. + */ + if (!js_EmitTree(cx, cg, pn->pn_kid)) + return JS_FALSE; + slot = AdjustBlockSlot(cx, cg, cg->arrayCompDepth); + if (slot < 0) + return JS_FALSE; + EMIT_UINT16_IMM_OP(PN_OP(pn), slot); + break; + } +#endif + + case TOK_RB: +#if JS_HAS_GENERATORS + case TOK_ARRAYCOMP: +#endif + /* + * Emit code for [a, b, c] that is equivalent to constructing a new + * array and in source order evaluating each element value and adding + * it to the array, without invoking latent setters. We use the + * JSOP_NEWINIT and JSOP_INITELEM bytecodes to ignore setters and to + * avoid dup'ing and popping the array as each element is added, as + * JSOP_SETELEM/JSOP_SETPROP would do. + * + * If no sharp variable is defined, the initializer is not for an array + * comprehension, the initializer is not overlarge, and the initializer + * is not in global code (whose stack growth cannot be precisely modeled + * due to the need to reserve space for global variables and regular + * expressions), use JSOP_NEWARRAY to minimize opcodes and to create the + * array using a fast, all-at-once process rather than a slow, element- + * by-element process. + */ +#if JS_HAS_SHARP_VARS + sharpnum = -1; + do_emit_array: +#endif + + op = (JS_LIKELY(pn->pn_count < JS_BIT(16)) && (cg->flags & TCF_IN_FUNCTION)) + ? JSOP_NEWARRAY + : JSOP_NEWINIT; + +#if JS_HAS_GENERATORS + if (pn->pn_type == TOK_ARRAYCOMP) + op = JSOP_NEWINIT; +#endif +#if JS_HAS_SHARP_VARS + JS_ASSERT_IF(sharpnum >= 0, cg->hasSharps()); + if (cg->hasSharps()) + op = JSOP_NEWINIT; +#endif + + if (op == JSOP_NEWINIT && !EmitNewInit(cx, cg, JSProto_Array, pn, sharpnum)) + return JS_FALSE; + +#if JS_HAS_GENERATORS + if (pn->pn_type == TOK_ARRAYCOMP) { + uintN saveDepth; + + /* + * Pass the new array's stack index to the TOK_ARRAYPUSH case via + * cg->arrayCompDepth, then simply traverse the TOK_FOR node and + * its kids under pn2 to generate this comprehension. + */ + JS_ASSERT(cg->stackDepth > 0); + saveDepth = cg->arrayCompDepth; + cg->arrayCompDepth = (uint32) (cg->stackDepth - 1); + if (!js_EmitTree(cx, cg, pn->pn_head)) + return JS_FALSE; + cg->arrayCompDepth = saveDepth; + + /* Emit the usual op needed for decompilation. */ + if (!EmitEndInit(cx, cg, 1)) + return JS_FALSE; + break; + } +#endif /* JS_HAS_GENERATORS */ + + pn2 = pn->pn_head; + for (atomIndex = 0; pn2; atomIndex++, pn2 = pn2->pn_next) { + if (op == JSOP_NEWINIT && !EmitNumberOp(cx, atomIndex, cg)) + return JS_FALSE; + if (pn2->pn_type == TOK_COMMA && pn2->pn_arity == PN_NULLARY) { + if (js_Emit1(cx, cg, JSOP_HOLE) < 0) + return JS_FALSE; + } else { + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + } + if (op == JSOP_NEWINIT && js_Emit1(cx, cg, JSOP_INITELEM) < 0) + return JS_FALSE; + } + JS_ASSERT(atomIndex == pn->pn_count); + + if (pn->pn_xflags & PNX_ENDCOMMA) { + /* Emit a source note so we know to decompile an extra comma. */ + if (js_NewSrcNote(cx, cg, SRC_CONTINUE) < 0) + return JS_FALSE; + } + + if (op == JSOP_NEWINIT) { + /* + * Emit an op to finish the array and, secondarily, to aid in sharp + * array cleanup (if JS_HAS_SHARP_VARS) and decompilation. + */ + if (!EmitEndInit(cx, cg, atomIndex)) + return JS_FALSE; + break; + } + + JS_ASSERT(atomIndex < JS_BIT(16)); + EMIT_UINT16_IMM_OP(JSOP_NEWARRAY, atomIndex); + break; + + case TOK_RC: +#if JS_HAS_SHARP_VARS + sharpnum = -1; + do_emit_object: +#endif +#if JS_HAS_DESTRUCTURING_SHORTHAND + if (pn->pn_xflags & PNX_DESTRUCT) { + js_ReportCompileErrorNumber(cx, CG_TS(cg), pn, JSREPORT_ERROR, + JSMSG_BAD_OBJECT_INIT); + return JS_FALSE; + } +#endif + /* + * Emit code for {p:a, '%q':b, 2:c} that is equivalent to constructing + * a new object and in source order evaluating each property value and + * adding the property to the object, without invoking latent setters. + * We use the JSOP_NEWINIT and JSOP_INITELEM/JSOP_INITPROP bytecodes to + * ignore setters and to avoid dup'ing and popping the object as each + * property is added, as JSOP_SETELEM/JSOP_SETPROP would do. + */ + if (!EmitNewInit(cx, cg, JSProto_Object, pn, sharpnum)) + return JS_FALSE; + + for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { + /* Emit an index for t[2] for later consumption by JSOP_INITELEM. */ + pn3 = pn2->pn_left; + if (pn3->pn_type == TOK_NUMBER) { + if (!EmitNumberOp(cx, pn3->pn_dval, cg)) + return JS_FALSE; + } + + /* Emit code for the property initializer. */ + if (!js_EmitTree(cx, cg, pn2->pn_right)) + return JS_FALSE; + +#if JS_HAS_GETTER_SETTER + op = PN_OP(pn2); + if (op == JSOP_GETTER || op == JSOP_SETTER) { + if (js_Emit1(cx, cg, op) < 0) + return JS_FALSE; + } +#endif + /* Annotate JSOP_INITELEM so we decompile 2:c and not just c. */ + if (pn3->pn_type == TOK_NUMBER) { + if (js_NewSrcNote(cx, cg, SRC_INITPROP) < 0) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_INITELEM) < 0) + return JS_FALSE; + } else { + JS_ASSERT(pn3->pn_type == TOK_NAME || + pn3->pn_type == TOK_STRING); + ale = cg->atomList.add(cg->compiler, pn3->pn_atom); + if (!ale) + return JS_FALSE; + + JSOp initOp = (PN_OP(pn2->pn_right) == JSOP_LAMBDA && + !(pn2->pn_right->pn_funbox->tcflags + & (TCF_FUN_USES_ARGUMENTS | TCF_FUN_USES_OWN_NAME)) +#if JS_HAS_GETTER_SETTER + && op != JSOP_GETTER && op != JSOP_SETTER +#endif + ) + ? JSOP_INITMETHOD + : JSOP_INITPROP; + EMIT_INDEX_OP(initOp, ALE_INDEX(ale)); + } + } + + if (!EmitEndInit(cx, cg, pn->pn_count)) + return JS_FALSE; + break; + +#if JS_HAS_SHARP_VARS + case TOK_DEFSHARP: + JS_ASSERT(cg->hasSharps()); + sharpnum = pn->pn_num; + pn = pn->pn_kid; + if (pn->pn_type == TOK_RB) + goto do_emit_array; +# if JS_HAS_GENERATORS + if (pn->pn_type == TOK_ARRAYCOMP) + goto do_emit_array; +# endif + if (pn->pn_type == TOK_RC) + goto do_emit_object; + + if (!js_EmitTree(cx, cg, pn)) + return JS_FALSE; + EMIT_UINT16PAIR_IMM_OP(JSOP_DEFSHARP, cg->sharpSlotBase, (jsatomid) sharpnum); + break; + + case TOK_USESHARP: + JS_ASSERT(cg->hasSharps()); + EMIT_UINT16PAIR_IMM_OP(JSOP_USESHARP, cg->sharpSlotBase, (jsatomid) pn->pn_num); + break; +#endif /* JS_HAS_SHARP_VARS */ + + case TOK_NAME: + /* + * Cope with a left-over function definition that was replaced by a use + * of a later function definition of the same name. See FunctionDef and + * MakeDefIntoUse in jsparse.cpp. + */ + if (pn->pn_op == JSOP_NOP) + return JS_TRUE; + if (!EmitNameOp(cx, cg, pn, JS_FALSE)) + return JS_FALSE; + break; + +#if JS_HAS_XML_SUPPORT + case TOK_XMLATTR: + case TOK_XMLSPACE: + case TOK_XMLTEXT: + case TOK_XMLCDATA: + case TOK_XMLCOMMENT: +#endif + case TOK_STRING: + ok = EmitAtomOp(cx, pn, PN_OP(pn), cg); + break; + + case TOK_NUMBER: + ok = EmitNumberOp(cx, pn->pn_dval, cg); + break; + + case TOK_REGEXP: + /* + * If the regexp's script is one-shot, we can avoid the extra + * fork-on-exec costs of JSOP_REGEXP by selecting JSOP_OBJECT. + * Otherwise, to avoid incorrect proto, parent, and lastIndex + * sharing among threads and sequentially across re-execution, + * select JSOP_REGEXP. + */ + JS_ASSERT(pn->pn_op == JSOP_REGEXP); + if (cg->flags & TCF_COMPILE_N_GO) { + ok = EmitObjectOp(cx, pn->pn_objbox, JSOP_OBJECT, cg); + } else { + ok = EmitIndexOp(cx, JSOP_REGEXP, + cg->regexpList.index(pn->pn_objbox), + cg); + } + break; + +#if JS_HAS_XML_SUPPORT + case TOK_ANYNAME: +#endif + case TOK_PRIMARY: + if (js_Emit1(cx, cg, PN_OP(pn)) < 0) + return JS_FALSE; + break; + +#if JS_HAS_DEBUGGER_KEYWORD + case TOK_DEBUGGER: + if (js_Emit1(cx, cg, JSOP_DEBUGGER) < 0) + return JS_FALSE; + break; +#endif /* JS_HAS_DEBUGGER_KEYWORD */ + +#if JS_HAS_XML_SUPPORT + case TOK_XMLELEM: + case TOK_XMLLIST: + if (pn->pn_op == JSOP_XMLOBJECT) { + ok = EmitObjectOp(cx, pn->pn_objbox, PN_OP(pn), cg); + break; + } + + JS_ASSERT(PN_TYPE(pn) == TOK_XMLLIST || pn->pn_count != 0); + switch (pn->pn_head ? PN_TYPE(pn->pn_head) : TOK_XMLLIST) { + case TOK_XMLETAGO: + JS_ASSERT(0); + /* FALL THROUGH */ + case TOK_XMLPTAGC: + case TOK_XMLSTAGO: + break; + default: + if (js_Emit1(cx, cg, JSOP_STARTXML) < 0) + return JS_FALSE; + } + + for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { + if (pn2->pn_type == TOK_LC && + js_Emit1(cx, cg, JSOP_STARTXMLEXPR) < 0) { + return JS_FALSE; + } + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + if (pn2 != pn->pn_head && js_Emit1(cx, cg, JSOP_ADD) < 0) + return JS_FALSE; + } + + if (pn->pn_xflags & PNX_XMLROOT) { + if (pn->pn_count == 0) { + JS_ASSERT(pn->pn_type == TOK_XMLLIST); + atom = cx->runtime->atomState.emptyAtom; + ale = cg->atomList.add(cg->compiler, atom); + if (!ale) + return JS_FALSE; + EMIT_INDEX_OP(JSOP_STRING, ALE_INDEX(ale)); + } + if (js_Emit1(cx, cg, PN_OP(pn)) < 0) + return JS_FALSE; + } +#ifdef DEBUG + else + JS_ASSERT(pn->pn_count != 0); +#endif + break; + + case TOK_XMLPTAGC: + if (pn->pn_op == JSOP_XMLOBJECT) { + ok = EmitObjectOp(cx, pn->pn_objbox, PN_OP(pn), cg); + break; + } + /* FALL THROUGH */ + + case TOK_XMLSTAGO: + case TOK_XMLETAGO: + { + uint32 i; + + if (js_Emit1(cx, cg, JSOP_STARTXML) < 0) + return JS_FALSE; + + ale = cg->atomList.add(cg->compiler, + (pn->pn_type == TOK_XMLETAGO) + ? cx->runtime->atomState.etagoAtom + : cx->runtime->atomState.stagoAtom); + if (!ale) + return JS_FALSE; + EMIT_INDEX_OP(JSOP_STRING, ALE_INDEX(ale)); + + JS_ASSERT(pn->pn_count != 0); + pn2 = pn->pn_head; + if (pn2->pn_type == TOK_LC && js_Emit1(cx, cg, JSOP_STARTXMLEXPR) < 0) + return JS_FALSE; + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_ADD) < 0) + return JS_FALSE; + + for (pn2 = pn2->pn_next, i = 0; pn2; pn2 = pn2->pn_next, i++) { + if (pn2->pn_type == TOK_LC && + js_Emit1(cx, cg, JSOP_STARTXMLEXPR) < 0) { + return JS_FALSE; + } + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + if ((i & 1) && pn2->pn_type == TOK_LC) { + if (js_Emit1(cx, cg, JSOP_TOATTRVAL) < 0) + return JS_FALSE; + } + if (js_Emit1(cx, cg, + (i & 1) ? JSOP_ADDATTRVAL : JSOP_ADDATTRNAME) < 0) { + return JS_FALSE; + } + } + + ale = cg->atomList.add(cg->compiler, + (pn->pn_type == TOK_XMLPTAGC) + ? cx->runtime->atomState.ptagcAtom + : cx->runtime->atomState.tagcAtom); + if (!ale) + return JS_FALSE; + EMIT_INDEX_OP(JSOP_STRING, ALE_INDEX(ale)); + if (js_Emit1(cx, cg, JSOP_ADD) < 0) + return JS_FALSE; + + if ((pn->pn_xflags & PNX_XMLROOT) && js_Emit1(cx, cg, PN_OP(pn)) < 0) + return JS_FALSE; + break; + } + + case TOK_XMLNAME: + if (pn->pn_arity == PN_LIST) { + JS_ASSERT(pn->pn_count != 0); + for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { + if (pn2->pn_type == TOK_LC && + js_Emit1(cx, cg, JSOP_STARTXMLEXPR) < 0) { + return JS_FALSE; + } + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + if (pn2 != pn->pn_head && js_Emit1(cx, cg, JSOP_ADD) < 0) + return JS_FALSE; + } + } else { + JS_ASSERT(pn->pn_arity == PN_NULLARY); + ok = (pn->pn_op == JSOP_OBJECT) + ? EmitObjectOp(cx, pn->pn_objbox, PN_OP(pn), cg) + : EmitAtomOp(cx, pn, PN_OP(pn), cg); + } + break; + + case TOK_XMLPI: + ale = cg->atomList.add(cg->compiler, pn->pn_atom2); + if (!ale) + return JS_FALSE; + if (!EmitIndexOp(cx, JSOP_QNAMEPART, ALE_INDEX(ale), cg)) + return JS_FALSE; + if (!EmitAtomOp(cx, pn, JSOP_XMLPI, cg)) + return JS_FALSE; + break; +#endif /* JS_HAS_XML_SUPPORT */ + + default: + JS_ASSERT(0); + } + + if (ok && --cg->emitLevel == 0) { + if (cg->spanDeps) + ok = OptimizeSpanDeps(cx, cg); + if (!UpdateLineNumberNotes(cx, cg, pn->pn_pos.end.lineno)) + return JS_FALSE; + } + + return ok; +} + +/* + * We should try to get rid of offsetBias (always 0 or 1, where 1 is + * JSOP_{NOP,POP}_LENGTH), which is used only by SRC_FOR and SRC_DECL. + */ +JS_FRIEND_DATA(JSSrcNoteSpec) js_SrcNoteSpec[] = { + {"null", 0, 0, 0}, + {"if", 0, 0, 0}, + {"if-else", 2, 0, 1}, + {"for", 3, 1, 1}, + {"while", 1, 0, 1}, + {"continue", 0, 0, 0}, + {"decl", 1, 1, 1}, + {"pcdelta", 1, 0, 1}, + {"assignop", 0, 0, 0}, + {"cond", 1, 0, 1}, + {"brace", 1, 0, 1}, + {"hidden", 0, 0, 0}, + {"pcbase", 1, 0, -1}, + {"label", 1, 0, 0}, + {"labelbrace", 1, 0, 0}, + {"endbrace", 0, 0, 0}, + {"break2label", 1, 0, 0}, + {"cont2label", 1, 0, 0}, + {"switch", 2, 0, 1}, + {"funcdef", 1, 0, 0}, + {"catch", 1, 0, 1}, + {"extended", -1, 0, 0}, + {"newline", 0, 0, 0}, + {"setline", 1, 0, 0}, + {"xdelta", 0, 0, 0}, +}; + +static intN +AllocSrcNote(JSContext *cx, JSCodeGenerator *cg) +{ + intN index; + JSArenaPool *pool; + size_t size; + + index = CG_NOTE_COUNT(cg); + if (((uintN)index & CG_NOTE_MASK(cg)) == 0) { + pool = cg->notePool; + size = SRCNOTE_SIZE(CG_NOTE_MASK(cg) + 1); + if (!CG_NOTES(cg)) { + /* Allocate the first note array lazily; leave noteMask alone. */ + JS_ARENA_ALLOCATE_CAST(CG_NOTES(cg), jssrcnote *, pool, size); + } else { + /* Grow by doubling note array size; update noteMask on success. */ + JS_ARENA_GROW_CAST(CG_NOTES(cg), jssrcnote *, pool, size, size); + if (CG_NOTES(cg)) + CG_NOTE_MASK(cg) = (CG_NOTE_MASK(cg) << 1) | 1; + } + if (!CG_NOTES(cg)) { + js_ReportOutOfScriptQuota(cx); + return -1; + } + } + + CG_NOTE_COUNT(cg) = index + 1; + return index; +} + +intN +js_NewSrcNote(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type) +{ + intN index, n; + jssrcnote *sn; + ptrdiff_t offset, delta, xdelta; + + /* + * Claim a note slot in CG_NOTES(cg) by growing it if necessary and then + * incrementing CG_NOTE_COUNT(cg). + */ + index = AllocSrcNote(cx, cg); + if (index < 0) + return -1; + sn = &CG_NOTES(cg)[index]; + + /* + * Compute delta from the last annotated bytecode's offset. If it's too + * big to fit in sn, allocate one or more xdelta notes and reset sn. + */ + offset = CG_OFFSET(cg); + delta = offset - CG_LAST_NOTE_OFFSET(cg); + CG_LAST_NOTE_OFFSET(cg) = offset; + if (delta >= SN_DELTA_LIMIT) { + do { + xdelta = JS_MIN(delta, SN_XDELTA_MASK); + SN_MAKE_XDELTA(sn, xdelta); + delta -= xdelta; + index = AllocSrcNote(cx, cg); + if (index < 0) + return -1; + sn = &CG_NOTES(cg)[index]; + } while (delta >= SN_DELTA_LIMIT); + } + + /* + * Initialize type and delta, then allocate the minimum number of notes + * needed for type's arity. Usually, we won't need more, but if an offset + * does take two bytes, js_SetSrcNoteOffset will grow CG_NOTES(cg). + */ + SN_MAKE_NOTE(sn, type, delta); + for (n = (intN)js_SrcNoteSpec[type].arity; n > 0; n--) { + if (js_NewSrcNote(cx, cg, SRC_NULL) < 0) + return -1; + } + return index; +} + +intN +js_NewSrcNote2(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type, + ptrdiff_t offset) +{ + intN index; + + index = js_NewSrcNote(cx, cg, type); + if (index >= 0) { + if (!js_SetSrcNoteOffset(cx, cg, index, 0, offset)) + return -1; + } + return index; +} + +intN +js_NewSrcNote3(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type, + ptrdiff_t offset1, ptrdiff_t offset2) +{ + intN index; + + index = js_NewSrcNote(cx, cg, type); + if (index >= 0) { + if (!js_SetSrcNoteOffset(cx, cg, index, 0, offset1)) + return -1; + if (!js_SetSrcNoteOffset(cx, cg, index, 1, offset2)) + return -1; + } + return index; +} + +static JSBool +GrowSrcNotes(JSContext *cx, JSCodeGenerator *cg) +{ + JSArenaPool *pool; + size_t size; + + /* Grow by doubling note array size; update noteMask on success. */ + pool = cg->notePool; + size = SRCNOTE_SIZE(CG_NOTE_MASK(cg) + 1); + JS_ARENA_GROW_CAST(CG_NOTES(cg), jssrcnote *, pool, size, size); + if (!CG_NOTES(cg)) { + js_ReportOutOfScriptQuota(cx); + return JS_FALSE; + } + CG_NOTE_MASK(cg) = (CG_NOTE_MASK(cg) << 1) | 1; + return JS_TRUE; +} + +jssrcnote * +js_AddToSrcNoteDelta(JSContext *cx, JSCodeGenerator *cg, jssrcnote *sn, + ptrdiff_t delta) +{ + ptrdiff_t base, limit, newdelta, diff; + intN index; + + /* + * Called only from OptimizeSpanDeps and js_FinishTakingSrcNotes to add to + * main script note deltas, and only by a small positive amount. + */ + JS_ASSERT(cg->current == &cg->main); + JS_ASSERT((unsigned) delta < (unsigned) SN_XDELTA_LIMIT); + + base = SN_DELTA(sn); + limit = SN_IS_XDELTA(sn) ? SN_XDELTA_LIMIT : SN_DELTA_LIMIT; + newdelta = base + delta; + if (newdelta < limit) { + SN_SET_DELTA(sn, newdelta); + } else { + index = sn - cg->main.notes; + if ((cg->main.noteCount & cg->main.noteMask) == 0) { + if (!GrowSrcNotes(cx, cg)) + return NULL; + sn = cg->main.notes + index; + } + diff = cg->main.noteCount - index; + cg->main.noteCount++; + memmove(sn + 1, sn, SRCNOTE_SIZE(diff)); + SN_MAKE_XDELTA(sn, delta); + sn++; + } + return sn; +} + +JS_FRIEND_API(uintN) +js_SrcNoteLength(jssrcnote *sn) +{ + uintN arity; + jssrcnote *base; + + arity = (intN)js_SrcNoteSpec[SN_TYPE(sn)].arity; + for (base = sn++; arity; sn++, arity--) { + if (*sn & SN_3BYTE_OFFSET_FLAG) + sn += 2; + } + return sn - base; +} + +JS_FRIEND_API(ptrdiff_t) +js_GetSrcNoteOffset(jssrcnote *sn, uintN which) +{ + /* Find the offset numbered which (i.e., skip exactly which offsets). */ + JS_ASSERT(SN_TYPE(sn) != SRC_XDELTA); + JS_ASSERT((intN) which < js_SrcNoteSpec[SN_TYPE(sn)].arity); + for (sn++; which; sn++, which--) { + if (*sn & SN_3BYTE_OFFSET_FLAG) + sn += 2; + } + if (*sn & SN_3BYTE_OFFSET_FLAG) { + return (ptrdiff_t)(((uint32)(sn[0] & SN_3BYTE_OFFSET_MASK) << 16) + | (sn[1] << 8) + | sn[2]); + } + return (ptrdiff_t)*sn; +} + +JSBool +js_SetSrcNoteOffset(JSContext *cx, JSCodeGenerator *cg, uintN index, + uintN which, ptrdiff_t offset) +{ + jssrcnote *sn; + ptrdiff_t diff; + + if ((jsuword)offset >= (jsuword)((ptrdiff_t)SN_3BYTE_OFFSET_FLAG << 16)) { + ReportStatementTooLarge(cx, cg); + return JS_FALSE; + } + + /* Find the offset numbered which (i.e., skip exactly which offsets). */ + sn = &CG_NOTES(cg)[index]; + JS_ASSERT(SN_TYPE(sn) != SRC_XDELTA); + JS_ASSERT((intN) which < js_SrcNoteSpec[SN_TYPE(sn)].arity); + for (sn++; which; sn++, which--) { + if (*sn & SN_3BYTE_OFFSET_FLAG) + sn += 2; + } + + /* See if the new offset requires three bytes. */ + if (offset > (ptrdiff_t)SN_3BYTE_OFFSET_MASK) { + /* Maybe this offset was already set to a three-byte value. */ + if (!(*sn & SN_3BYTE_OFFSET_FLAG)) { + /* Losing, need to insert another two bytes for this offset. */ + index = sn - CG_NOTES(cg); + + /* + * Simultaneously test to see if the source note array must grow to + * accommodate either the first or second byte of additional storage + * required by this 3-byte offset. + */ + if (((CG_NOTE_COUNT(cg) + 1) & CG_NOTE_MASK(cg)) <= 1) { + if (!GrowSrcNotes(cx, cg)) + return JS_FALSE; + sn = CG_NOTES(cg) + index; + } + CG_NOTE_COUNT(cg) += 2; + + diff = CG_NOTE_COUNT(cg) - (index + 3); + JS_ASSERT(diff >= 0); + if (diff > 0) + memmove(sn + 3, sn + 1, SRCNOTE_SIZE(diff)); + } + *sn++ = (jssrcnote)(SN_3BYTE_OFFSET_FLAG | (offset >> 16)); + *sn++ = (jssrcnote)(offset >> 8); + } + *sn = (jssrcnote)offset; + return JS_TRUE; +} + +#ifdef DEBUG_notme +#define DEBUG_srcnotesize +#endif + +#ifdef DEBUG_srcnotesize +#define NBINS 10 +static uint32 hist[NBINS]; + +void DumpSrcNoteSizeHist() +{ + static FILE *fp; + int i, n; + + if (!fp) { + fp = fopen("/tmp/srcnotes.hist", "w"); + if (!fp) + return; + setvbuf(fp, NULL, _IONBF, 0); + } + fprintf(fp, "SrcNote size histogram:\n"); + for (i = 0; i < NBINS; i++) { + fprintf(fp, "%4u %4u ", JS_BIT(i), hist[i]); + for (n = (int) JS_HOWMANY(hist[i], 10); n > 0; --n) + fputc('*', fp); + fputc('\n', fp); + } + fputc('\n', fp); +} +#endif + +/* + * Fill in the storage at notes with prolog and main srcnotes; the space at + * notes was allocated using the CG_COUNT_FINAL_SRCNOTES macro from jsemit.h. + * SO DON'T CHANGE THIS FUNCTION WITHOUT AT LEAST CHECKING WHETHER jsemit.h's + * CG_COUNT_FINAL_SRCNOTES MACRO NEEDS CORRESPONDING CHANGES! + */ +JSBool +js_FinishTakingSrcNotes(JSContext *cx, JSCodeGenerator *cg, jssrcnote *notes) +{ + uintN prologCount, mainCount, totalCount; + ptrdiff_t offset, delta; + jssrcnote *sn; + + JS_ASSERT(cg->current == &cg->main); + + prologCount = cg->prolog.noteCount; + if (prologCount && cg->prolog.currentLine != cg->firstLine) { + CG_SWITCH_TO_PROLOG(cg); + if (js_NewSrcNote2(cx, cg, SRC_SETLINE, (ptrdiff_t)cg->firstLine) < 0) + return JS_FALSE; + prologCount = cg->prolog.noteCount; + CG_SWITCH_TO_MAIN(cg); + } else { + /* + * Either no prolog srcnotes, or no line number change over prolog. + * We don't need a SRC_SETLINE, but we may need to adjust the offset + * of the first main note, by adding to its delta and possibly even + * prepending SRC_XDELTA notes to it to account for prolog bytecodes + * that came at and after the last annotated bytecode. + */ + offset = CG_PROLOG_OFFSET(cg) - cg->prolog.lastNoteOffset; + JS_ASSERT(offset >= 0); + if (offset > 0 && cg->main.noteCount != 0) { + /* NB: Use as much of the first main note's delta as we can. */ + sn = cg->main.notes; + delta = SN_IS_XDELTA(sn) + ? SN_XDELTA_MASK - (*sn & SN_XDELTA_MASK) + : SN_DELTA_MASK - (*sn & SN_DELTA_MASK); + if (offset < delta) + delta = offset; + for (;;) { + if (!js_AddToSrcNoteDelta(cx, cg, sn, delta)) + return JS_FALSE; + offset -= delta; + if (offset == 0) + break; + delta = JS_MIN(offset, SN_XDELTA_MASK); + sn = cg->main.notes; + } + } + } + + mainCount = cg->main.noteCount; + totalCount = prologCount + mainCount; + if (prologCount) + memcpy(notes, cg->prolog.notes, SRCNOTE_SIZE(prologCount)); + memcpy(notes + prologCount, cg->main.notes, SRCNOTE_SIZE(mainCount)); + SN_MAKE_TERMINATOR(¬es[totalCount]); + +#ifdef DEBUG_notme + { int bin = JS_CeilingLog2(totalCount); + if (bin >= NBINS) + bin = NBINS - 1; + ++hist[bin]; + } +#endif + return JS_TRUE; +} + +static JSBool +NewTryNote(JSContext *cx, JSCodeGenerator *cg, JSTryNoteKind kind, + uintN stackDepth, size_t start, size_t end) +{ + JSTryNode *tryNode; + + JS_ASSERT((uintN)(uint16)stackDepth == stackDepth); + JS_ASSERT(start <= end); + JS_ASSERT((size_t)(uint32)start == start); + JS_ASSERT((size_t)(uint32)end == end); + + JS_ARENA_ALLOCATE_TYPE(tryNode, JSTryNode, &cx->tempPool); + if (!tryNode) { + js_ReportOutOfScriptQuota(cx); + return JS_FALSE; + } + + tryNode->note.kind = kind; + tryNode->note.stackDepth = (uint16)stackDepth; + tryNode->note.start = (uint32)start; + tryNode->note.length = (uint32)(end - start); + tryNode->prev = cg->lastTryNode; + cg->lastTryNode = tryNode; + cg->ntrynotes++; + return JS_TRUE; +} + +void +js_FinishTakingTryNotes(JSCodeGenerator *cg, JSTryNoteArray *array) +{ + JSTryNode *tryNode; + JSTryNote *tn; + + JS_ASSERT(array->length > 0 && array->length == cg->ntrynotes); + tn = array->vector + array->length; + tryNode = cg->lastTryNode; + do { + *--tn = tryNode->note; + } while ((tryNode = tryNode->prev) != NULL); + JS_ASSERT(tn == array->vector); +} + +/* + * Find the index of the given object for code generator. + * + * Since the emitter refers to each parsed object only once, for the index we + * use the number of already indexes objects. We also add the object to a list + * to convert the list to a fixed-size array when we complete code generation, + * see JSCGObjectList::finish below. + * + * Most of the objects go to JSCodeGenerator.objectList but for regexp we use a + * separated JSCodeGenerator.regexpList. In this way the emitted index can be + * directly used to store and fetch a reference to a cloned RegExp object that + * shares the same JSRegExp private data created for the object literal in + * objbox. We need a cloned object to hold lastIndex and other direct properties + * that should not be shared among threads sharing a precompiled function or + * script. + * + * If the code being compiled is function code, allocate a reserved slot in + * the cloned function object that shares its precompiled script with other + * cloned function objects and with the compiler-created clone-parent. There + * are nregexps = script->regexps()->length such reserved slots in each + * function object cloned from fun->object. NB: during compilation, a funobj + * slots element must never be allocated, because js_AllocSlot could hand out + * one of the slots that should be given to a regexp clone. + * + * If the code being compiled is global code, the cloned regexp are stored in + * fp->vars slot after cg->ngvars and to protect regexp slots from GC we set + * fp->nvars to ngvars + nregexps. + * + * The slots initially contain undefined or null. We populate them lazily when + * JSOP_REGEXP is executed for the first time. + * + * Why clone regexp objects? ECMA specifies that when a regular expression + * literal is scanned, a RegExp object is created. In the spec, compilation + * and execution happen indivisibly, but in this implementation and many of + * its embeddings, code is precompiled early and re-executed in multiple + * threads, or using multiple global objects, or both, for efficiency. + * + * In such cases, naively following ECMA leads to wrongful sharing of RegExp + * objects, which makes for collisions on the lastIndex property (especially + * for global regexps) and on any ad-hoc properties. Also, __proto__ and + * __parent__ refer to the pre-compilation prototype and global objects, a + * pigeon-hole problem for instanceof tests. + */ +uintN +JSCGObjectList::index(JSObjectBox *objbox) +{ + JS_ASSERT(!objbox->emitLink); + objbox->emitLink = lastbox; + lastbox = objbox; + return length++; +} + +void +JSCGObjectList::finish(JSObjectArray *array) +{ + JSObject **cursor; + JSObjectBox *objbox; + + JS_ASSERT(length <= INDEX_LIMIT); + JS_ASSERT(length == array->length); + + cursor = array->vector + array->length; + objbox = lastbox; + do { + --cursor; + JS_ASSERT(!*cursor); + *cursor = objbox->object; + } while ((objbox = objbox->emitLink) != NULL); + JS_ASSERT(cursor == array->vector); +} diff --git a/ape-server/deps/js/src/jsemit.h b/ape-server/deps/js/src/jsemit.h new file mode 100755 index 0000000..b94dcdc --- /dev/null +++ b/ape-server/deps/js/src/jsemit.h @@ -0,0 +1,845 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=79: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsemit_h___ +#define jsemit_h___ +/* + * JS bytecode generation. + */ +#include "jstypes.h" +#include "jsatom.h" +#include "jsopcode.h" +#include "jsparse.h" +#include "jsscript.h" +#include "jsprvtd.h" +#include "jspubtd.h" + +JS_BEGIN_EXTERN_C + +/* + * NB: If you add enumerators for scope statements, add them between STMT_WITH + * and STMT_CATCH, or you will break the STMT_TYPE_IS_SCOPE macro. If you add + * non-looping statement enumerators, add them before STMT_DO_LOOP or you will + * break the STMT_TYPE_IS_LOOP macro. + * + * Also remember to keep the statementName array in jsemit.c in sync. + */ +typedef enum JSStmtType { + STMT_LABEL, /* labeled statement: L: s */ + STMT_IF, /* if (then) statement */ + STMT_ELSE, /* else clause of if statement */ + STMT_SEQ, /* synthetic sequence of statements */ + STMT_BLOCK, /* compound statement: { s1[;... sN] } */ + STMT_SWITCH, /* switch statement */ + STMT_WITH, /* with statement */ + STMT_CATCH, /* catch block */ + STMT_TRY, /* try block */ + STMT_FINALLY, /* finally block */ + STMT_SUBROUTINE, /* gosub-target subroutine body */ + STMT_DO_LOOP, /* do/while loop statement */ + STMT_FOR_LOOP, /* for loop statement */ + STMT_FOR_IN_LOOP, /* for/in loop statement */ + STMT_WHILE_LOOP, /* while loop statement */ + STMT_LIMIT +} JSStmtType; + +#define STMT_TYPE_IN_RANGE(t,b,e) ((uint)((t) - (b)) <= (uintN)((e) - (b))) + +/* + * A comment on the encoding of the JSStmtType enum and type-testing macros: + * + * STMT_TYPE_MAYBE_SCOPE tells whether a statement type is always, or may + * become, a lexical scope. It therefore includes block and switch (the two + * low-numbered "maybe" scope types) and excludes with (with has dynamic scope + * pending the "reformed with" in ES4/JS2). It includes all try-catch-finally + * types, which are high-numbered maybe-scope types. + * + * STMT_TYPE_LINKS_SCOPE tells whether a JSStmtInfo of the given type eagerly + * links to other scoping statement info records. It excludes the two early + * "maybe" types, block and switch, as well as the try and both finally types, + * since try and the other trailing maybe-scope types don't need block scope + * unless they contain let declarations. + * + * We treat WITH as a static scope because it prevents lexical binding from + * continuing further up the static scope chain. With the lost "reformed with" + * proposal for ES4, we would be able to model it statically, too. + */ +#define STMT_TYPE_MAYBE_SCOPE(type) \ + (type != STMT_WITH && \ + STMT_TYPE_IN_RANGE(type, STMT_BLOCK, STMT_SUBROUTINE)) + +#define STMT_TYPE_LINKS_SCOPE(type) \ + STMT_TYPE_IN_RANGE(type, STMT_WITH, STMT_CATCH) + +#define STMT_TYPE_IS_TRYING(type) \ + STMT_TYPE_IN_RANGE(type, STMT_TRY, STMT_SUBROUTINE) + +#define STMT_TYPE_IS_LOOP(type) ((type) >= STMT_DO_LOOP) + +#define STMT_MAYBE_SCOPE(stmt) STMT_TYPE_MAYBE_SCOPE((stmt)->type) +#define STMT_LINKS_SCOPE(stmt) (STMT_TYPE_LINKS_SCOPE((stmt)->type) || \ + ((stmt)->flags & SIF_SCOPE)) +#define STMT_IS_TRYING(stmt) STMT_TYPE_IS_TRYING((stmt)->type) +#define STMT_IS_LOOP(stmt) STMT_TYPE_IS_LOOP((stmt)->type) + +typedef struct JSStmtInfo JSStmtInfo; + +struct JSStmtInfo { + uint16 type; /* statement type */ + uint16 flags; /* flags, see below */ + uint32 blockid; /* for simplified dominance computation */ + ptrdiff_t update; /* loop update offset (top if none) */ + ptrdiff_t breaks; /* offset of last break in loop */ + ptrdiff_t continues; /* offset of last continue in loop */ + union { + JSAtom *label; /* name of LABEL */ + JSObject *blockObj; /* block scope object */ + }; + JSStmtInfo *down; /* info for enclosing statement */ + JSStmtInfo *downScope; /* next enclosing lexical scope */ +}; + +#define SIF_SCOPE 0x0001 /* statement has its own lexical scope */ +#define SIF_BODY_BLOCK 0x0002 /* STMT_BLOCK type is a function body */ +#define SIF_FOR_BLOCK 0x0004 /* for (let ...) induced block scope */ + +/* + * To reuse space in JSStmtInfo, rename breaks and continues for use during + * try/catch/finally code generation and backpatching. To match most common + * use cases, the macro argument is a struct, not a struct pointer. Only a + * loop, switch, or label statement info record can have breaks and continues, + * and only a for loop has an update backpatch chain, so it's safe to overlay + * these for the "trying" JSStmtTypes. + */ +#define CATCHNOTE(stmt) ((stmt).update) +#define GOSUBS(stmt) ((stmt).breaks) +#define GUARDJUMP(stmt) ((stmt).continues) + +#define SET_STATEMENT_TOP(stmt, top) \ + ((stmt)->update = (top), (stmt)->breaks = (stmt)->continues = (-1)) + +#ifdef JS_SCOPE_DEPTH_METER +# define JS_SCOPE_DEPTH_METERING(code) ((void) (code)) +#else +# define JS_SCOPE_DEPTH_METERING(code) ((void) 0) +#endif + +struct JSTreeContext { /* tree context for semantic checks */ + uint32 flags; /* statement state flags, see below */ + uint16 ngvars; /* max. no. of global variables/regexps */ + uint32 bodyid; /* block number of program/function body */ + uint32 blockidGen; /* preincremented block number generator */ + JSStmtInfo *topStmt; /* top of statement info stack */ + JSStmtInfo *topScopeStmt; /* top lexical scope statement */ + JSObject *blockChain; /* compile time block scope chain (NB: one + deeper than the topScopeStmt/downScope + chain when in head of let block/expr) */ + JSParseNode *blockNode; /* parse node for a block with let declarations + (block with its own lexical scope) */ + JSAtomList decls; /* function, const, and var declarations */ + JSCompiler *compiler; /* ptr to common parsing and lexing data */ + + union { + JSFunction *fun; /* function to store argument and variable + names when flags & TCF_IN_FUNCTION */ + JSObject *scopeChain; /* scope chain object for the script */ + }; + + JSAtomList lexdeps; /* unresolved lexical name dependencies */ + JSTreeContext *parent; /* enclosing function or global context */ + uintN staticLevel; /* static compilation unit nesting level */ + + JSFunctionBox *funbox; /* null or box for function we're compiling + if (flags & TCF_IN_FUNCTION) and not in + JSCompiler::compileFunctionBody */ + JSFunctionBox *functionList; + +#ifdef JS_SCOPE_DEPTH_METER + uint16 scopeDepth; /* current lexical scope chain depth */ + uint16 maxScopeDepth; /* maximum lexical scope chain depth */ +#endif + + JSTreeContext(JSCompiler *jsc) + : flags(0), ngvars(0), bodyid(0), blockidGen(0), + topStmt(NULL), topScopeStmt(NULL), blockChain(NULL), blockNode(NULL), + compiler(jsc), scopeChain(NULL), parent(NULL), staticLevel(0), + funbox(NULL), functionList(NULL), sharpSlotBase(-1) + { + JS_SCOPE_DEPTH_METERING(scopeDepth = maxScopeDepth = 0); + } + + /* + * For functions the tree context is constructed and destructed a second + * time during code generation. To avoid a redundant stats update in such + * cases, we store (uintN) -1 in maxScopeDepth. + */ + ~JSTreeContext() { + JS_SCOPE_DEPTH_METERING(maxScopeDepth == (uintN) -1 || + JS_BASIC_STATS_ACCUM(&compiler + ->context + ->runtime + ->lexicalScopeDepthStats, + maxScopeDepth)); + } + + uintN blockid() { return topStmt ? topStmt->blockid : bodyid; } + + bool atTopLevel() { return !topStmt || (topStmt->flags & SIF_BODY_BLOCK); } + + /* Test whether we're in a statement of given type. */ + bool inStatement(JSStmtType type); + + inline bool needStrictChecks(); + + /* + * sharpSlotBase is -1 or first slot of pair for [sharpArray, sharpDepth]. + * The parser calls ensureSharpSlots to allocate these two stack locals. + */ + int sharpSlotBase; + bool ensureSharpSlots(); +}; + +#define TCF_COMPILING 0x01 /* JSTreeContext is JSCodeGenerator */ +#define TCF_IN_FUNCTION 0x02 /* parsing inside function body */ +#define TCF_RETURN_EXPR 0x04 /* function has 'return expr;' */ +#define TCF_RETURN_VOID 0x08 /* function has 'return;' */ +#define TCF_IN_FOR_INIT 0x10 /* parsing init expr of for; exclude 'in' */ +#define TCF_FUN_SETS_OUTER_NAME 0x20 /* function set outer name (lexical or free) */ +#define TCF_FUN_PARAM_ARGUMENTS 0x40 /* function has parameter named arguments */ +#define TCF_FUN_USES_ARGUMENTS 0x80 /* function uses arguments except as a + parameter name */ +#define TCF_FUN_HEAVYWEIGHT 0x100 /* function needs Call object per call */ +#define TCF_FUN_IS_GENERATOR 0x200 /* parsed yield statement in function */ +#define TCF_FUN_USES_OWN_NAME 0x400 /* named function expression that uses its + own name */ +#define TCF_HAS_FUNCTION_STMT 0x800 /* block contains a function statement */ +#define TCF_GENEXP_LAMBDA 0x1000 /* flag lambda from generator expression */ +#define TCF_COMPILE_N_GO 0x2000 /* compiler-and-go mode of script, can + optimize name references based on scope + chain */ +#define TCF_NO_SCRIPT_RVAL 0x4000 /* API caller does not want result value + from global script */ +#define TCF_HAS_SHARPS 0x8000 /* source contains sharp defs or uses */ +#define TCF_FUN_PARAM_EVAL 0x10000 /* function has parameter named 'eval' */ + +/* + * Set when parsing a declaration-like destructuring pattern. This + * flag causes PrimaryExpr to create PN_NAME parse nodes for variable + * references which are not hooked into any definition's use chain, + * added to any tree context's AtomList, etc. etc. CheckDestructuring + * will do that work later. + * + * The comments atop CheckDestructuring explain the distinction + * between assignment-like and declaration-like destructuring + * patterns, and why they need to be treated differently. + */ +#define TCF_DECL_DESTRUCTURING 0x10000 + +/* + * A request flag passed to JSCompiler::compileScript and then down via + * JSCodeGenerator to js_NewScriptFromCG, from script_compile_sub and any + * kindred functions that need to make mutable scripts (even empty ones; + * i.e., they can't share the const JSScript::emptyScript() singleton). + */ +#define TCF_NEED_MUTABLE_SCRIPT 0x20000 + +/* + * This function/global/eval code body contained a Use Strict + * Directive. Treat certain strict warnings as errors, and forbid + * the use of 'with'. See also TSF_STRICT_MODE_CODE, + * JSScript::strictModeCode, and JSREPORT_STRICT_ERROR. + */ +#define TCF_STRICT_MODE_CODE 0x40000 + +/* + * Flags to propagate out of the blocks. + */ +#define TCF_RETURN_FLAGS (TCF_RETURN_EXPR | TCF_RETURN_VOID) + +/* + * Sticky deoptimization flags to propagate from FunctionBody. + */ +#define TCF_FUN_FLAGS (TCF_FUN_SETS_OUTER_NAME | \ + TCF_FUN_USES_ARGUMENTS | \ + TCF_FUN_PARAM_ARGUMENTS | \ + TCF_FUN_HEAVYWEIGHT | \ + TCF_FUN_IS_GENERATOR | \ + TCF_FUN_USES_OWN_NAME | \ + TCF_HAS_SHARPS | \ + TCF_STRICT_MODE_CODE) + +/* + * Return true if we need to check for conditions that elicit + * JSOPTION_STRICT warnings or strict mode errors. + */ +inline bool JSTreeContext::needStrictChecks() { + return JS_HAS_STRICT_OPTION(compiler->context) || + (flags & TCF_STRICT_MODE_CODE); +} + +/* + * Span-dependent instructions are jumps whose span (from the jump bytecode to + * the jump target) may require 2 or 4 bytes of immediate operand. + */ +typedef struct JSSpanDep JSSpanDep; +typedef struct JSJumpTarget JSJumpTarget; + +struct JSSpanDep { + ptrdiff_t top; /* offset of first bytecode in an opcode */ + ptrdiff_t offset; /* offset - 1 within opcode of jump operand */ + ptrdiff_t before; /* original offset - 1 of jump operand */ + JSJumpTarget *target; /* tagged target pointer or backpatch delta */ +}; + +/* + * Jump targets are stored in an AVL tree, for O(log(n)) lookup with targets + * sorted by offset from left to right, so that targets after a span-dependent + * instruction whose jump offset operand must be extended can be found quickly + * and adjusted upward (toward higher offsets). + */ +struct JSJumpTarget { + ptrdiff_t offset; /* offset of span-dependent jump target */ + int balance; /* AVL tree balance number */ + JSJumpTarget *kids[2]; /* left and right AVL tree child pointers */ +}; + +#define JT_LEFT 0 +#define JT_RIGHT 1 +#define JT_OTHER_DIR(dir) (1 - (dir)) +#define JT_IMBALANCE(dir) (((dir) << 1) - 1) +#define JT_DIR(imbalance) (((imbalance) + 1) >> 1) + +/* + * Backpatch deltas are encoded in JSSpanDep.target if JT_TAG_BIT is clear, + * so we can maintain backpatch chains when using span dependency records to + * hold jump offsets that overflow 16 bits. + */ +#define JT_TAG_BIT ((jsword) 1) +#define JT_UNTAG_SHIFT 1 +#define JT_SET_TAG(jt) ((JSJumpTarget *)((jsword)(jt) | JT_TAG_BIT)) +#define JT_CLR_TAG(jt) ((JSJumpTarget *)((jsword)(jt) & ~JT_TAG_BIT)) +#define JT_HAS_TAG(jt) ((jsword)(jt) & JT_TAG_BIT) + +#define BITS_PER_PTRDIFF (sizeof(ptrdiff_t) * JS_BITS_PER_BYTE) +#define BITS_PER_BPDELTA (BITS_PER_PTRDIFF - 1 - JT_UNTAG_SHIFT) +#define BPDELTA_MAX (((ptrdiff_t)1 << BITS_PER_BPDELTA) - 1) +#define BPDELTA_TO_JT(bp) ((JSJumpTarget *)((bp) << JT_UNTAG_SHIFT)) +#define JT_TO_BPDELTA(jt) ((ptrdiff_t)((jsword)(jt) >> JT_UNTAG_SHIFT)) + +#define SD_SET_TARGET(sd,jt) ((sd)->target = JT_SET_TAG(jt)) +#define SD_GET_TARGET(sd) (JS_ASSERT(JT_HAS_TAG((sd)->target)), \ + JT_CLR_TAG((sd)->target)) +#define SD_SET_BPDELTA(sd,bp) ((sd)->target = BPDELTA_TO_JT(bp)) +#define SD_GET_BPDELTA(sd) (JS_ASSERT(!JT_HAS_TAG((sd)->target)), \ + JT_TO_BPDELTA((sd)->target)) + +/* Avoid asserting twice by expanding SD_GET_TARGET in the "then" clause. */ +#define SD_SPAN(sd,pivot) (SD_GET_TARGET(sd) \ + ? JT_CLR_TAG((sd)->target)->offset - (pivot) \ + : 0) + +typedef struct JSTryNode JSTryNode; + +struct JSTryNode { + JSTryNote note; + JSTryNode *prev; +}; + +struct JSCGObjectList { + uint32 length; /* number of emitted so far objects */ + JSObjectBox *lastbox; /* last emitted object */ + + JSCGObjectList() : length(0), lastbox(NULL) {} + + uintN index(JSObjectBox *objbox); + void finish(JSObjectArray *array); +}; + +struct JSCodeGenerator : public JSTreeContext +{ + JSArenaPool *codePool; /* pointer to thread code arena pool */ + JSArenaPool *notePool; /* pointer to thread srcnote arena pool */ + void *codeMark; /* low watermark in cg->codePool */ + void *noteMark; /* low watermark in cg->notePool */ + + struct { + jsbytecode *base; /* base of JS bytecode vector */ + jsbytecode *limit; /* one byte beyond end of bytecode */ + jsbytecode *next; /* pointer to next free bytecode */ + jssrcnote *notes; /* source notes, see below */ + uintN noteCount; /* number of source notes so far */ + uintN noteMask; /* growth increment for notes */ + ptrdiff_t lastNoteOffset; /* code offset for last source note */ + uintN currentLine; /* line number for tree-based srcnote gen */ + } prolog, main, *current; + + JSAtomList atomList; /* literals indexed for mapping */ + uintN firstLine; /* first line, for js_NewScriptFromCG */ + + intN stackDepth; /* current stack depth in script frame */ + uintN maxStackDepth; /* maximum stack depth so far */ + + uintN ntrynotes; /* number of allocated so far try notes */ + JSTryNode *lastTryNode; /* the last allocated try node */ + + JSSpanDep *spanDeps; /* span dependent instruction records */ + JSJumpTarget *jumpTargets; /* AVL tree of jump target offsets */ + JSJumpTarget *jtFreeList; /* JT_LEFT-linked list of free structs */ + uintN numSpanDeps; /* number of span dependencies */ + uintN numJumpTargets; /* number of jump targets */ + ptrdiff_t spanDepTodo; /* offset from main.base of potentially + unoptimized spandeps */ + + uintN arrayCompDepth; /* stack depth of array in comprehension */ + + uintN emitLevel; /* js_EmitTree recursion level */ + JSAtomList constList; /* compile time constants */ + + JSCGObjectList objectList; /* list of emitted objects */ + JSCGObjectList regexpList; /* list of emitted regexp that will be + cloned during execution */ + + JSAtomList upvarList; /* map of atoms to upvar indexes */ + JSUpvarArray upvarMap; /* indexed upvar pairs (JS_realloc'ed) */ + + /* + * Initialize cg to allocate bytecode space from codePool, source note + * space from notePool, and all other arena-allocated temporaries from + * jsc->context->tempPool. + */ + JSCodeGenerator(JSCompiler *jsc, + JSArenaPool *codePool, JSArenaPool *notePool, + uintN lineno); + + /* + * Release cg->codePool, cg->notePool, and compiler->context->tempPool to + * marks set by JSCodeGenerator's ctor. Note that cgs are magic: they own + * the arena pool "tops-of-stack" space above their codeMark, noteMark, and + * tempMark points. This means you cannot alloc from tempPool and save the + * pointer beyond the next JSCodeGenerator destructor call. + */ + ~JSCodeGenerator(); + + bool hasSharps() { + bool rv = !!(flags & TCF_HAS_SHARPS); + JS_ASSERT((sharpSlotBase >= 0) == rv); + return rv; + } + + uintN sharpSlots() { + return hasSharps() ? SHARP_NSLOTS : 0; + } +}; + +#define CG_TS(cg) TS((cg)->compiler) + +#define CG_BASE(cg) ((cg)->current->base) +#define CG_LIMIT(cg) ((cg)->current->limit) +#define CG_NEXT(cg) ((cg)->current->next) +#define CG_CODE(cg,offset) (CG_BASE(cg) + (offset)) +#define CG_OFFSET(cg) (CG_NEXT(cg) - CG_BASE(cg)) + +#define CG_NOTES(cg) ((cg)->current->notes) +#define CG_NOTE_COUNT(cg) ((cg)->current->noteCount) +#define CG_NOTE_MASK(cg) ((cg)->current->noteMask) +#define CG_LAST_NOTE_OFFSET(cg) ((cg)->current->lastNoteOffset) +#define CG_CURRENT_LINE(cg) ((cg)->current->currentLine) + +#define CG_PROLOG_BASE(cg) ((cg)->prolog.base) +#define CG_PROLOG_LIMIT(cg) ((cg)->prolog.limit) +#define CG_PROLOG_NEXT(cg) ((cg)->prolog.next) +#define CG_PROLOG_CODE(cg,poff) (CG_PROLOG_BASE(cg) + (poff)) +#define CG_PROLOG_OFFSET(cg) (CG_PROLOG_NEXT(cg) - CG_PROLOG_BASE(cg)) + +#define CG_SWITCH_TO_MAIN(cg) ((cg)->current = &(cg)->main) +#define CG_SWITCH_TO_PROLOG(cg) ((cg)->current = &(cg)->prolog) + +/* + * Emit one bytecode. + */ +extern ptrdiff_t +js_Emit1(JSContext *cx, JSCodeGenerator *cg, JSOp op); + +/* + * Emit two bytecodes, an opcode (op) with a byte of immediate operand (op1). + */ +extern ptrdiff_t +js_Emit2(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1); + +/* + * Emit three bytecodes, an opcode with two bytes of immediate operands. + */ +extern ptrdiff_t +js_Emit3(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1, + jsbytecode op2); + +/* + * Emit (1 + extra) bytecodes, for N bytes of op and its immediate operand. + */ +extern ptrdiff_t +js_EmitN(JSContext *cx, JSCodeGenerator *cg, JSOp op, size_t extra); + +/* + * Unsafe macro to call js_SetJumpOffset and return false if it does. + */ +#define CHECK_AND_SET_JUMP_OFFSET_CUSTOM(cx,cg,pc,off,BAD_EXIT) \ + JS_BEGIN_MACRO \ + if (!js_SetJumpOffset(cx, cg, pc, off)) { \ + BAD_EXIT; \ + } \ + JS_END_MACRO + +#define CHECK_AND_SET_JUMP_OFFSET(cx,cg,pc,off) \ + CHECK_AND_SET_JUMP_OFFSET_CUSTOM(cx,cg,pc,off,return JS_FALSE) + +#define CHECK_AND_SET_JUMP_OFFSET_AT_CUSTOM(cx,cg,off,BAD_EXIT) \ + CHECK_AND_SET_JUMP_OFFSET_CUSTOM(cx, cg, CG_CODE(cg,off), \ + CG_OFFSET(cg) - (off), BAD_EXIT) + +#define CHECK_AND_SET_JUMP_OFFSET_AT(cx,cg,off) \ + CHECK_AND_SET_JUMP_OFFSET_AT_CUSTOM(cx, cg, off, return JS_FALSE) + +extern JSBool +js_SetJumpOffset(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc, + ptrdiff_t off); + +/* + * Push the C-stack-allocated struct at stmt onto the stmtInfo stack. + */ +extern void +js_PushStatement(JSTreeContext *tc, JSStmtInfo *stmt, JSStmtType type, + ptrdiff_t top); + +/* + * Push a block scope statement and link blockObj into tc->blockChain. To pop + * this statement info record, use js_PopStatement as usual, or if appropriate + * (if generating code), js_PopStatementCG. + */ +extern void +js_PushBlockScope(JSTreeContext *tc, JSStmtInfo *stmt, JSObject *blockObj, + ptrdiff_t top); + +/* + * Pop tc->topStmt. If the top JSStmtInfo struct is not stack-allocated, it + * is up to the caller to free it. + */ +extern void +js_PopStatement(JSTreeContext *tc); + +/* + * Like js_PopStatement(cg), also patch breaks and continues unless the top + * statement info record represents a try-catch-finally suite. May fail if a + * jump offset overflows. + */ +extern JSBool +js_PopStatementCG(JSContext *cx, JSCodeGenerator *cg); + +/* + * Define and lookup a primitive jsval associated with the const named by atom. + * js_DefineCompileTimeConstant analyzes the constant-folded initializer at pn + * and saves the const's value in cg->constList, if it can be used at compile + * time. It returns true unless an error occurred. + * + * If the initializer's value could not be saved, js_DefineCompileTimeConstant + * calls will return the undefined value. js_DefineCompileTimeConstant tries + * to find a const value memorized for atom, returning true with *vp set to a + * value other than undefined if the constant was found, true with *vp set to + * JSVAL_VOID if not found, and false on error. + */ +extern JSBool +js_DefineCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, + JSParseNode *pn); + +/* + * Find a lexically scoped variable (one declared by let, catch, or an array + * comprehension) named by atom, looking in tc's compile-time scopes. + * + * If a WITH statement is reached along the scope stack, return its statement + * info record, so callers can tell that atom is ambiguous. If slotp is not + * null, then if atom is found, set *slotp to its stack slot, otherwise to -1. + * This means that if slotp is not null, all the block objects on the lexical + * scope chain must have had their depth slots computed by the code generator, + * so the caller must be under js_EmitTree. + * + * In any event, directly return the statement info record in which atom was + * found. Otherwise return null. + */ +extern JSStmtInfo * +js_LexicalLookup(JSTreeContext *tc, JSAtom *atom, jsint *slotp, + JSStmtInfo *stmt = NULL); + +/* + * Emit code into cg for the tree rooted at pn. + */ +extern JSBool +js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn); + +/* + * Emit function code using cg for the tree rooted at body. + */ +extern JSBool +js_EmitFunctionScript(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body); + +/* + * Source notes generated along with bytecode for decompiling and debugging. + * A source note is a uint8 with 5 bits of type and 3 of offset from the pc of + * the previous note. If 3 bits of offset aren't enough, extended delta notes + * (SRC_XDELTA) consisting of 2 set high order bits followed by 6 offset bits + * are emitted before the next note. Some notes have operand offsets encoded + * immediately after them, in note bytes or byte-triples. + * + * Source Note Extended Delta + * +7-6-5-4-3+2-1-0+ +7-6-5+4-3-2-1-0+ + * |note-type|delta| |1 1| ext-delta | + * +---------+-----+ +---+-----------+ + * + * At most one "gettable" note (i.e., a note of type other than SRC_NEWLINE, + * SRC_SETLINE, and SRC_XDELTA) applies to a given bytecode. + * + * NB: the js_SrcNoteSpec array in jsemit.c is indexed by this enum, so its + * initializers need to match the order here. + * + * Note on adding new source notes: every pair of bytecodes (A, B) where A and + * B have disjoint sets of source notes that could apply to each bytecode may + * reuse the same note type value for two notes (snA, snB) that have the same + * arity, offsetBias, and isSpanDep initializers in js_SrcNoteSpec. This is + * why SRC_IF and SRC_INITPROP have the same value below. For bad historical + * reasons, some bytecodes below that could be overlayed have not been, but + * before using SRC_EXTENDED, consider compressing the existing note types. + * + * Don't forget to update JSXDR_BYTECODE_VERSION in jsxdrapi.h for all such + * incompatible source note or other bytecode changes. + */ +typedef enum JSSrcNoteType { + SRC_NULL = 0, /* terminates a note vector */ + SRC_IF = 1, /* JSOP_IFEQ bytecode is from an if-then */ + SRC_BREAK = 1, /* JSOP_GOTO is a break */ + SRC_INITPROP = 1, /* disjoint meaning applied to JSOP_INITELEM or + to an index label in a regular (structuring) + or a destructuring object initialiser */ + SRC_GENEXP = 1, /* JSOP_LAMBDA from generator expression */ + SRC_IF_ELSE = 2, /* JSOP_IFEQ bytecode is from an if-then-else */ + SRC_FOR_IN = 2, /* JSOP_GOTO to for-in loop condition from + before loop (same arity as SRC_IF_ELSE) */ + SRC_FOR = 3, /* JSOP_NOP or JSOP_POP in for(;;) loop head */ + SRC_WHILE = 4, /* JSOP_GOTO to for or while loop condition + from before loop, else JSOP_NOP at top of + do-while loop */ + SRC_CONTINUE = 5, /* JSOP_GOTO is a continue, not a break; + also used on JSOP_ENDINIT if extra comma + at end of array literal: [1,2,,]; + JSOP_DUP continuing destructuring pattern */ + SRC_DECL = 6, /* type of a declaration (var, const, let*) */ + SRC_DESTRUCT = 6, /* JSOP_DUP starting a destructuring assignment + operation, with SRC_DECL_* offset operand */ + SRC_PCDELTA = 7, /* distance forward from comma-operator to + next POP, or from CONDSWITCH to first CASE + opcode, etc. -- always a forward delta */ + SRC_GROUPASSIGN = 7, /* SRC_DESTRUCT variant for [a, b] = [c, d] */ + SRC_ASSIGNOP = 8, /* += or another assign-op follows */ + SRC_COND = 9, /* JSOP_IFEQ is from conditional ?: operator */ + SRC_BRACE = 10, /* mandatory brace, for scope or to avoid + dangling else */ + SRC_HIDDEN = 11, /* opcode shouldn't be decompiled */ + SRC_PCBASE = 12, /* distance back from annotated getprop or + setprop op to left-most obj.prop.subprop + bytecode -- always a backward delta */ + SRC_LABEL = 13, /* JSOP_NOP for label: with atomid immediate */ + SRC_LABELBRACE = 14, /* JSOP_NOP for label: {...} begin brace */ + SRC_ENDBRACE = 15, /* JSOP_NOP for label: {...} end brace */ + SRC_BREAK2LABEL = 16, /* JSOP_GOTO for 'break label' with atomid */ + SRC_CONT2LABEL = 17, /* JSOP_GOTO for 'continue label' with atomid */ + SRC_SWITCH = 18, /* JSOP_*SWITCH with offset to end of switch, + 2nd off to first JSOP_CASE if condswitch */ + SRC_FUNCDEF = 19, /* JSOP_NOP for function f() with atomid */ + SRC_CATCH = 20, /* catch block has guard */ + SRC_EXTENDED = 21, /* extended source note, 32-159, in next byte */ + SRC_NEWLINE = 22, /* bytecode follows a source newline */ + SRC_SETLINE = 23, /* a file-absolute source line number note */ + SRC_XDELTA = 24 /* 24-31 are for extended delta notes */ +} JSSrcNoteType; + +/* + * Constants for the SRC_DECL source note. Note that span-dependent bytecode + * selection means that any SRC_DECL offset greater than SRC_DECL_LET may need + * to be adjusted, but these "offsets" are too small to span a span-dependent + * instruction, so can be used to denote distinct declaration syntaxes to the + * decompiler. + * + * NB: the var_prefix array in jsopcode.c depends on these dense indexes from + * SRC_DECL_VAR through SRC_DECL_LET. + */ +#define SRC_DECL_VAR 0 +#define SRC_DECL_CONST 1 +#define SRC_DECL_LET 2 +#define SRC_DECL_NONE 3 + +#define SN_TYPE_BITS 5 +#define SN_DELTA_BITS 3 +#define SN_XDELTA_BITS 6 +#define SN_TYPE_MASK (JS_BITMASK(SN_TYPE_BITS) << SN_DELTA_BITS) +#define SN_DELTA_MASK ((ptrdiff_t)JS_BITMASK(SN_DELTA_BITS)) +#define SN_XDELTA_MASK ((ptrdiff_t)JS_BITMASK(SN_XDELTA_BITS)) + +#define SN_MAKE_NOTE(sn,t,d) (*(sn) = (jssrcnote) \ + (((t) << SN_DELTA_BITS) \ + | ((d) & SN_DELTA_MASK))) +#define SN_MAKE_XDELTA(sn,d) (*(sn) = (jssrcnote) \ + ((SRC_XDELTA << SN_DELTA_BITS) \ + | ((d) & SN_XDELTA_MASK))) + +#define SN_IS_XDELTA(sn) ((*(sn) >> SN_DELTA_BITS) >= SRC_XDELTA) +#define SN_TYPE(sn) ((JSSrcNoteType)(SN_IS_XDELTA(sn) \ + ? SRC_XDELTA \ + : *(sn) >> SN_DELTA_BITS)) +#define SN_SET_TYPE(sn,type) SN_MAKE_NOTE(sn, type, SN_DELTA(sn)) +#define SN_IS_GETTABLE(sn) (SN_TYPE(sn) < SRC_NEWLINE) + +#define SN_DELTA(sn) ((ptrdiff_t)(SN_IS_XDELTA(sn) \ + ? *(sn) & SN_XDELTA_MASK \ + : *(sn) & SN_DELTA_MASK)) +#define SN_SET_DELTA(sn,delta) (SN_IS_XDELTA(sn) \ + ? SN_MAKE_XDELTA(sn, delta) \ + : SN_MAKE_NOTE(sn, SN_TYPE(sn), delta)) + +#define SN_DELTA_LIMIT ((ptrdiff_t)JS_BIT(SN_DELTA_BITS)) +#define SN_XDELTA_LIMIT ((ptrdiff_t)JS_BIT(SN_XDELTA_BITS)) + +/* + * Offset fields follow certain notes and are frequency-encoded: an offset in + * [0,0x7f] consumes one byte, an offset in [0x80,0x7fffff] takes three, and + * the high bit of the first byte is set. + */ +#define SN_3BYTE_OFFSET_FLAG 0x80 +#define SN_3BYTE_OFFSET_MASK 0x7f + +typedef struct JSSrcNoteSpec { + const char *name; /* name for disassembly/debugging output */ + int8 arity; /* number of offset operands */ + uint8 offsetBias; /* bias of offset(s) from annotated pc */ + int8 isSpanDep; /* 1 or -1 if offsets could span extended ops, + 0 otherwise; sign tells span direction */ +} JSSrcNoteSpec; + +extern JS_FRIEND_DATA(JSSrcNoteSpec) js_SrcNoteSpec[]; +extern JS_FRIEND_API(uintN) js_SrcNoteLength(jssrcnote *sn); + +#define SN_LENGTH(sn) ((js_SrcNoteSpec[SN_TYPE(sn)].arity == 0) ? 1 \ + : js_SrcNoteLength(sn)) +#define SN_NEXT(sn) ((sn) + SN_LENGTH(sn)) + +/* A source note array is terminated by an all-zero element. */ +#define SN_MAKE_TERMINATOR(sn) (*(sn) = SRC_NULL) +#define SN_IS_TERMINATOR(sn) (*(sn) == SRC_NULL) + +/* + * Append a new source note of the given type (and therefore size) to cg's + * notes dynamic array, updating cg->noteCount. Return the new note's index + * within the array pointed at by cg->current->notes. Return -1 if out of + * memory. + */ +extern intN +js_NewSrcNote(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type); + +extern intN +js_NewSrcNote2(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type, + ptrdiff_t offset); + +extern intN +js_NewSrcNote3(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type, + ptrdiff_t offset1, ptrdiff_t offset2); + +/* + * NB: this function can add at most one extra extended delta note. + */ +extern jssrcnote * +js_AddToSrcNoteDelta(JSContext *cx, JSCodeGenerator *cg, jssrcnote *sn, + ptrdiff_t delta); + +/* + * Get and set the offset operand identified by which (0 for the first, etc.). + */ +extern JS_FRIEND_API(ptrdiff_t) +js_GetSrcNoteOffset(jssrcnote *sn, uintN which); + +extern JSBool +js_SetSrcNoteOffset(JSContext *cx, JSCodeGenerator *cg, uintN index, + uintN which, ptrdiff_t offset); + +/* + * Finish taking source notes in cx's notePool, copying final notes to the new + * stable store allocated by the caller and passed in via notes. Return false + * on malloc failure, which means this function reported an error. + * + * To compute the number of jssrcnotes to allocate and pass in via notes, use + * the CG_COUNT_FINAL_SRCNOTES macro. This macro knows a lot about details of + * js_FinishTakingSrcNotes, SO DON'T CHANGE jsemit.c's js_FinishTakingSrcNotes + * FUNCTION WITHOUT CHECKING WHETHER THIS MACRO NEEDS CORRESPONDING CHANGES! + */ +#define CG_COUNT_FINAL_SRCNOTES(cg, cnt) \ + JS_BEGIN_MACRO \ + ptrdiff_t diff_ = CG_PROLOG_OFFSET(cg) - (cg)->prolog.lastNoteOffset; \ + cnt = (cg)->prolog.noteCount + (cg)->main.noteCount + 1; \ + if ((cg)->prolog.noteCount && \ + (cg)->prolog.currentLine != (cg)->firstLine) { \ + if (diff_ > SN_DELTA_MASK) \ + cnt += JS_HOWMANY(diff_ - SN_DELTA_MASK, SN_XDELTA_MASK); \ + cnt += 2 + (((cg)->firstLine > SN_3BYTE_OFFSET_MASK) << 1); \ + } else if (diff_ > 0) { \ + if (cg->main.noteCount) { \ + jssrcnote *sn_ = (cg)->main.notes; \ + diff_ -= SN_IS_XDELTA(sn_) \ + ? SN_XDELTA_MASK - (*sn_ & SN_XDELTA_MASK) \ + : SN_DELTA_MASK - (*sn_ & SN_DELTA_MASK); \ + } \ + if (diff_ > 0) \ + cnt += JS_HOWMANY(diff_, SN_XDELTA_MASK); \ + } \ + JS_END_MACRO + +extern JSBool +js_FinishTakingSrcNotes(JSContext *cx, JSCodeGenerator *cg, jssrcnote *notes); + +extern void +js_FinishTakingTryNotes(JSCodeGenerator *cg, JSTryNoteArray *array); + +JS_END_EXTERN_C + +#endif /* jsemit_h___ */ diff --git a/ape-server/deps/js/src/jsexn.cpp b/ape-server/deps/js/src/jsexn.cpp new file mode 100755 index 0000000..e76d0e0 --- /dev/null +++ b/ape-server/deps/js/src/jsexn.cpp @@ -0,0 +1,1321 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * JS standard exception implementation. + */ + +#include +#include +#include "jstypes.h" +#include "jsstdint.h" +#include "jsbit.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jsprf.h" +#include "jsapi.h" +#include "jscntxt.h" +#include "jsversion.h" +#include "jsdbgapi.h" +#include "jsexn.h" +#include "jsfun.h" +#include "jsinterp.h" +#include "jsnum.h" +#include "jsobj.h" +#include "jsopcode.h" +#include "jsscope.h" +#include "jsscript.h" +#include "jsstaticcheck.h" + +/* Forward declarations for js_ErrorClass's initializer. */ +static JSBool +Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); + +static void +exn_trace(JSTracer *trc, JSObject *obj); + +static void +exn_finalize(JSContext *cx, JSObject *obj); + +static JSBool +exn_enumerate(JSContext *cx, JSObject *obj); + +static JSBool +exn_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, + JSObject **objp); + +JSClass js_ErrorClass = { + js_Error_str, + JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_MARK_IS_TRACE | + JSCLASS_HAS_CACHED_PROTO(JSProto_Error), + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + exn_enumerate, (JSResolveOp)exn_resolve, JS_ConvertStub, exn_finalize, + NULL, NULL, NULL, Exception, + NULL, NULL, JS_CLASS_TRACE(exn_trace), NULL +}; + +typedef struct JSStackTraceElem { + JSString *funName; + size_t argc; + const char *filename; + uintN ulineno; +} JSStackTraceElem; + +typedef struct JSExnPrivate { + /* A copy of the JSErrorReport originally generated. */ + JSErrorReport *errorReport; + JSString *message; + JSString *filename; + uintN lineno; + size_t stackDepth; + JSStackTraceElem stackElems[1]; +} JSExnPrivate; + +static JSString * +StackTraceToString(JSContext *cx, JSExnPrivate *priv); + +static JSErrorReport * +CopyErrorReport(JSContext *cx, JSErrorReport *report) +{ + /* + * We use a single malloc block to make a deep copy of JSErrorReport with + * the following layout: + * JSErrorReport + * array of copies of report->messageArgs + * jschar array with characters for all messageArgs + * jschar array with characters for ucmessage + * jschar array with characters for uclinebuf and uctokenptr + * char array with characters for linebuf and tokenptr + * char array with characters for filename + * Such layout together with the properties enforced by the following + * asserts does not need any extra alignment padding. + */ + JS_STATIC_ASSERT(sizeof(JSErrorReport) % sizeof(const char *) == 0); + JS_STATIC_ASSERT(sizeof(const char *) % sizeof(jschar) == 0); + + size_t filenameSize; + size_t linebufSize; + size_t uclinebufSize; + size_t ucmessageSize; + size_t i, argsArraySize, argsCopySize, argSize; + size_t mallocSize; + JSErrorReport *copy; + uint8 *cursor; + +#define JS_CHARS_SIZE(jschars) ((js_strlen(jschars) + 1) * sizeof(jschar)) + + filenameSize = report->filename ? strlen(report->filename) + 1 : 0; + linebufSize = report->linebuf ? strlen(report->linebuf) + 1 : 0; + uclinebufSize = report->uclinebuf ? JS_CHARS_SIZE(report->uclinebuf) : 0; + ucmessageSize = 0; + argsArraySize = 0; + argsCopySize = 0; + if (report->ucmessage) { + ucmessageSize = JS_CHARS_SIZE(report->ucmessage); + if (report->messageArgs) { + for (i = 0; report->messageArgs[i]; ++i) + argsCopySize += JS_CHARS_SIZE(report->messageArgs[i]); + + /* Non-null messageArgs should have at least one non-null arg. */ + JS_ASSERT(i != 0); + argsArraySize = (i + 1) * sizeof(const jschar *); + } + } + + /* + * The mallocSize can not overflow since it represents the sum of the + * sizes of already allocated objects. + */ + mallocSize = sizeof(JSErrorReport) + argsArraySize + argsCopySize + + ucmessageSize + uclinebufSize + linebufSize + filenameSize; + cursor = (uint8 *)cx->malloc(mallocSize); + if (!cursor) + return NULL; + + copy = (JSErrorReport *)cursor; + memset(cursor, 0, sizeof(JSErrorReport)); + cursor += sizeof(JSErrorReport); + + if (argsArraySize != 0) { + copy->messageArgs = (const jschar **)cursor; + cursor += argsArraySize; + for (i = 0; report->messageArgs[i]; ++i) { + copy->messageArgs[i] = (const jschar *)cursor; + argSize = JS_CHARS_SIZE(report->messageArgs[i]); + memcpy(cursor, report->messageArgs[i], argSize); + cursor += argSize; + } + copy->messageArgs[i] = NULL; + JS_ASSERT(cursor == (uint8 *)copy->messageArgs[0] + argsCopySize); + } + + if (report->ucmessage) { + copy->ucmessage = (const jschar *)cursor; + memcpy(cursor, report->ucmessage, ucmessageSize); + cursor += ucmessageSize; + } + + if (report->uclinebuf) { + copy->uclinebuf = (const jschar *)cursor; + memcpy(cursor, report->uclinebuf, uclinebufSize); + cursor += uclinebufSize; + if (report->uctokenptr) { + copy->uctokenptr = copy->uclinebuf + (report->uctokenptr - + report->uclinebuf); + } + } + + if (report->linebuf) { + copy->linebuf = (const char *)cursor; + memcpy(cursor, report->linebuf, linebufSize); + cursor += linebufSize; + if (report->tokenptr) { + copy->tokenptr = copy->linebuf + (report->tokenptr - + report->linebuf); + } + } + + if (report->filename) { + copy->filename = (const char *)cursor; + memcpy(cursor, report->filename, filenameSize); + } + JS_ASSERT(cursor + filenameSize == (uint8 *)copy + mallocSize); + + /* Copy non-pointer members. */ + copy->lineno = report->lineno; + copy->errorNumber = report->errorNumber; + + /* Note that this is before it gets flagged with JSREPORT_EXCEPTION */ + copy->flags = report->flags; + +#undef JS_CHARS_SIZE + return copy; +} + +static jsval * +GetStackTraceValueBuffer(JSExnPrivate *priv) +{ + /* + * We use extra memory after JSExnPrivateInfo.stackElems to store jsvals + * that helps to produce more informative stack traces. The following + * assert allows us to assume that no gap after stackElems is necessary to + * align the buffer properly. + */ + JS_STATIC_ASSERT(sizeof(JSStackTraceElem) % sizeof(jsval) == 0); + + return (jsval *)(priv->stackElems + priv->stackDepth); +} + +static JSBool +InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message, + JSString *filename, uintN lineno, JSErrorReport *report) +{ + JSSecurityCallbacks *callbacks; + JSCheckAccessOp checkAccess; + JSErrorReporter older; + JSExceptionState *state; + jsval callerid, v; + JSStackFrame *fp, *fpstop; + size_t stackDepth, valueCount, size; + JSBool overflow; + JSExnPrivate *priv; + JSStackTraceElem *elem; + jsval *values; + + JS_ASSERT(OBJ_GET_CLASS(cx, exnObject) == &js_ErrorClass); + + /* + * Prepare stack trace data. + * + * Set aside any error reporter for cx and save its exception state + * so we can suppress any checkAccess failures. Such failures should stop + * the backtrace procedure, not result in a failure of this constructor. + */ + callbacks = JS_GetSecurityCallbacks(cx); + checkAccess = callbacks + ? callbacks->checkObjectAccess + : NULL; + older = JS_SetErrorReporter(cx, NULL); + state = JS_SaveExceptionState(cx); + + callerid = ATOM_KEY(cx->runtime->atomState.callerAtom); + stackDepth = 0; + valueCount = 0; + for (fp = js_GetTopStackFrame(cx); fp; fp = fp->down) { + if (fp->fun && fp->argv) { + v = JSVAL_NULL; + if (checkAccess && + !checkAccess(cx, fp->callee(), callerid, JSACC_READ, &v)) { + break; + } + valueCount += fp->argc; + } + ++stackDepth; + } + JS_RestoreExceptionState(cx, state); + JS_SetErrorReporter(cx, older); + fpstop = fp; + + size = offsetof(JSExnPrivate, stackElems); + overflow = (stackDepth > ((size_t)-1 - size) / sizeof(JSStackTraceElem)); + size += stackDepth * sizeof(JSStackTraceElem); + overflow |= (valueCount > ((size_t)-1 - size) / sizeof(jsval)); + size += valueCount * sizeof(jsval); + if (overflow) { + js_ReportAllocationOverflow(cx); + return JS_FALSE; + } + priv = (JSExnPrivate *)cx->malloc(size); + if (!priv) + return JS_FALSE; + + /* + * We initialize errorReport with a copy of report after setting the + * private slot, to prevent GC accessing a junk value we clear the field + * here. + */ + priv->errorReport = NULL; + priv->message = message; + priv->filename = filename; + priv->lineno = lineno; + priv->stackDepth = stackDepth; + + values = GetStackTraceValueBuffer(priv); + elem = priv->stackElems; + for (fp = js_GetTopStackFrame(cx); fp != fpstop; fp = fp->down) { + if (!fp->fun) { + elem->funName = NULL; + elem->argc = 0; + } else { + elem->funName = fp->fun->atom + ? ATOM_TO_STRING(fp->fun->atom) + : cx->runtime->emptyString; + elem->argc = fp->argc; + memcpy(values, fp->argv, fp->argc * sizeof(jsval)); + values += fp->argc; + } + elem->ulineno = 0; + elem->filename = NULL; + if (fp->script) { + elem->filename = fp->script->filename; + if (fp->regs) + elem->ulineno = js_FramePCToLineNumber(cx, fp); + } + ++elem; + } + JS_ASSERT(priv->stackElems + stackDepth == elem); + JS_ASSERT(GetStackTraceValueBuffer(priv) + valueCount == values); + + exnObject->setPrivate(priv); + + if (report) { + /* + * Construct a new copy of the error report struct. We can't use the + * error report struct that was passed in, because it's allocated on + * the stack, and also because it may point to transient data in the + * JSTokenStream. + */ + priv->errorReport = CopyErrorReport(cx, report); + if (!priv->errorReport) { + /* The finalizer realeases priv since it is in the private slot. */ + return JS_FALSE; + } + } + + return JS_TRUE; +} + +static inline JSExnPrivate * +GetExnPrivate(JSContext *cx, JSObject *obj) +{ + return (JSExnPrivate *) obj->getPrivate(); +} + +static void +exn_trace(JSTracer *trc, JSObject *obj) +{ + JSExnPrivate *priv; + JSStackTraceElem *elem; + size_t vcount, i; + jsval *vp, v; + + priv = GetExnPrivate(trc->context, obj); + if (priv) { + if (priv->message) + JS_CALL_STRING_TRACER(trc, priv->message, "exception message"); + if (priv->filename) + JS_CALL_STRING_TRACER(trc, priv->filename, "exception filename"); + + elem = priv->stackElems; + for (vcount = i = 0; i != priv->stackDepth; ++i, ++elem) { + if (elem->funName) { + JS_CALL_STRING_TRACER(trc, elem->funName, + "stack trace function name"); + } + if (IS_GC_MARKING_TRACER(trc) && elem->filename) + js_MarkScriptFilename(elem->filename); + vcount += elem->argc; + } + vp = GetStackTraceValueBuffer(priv); + for (i = 0; i != vcount; ++i, ++vp) { + v = *vp; + JS_CALL_VALUE_TRACER(trc, v, "stack trace argument"); + } + } +} + +static void +exn_finalize(JSContext *cx, JSObject *obj) +{ + JSExnPrivate *priv; + + priv = GetExnPrivate(cx, obj); + if (priv) { + if (priv->errorReport) + cx->free(priv->errorReport); + cx->free(priv); + } +} + +static JSBool +exn_enumerate(JSContext *cx, JSObject *obj) +{ + JSAtomState *atomState; + uintN i; + JSAtom *atom; + JSObject *pobj; + JSProperty *prop; + + JS_STATIC_ASSERT(sizeof(JSAtomState) <= (size_t)(uint16)-1); + static const uint16 offsets[] = { + (uint16)offsetof(JSAtomState, messageAtom), + (uint16)offsetof(JSAtomState, fileNameAtom), + (uint16)offsetof(JSAtomState, lineNumberAtom), + (uint16)offsetof(JSAtomState, stackAtom), + }; + + atomState = &cx->runtime->atomState; + for (i = 0; i != JS_ARRAY_LENGTH(offsets); ++i) { + atom = *(JSAtom **)((uint8 *)atomState + offsets[i]); + if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop)) + return JS_FALSE; + if (prop) + pobj->dropProperty(cx, prop); + } + return JS_TRUE; +} + +static JSBool +exn_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, + JSObject **objp) +{ + JSExnPrivate *priv; + JSString *str; + JSAtom *atom; + JSString *stack; + const char *prop; + jsval v; + + *objp = NULL; + priv = GetExnPrivate(cx, obj); + if (priv && JSVAL_IS_STRING(id)) { + str = JSVAL_TO_STRING(id); + + atom = cx->runtime->atomState.messageAtom; + if (str == ATOM_TO_STRING(atom)) { + prop = js_message_str; + v = STRING_TO_JSVAL(priv->message); + goto define; + } + + atom = cx->runtime->atomState.fileNameAtom; + if (str == ATOM_TO_STRING(atom)) { + prop = js_fileName_str; + v = STRING_TO_JSVAL(priv->filename); + goto define; + } + + atom = cx->runtime->atomState.lineNumberAtom; + if (str == ATOM_TO_STRING(atom)) { + prop = js_lineNumber_str; + v = INT_TO_JSVAL(priv->lineno); + goto define; + } + + atom = cx->runtime->atomState.stackAtom; + if (str == ATOM_TO_STRING(atom)) { + stack = StackTraceToString(cx, priv); + if (!stack) + return JS_FALSE; + + /* Allow to GC all things that were used to build stack trace. */ + priv->stackDepth = 0; + prop = js_stack_str; + v = STRING_TO_JSVAL(stack); + goto define; + } + } + return JS_TRUE; + + define: + if (!JS_DefineProperty(cx, obj, prop, v, NULL, NULL, JSPROP_ENUMERATE)) + return JS_FALSE; + *objp = obj; + return JS_TRUE; +} + +JSErrorReport * +js_ErrorFromException(JSContext *cx, jsval exn) +{ + JSObject *obj; + JSExnPrivate *priv; + + if (JSVAL_IS_PRIMITIVE(exn)) + return NULL; + obj = JSVAL_TO_OBJECT(exn); + if (OBJ_GET_CLASS(cx, obj) != &js_ErrorClass) + return NULL; + priv = GetExnPrivate(cx, obj); + if (!priv) + return NULL; + return priv->errorReport; +} + +static JSString * +ValueToShortSource(JSContext *cx, jsval v) +{ + JSString *str; + + /* Avoid toSource bloat and fallibility for object types. */ + if (JSVAL_IS_PRIMITIVE(v)) { + str = js_ValueToSource(cx, v); + } else if (VALUE_IS_FUNCTION(cx, v)) { + /* + * XXX Avoid function decompilation bloat for now. + */ + str = JS_GetFunctionId(JS_ValueToFunction(cx, v)); + if (!str && !(str = js_ValueToSource(cx, v))) { + /* + * Continue to soldier on if the function couldn't be + * converted into a string. + */ + JS_ClearPendingException(cx); + str = JS_NewStringCopyZ(cx, "[unknown function]"); + } + } else { + /* + * XXX Avoid toString on objects, it takes too long and uses too much + * memory, for too many classes (see Mozilla bug 166743). + */ + char buf[100]; + JS_snprintf(buf, sizeof buf, "[object %s]", + OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v))->name); + str = JS_NewStringCopyZ(cx, buf); + } + return str; +} + +static JSString * +StackTraceToString(JSContext *cx, JSExnPrivate *priv) +{ + jschar *stackbuf; + size_t stacklen, stackmax; + JSStackTraceElem *elem, *endElem; + jsval *values; + size_t i; + JSString *str; + const char *cp; + char ulnbuf[11]; + + /* After this point, failing control flow must goto bad. */ + stackbuf = NULL; + stacklen = stackmax = 0; + +/* Limit the stackbuf length to a reasonable value to avoid overflow checks. */ +#define STACK_LENGTH_LIMIT JS_BIT(20) + +#define APPEND_CHAR_TO_STACK(c) \ + JS_BEGIN_MACRO \ + if (stacklen == stackmax) { \ + void *ptr_; \ + if (stackmax >= STACK_LENGTH_LIMIT) \ + goto done; \ + stackmax = stackmax ? 2 * stackmax : 64; \ + ptr_ = cx->realloc(stackbuf, (stackmax+1) * sizeof(jschar)); \ + if (!ptr_) \ + goto bad; \ + stackbuf = (jschar *) ptr_; \ + } \ + stackbuf[stacklen++] = (c); \ + JS_END_MACRO + +#define APPEND_STRING_TO_STACK(str) \ + JS_BEGIN_MACRO \ + JSString *str_ = str; \ + const jschar *chars_; \ + size_t length_; \ + \ + str_->getCharsAndLength(chars_, length_); \ + if (length_ > stackmax - stacklen) { \ + void *ptr_; \ + if (stackmax >= STACK_LENGTH_LIMIT || \ + length_ >= STACK_LENGTH_LIMIT - stacklen) { \ + goto done; \ + } \ + stackmax = JS_BIT(JS_CeilingLog2(stacklen + length_)); \ + ptr_ = cx->realloc(stackbuf, (stackmax+1) * sizeof(jschar)); \ + if (!ptr_) \ + goto bad; \ + stackbuf = (jschar *) ptr_; \ + } \ + js_strncpy(stackbuf + stacklen, chars_, length_); \ + stacklen += length_; \ + JS_END_MACRO + + values = GetStackTraceValueBuffer(priv); + elem = priv->stackElems; + for (endElem = elem + priv->stackDepth; elem != endElem; elem++) { + if (elem->funName) { + APPEND_STRING_TO_STACK(elem->funName); + APPEND_CHAR_TO_STACK('('); + for (i = 0; i != elem->argc; i++, values++) { + if (i > 0) + APPEND_CHAR_TO_STACK(','); + str = ValueToShortSource(cx, *values); + if (!str) + goto bad; + APPEND_STRING_TO_STACK(str); + } + APPEND_CHAR_TO_STACK(')'); + } + APPEND_CHAR_TO_STACK('@'); + if (elem->filename) { + for (cp = elem->filename; *cp; cp++) + APPEND_CHAR_TO_STACK(*cp); + } + APPEND_CHAR_TO_STACK(':'); + JS_snprintf(ulnbuf, sizeof ulnbuf, "%u", elem->ulineno); + for (cp = ulnbuf; *cp; cp++) + APPEND_CHAR_TO_STACK(*cp); + APPEND_CHAR_TO_STACK('\n'); + } +#undef APPEND_CHAR_TO_STACK +#undef APPEND_STRING_TO_STACK +#undef STACK_LENGTH_LIMIT + + done: + if (stacklen == 0) { + JS_ASSERT(!stackbuf); + return cx->runtime->emptyString; + } + if (stacklen < stackmax) { + /* + * Realloc can fail when shrinking on some FreeBSD versions, so + * don't use JS_realloc here; simply let the oversized allocation + * be owned by the string in that rare case. + */ + void *shrunk = cx->realloc(stackbuf, (stacklen+1) * sizeof(jschar)); + if (shrunk) + stackbuf = (jschar *) shrunk; + } + + stackbuf[stacklen] = 0; + str = js_NewString(cx, stackbuf, stacklen); + if (str) + return str; + + bad: + if (stackbuf) + cx->free(stackbuf); + return NULL; +} + +/* XXXbe Consolidate the ugly truth that we don't treat filename as UTF-8 + with these two functions. */ +static JSString * +FilenameToString(JSContext *cx, const char *filename) +{ + return JS_NewStringCopyZ(cx, filename); +} + +static const char * +StringToFilename(JSContext *cx, JSString *str) +{ + return js_GetStringBytes(cx, str); +} + +static JSBool +Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + uint32 lineno; + JSString *message, *filename; + JSStackFrame *fp; + + if (!JS_IsConstructing(cx)) { + /* + * ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when + * called as functions, without operator new. But as we do not give + * each constructor a distinct JSClass, whose .name member is used by + * js_NewObject to find the class prototype, we must get the class + * prototype ourselves. + */ + if (!JSVAL_TO_OBJECT(argv[-2])->getProperty(cx, + ATOM_TO_JSID(cx->runtime->atomState + .classPrototypeAtom), + rval)) { + return JS_FALSE; + } + obj = js_NewObject(cx, &js_ErrorClass, JSVAL_TO_OBJECT(*rval), NULL); + if (!obj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + } + + /* + * If it's a new object of class Exception, then null out the private + * data so that the finalizer doesn't attempt to free it. + */ + if (OBJ_GET_CLASS(cx, obj) == &js_ErrorClass) + obj->setPrivate(NULL); + + /* Set the 'message' property. */ + if (argc != 0) { + message = js_ValueToString(cx, argv[0]); + if (!message) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(message); + } else { + message = cx->runtime->emptyString; + } + + /* Set the 'fileName' property. */ + if (argc > 1) { + filename = js_ValueToString(cx, argv[1]); + if (!filename) + return JS_FALSE; + argv[1] = STRING_TO_JSVAL(filename); + fp = NULL; + } else { + fp = js_GetScriptedCaller(cx, NULL); + if (fp) { + filename = FilenameToString(cx, fp->script->filename); + if (!filename) + return JS_FALSE; + } else { + filename = cx->runtime->emptyString; + } + } + + /* Set the 'lineNumber' property. */ + if (argc > 2) { + lineno = js_ValueToECMAUint32(cx, &argv[2]); + if (JSVAL_IS_NULL(argv[2])) + return JS_FALSE; + } else { + if (!fp) + fp = js_GetScriptedCaller(cx, NULL); + lineno = (fp && fp->regs) ? js_FramePCToLineNumber(cx, fp) : 0; + } + + return (OBJ_GET_CLASS(cx, obj) != &js_ErrorClass) || + InitExnPrivate(cx, obj, message, filename, lineno, NULL); +} + +/* + * Convert to string. + * + * This method only uses JavaScript-modifiable properties name, message. It + * is left to the host to check for private data and report filename and line + * number information along with this message. + */ +static JSBool +exn_toString(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *obj; + jsval v; + JSString *name, *message, *result; + jschar *chars, *cp; + size_t name_length, message_length, length; + + obj = JS_THIS_OBJECT(cx, vp); + if (!obj || !obj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.nameAtom), &v)) + return JS_FALSE; + name = JSVAL_IS_STRING(v) ? JSVAL_TO_STRING(v) : cx->runtime->emptyString; + *vp = STRING_TO_JSVAL(name); + + if (!JS_GetProperty(cx, obj, js_message_str, &v)) + return JS_FALSE; + message = JSVAL_IS_STRING(v) ? JSVAL_TO_STRING(v) + : cx->runtime->emptyString; + + if (message->length() != 0) { + name_length = name->length(); + message_length = message->length(); + length = (name_length ? name_length + 2 : 0) + message_length; + cp = chars = (jschar *) cx->malloc((length + 1) * sizeof(jschar)); + if (!chars) + return JS_FALSE; + + if (name_length) { + js_strncpy(cp, name->chars(), name_length); + cp += name_length; + *cp++ = ':'; *cp++ = ' '; + } + js_strncpy(cp, message->chars(), message_length); + cp += message_length; + *cp = 0; + + result = js_NewString(cx, chars, length); + if (!result) { + cx->free(chars); + return JS_FALSE; + } + } else { + result = name; + } + + *vp = STRING_TO_JSVAL(result); + return JS_TRUE; +} + +#if JS_HAS_TOSOURCE +/* + * Return a string that may eval to something similar to the original object. + */ +static JSBool +exn_toSource(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *obj; + JSString *name, *message, *filename, *lineno_as_str, *result; + jsval localroots[3] = {JSVAL_NULL, JSVAL_NULL, JSVAL_NULL}; + JSTempValueRooter tvr; + JSBool ok; + uint32 lineno; + size_t lineno_length, name_length, message_length, filename_length, length; + jschar *chars, *cp; + + obj = JS_THIS_OBJECT(cx, vp); + if (!obj || !obj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.nameAtom), vp)) + return JS_FALSE; + name = js_ValueToString(cx, *vp); + if (!name) + return JS_FALSE; + *vp = STRING_TO_JSVAL(name); + + MUST_FLOW_THROUGH("out"); + JS_PUSH_TEMP_ROOT(cx, 3, localroots, &tvr); + +#ifdef __GNUC__ + message = filename = NULL; +#endif + ok = JS_GetProperty(cx, obj, js_message_str, &localroots[0]) && + (message = js_ValueToSource(cx, localroots[0])); + if (!ok) + goto out; + localroots[0] = STRING_TO_JSVAL(message); + + ok = JS_GetProperty(cx, obj, js_fileName_str, &localroots[1]) && + (filename = js_ValueToSource(cx, localroots[1])); + if (!ok) + goto out; + localroots[1] = STRING_TO_JSVAL(filename); + + ok = JS_GetProperty(cx, obj, js_lineNumber_str, &localroots[2]); + if (!ok) + goto out; + lineno = js_ValueToECMAUint32 (cx, &localroots[2]); + ok = !JSVAL_IS_NULL(localroots[2]); + if (!ok) + goto out; + + if (lineno != 0) { + lineno_as_str = js_ValueToString(cx, localroots[2]); + if (!lineno_as_str) { + ok = JS_FALSE; + goto out; + } + lineno_length = lineno_as_str->length(); + } else { + lineno_as_str = NULL; + lineno_length = 0; + } + + /* Magic 8, for the characters in ``(new ())''. */ + name_length = name->length(); + message_length = message->length(); + length = 8 + name_length + message_length; + + filename_length = filename->length(); + if (filename_length != 0) { + /* append filename as ``, {filename}'' */ + length += 2 + filename_length; + if (lineno_as_str) { + /* append lineno as ``, {lineno_as_str}'' */ + length += 2 + lineno_length; + } + } else { + if (lineno_as_str) { + /* + * no filename, but have line number, + * need to append ``, "", {lineno_as_str}'' + */ + length += 6 + lineno_length; + } + } + + cp = chars = (jschar *) cx->malloc((length + 1) * sizeof(jschar)); + if (!chars) { + ok = JS_FALSE; + goto out; + } + + *cp++ = '('; *cp++ = 'n'; *cp++ = 'e'; *cp++ = 'w'; *cp++ = ' '; + js_strncpy(cp, name->chars(), name_length); + cp += name_length; + *cp++ = '('; + if (message_length != 0) { + js_strncpy(cp, message->chars(), message_length); + cp += message_length; + } + + if (filename_length != 0) { + /* append filename as ``, {filename}'' */ + *cp++ = ','; *cp++ = ' '; + js_strncpy(cp, filename->chars(), filename_length); + cp += filename_length; + } else { + if (lineno_as_str) { + /* + * no filename, but have line number, + * need to append ``, "", {lineno_as_str}'' + */ + *cp++ = ','; *cp++ = ' '; *cp++ = '"'; *cp++ = '"'; + } + } + if (lineno_as_str) { + /* append lineno as ``, {lineno_as_str}'' */ + *cp++ = ','; *cp++ = ' '; + js_strncpy(cp, lineno_as_str->chars(), lineno_length); + cp += lineno_length; + } + + *cp++ = ')'; *cp++ = ')'; *cp = 0; + + result = js_NewString(cx, chars, length); + if (!result) { + cx->free(chars); + ok = JS_FALSE; + goto out; + } + *vp = STRING_TO_JSVAL(result); + ok = JS_TRUE; + +out: + JS_POP_TEMP_ROOT(cx, &tvr); + return ok; +} +#endif + +static JSFunctionSpec exception_methods[] = { +#if JS_HAS_TOSOURCE + JS_FN(js_toSource_str, exn_toSource, 0,0), +#endif + JS_FN(js_toString_str, exn_toString, 0,0), + JS_FS_END +}; + +/* JSProto_ ordering for exceptions shall match JSEXN_ constants. */ +JS_STATIC_ASSERT(JSEXN_ERR == 0); +JS_STATIC_ASSERT(JSProto_Error + JSEXN_INTERNALERR == JSProto_InternalError); +JS_STATIC_ASSERT(JSProto_Error + JSEXN_EVALERR == JSProto_EvalError); +JS_STATIC_ASSERT(JSProto_Error + JSEXN_RANGEERR == JSProto_RangeError); +JS_STATIC_ASSERT(JSProto_Error + JSEXN_REFERENCEERR == JSProto_ReferenceError); +JS_STATIC_ASSERT(JSProto_Error + JSEXN_SYNTAXERR == JSProto_SyntaxError); +JS_STATIC_ASSERT(JSProto_Error + JSEXN_TYPEERR == JSProto_TypeError); +JS_STATIC_ASSERT(JSProto_Error + JSEXN_URIERR == JSProto_URIError); + +static JS_INLINE JSProtoKey +GetExceptionProtoKey(intN exn) +{ + JS_ASSERT(JSEXN_ERR <= exn); + JS_ASSERT(exn < JSEXN_LIMIT); + return (JSProtoKey) (JSProto_Error + exn); +} + +JSObject * +js_InitExceptionClasses(JSContext *cx, JSObject *obj) +{ + jsval roots[3]; + JSObject *obj_proto, *error_proto; + jsval empty; + + /* + * If lazy class initialization occurs for any Error subclass, then all + * classes are initialized, starting with Error. To avoid reentry and + * redundant initialization, we must not pass a null proto parameter to + * js_NewObject below, when called for the Error superclass. We need to + * ensure that Object.prototype is the proto of Error.prototype. + * + * See the equivalent code to ensure that parent_proto is non-null when + * JS_InitClass calls js_NewObject, in jsapi.c. + */ + if (!js_GetClassPrototype(cx, obj, INT_TO_JSID(JSProto_Object), + &obj_proto)) { + return NULL; + } + + memset(roots, 0, sizeof(roots)); + JSAutoTempValueRooter tvr(cx, JS_ARRAY_LENGTH(roots), roots); + +#ifdef __GNUC__ + error_proto = NULL; /* quell GCC overwarning */ +#endif + + /* Initialize the prototypes first. */ + for (intN i = JSEXN_ERR; i != JSEXN_LIMIT; i++) { + JSObject *proto; + JSProtoKey protoKey; + JSAtom *atom; + JSFunction *fun; + + /* Make the prototype for the current constructor name. */ + proto = js_NewObject(cx, &js_ErrorClass, + (i != JSEXN_ERR) ? error_proto : obj_proto, + obj); + if (!proto) + return NULL; + if (i == JSEXN_ERR) { + error_proto = proto; + roots[0] = OBJECT_TO_JSVAL(proto); + } else { + // We cannot share the root for error_proto and other prototypes + // as error_proto must be rooted until the function returns. + roots[1] = OBJECT_TO_JSVAL(proto); + } + + /* So exn_finalize knows whether to destroy private data. */ + proto->setPrivate(NULL); + + /* Make a constructor function for the current name. */ + protoKey = GetExceptionProtoKey(i); + atom = cx->runtime->atomState.classAtoms[protoKey]; + fun = js_DefineFunction(cx, obj, atom, Exception, 3, 0); + if (!fun) + return NULL; + roots[2] = OBJECT_TO_JSVAL(FUN_OBJECT(fun)); + + /* Make this constructor make objects of class Exception. */ + FUN_CLASP(fun) = &js_ErrorClass; + + /* Make the prototype and constructor links. */ + if (!js_SetClassPrototype(cx, FUN_OBJECT(fun), proto, + JSPROP_READONLY | JSPROP_PERMANENT)) { + return NULL; + } + + /* Add the name property to the prototype. */ + if (!JS_DefineProperty(cx, proto, js_name_str, ATOM_KEY(atom), + NULL, NULL, JSPROP_ENUMERATE)) { + return NULL; + } + + /* Finally, stash the constructor for later uses. */ + if (!js_SetClassObject(cx, obj, protoKey, FUN_OBJECT(fun))) + return NULL; + } + + /* + * Set default values and add methods. We do it only for Error.prototype + * as the rest of exceptions delegate to it. + */ + empty = STRING_TO_JSVAL(cx->runtime->emptyString); + if (!JS_DefineProperty(cx, error_proto, js_message_str, empty, + NULL, NULL, JSPROP_ENUMERATE) || + !JS_DefineProperty(cx, error_proto, js_fileName_str, empty, + NULL, NULL, JSPROP_ENUMERATE) || + !JS_DefineProperty(cx, error_proto, js_lineNumber_str, JSVAL_ZERO, + NULL, NULL, JSPROP_ENUMERATE) || + !JS_DefineFunctions(cx, error_proto, exception_methods)) { + return NULL; + } + + return error_proto; +} + +const JSErrorFormatString* +js_GetLocalizedErrorMessage(JSContext* cx, void *userRef, const char *locale, + const uintN errorNumber) +{ + const JSErrorFormatString *errorString = NULL; + + if (cx->localeCallbacks && cx->localeCallbacks->localeGetErrorMessage) { + errorString = cx->localeCallbacks + ->localeGetErrorMessage(userRef, locale, errorNumber); + } + if (!errorString) + errorString = js_GetErrorMessage(userRef, locale, errorNumber); + return errorString; +} + +#if defined ( DEBUG_mccabe ) && defined ( PRINTNAMES ) +/* For use below... get character strings for error name and exception name */ +static struct exnname { char *name; char *exception; } errortoexnname[] = { +#define MSG_DEF(name, number, count, exception, format) \ + {#name, #exception}, +#include "js.msg" +#undef MSG_DEF +}; +#endif /* DEBUG */ + +JSBool +js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp) +{ + JSErrNum errorNumber; + const JSErrorFormatString *errorString; + JSExnType exn; + jsval tv[4]; + JSTempValueRooter tvr; + JSBool ok; + JSObject *errProto, *errObject; + JSString *messageStr, *filenameStr; + + /* + * Tell our caller to report immediately if this report is just a warning. + */ + JS_ASSERT(reportp); + if (JSREPORT_IS_WARNING(reportp->flags)) + return JS_FALSE; + + /* Find the exception index associated with this error. */ + errorNumber = (JSErrNum) reportp->errorNumber; + errorString = js_GetLocalizedErrorMessage(cx, NULL, NULL, errorNumber); + exn = errorString ? (JSExnType) errorString->exnType : JSEXN_NONE; + JS_ASSERT(exn < JSEXN_LIMIT); + +#if defined( DEBUG_mccabe ) && defined ( PRINTNAMES ) + /* Print the error name and the associated exception name to stderr */ + fprintf(stderr, "%s\t%s\n", + errortoexnname[errorNumber].name, + errortoexnname[errorNumber].exception); +#endif + + /* + * Return false (no exception raised) if no exception is associated + * with the given error number. + */ + if (exn == JSEXN_NONE) + return JS_FALSE; + + /* + * Prevent runaway recursion, via cx->generatingError. If an out-of-memory + * error occurs, no exception object will be created, but we don't assume + * that OOM is the only kind of error that subroutines of this function + * called below might raise. + */ + if (cx->generatingError) + return JS_FALSE; + + MUST_FLOW_THROUGH("out"); + cx->generatingError = JS_TRUE; + + /* Protect the newly-created strings below from nesting GCs. */ + memset(tv, 0, sizeof tv); + JS_PUSH_TEMP_ROOT(cx, JS_ARRAY_LENGTH(tv), tv, &tvr); + + /* + * Try to get an appropriate prototype by looking up the corresponding + * exception constructor name in the scope chain of the current context's + * top stack frame, or in the global object if no frame is active. + */ + ok = js_GetClassPrototype(cx, NULL, INT_TO_JSID(GetExceptionProtoKey(exn)), + &errProto); + if (!ok) + goto out; + tv[0] = OBJECT_TO_JSVAL(errProto); + + errObject = js_NewObject(cx, &js_ErrorClass, errProto, NULL); + if (!errObject) { + ok = JS_FALSE; + goto out; + } + tv[1] = OBJECT_TO_JSVAL(errObject); + + messageStr = JS_NewStringCopyZ(cx, message); + if (!messageStr) { + ok = JS_FALSE; + goto out; + } + tv[2] = STRING_TO_JSVAL(messageStr); + + filenameStr = JS_NewStringCopyZ(cx, reportp->filename); + if (!filenameStr) { + ok = JS_FALSE; + goto out; + } + tv[3] = STRING_TO_JSVAL(filenameStr); + + ok = InitExnPrivate(cx, errObject, messageStr, filenameStr, + reportp->lineno, reportp); + if (!ok) + goto out; + + JS_SetPendingException(cx, OBJECT_TO_JSVAL(errObject)); + + /* Flag the error report passed in to indicate an exception was raised. */ + reportp->flags |= JSREPORT_EXCEPTION; + +out: + JS_POP_TEMP_ROOT(cx, &tvr); + cx->generatingError = JS_FALSE; + return ok; +} + +JSBool +js_ReportUncaughtException(JSContext *cx) +{ + jsval exn; + JSObject *exnObject; + jsval roots[5]; + JSTempValueRooter tvr; + JSErrorReport *reportp, report; + JSString *str; + const char *bytes; + JSBool ok; + + if (!JS_IsExceptionPending(cx)) + return JS_TRUE; + + if (!JS_GetPendingException(cx, &exn)) + return JS_FALSE; + + memset(roots, 0, sizeof roots); + JS_PUSH_TEMP_ROOT(cx, JS_ARRAY_LENGTH(roots), roots, &tvr); + + /* + * Because js_ValueToString below could error and an exception object + * could become unrooted, we must root exnObject. Later, if exnObject is + * non-null, we need to root other intermediates, so allocate an operand + * stack segment to protect all of these values. + */ + if (JSVAL_IS_PRIMITIVE(exn)) { + exnObject = NULL; + } else { + exnObject = JSVAL_TO_OBJECT(exn); + roots[0] = exn; + } + + JS_ClearPendingException(cx); + reportp = js_ErrorFromException(cx, exn); + + /* XXX L10N angels cry once again (see also jsemit.c, /L10N gaffes/) */ + str = js_ValueToString(cx, exn); + if (!str) { + bytes = "unknown (can't convert to string)"; + } else { + roots[1] = STRING_TO_JSVAL(str); + bytes = js_GetStringBytes(cx, str); + if (!bytes) { + ok = JS_FALSE; + goto out; + } + } + ok = JS_TRUE; + + if (!reportp && + exnObject && + OBJ_GET_CLASS(cx, exnObject) == &js_ErrorClass) { + const char *filename; + uint32 lineno; + + ok = JS_GetProperty(cx, exnObject, js_message_str, &roots[2]); + if (!ok) + goto out; + if (JSVAL_IS_STRING(roots[2])) { + bytes = js_GetStringBytes(cx, JSVAL_TO_STRING(roots[2])); + if (!bytes) { + ok = JS_FALSE; + goto out; + } + } + + ok = JS_GetProperty(cx, exnObject, js_fileName_str, &roots[3]); + if (!ok) + goto out; + str = js_ValueToString(cx, roots[3]); + if (!str) { + ok = JS_FALSE; + goto out; + } + filename = StringToFilename(cx, str); + if (!filename) { + ok = JS_FALSE; + goto out; + } + + ok = JS_GetProperty(cx, exnObject, js_lineNumber_str, &roots[4]); + if (!ok) + goto out; + lineno = js_ValueToECMAUint32 (cx, &roots[4]); + ok = !JSVAL_IS_NULL(roots[4]); + if (!ok) + goto out; + + reportp = &report; + memset(&report, 0, sizeof report); + report.filename = filename; + report.lineno = (uintN) lineno; + } + + if (!reportp) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_UNCAUGHT_EXCEPTION, bytes); + } else { + /* Flag the error as an exception. */ + reportp->flags |= JSREPORT_EXCEPTION; + + /* Pass the exception object. */ + JS_SetPendingException(cx, exn); + js_ReportErrorAgain(cx, bytes, reportp); + JS_ClearPendingException(cx); + } + +out: + JS_POP_TEMP_ROOT(cx, &tvr); + return ok; +} diff --git a/ape-server/deps/js/src/jsexn.h b/ape-server/deps/js/src/jsexn.h new file mode 100755 index 0000000..867586d --- /dev/null +++ b/ape-server/deps/js/src/jsexn.h @@ -0,0 +1,96 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * JS runtime exception classes. + */ + +#ifndef jsexn_h___ +#define jsexn_h___ + +JS_BEGIN_EXTERN_C + +extern JSClass js_ErrorClass; + +/* + * Initialize the exception constructor/prototype hierarchy. + */ +extern JSObject * +js_InitExceptionClasses(JSContext *cx, JSObject *obj); + +/* + * Given a JSErrorReport, check to see if there is an exception associated with + * the error number. If there is, then create an appropriate exception object, + * set it as the pending exception, and set the JSREPORT_EXCEPTION flag on the + * error report. Exception-aware host error reporters should probably ignore + * error reports so flagged. Returns JS_TRUE if an associated exception is + * found and set, JS_FALSE otherwise. + */ +extern JSBool +js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp); + +/* + * Called if a JS API call to js_Execute or js_InternalCall fails; calls the + * error reporter with the error report associated with any uncaught exception + * that has been raised. Returns true if there was an exception pending, and + * the error reporter was actually called. + * + * The JSErrorReport * that the error reporter is called with is currently + * associated with a JavaScript object, and is not guaranteed to persist after + * the object is collected. Any persistent uses of the JSErrorReport contents + * should make their own copy. + * + * The flags field of the JSErrorReport will have the JSREPORT_EXCEPTION flag + * set; embeddings that want to silently propagate JavaScript exceptions to + * other contexts may want to use an error reporter that ignores errors with + * this flag. + */ +extern JSBool +js_ReportUncaughtException(JSContext *cx); + +extern JSErrorReport * +js_ErrorFromException(JSContext *cx, jsval exn); + +extern const JSErrorFormatString * +js_GetLocalizedErrorMessage(JSContext* cx, void *userRef, const char *locale, + const uintN errorNumber); + +JS_END_EXTERN_C + +#endif /* jsexn_h___ */ diff --git a/ape-server/deps/js/src/jsfile.cpp b/ape-server/deps/js/src/jsfile.cpp new file mode 100755 index 0000000..77f3d1c --- /dev/null +++ b/ape-server/deps/js/src/jsfile.cpp @@ -0,0 +1,2767 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * JS File object + */ +#if JS_HAS_FILE_OBJECT + +#include "jsfile.h" +#include "jsstdint.h" + +/* ----------------- Platform-specific includes and defines ----------------- */ +#if defined(XP_WIN) || defined(XP_OS2) +# include +# include +# include +# include +# define FILESEPARATOR '\\' +# define FILESEPARATOR2 '/' +# define CURRENT_DIR "c:\\" +# define POPEN _popen +# define PCLOSE _pclose +#elif defined(SYMBIAN) +# include +# include +# include +# include +# include +# define FILESEPARATOR '\\' +# define FILESEPARATOR2 '/' +# define CURRENT_DIR "c:\\" +# define POPEN popen +# define PCLOSE pclose +#elif defined(XP_UNIX) || defined(XP_BEOS) +# include +# include +# include +# include +# include +# define FILESEPARATOR '/' +# define FILESEPARATOR2 '\0' +# define CURRENT_DIR "/" +# define POPEN popen +# define PCLOSE pclose +#endif + +/* --------------- Platform-independent includes and defines ---------------- */ +#include "jsapi.h" +#include "jsatom.h" +#include "jscntxt.h" +#include "jsdate.h" +#include "jsdbgapi.h" +#include "jsemit.h" +#include "jsfun.h" +#include "jslock.h" +#include "jsobj.h" +#include "jsparse.h" +#include "jsscan.h" +#include "jsscope.h" +#include "jsscript.h" +#include "jsstr.h" +#include "jsutil.h" /* Added by JSIFY */ +#include + +/* NSPR dependencies */ +#include "prio.h" +#include "prerror.h" + +#define SPECIAL_FILE_STRING "Special File" +#define CURRENTDIR_PROPERTY "currentDir" +#define SEPARATOR_PROPERTY "separator" +#define FILE_CONSTRUCTOR "File" +#define PIPE_SYMBOL '|' + +#define ASCII 0 +#define UTF8 1 +#define UCS2 2 + +#define asciistring "text" +#define utfstring "binary" +#define unicodestring "unicode" + +#ifdef PATH_MAX +#define MAX_PATH_LENGTH PATH_MAX +#else +#define MAX_PATH_LENGTH 1024 +#endif +#define MODE_SIZE 256 +#define NUMBER_SIZE 32 +#define MAX_LINE_LENGTH 256 +#define URL_PREFIX "file://" + +#define STDINPUT_NAME "Standard input stream" +#define STDOUTPUT_NAME "Standard output stream" +#define STDERROR_NAME "Standard error stream" + +#define RESOLVE_PATH js_canonicalPath /* js_absolutePath */ + +/* Error handling */ +typedef enum JSFileErrNum { +#define MSG_DEF(name, number, count, exception, format) \ + name = number, +#include "jsfile.msg" +#undef MSG_DEF + JSFileErr_Limit +#undef MSGDEF +} JSFileErrNum; + +#define JSFILE_HAS_DFLT_MSG_STRINGS 1 + +JSErrorFormatString JSFile_ErrorFormatString[JSFileErr_Limit] = { +#if JSFILE_HAS_DFLT_MSG_STRINGS +#define MSG_DEF(name, number, count, exception, format) \ + { format, count }, +#else +#define MSG_DEF(name, number, count, exception, format) \ + { NULL, count }, +#endif +#include "jsfile.msg" +#undef MSG_DEF +}; + +const JSErrorFormatString * +JSFile_GetErrorMessage(void *userRef, const char *locale, + const uintN errorNumber) +{ + if ((errorNumber > 0) && (errorNumber < JSFileErr_Limit)) + return &JSFile_ErrorFormatString[errorNumber]; + else + return NULL; +} + +#define JSFILE_CHECK_NATIVE(op) \ + if (file->isNative) { \ + JS_ReportWarning(cx, "Cannot call or access \"%s\" on native file %s",\ + op, file->path); \ + goto out; \ + } + +#define JSFILE_CHECK_WRITE \ + if (!file->isOpen) { \ + JS_ReportWarning(cx, \ + "File %s is closed, will open it for writing, proceeding", \ + file->path); \ + js_FileOpen(cx, obj, file, "write,append,create"); \ + } \ + if (!js_canWrite(cx, file)) { \ + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \ + JSFILEMSG_CANNOT_WRITE, file->path); \ + goto out; \ + } + +#define JSFILE_CHECK_READ \ + if (!file->isOpen) { \ + JS_ReportWarning(cx, \ + "File %s is closed, will open it for reading, proceeding", \ + file->path); \ + js_FileOpen(cx, obj, file, "read"); \ + } \ + if (!js_canRead(cx, file)) { \ + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \ + JSFILEMSG_CANNOT_READ, file->path); \ + goto out; \ + } + +#define JSFILE_CHECK_OPEN(op) \ + if (!file->isOpen) { \ + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \ + JSFILEMSG_FILE_MUST_BE_OPEN, op); \ + goto out; \ + } + +#define JSFILE_CHECK_CLOSED(op) \ + if (file->isOpen) { \ + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \ + JSFILEMSG_FILE_MUST_BE_CLOSED, op); \ + goto out; \ + } + +#define JSFILE_CHECK_ONE_ARG(op) \ + if (argc != 1) { \ + char str[NUMBER_SIZE]; \ + sprintf(str, "%d", argc); \ + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \ + JSFILEMSG_EXPECTS_ONE_ARG_ERROR, op, str); \ + goto out; \ + } + + +/* + Security mechanism, should define a callback for this. + The parameters are as follows: + SECURITY_CHECK(JSContext *cx, JSPrincipals *ps, char *op_name, JSFile *file) + XXX Should this be a real function returning a JSBool result (and getting + some typesafety help from the compiler?). +*/ +#define SECURITY_CHECK(cx, ps, op, file) \ + /* Define a callback here... */ + + +/* Structure representing the file internally */ +typedef struct JSFile { + char *path; /* the path to the file. */ + JSBool isOpen; + int32 mode; /* mode used to open the file: read, write, append, create, etc.. */ + int32 type; /* Asciiz, utf, unicode */ + char byteBuffer[3]; /* bytes read in advance by js_FileRead ( UTF8 encoding ) */ + jsint nbBytesInBuf; /* number of bytes stored in the buffer above */ + jschar charBuffer; /* character read in advance by readln ( mac files only ) */ + JSBool charBufferUsed; /* flag indicating if the buffer above is being used */ + JSBool hasRandomAccess;/* can the file be randomly accessed? false for stdin, and + UTF-encoded files. */ + JSBool hasAutoflush; /* should we force a flush for each line break? */ + JSBool isNative; /* if the file is using OS-specific file FILE type */ + /* We can actually put the following two in a union since they should never be used at the same time */ + PRFileDesc *handle; /* the handle for the file, if open. */ + FILE *nativehandle; /* native handle, for stuff NSPR doesn't do. */ + JSBool isPipe; /* if the file is really an OS pipe */ +} JSFile; + +/* a few forward declarations... */ +JS_PUBLIC_API(JSObject*) js_NewFileObject(JSContext *cx, char *filename); +static JSBool file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); +static JSBool file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); + +/* New filename manipulation procesures */ +/* assumes we don't have leading/trailing spaces */ +static JSBool +js_filenameHasAPipe(const char *filename) +{ + if (!filename) + return JS_FALSE; + + return filename[0] == PIPE_SYMBOL || + filename[strlen(filename) - 1] == PIPE_SYMBOL; +} + +static JSBool +js_isAbsolute(const char *name) +{ +#if defined(XP_WIN) || defined(XP_OS2) || defined(SYMBIAN) + return *name && name[1] == ':'; +#else + return (name[0] +# if defined(XP_UNIX) || defined(XP_BEOS) + == +# else + != +# endif + FILESEPARATOR); +#endif +} + +/* + * Concatenates base and name to produce a valid filename. + * Returned string must be freed. +*/ +static char* +js_combinePath(JSContext *cx, const char *base, const char *name) +{ + int len = strlen(base); + char* result = cx->malloc(len + strlen(name) + 2); + + if (!result) + return NULL; + + strcpy(result, base); + + if (base[len - 1] != FILESEPARATOR && base[len - 1] != FILESEPARATOR2) { + result[len] = FILESEPARATOR; + result[len + 1] = '\0'; + } + strcat(result, name); + return result; +} + +/* Extract the last component from a path name. Returned string must be freed */ +static char * +js_fileBaseName(JSContext *cx, const char *pathname) +{ + jsint index, aux; + char *result; + + index = strlen(pathname)-1; + + /* Chop off trailing separators. */ + while (index > 0 && (pathname[index]==FILESEPARATOR || + pathname[index]==FILESEPARATOR2)) { + --index; + } + + aux = index; + + /* Now find the next separator. */ + while (index >= 0 && pathname[index] != FILESEPARATOR && + pathname[index] != FILESEPARATOR2) { + --index; + } + + /* Allocate and copy. */ + result = cx->malloc(aux - index + 1); + if (!result) + return NULL; + strncpy(result, pathname + index + 1, aux - index); + result[aux - index] = '\0'; + return result; +} + +/* + * Returns everything but the last component from a path name. + * Returned string must be freed. + */ +static char * +js_fileDirectoryName(JSContext *cx, const char *pathname) +{ + char *result; + const char *cp, *end; + size_t pathsize; + + end = pathname + strlen(pathname); + cp = end - 1; + + /* If this is already a directory, chop off the trailing /s. */ + while (cp >= pathname) { + if (*cp != FILESEPARATOR && *cp != FILESEPARATOR2) + break; + --cp; + } + + if (cp < pathname && end != pathname) { + /* There were just /s, return the root. */ + result = cx->malloc(1 + 1); /* The separator + trailing NUL. */ + result[0] = FILESEPARATOR; + result[1] = '\0'; + return result; + } + + /* Now chop off the last portion. */ + while (cp >= pathname) { + if (*cp == FILESEPARATOR || *cp == FILESEPARATOR2) + break; + --cp; + } + + /* Check if this is a leaf. */ + if (cp < pathname) { + /* It is, return "pathname/". */ + if (end[-1] == FILESEPARATOR || end[-1] == FILESEPARATOR2) { + /* Already has its terminating /. */ + return JS_strdup(cx, pathname); + } + + pathsize = end - pathname + 1; + result = cx->malloc(pathsize + 1); + if (!result) + return NULL; + + strcpy(result, pathname); + result[pathsize - 1] = FILESEPARATOR; + result[pathsize] = '\0'; + + return result; + } + + /* Return everything up to and including the seperator. */ + pathsize = cp - pathname + 1; + result = cx->malloc(pathsize + 1); + if (!result) + return NULL; + + strncpy(result, pathname, pathsize); + result[pathsize] = '\0'; + + return result; +} + +static char * +js_absolutePath(JSContext *cx, const char * path) +{ + JSObject *obj; + JSString *str; + jsval prop; + + if (js_isAbsolute(path)) { + return JS_strdup(cx, path); + } else { + obj = JS_GetGlobalObject(cx); + if (!JS_GetProperty(cx, obj, FILE_CONSTRUCTOR, &prop)) { + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_FILE_CONSTRUCTOR_UNDEFINED_ERROR); + return JS_strdup(cx, path); + } + + obj = JSVAL_TO_OBJECT(prop); + if (!JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, &prop)) { + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_FILE_CURRENTDIR_UNDEFINED_ERROR); + return JS_strdup(cx, path); + } + + str = JS_ValueToString(cx, prop); + if (!str) + return JS_strdup(cx, path); + + /* should we have an array of curr dirs indexed by drive for windows? */ + return js_combinePath(cx, JS_GetStringBytes(str), path); + } +} + +/* Side effect: will remove spaces in the beginning/end of the filename */ +static char * +js_canonicalPath(JSContext *cx, char *oldpath) +{ + char *tmp; + char *path = oldpath; + char *base, *dir, *current, *result; + jsint c; + jsint back = 0; + unsigned int i = 0, j = strlen(path)-1; + + /* This is probably optional */ + /* Remove possible spaces in the beginning and end */ + while (i < j && path[i] == ' ') + i++; + while (j >= 0 && path[j] == ' ') + j--; + + tmp = cx->malloc(j-i+2); + if (!tmp) + return NULL; + + strncpy(tmp, path + i, j - i + 1); + tmp[j - i + 1] = '\0'; + + path = tmp; + + /* Pipe support. */ + if (js_filenameHasAPipe(path)) + return path; + + /* file:// support. */ + if (!strncmp(path, URL_PREFIX, strlen(URL_PREFIX))) { + tmp = js_canonicalPath(cx, path + strlen(URL_PREFIX)); + cx->free(path); + return tmp; + } + + if (!js_isAbsolute(path)) { + tmp = js_absolutePath(cx, path); + if (!tmp) + return NULL; + cx->free(path); + path = tmp; + } + + result = JS_strdup(cx, ""); + + current = path; + + base = js_fileBaseName(cx, current); + dir = js_fileDirectoryName(cx, current); + + while (strcmp(dir, current)) { + if (!strcmp(base, "..")) { + back++; + } else { + if (back > 0) { + back--; + } else { + tmp = result; + result = cx->malloc(strlen(base) + 1 + strlen(tmp) + 1); + if (!result) + goto out; + + strcpy(result, base); + c = strlen(result); + if (*tmp) { + result[c] = FILESEPARATOR; + result[c + 1] = '\0'; + strcat(result, tmp); + } + cx->free(tmp); + } + } + cx->free(current); + cx->free(base); + current = dir; + base = js_fileBaseName(cx, current); + dir = js_fileDirectoryName(cx, current); + } + + tmp = result; + result = cx->malloc(strlen(dir) + 1 + strlen(tmp) + 1); + if (!result) + goto out; + + strcpy(result, dir); + c = strlen(result); + if (tmp[0]!='\0') { + if ((result[c-1]!=FILESEPARATOR)&&(result[c-1]!=FILESEPARATOR2)) { + result[c] = FILESEPARATOR; + result[c+1] = '\0'; + } + strcat(result, tmp); + } + +out: + if (tmp) + cx->free(tmp); + if (dir) + cx->free(dir); + if (base) + cx->free(base); + if (current) + cx->free(current); + + return result; +} + +/* -------------------------- Text conversion ------------------------------- */ +/* The following is ripped from libi18n/unicvt.c and include files.. */ + +/* + * UTF8 defines and macros + */ +#define ONE_OCTET_BASE 0x00 /* 0xxxxxxx */ +#define ONE_OCTET_MASK 0x7F /* x1111111 */ +#define CONTINUING_OCTET_BASE 0x80 /* 10xxxxxx */ +#define CONTINUING_OCTET_MASK 0x3F /* 00111111 */ +#define TWO_OCTET_BASE 0xC0 /* 110xxxxx */ +#define TWO_OCTET_MASK 0x1F /* 00011111 */ +#define THREE_OCTET_BASE 0xE0 /* 1110xxxx */ +#define THREE_OCTET_MASK 0x0F /* 00001111 */ +#define FOUR_OCTET_BASE 0xF0 /* 11110xxx */ +#define FOUR_OCTET_MASK 0x07 /* 00000111 */ +#define FIVE_OCTET_BASE 0xF8 /* 111110xx */ +#define FIVE_OCTET_MASK 0x03 /* 00000011 */ +#define SIX_OCTET_BASE 0xFC /* 1111110x */ +#define SIX_OCTET_MASK 0x01 /* 00000001 */ + +#define IS_UTF8_1ST_OF_1(x) (( (x)&~ONE_OCTET_MASK ) == ONE_OCTET_BASE) +#define IS_UTF8_1ST_OF_2(x) (( (x)&~TWO_OCTET_MASK ) == TWO_OCTET_BASE) +#define IS_UTF8_1ST_OF_3(x) (( (x)&~THREE_OCTET_MASK) == THREE_OCTET_BASE) +#define IS_UTF8_1ST_OF_4(x) (( (x)&~FOUR_OCTET_MASK ) == FOUR_OCTET_BASE) +#define IS_UTF8_1ST_OF_5(x) (( (x)&~FIVE_OCTET_MASK ) == FIVE_OCTET_BASE) +#define IS_UTF8_1ST_OF_6(x) (( (x)&~SIX_OCTET_MASK ) == SIX_OCTET_BASE) +#define IS_UTF8_2ND_THRU_6TH(x) \ + (( (x)&~CONTINUING_OCTET_MASK ) == CONTINUING_OCTET_BASE) +#define IS_UTF8_1ST_OF_UCS2(x) \ + IS_UTF8_1ST_OF_1(x) \ + || IS_UTF8_1ST_OF_2(x) \ + || IS_UTF8_1ST_OF_3(x) + + +#define MAX_UCS2 0xFFFF +#define DEFAULT_CHAR 0x003F /* Default char is "?" */ +#define BYTE_MASK 0xBF +#define BYTE_MARK 0x80 + + +/* Function: one_ucs2_to_utf8_char + * + * Function takes one UCS-2 char and writes it to a UTF-8 buffer. + * We need a UTF-8 buffer because we don't know before this + * function how many bytes of utf-8 data will be written. It also + * takes a pointer to the end of the UTF-8 buffer so that we don't + * overwrite data. This function returns the number of UTF-8 bytes + * of data written, or -1 if the buffer would have been overrun. + */ + +#define LINE_SEPARATOR 0x2028 +#define PARAGRAPH_SEPARATOR 0x2029 +static int16 one_ucs2_to_utf8_char(unsigned char *tobufp, + unsigned char *tobufendp, + uint16 onechar) +{ + int16 numUTF8bytes = 0; + + if (onechar == LINE_SEPARATOR || onechar == PARAGRAPH_SEPARATOR) { + strcpy((char*)tobufp, "\n"); + return strlen((char*)tobufp); + } + + if (onechar < 0x80) { + numUTF8bytes = 1; + } else if (onechar < 0x800) { + numUTF8bytes = 2; + } else { + /* 0x800 >= onechar <= MAX_UCS2 */ + numUTF8bytes = 3; + } + + tobufp += numUTF8bytes; + + /* return error if we don't have space for the whole character */ + if (tobufp > tobufendp) { + return(-1); + } + + switch(numUTF8bytes) { + case 3: *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6; + *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6; + *--tobufp = onechar | THREE_OCTET_BASE; + break; + + case 2: *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6; + *--tobufp = onechar | TWO_OCTET_BASE; + break; + + case 1: *--tobufp = (unsigned char)onechar; + break; + } + + return numUTF8bytes; +} + +/* + * utf8_to_ucs2_char + * + * Convert a utf8 multibyte character to ucs2 + * + * inputs: pointer to utf8 character(s) + * length of utf8 buffer ("read" length limit) + * pointer to return ucs2 character + * + * outputs: number of bytes in the utf8 character + * -1 if not a valid utf8 character sequence + * -2 if the buffer is too short + */ +static int16 +utf8_to_ucs2_char(const unsigned char *utf8p, int16 buflen, uint16 *ucs2p) +{ + uint16 lead, cont1, cont2; + + /* + * Check for minimum buffer length + */ + if ((buflen < 1) || (utf8p == NULL)) { + return -2; + } + lead = (uint16) (*utf8p); + + /* + * Check for a one octet sequence + */ + if (IS_UTF8_1ST_OF_1(lead)) { + *ucs2p = lead & ONE_OCTET_MASK; + return 1; + } + + /* + * Check for a two octet sequence + */ + if (IS_UTF8_1ST_OF_2(*utf8p)) { + if (buflen < 2) + return -2; + cont1 = (uint16) *(utf8p+1); + if (!IS_UTF8_2ND_THRU_6TH(cont1)) + return -1; + *ucs2p = (lead & TWO_OCTET_MASK) << 6; + *ucs2p |= cont1 & CONTINUING_OCTET_MASK; + return 2; + } + + /* + * Check for a three octet sequence + */ + else if (IS_UTF8_1ST_OF_3(lead)) { + if (buflen < 3) + return -2; + cont1 = (uint16) *(utf8p+1); + cont2 = (uint16) *(utf8p+2); + if ( (!IS_UTF8_2ND_THRU_6TH(cont1)) + || (!IS_UTF8_2ND_THRU_6TH(cont2))) + return -1; + *ucs2p = (lead & THREE_OCTET_MASK) << 12; + *ucs2p |= (cont1 & CONTINUING_OCTET_MASK) << 6; + *ucs2p |= cont2 & CONTINUING_OCTET_MASK; + return 3; + } + else { /* not a valid utf8/ucs2 character */ + return -1; + } +} + +/* ----------------------------- Helper functions --------------------------- */ +/* Ripped off from lm_win.c .. */ +/* where is strcasecmp?.. for now, it's case sensitive.. + * + * strcasecmp is in strings.h, but on windows it's called _stricmp... + * will need to #ifdef this +*/ + +static int32 +js_FileHasOption(JSContext *cx, const char *oldoptions, const char *name) +{ + char *comma, *equal, *current; + char *options = JS_strdup(cx, oldoptions); + int32 found = 0; + + current = options; + for (;;) { + comma = strchr(current, ','); + if (comma) *comma = '\0'; + equal = strchr(current, '='); + if (equal) *equal = '\0'; + if (strcmp(current, name) == 0) { + if (!equal || strcmp(equal + 1, "yes") == 0) + found = 1; + else + found = atoi(equal + 1); + } + if (equal) *equal = '='; + if (comma) *comma = ','; + if (found || !comma) + break; + current = comma + 1; + } + cx->free(options); + return found; +} + +/* empty the buffer */ +static void +js_ResetBuffers(JSFile * file) +{ + file->charBufferUsed = JS_FALSE; + file->nbBytesInBuf = 0; +} + +/* Reset file attributes */ +static void +js_ResetAttributes(JSFile * file) +{ + file->mode = file->type = 0; + file->isOpen = JS_FALSE; + file->handle = NULL; + file->nativehandle = NULL; + file->hasRandomAccess = JS_TRUE; /* Innocent until proven guilty. */ + file->hasAutoflush = JS_FALSE; + file->isNative = JS_FALSE; + file->isPipe = JS_FALSE; + + js_ResetBuffers(file); +} + +static JSBool +js_FileOpen(JSContext *cx, JSObject *obj, JSFile *file, char *mode){ + JSString *type, *mask; + jsval v[2]; + jsval rval; + + type = JS_InternString(cx, asciistring); + mask = JS_NewStringCopyZ(cx, mode); + v[0] = STRING_TO_JSVAL(mask); + v[1] = STRING_TO_JSVAL(type); + + if (!file_open(cx, obj, 2, v, &rval)) + return JS_FALSE; + return JS_TRUE; +} + +/* Buffered version of PR_Read. Used by js_FileRead */ +static int32 +js_BufferedRead(JSFile *f, unsigned char *buf, int32 len) +{ + int32 count = 0; + + while (f->nbBytesInBuf>0&&len>0) { + buf[0] = f->byteBuffer[0]; + f->byteBuffer[0] = f->byteBuffer[1]; + f->byteBuffer[1] = f->byteBuffer[2]; + f->nbBytesInBuf--; + len--; + buf+=1; + count++; + } + + if (len > 0) { + count += (!f->isNative) + ? PR_Read(f->handle, buf, len) + : fread(buf, 1, len, f->nativehandle); + } + return count; +} + +static int32 +js_FileRead(JSContext *cx, JSFile *file, jschar *buf, int32 len, int32 mode) +{ + unsigned char *aux; + int32 count = 0, i; + jsint remainder; + unsigned char utfbuf[3]; + + if (file->charBufferUsed) { + buf[0] = file->charBuffer; + buf++; + len--; + file->charBufferUsed = JS_FALSE; + } + + switch (mode) { + case ASCII: + aux = (unsigned char*)cx->malloc(len); + if (!aux) + return 0; + + count = js_BufferedRead(file, aux, len); + if (count == -1) { + cx->free(aux); + return 0; + } + + for (i = 0; i < len; i++) + buf[i] = (jschar)aux[i]; + + cx->free(aux); + break; + + case UTF8: + remainder = 0; + for (count = 0;count0) { + file->byteBuffer[file->nbBytesInBuf] = utfbuf[0]; + file->nbBytesInBuf++; + utfbuf[0] = utfbuf[1]; + utfbuf[1] = utfbuf[2]; + remainder--; + } + break; + + case UCS2: + count = js_BufferedRead(file, (unsigned char *)buf, len * 2) >> 1; + if (count == -1) + return 0; + + break; + + default: + /* Not reached. */ + JS_ASSERT(0); + } + + if(count == -1) { + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_OP_FAILED, "read", file->path); + } + + return count; +} + +static int32 +js_FileSeek(JSContext *cx, JSFile *file, int32 len, int32 mode) +{ + int32 count = 0, i; + jsint remainder; + unsigned char utfbuf[3]; + jschar tmp; + + switch (mode) { + case ASCII: + count = PR_Seek(file->handle, len, PR_SEEK_CUR); + break; + + case UTF8: + remainder = 0; + for (count = 0;count0) { + file->byteBuffer[file->nbBytesInBuf] = utfbuf[0]; + file->nbBytesInBuf++; + utfbuf[0] = utfbuf[1]; + utfbuf[1] = utfbuf[2]; + remainder--; + } + break; + + case UCS2: + count = PR_Seek(file->handle, len*2, PR_SEEK_CUR)/2; + break; + + default: + /* Not reached. */ + JS_ASSERT(0); + } + + if(count == -1) { + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_OP_FAILED, "seek", file->path); + } + + return count; +} + +static int32 +js_FileWrite(JSContext *cx, JSFile *file, jschar *buf, int32 len, int32 mode) +{ + unsigned char *aux; + int32 count = 0, i, j; + unsigned char *utfbuf; + + switch (mode) { + case ASCII: + aux = (unsigned char*)cx->malloc(len); + if (!aux) + return 0; + + for (i = 0; iisNative) + ? PR_Write(file->handle, aux, len) + : fwrite(aux, 1, len, file->nativehandle); + + if (count==-1) { + cx->free(aux); + return 0; + } + + cx->free(aux); + break; + + case UTF8: + utfbuf = (unsigned char*)cx->malloc(len*3); + if (!utfbuf) return 0; + i = 0; + for (count = 0;countfree(utfbuf); + return 0; + } + i+=j; + } + j = (!file->isNative) + ? PR_Write(file->handle, utfbuf, i) + : fwrite(utfbuf, 1, i, file->nativehandle); + + if (jfree(utfbuf); + return 0; + } + cx->free(utfbuf); + break; + + case UCS2: + count = (!file->isNative) + ? PR_Write(file->handle, buf, len*2) >> 1 + : fwrite(buf, 1, len*2, file->nativehandle) >> 1; + + if (count == -1) + return 0; + break; + + default: + /* Not reached. */ + JS_ASSERT(0); + } + + if(count == -1) { + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_OP_FAILED, "write", file->path); + } + + return count; +} + +/* ----------------------------- Property checkers -------------------------- */ +static JSBool +js_exists(JSContext *cx, JSFile *file) +{ + if (file->isNative) { + /* It doesn't make sense for a pipe of stdstream. */ + return JS_FALSE; + } + + return PR_Access(file->path, PR_ACCESS_EXISTS) == PR_SUCCESS; +} + +static JSBool +js_canRead(JSContext *cx, JSFile *file) +{ + if (!file->isNative) { + if (file->isOpen && !(file->mode & PR_RDONLY)) + return JS_FALSE; + return PR_Access(file->path, PR_ACCESS_READ_OK) == PR_SUCCESS; + } + + if (file->isPipe) { + /* Is this pipe open for reading? */ + return file->path[0] == PIPE_SYMBOL; + } + + return !strcmp(file->path, STDINPUT_NAME); +} + +static JSBool +js_canWrite(JSContext *cx, JSFile *file) +{ + if (!file->isNative) { + if (file->isOpen && !(file->mode & PR_WRONLY)) + return JS_FALSE; + return PR_Access(file->path, PR_ACCESS_WRITE_OK) == PR_SUCCESS; + } + + if(file->isPipe) { + /* Is this pipe open for writing? */ + return file->path[strlen(file->path)-1] == PIPE_SYMBOL; + } + + return !strcmp(file->path, STDOUTPUT_NAME) || + !strcmp(file->path, STDERROR_NAME); +} + +static JSBool +js_isFile(JSContext *cx, JSFile *file) +{ + if (!file->isNative) { + PRFileInfo info; + + if (file->isOpen + ? PR_GetOpenFileInfo(file->handle, &info) + : PR_GetFileInfo(file->path, &info) != PR_SUCCESS) { + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path); + return JS_FALSE; + } + + return info.type == PR_FILE_FILE; + } + + /* This doesn't make sense for a pipe of stdstream. */ + return JS_FALSE; +} + +static JSBool +js_isDirectory(JSContext *cx, JSFile *file) +{ + if(!file->isNative){ + PRFileInfo info; + + /* Hack needed to get get_property to work. */ + if (!js_exists(cx, file)) + return JS_FALSE; + + if (file->isOpen + ? PR_GetOpenFileInfo(file->handle, &info) + : PR_GetFileInfo(file->path, &info) != PR_SUCCESS) { + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path); + return JS_FALSE; + } + + return info.type == PR_FILE_DIRECTORY; + } + + /* This doesn't make sense for a pipe of stdstream. */ + return JS_FALSE; +} + +static jsval +js_size(JSContext *cx, JSFile *file) +{ + PRFileInfo info; + + JSFILE_CHECK_NATIVE("size"); + + if (file->isOpen + ? PR_GetOpenFileInfo(file->handle, &info) + : PR_GetFileInfo(file->path, &info) != PR_SUCCESS) { + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path); + return JSVAL_VOID; + } + + return INT_TO_JSVAL(info.size); + +out: + return JSVAL_VOID; +} + +/* + * Return the parent object + */ +static JSBool +js_parent(JSContext *cx, JSFile *file, jsval *resultp) +{ + char *str; + + /* Since we only care about pipes and native files, return NULL. */ + if (file->isNative) { + *resultp = JSVAL_VOID; + return JS_TRUE; + } + + str = js_fileDirectoryName(cx, file->path); + if (!str) + return JS_FALSE; + + /* If the directory is equal to the original path, we're at the root. */ + if (!strcmp(file->path, str)) { + *resultp = JSVAL_NULL; + } else { + JSObject *obj = js_NewFileObject(cx, str); + if (!obj) { + cx->free(str); + return JS_FALSE; + } + *resultp = OBJECT_TO_JSVAL(obj); + } + + cx->free(str); + return JS_TRUE; +} + +static JSBool +js_name(JSContext *cx, JSFile *file, jsval *vp) +{ + char *name; + JSString *str; + + if (file->isPipe) { + *vp = JSVAL_VOID; + return JS_TRUE; + } + + name = js_fileBaseName(cx, file->path); + if (!name) + return JS_FALSE; + + str = JS_NewString(cx, name, strlen(name)); + if (!str) { + cx->free(name); + return JS_FALSE; + } + + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +/* ------------------------------ File object methods ---------------------------- */ +static JSBool +file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + JSString *strmode, *strtype; + char *ctype, *mode; + int32 mask, type; + int len; + + mode = NULL; + + SECURITY_CHECK(cx, NULL, "open", file); + + /* A native file that is already open */ + if(file->isOpen && file->isNative) { + JS_ReportWarning(cx, "Native file %s is already open, proceeding", + file->path); + goto good; + } + + /* Close before proceeding */ + if (file->isOpen) { + JS_ReportWarning(cx, "File %s is already open, we will close it and " + "reopen, proceeding", file->path); + if(!file_close(cx, obj, 0, NULL, rval)) + goto out; + } + + if (js_isDirectory(cx, file)) { + JS_ReportWarning(cx, "%s seems to be a directory, there is no point in " + "trying to open it, proceeding", file->path); + goto good; + } + + /* Path must be defined at this point */ + len = strlen(file->path); + + /* Mode */ + if (argc >= 1) { + strmode = JS_ValueToString(cx, argv[0]); + if (!strmode) { + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_FIRST_ARGUMENT_OPEN_NOT_STRING_ERROR, + argv[0]); + goto out; + } + mode = JS_strdup(cx, JS_GetStringBytes(strmode)); + } else { + if(file->path[0]==PIPE_SYMBOL) { + /* pipe default mode */ + mode = JS_strdup(cx, "read"); + } else if(file->path[len-1]==PIPE_SYMBOL) { + /* pipe default mode */ + mode = JS_strdup(cx, "write"); + } else { + /* non-destructive, permissive defaults. */ + mode = JS_strdup(cx, "readWrite,append,create"); + } + } + + /* Process the mode */ + mask = 0; + /* TODO: this is pretty ugly, we walk thru the string too many times */ + mask |= js_FileHasOption(cx, mode, "read") ? PR_RDONLY : 0; + mask |= js_FileHasOption(cx, mode, "write") ? PR_WRONLY : 0; + mask |= js_FileHasOption(cx, mode, "readWrite")? PR_RDWR : 0; + mask |= js_FileHasOption(cx, mode, "append") ? PR_APPEND : 0; + mask |= js_FileHasOption(cx, mode, "create") ? PR_CREATE_FILE : 0; + mask |= js_FileHasOption(cx, mode, "replace") ? PR_TRUNCATE : 0; + + if (mask & PR_RDWR) + mask |= (PR_RDONLY | PR_WRONLY); + if ((mask & PR_RDONLY) && (mask & PR_WRONLY)) + mask |= PR_RDWR; + + file->hasAutoflush |= js_FileHasOption(cx, mode, "autoflush"); + + /* Type */ + if (argc > 1) { + strtype = JS_ValueToString(cx, argv[1]); + if (!strtype) { + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_SECOND_ARGUMENT_OPEN_NOT_STRING_ERROR, + argv[1]); + goto out; + } + ctype = JS_GetStringBytes(strtype); + + if(!strcmp(ctype, utfstring)) { + type = UTF8; + } else if (!strcmp(ctype, unicodestring)) { + type = UCS2; + } else { + if (strcmp(ctype, asciistring)) { + JS_ReportWarning(cx, "File type %s is not supported, using " + "'text' instead, proceeding", ctype); + } + type = ASCII; + } + } else { + type = ASCII; + } + + /* Save the relevant fields */ + file->type = type; + file->mode = mask; + file->nativehandle = NULL; + file->hasRandomAccess = (type != UTF8); + + /* + * Deal with pipes here. We can't use NSPR for pipes, so we have to use + * POPEN. + */ + if (file->path[0]==PIPE_SYMBOL || file->path[len-1]==PIPE_SYMBOL) { + if (file->path[0] == PIPE_SYMBOL && file->path[len-1] == PIPE_SYMBOL) { + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_BIDIRECTIONAL_PIPE_NOT_SUPPORTED); + goto out; + } else { + int i = 0; + char pipemode[3]; + SECURITY_CHECK(cx, NULL, "pipe_open", file); + + if(file->path[0] == PIPE_SYMBOL){ + if(mask & (PR_WRONLY | PR_APPEND | PR_CREATE_FILE | PR_TRUNCATE)){ + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_OPEN_MODE_NOT_SUPPORTED_WITH_PIPES, + mode, file->path); + goto out; + } + /* open(SPOOLER, "| cat -v | lpr -h 2>/dev/null") -- pipe for writing */ + pipemode[i++] = 'r'; +#ifndef XP_UNIX + pipemode[i++] = file->type==UTF8 ? 'b' : 't'; +#endif + pipemode[i++] = '\0'; + file->nativehandle = POPEN(&file->path[1], pipemode); + } else if(file->path[len-1] == PIPE_SYMBOL) { + char *command = cx->malloc(len); + + strncpy(command, file->path, len-1); + command[len-1] = '\0'; + /* open(STATUS, "netstat -an 2>&1 |") */ + pipemode[i++] = 'w'; +#ifndef XP_UNIX + pipemode[i++] = file->type==UTF8 ? 'b' : 't'; +#endif + pipemode[i++] = '\0'; + file->nativehandle = POPEN(command, pipemode); + cx->free(command); + } + /* set the flags */ + file->isNative = JS_TRUE; + file->isPipe = JS_TRUE; + file->hasRandomAccess = JS_FALSE; + } + } else { + /* TODO: what about the permissions?? Java ignores the problem... */ + file->handle = PR_Open(file->path, mask, 0644); + } + + js_ResetBuffers(file); + cx->free(mode); + mode = NULL; + + /* Set the open flag and return result */ + if (file->handle == NULL && file->nativehandle == NULL) { + file->isOpen = JS_FALSE; + + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_OP_FAILED, "open", file->path); + goto out; + } + +good: + file->isOpen = JS_TRUE; + *rval = JSVAL_TRUE; + return JS_TRUE; + +out: + if(mode) + cx->free(mode); + return JS_FALSE; +} + +static JSBool +file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + + SECURITY_CHECK(cx, NULL, "close", file); + + if(!file->isOpen){ + JS_ReportWarning(cx, "File %s is not open, can't close it, proceeding", + file->path); + goto out; + } + + if(!file->isPipe){ + if(file->isNative){ + JS_ReportWarning(cx, "Unable to close a native file, proceeding", file->path); + goto out; + }else{ + if(file->handle && PR_Close(file->handle)){ + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_OP_FAILED, "close", file->path); + + goto out; + } + } + }else{ + if(PCLOSE(file->nativehandle)==-1){ + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_OP_FAILED, "pclose", file->path); + goto out; + } + } + + js_ResetAttributes(file); + *rval = JSVAL_TRUE; + return JS_TRUE; + +out: + return JS_FALSE; +} + + +static JSBool +file_remove(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + + SECURITY_CHECK(cx, NULL, "remove", file); + JSFILE_CHECK_NATIVE("remove"); + JSFILE_CHECK_CLOSED("remove"); + + if ((js_isDirectory(cx, file) ? + PR_RmDir(file->path) : PR_Delete(file->path))==PR_SUCCESS) { + js_ResetAttributes(file); + *rval = JSVAL_TRUE; + return JS_TRUE; + } else { + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_OP_FAILED, "remove", file->path); + goto out; + } +out: + *rval = JSVAL_FALSE; + return JS_FALSE; +} + +/* Raw PR-based function. No text processing. Just raw data copying. */ +static JSBool +file_copyTo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + char *dest = NULL; + PRFileDesc *handle = NULL; + char *buffer; + jsval count, size; + JSBool fileInitiallyOpen=JS_FALSE; + + SECURITY_CHECK(cx, NULL, "copyTo", file); /* may need a second argument!*/ + JSFILE_CHECK_ONE_ARG("copyTo"); + JSFILE_CHECK_NATIVE("copyTo"); + /* remeber the state */ + fileInitiallyOpen = file->isOpen; + JSFILE_CHECK_READ; + + JSString *str = JS_ValueToString(cx, argv[0]); + if (!str) + goto out; + + dest = JS_GetStringBytes(str); + + /* make sure we are not reading a file open for writing */ + if (file->isOpen && !js_canRead(cx, file)) { + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_CANNOT_COPY_FILE_OPEN_FOR_WRITING_ERROR, file->path); + goto out; + } + + if (file->handle==NULL){ + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_OP_FAILED, "open", file->path); + goto out; + } + + handle = PR_Open(dest, PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE, 0644); + + if(!handle){ + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_OP_FAILED, "open", dest); + goto out; + } + + if ((size=js_size(cx, file))==JSVAL_VOID) { + goto out; + } + + buffer = cx->malloc(size); + + count = INT_TO_JSVAL(PR_Read(file->handle, buffer, size)); + + /* reading panic */ + if (count!=size) { + cx->free(buffer); + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_COPY_READ_ERROR, file->path); + goto out; + } + + count = INT_TO_JSVAL(PR_Write(handle, buffer, JSVAL_TO_INT(size))); + + /* writing panic */ + if (count!=size) { + cx->free(buffer); + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_COPY_WRITE_ERROR, file->path); + goto out; + } + + cx->free(buffer); + + if(!fileInitiallyOpen){ + if(!file_close(cx, obj, 0, NULL, rval)) goto out; + } + + if(PR_Close(handle)!=PR_SUCCESS){ + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_OP_FAILED, "close", dest); + goto out; + } + + *rval = JSVAL_TRUE; + return JS_TRUE; +out: + if(file->isOpen && !fileInitiallyOpen){ + if(PR_Close(file->handle)!=PR_SUCCESS){ + JS_ReportWarning(cx, "Can't close %s, proceeding", file->path); + } + } + + if(handle && PR_Close(handle)!=PR_SUCCESS){ + JS_ReportWarning(cx, "Can't close %s, proceeding", dest); + } + + *rval = JSVAL_FALSE; + return JS_FALSE; +} + +static JSBool +file_renameTo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + char *dest; + + SECURITY_CHECK(cx, NULL, "renameTo", file); /* may need a second argument!*/ + JSFILE_CHECK_ONE_ARG("renameTo"); + JSFILE_CHECK_NATIVE("renameTo"); + JSFILE_CHECK_CLOSED("renameTo"); + + JSString *str = JS_ValueToString(cx, argv[0]); + if (!str) + goto out; + + dest = RESOLVE_PATH(cx, JS_GetStringBytes(str)); + + if (PR_Rename(file->path, dest)==PR_SUCCESS){ + /* copy the new filename */ + cx->free(file->path); + file->path = dest; + *rval = JSVAL_TRUE; + return JS_TRUE; + }else{ + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_RENAME_FAILED, file->path, dest); + goto out; + } +out: + *rval = JSVAL_FALSE; + return JS_FALSE; +} + +static JSBool +file_flush(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + + SECURITY_CHECK(cx, NULL, "flush", file); + JSFILE_CHECK_NATIVE("flush"); + JSFILE_CHECK_OPEN("flush"); + + if (PR_Sync(file->handle)==PR_SUCCESS){ + *rval = JSVAL_TRUE; + return JS_TRUE; + }else{ + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_OP_FAILED, "flush", file->path); + goto out; + } +out: + *rval = JSVAL_FALSE; + return JS_FALSE; +} + +static JSBool +file_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + JSString *str; + int32 count; + uintN i; + + SECURITY_CHECK(cx, NULL, "write", file); + JSFILE_CHECK_WRITE; + + for (i = 0; itype); + if (count==-1){ + *rval = JSVAL_FALSE; + return JS_FALSE; + } + } + + *rval = JSVAL_TRUE; + return JS_TRUE; +out: + *rval = JSVAL_FALSE; + return JS_FALSE; +} + +static JSBool +file_writeln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + JSString *str; + + SECURITY_CHECK(cx, NULL, "writeln", file); + JSFILE_CHECK_WRITE; + + /* don't report an error here */ + if(!file_write(cx, obj, argc, argv, rval)) return JS_FALSE; + /* don't do security here -- we passed the check in file_write */ + str = JS_NewStringCopyZ(cx, "\n"); + + if (js_FileWrite(cx, file, JS_GetStringChars(str), JS_GetStringLength(str), + file->type)==-1){ + *rval = JSVAL_FALSE; + return JS_FALSE; + } + + /* eol causes flush if hasAutoflush is turned on */ + if (file->hasAutoflush) + file_flush(cx, obj, 0, NULL, rval); + + *rval = JSVAL_TRUE; + return JS_TRUE; +out: + *rval = JSVAL_FALSE; + return JS_FALSE; +} + +static JSBool +file_writeAll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + jsuint i; + jsuint limit; + JSObject *array; + JSObject *elem; + jsval elemval; + + SECURITY_CHECK(cx, NULL, "writeAll", file); + JSFILE_CHECK_ONE_ARG("writeAll"); + JSFILE_CHECK_WRITE; + + if (!JS_IsArrayObject(cx, JSVAL_TO_OBJECT(argv[0]))) { + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_FIRST_ARGUMENT_WRITEALL_NOT_ARRAY_ERROR); + goto out; + } + + array = JSVAL_TO_OBJECT(argv[0]); + + JS_GetArrayLength(cx, array, &limit); + + for (i = 0; i262144)?262144:want; * arbitrary size limitation */ + + buf = cx->malloc(want*sizeof buf[0]); + if (!buf) goto out; + + count = js_FileRead(cx, file, buf, want, file->type); + if (count>0) { + str = JS_NewUCStringCopyN(cx, buf, count); + *rval = STRING_TO_JSVAL(str); + cx->free(buf); + return JS_TRUE; + } else { + cx->free(buf); + goto out; + } +out: + *rval = JSVAL_FALSE; + return JS_FALSE; +} + +static JSBool +file_readln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + JSString *str; + jschar *buf = NULL, *tmp; + int32 offset, read; + intN room; + jschar data, data2; + + SECURITY_CHECK(cx, NULL, "readln", file); + JSFILE_CHECK_READ; + + buf = cx->malloc(MAX_LINE_LENGTH * sizeof data); + if (!buf) + return JS_FALSE; + + room = MAX_LINE_LENGTH - 1; + offset = 0; + + for (;;) { + read = js_FileRead(cx, file, &data, 1, file->type); + if (read < 0) + goto out; + if (read == 0) + goto eof; + + switch (data) { + case '\r': + read = js_FileRead(cx, file, &data2, 1, file->type); + if (read < 0) + goto out; + + if (read == 1 && data2 != '\n') { + /* We read one char too far. Buffer it. */ + file->charBuffer = data2; + file->charBufferUsed = JS_TRUE; + } + + /* Fall through. */ + case '\n': + goto done; + + default: + if (--room < 0) { + tmp = cx->realloc(buf, (offset + MAX_LINE_LENGTH) * sizeof data); + if (!tmp) + goto out; + + room = MAX_LINE_LENGTH - 1; + buf = tmp; + } + + buf[offset++] = data; + break; + } + } + +eof: + if (offset == 0) { + *rval = JSVAL_NULL; + return JS_TRUE; + } + +done: + buf[offset] = 0; + tmp = cx->realloc(buf, (offset + 1) * sizeof data); + if (!tmp) + goto out; + + str = JS_NewUCString(cx, tmp, offset); + if (!str) + goto out; + + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; + +out: + if (buf) + cx->free(buf); + + return JS_FALSE; +} + +static JSBool +file_readAll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + JSObject *array; + jsint len; + jsval line; + JSBool lineok = JS_FALSE; + + SECURITY_CHECK(cx, NULL, "readAll", file); + JSFILE_CHECK_READ; + + array = JS_NewArrayObject(cx, 0, NULL); + if (!array) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(array); + + len = 0; + + lineok = file_readln(cx, obj, 0, NULL, &line); + while (lineok && !JSVAL_IS_NULL(line)) { + JS_SetElement(cx, array, len++, &line); + lineok = file_readln(cx, obj, 0, NULL, &line); + } + +out: + return lineok; +} + +static JSBool +file_seek(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + int32 toskip; + int32 pos; + + SECURITY_CHECK(cx, NULL, "seek", file); + JSFILE_CHECK_ONE_ARG("seek"); + JSFILE_CHECK_NATIVE("seek"); + JSFILE_CHECK_READ; + + if (!JS_ValueToInt32(cx, argv[0], &toskip)){ + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, "seek", argv[0]); + goto out; + } + + if(!file->hasRandomAccess){ + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_NO_RANDOM_ACCESS, file->path); + goto out; + } + + if(js_isDirectory(cx, file)){ + JS_ReportWarning(cx,"Seek on directories is not supported, proceeding"); + goto out; + } + + pos = js_FileSeek(cx, file, toskip, file->type); + + if (pos!=-1) { + *rval = INT_TO_JSVAL(pos); + return JS_TRUE; + } +out: + *rval = JSVAL_VOID; + return JS_FALSE; +} + +static JSBool +file_list(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + PRDir *dir; + PRDirEntry *entry; + JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + JSObject *array; + JSObject *eachFile; + jsint len; + jsval v; + JSRegExp *re = NULL; + JSFunction *func = NULL; + JSString *str; + jsval args[1]; + char *filePath; + + SECURITY_CHECK(cx, NULL, "list", file); + JSFILE_CHECK_NATIVE("list"); + + if (argc==1) { + if (VALUE_IS_REGEXP(cx, argv[0])) { + re = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0])); + }else + if (VALUE_IS_FUNCTION(cx, argv[0])) { + func = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0])); + }else{ + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_FUNCTION_OR_REGEX, argv[0]); + goto out; + } + } + + if (!js_isDirectory(cx, file)) { + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_CANNOT_DO_LIST_ON_A_FILE, file->path); + goto out; + } + + dir = PR_OpenDir(file->path); + if(!dir){ + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_OP_FAILED, "open", file->path); + goto out; + } + + /* create JSArray here... */ + array = JS_NewArrayObject(cx, 0, NULL); + len = 0; + + while ((entry = PR_ReadDir(dir, PR_SKIP_BOTH))!=NULL) { + /* first, check if we have a regexp */ + if (re!=NULL) { + size_t index = 0; + + str = JS_NewStringCopyZ(cx, entry->name); + if(!js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, &v)){ + /* don't report anything here */ + goto out; + } + /* not matched! */ + if (JSVAL_IS_NULL(v)) { + continue; + } + }else + if (func!=NULL) { + str = JS_NewStringCopyZ(cx, entry->name); + args[0] = STRING_TO_JSVAL(str); + if(!JS_CallFunction(cx, obj, func, 1, args, &v)){ + goto out; + } + + if (v==JSVAL_FALSE) { + continue; + } + } + + filePath = js_combinePath(cx, file->path, (char*)entry->name); + + eachFile = js_NewFileObject(cx, filePath); + cx->free(filePath); + if (!eachFile){ + JS_ReportWarning(cx, "File %s cannot be retrieved", filePath); + continue; + } + v = OBJECT_TO_JSVAL(eachFile); + JS_SetElement(cx, array, len, &v); + JS_SetProperty(cx, array, entry->name, &v); + len++; + } + + if(PR_CloseDir(dir)!=PR_SUCCESS){ + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_OP_FAILED, "close", file->path); + goto out; + } + *rval = OBJECT_TO_JSVAL(array); + return JS_TRUE; +out: + *rval = JSVAL_NULL; + return JS_FALSE; +} + +static JSBool +file_mkdir(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + + SECURITY_CHECK(cx, NULL, "mkdir", file); + JSFILE_CHECK_ONE_ARG("mkdir"); + JSFILE_CHECK_NATIVE("mkdir"); + + /* if the current file is not a directory, find out the directory name */ + if (!js_isDirectory(cx, file)) { + char *dir = js_fileDirectoryName(cx, file->path); + JSObject *dirObj = js_NewFileObject(cx, dir); + + cx->free(dir); + + /* call file_mkdir with the right set of parameters if needed */ + if (file_mkdir(cx, dirObj, argc, argv, rval)) + return JS_TRUE; + else + goto out; + }else{ + JSString *str = JS_ValueToString(cx, argv[0]); + if (!str) + goto out; + + char *dirName = JS_GetStringBytes(str); + char *fullName; + + fullName = js_combinePath(cx, file->path, dirName); + if (PR_MkDir(fullName, 0755)==PR_SUCCESS){ + *rval = JSVAL_TRUE; + cx->free(fullName); + return JS_TRUE; + }else{ + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_OP_FAILED, "mkdir", fullName); + cx->free(fullName); + goto out; + } + } +out: + *rval = JSVAL_FALSE; + return JS_FALSE; +} + +static JSBool +file_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval*rval) +{ + JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + JSString *str; + + str = JS_NewStringCopyZ(cx, file->path); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +file_toURL(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + char url[MAX_PATH_LENGTH]; + jschar *urlChars; + size_t len; + JSString *str; + + JSFILE_CHECK_NATIVE("toURL"); + + sprintf(url, "file://%s", file->path); + + len = strlen(url); + urlChars = js_InflateString(cx, url, &len); + if (!urlChars) + return JS_FALSE; + str = js_NewString(cx, urlChars, len); + if (!str) { + cx->free(urlChars); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + + /* TODO: js_escape in jsstr.h may go away at some point */ + return js_str_escape(cx, obj, 0, rval, rval); + +out: + *rval = JSVAL_VOID; + return JS_FALSE; +} + + +static void +file_finalize(JSContext *cx, JSObject *obj) +{ + JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + + if(file) { + /* Close the file before exiting. */ + if(file->isOpen && !file->isNative) { + jsval vp; + file_close(cx, obj, 0, NULL, &vp); + } + + if (file->path) + cx->free(file->path); + + cx->free(file); + } +} + +/* + Allocates memory for the file object, sets fields to defaults. +*/ +static JSFile* +file_init(JSContext *cx, JSObject *obj, char *bytes) +{ + JSFile *file; + + file = cx->malloc(sizeof *file); + if (!file) + return NULL; + memset(file, 0 , sizeof *file); + + js_ResetAttributes(file); + + file->path = RESOLVE_PATH(cx, bytes); + + if (!JS_SetPrivate(cx, obj, file)) { + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_CANNOT_SET_PRIVATE_FILE, file->path); + cx->free(file); + return NULL; + } + + return file; +} + +/* Returns a JSObject. This function is globally visible */ +JS_PUBLIC_API(JSObject*) +js_NewFileObject(JSContext *cx, char *filename) +{ + JSObject *obj; + JSFile *file; + + obj = JS_NewObject(cx, &js_FileClass, NULL, NULL); + if (!obj){ + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_OBJECT_CREATION_FAILED, "js_NewFileObject"); + return NULL; + } + file = file_init(cx, obj, filename); + if(!file) return NULL; + return obj; +} + +/* Internal function, used for cases which NSPR file support doesn't cover */ +JSObject* +js_NewFileObjectFromFILE(JSContext *cx, FILE *nativehandle, char *filename, + int32 mode, JSBool open, JSBool randomAccess) +{ + JSObject *obj; + JSFile *file; + + obj = JS_NewObject(cx, &js_FileClass, NULL, NULL); + if (!obj){ + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_OBJECT_CREATION_FAILED, "js_NewFileObjectFromFILE"); + return NULL; + } + file = file_init(cx, obj, filename); + if(!file) return NULL; + + file->nativehandle = nativehandle; + + /* free result of RESOLVE_PATH from file_init. */ + JS_ASSERT(file->path != NULL); + cx->free(file->path); + + file->path = strdup(filename); + file->isOpen = open; + file->mode = mode; + file->hasRandomAccess = randomAccess; + file->isNative = JS_TRUE; + return obj; +} + +/* + Real file constructor that is called from JavaScript. + Basically, does error processing and calls file_init. +*/ +static JSBool +file_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + JSFile *file; + + if (!JS_IsConstructing(cx)) { + /* Replace obj with a new File object. */ + obj = JS_NewObject(cx, &js_FileClass, NULL, NULL); + if (!obj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + } + + str = (argc == 0) + ? JS_InternString(cx, "") + : JS_ValueToString(cx, argv[0]); + + if (!str) { + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_FIRST_ARGUMENT_CONSTRUCTOR_NOT_STRING_ERROR, + argv[0]); + return JS_FALSE; + } + + file = file_init(cx, obj, JS_GetStringBytes(str)); + if (!file) + return JS_FALSE; + + SECURITY_CHECK(cx, NULL, "constructor", file); + + return JS_TRUE; +} + +/* -------------------- File methods and properties ------------------------- */ +static JSFunctionSpec file_functions[] = { + { "open", file_open, 0}, + { "close", file_close, 0}, + { "remove", file_remove, 0}, + { "copyTo", file_copyTo, 0}, + { "renameTo", file_renameTo, 0}, + { "flush", file_flush, 0}, + { "seek", file_seek, 0}, + { "read", file_read, 0}, + { "readln", file_readln, 0}, + { "readAll", file_readAll, 0}, + { "write", file_write, 0}, + { "writeln", file_writeln, 0}, + { "writeAll", file_writeAll, 0}, + { "list", file_list, 0}, + { "mkdir", file_mkdir, 0}, + { "toString", file_toString, 0}, + { "toURL", file_toURL, 0}, + {0} +}; + +enum file_tinyid { + FILE_LENGTH = -2, + FILE_PARENT = -3, + FILE_PATH = -4, + FILE_NAME = -5, + FILE_ISDIR = -6, + FILE_ISFILE = -7, + FILE_EXISTS = -8, + FILE_CANREAD = -9, + FILE_CANWRITE = -10, + FILE_OPEN = -11, + FILE_TYPE = -12, + FILE_MODE = -13, + FILE_CREATED = -14, + FILE_MODIFIED = -15, + FILE_SIZE = -16, + FILE_RANDOMACCESS = -17, + FILE_POSITION = -18, + FILE_APPEND = -19, + FILE_REPLACE = -20, + FILE_AUTOFLUSH = -21, + FILE_ISNATIVE = -22, +}; + +static JSPropertySpec file_props[] = { + {"length", FILE_LENGTH, JSPROP_ENUMERATE | JSPROP_READONLY }, + {"parent", FILE_PARENT, JSPROP_ENUMERATE | JSPROP_READONLY }, + {"path", FILE_PATH, JSPROP_ENUMERATE | JSPROP_READONLY }, + {"name", FILE_NAME, JSPROP_ENUMERATE | JSPROP_READONLY }, + {"isDirectory", FILE_ISDIR, JSPROP_ENUMERATE | JSPROP_READONLY }, + {"isFile", FILE_ISFILE, JSPROP_ENUMERATE | JSPROP_READONLY }, + {"exists", FILE_EXISTS, JSPROP_ENUMERATE | JSPROP_READONLY }, + {"canRead", FILE_CANREAD, JSPROP_ENUMERATE | JSPROP_READONLY }, + {"canWrite", FILE_CANWRITE, JSPROP_ENUMERATE | JSPROP_READONLY }, + {"canAppend", FILE_APPEND, JSPROP_ENUMERATE | JSPROP_READONLY }, + {"canReplace", FILE_REPLACE, JSPROP_ENUMERATE | JSPROP_READONLY }, + {"isOpen", FILE_OPEN, JSPROP_ENUMERATE | JSPROP_READONLY }, + {"type", FILE_TYPE, JSPROP_ENUMERATE | JSPROP_READONLY }, + {"mode", FILE_MODE, JSPROP_ENUMERATE | JSPROP_READONLY }, + {"creationTime", FILE_CREATED, JSPROP_ENUMERATE | JSPROP_READONLY }, + {"lastModified", FILE_MODIFIED, JSPROP_ENUMERATE | JSPROP_READONLY }, + {"size", FILE_SIZE, JSPROP_ENUMERATE | JSPROP_READONLY }, + {"hasRandomAccess", FILE_RANDOMACCESS, JSPROP_ENUMERATE | JSPROP_READONLY }, + {"hasAutoFlush", FILE_AUTOFLUSH, JSPROP_ENUMERATE | JSPROP_READONLY }, + {"position", FILE_POSITION, JSPROP_ENUMERATE }, + {"isNative", FILE_ISNATIVE, JSPROP_ENUMERATE | JSPROP_READONLY }, + {0} +}; + +/* ------------------------- Property getter/setter ------------------------- */ +static JSBool +file_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + char *bytes; + JSString *str; + jsint tiny; + PRFileInfo info; + JSBool flag; + PRExplodedTime expandedTime; + + tiny = JSVAL_TO_INT(id); + if (!file) + return JS_TRUE; + + switch (tiny) { + case FILE_PARENT: + SECURITY_CHECK(cx, NULL, "parent", file); + if (!js_parent(cx, file, vp)) + return JS_FALSE; + break; + case FILE_PATH: + str = JS_NewStringCopyZ(cx, file->path); + if (!str) + return JS_FALSE; + *vp = STRING_TO_JSVAL(str); + break; + case FILE_NAME: + if (!js_name(cx, file, vp)) + return JS_FALSE; + break; + case FILE_ISDIR: + SECURITY_CHECK(cx, NULL, "isDirectory", file); + *vp = BOOLEAN_TO_JSVAL(js_isDirectory(cx, file)); + break; + case FILE_ISFILE: + SECURITY_CHECK(cx, NULL, "isFile", file); + *vp = BOOLEAN_TO_JSVAL(js_isFile(cx, file)); + break; + case FILE_EXISTS: + SECURITY_CHECK(cx, NULL, "exists", file); + *vp = BOOLEAN_TO_JSVAL(js_exists(cx, file)); + break; + case FILE_ISNATIVE: + SECURITY_CHECK(cx, NULL, "isNative", file); + *vp = BOOLEAN_TO_JSVAL(file->isNative); + break; + case FILE_CANREAD: + SECURITY_CHECK(cx, NULL, "canRead", file); + *vp = BOOLEAN_TO_JSVAL(js_canRead(cx, file)); + break; + case FILE_CANWRITE: + SECURITY_CHECK(cx, NULL, "canWrite", file); + *vp = BOOLEAN_TO_JSVAL(js_canWrite(cx, file)); + break; + case FILE_OPEN: + SECURITY_CHECK(cx, NULL, "isOpen", file); + *vp = BOOLEAN_TO_JSVAL(file->isOpen); + break; + case FILE_APPEND : + SECURITY_CHECK(cx, NULL, "canAppend", file); + JSFILE_CHECK_OPEN("canAppend"); + *vp = BOOLEAN_TO_JSVAL(!file->isNative && + (file->mode&PR_APPEND)==PR_APPEND); + break; + case FILE_REPLACE : + SECURITY_CHECK(cx, NULL, "canReplace", file); + JSFILE_CHECK_OPEN("canReplace"); + *vp = BOOLEAN_TO_JSVAL(!file->isNative && + (file->mode&PR_TRUNCATE)==PR_TRUNCATE); + break; + case FILE_AUTOFLUSH : + SECURITY_CHECK(cx, NULL, "hasAutoFlush", file); + JSFILE_CHECK_OPEN("hasAutoFlush"); + *vp = BOOLEAN_TO_JSVAL(!file->isNative && file->hasAutoflush); + break; + case FILE_TYPE: + SECURITY_CHECK(cx, NULL, "type", file); + JSFILE_CHECK_OPEN("type"); + if(js_isDirectory(cx, file)){ + *vp = JSVAL_VOID; + break; + } + + switch (file->type) { + case ASCII: + *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, asciistring)); + break; + case UTF8: + *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, utfstring)); + break; + case UCS2: + *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, unicodestring)); + break; + default: + JS_ReportWarning(cx, "Unsupported file type %d, proceeding", + file->type); + } + break; + case FILE_MODE: + SECURITY_CHECK(cx, NULL, "mode", file); + JSFILE_CHECK_OPEN("mode"); + bytes = cx->malloc(MODE_SIZE); + bytes[0] = '\0'; + flag = JS_FALSE; + + if ((file->mode&PR_RDONLY)==PR_RDONLY) { + if (flag) strcat(bytes, ","); + strcat(bytes, "read"); + flag = JS_TRUE; + } + if ((file->mode&PR_WRONLY)==PR_WRONLY) { + if (flag) strcat(bytes, ","); + strcat(bytes, "write"); + flag = JS_TRUE; + } + if ((file->mode&PR_RDWR)==PR_RDWR) { + if (flag) strcat(bytes, ","); + strcat(bytes, "readWrite"); + flag = JS_TRUE; + } + if ((file->mode&PR_APPEND)==PR_APPEND) { + if (flag) strcat(bytes, ","); + strcat(bytes, "append"); + flag = JS_TRUE; + } + if ((file->mode&PR_CREATE_FILE)==PR_CREATE_FILE) { + if (flag) strcat(bytes, ","); + strcat(bytes, "create"); + flag = JS_TRUE; + } + if ((file->mode&PR_TRUNCATE)==PR_TRUNCATE) { + if (flag) strcat(bytes, ","); + strcat(bytes, "replace"); + flag = JS_TRUE; + } + if (file->hasAutoflush) { + if (flag) strcat(bytes, ","); + strcat(bytes, "hasAutoFlush"); + flag = JS_TRUE; + } + *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, bytes)); + cx->free(bytes); + break; + case FILE_CREATED: + SECURITY_CHECK(cx, NULL, "creationTime", file); + JSFILE_CHECK_NATIVE("creationTime"); + if(((file->isOpen)? + PR_GetOpenFileInfo(file->handle, &info): + PR_GetFileInfo(file->path, &info))!=PR_SUCCESS){ + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path); + goto out; + } + + PR_ExplodeTime(info.creationTime, PR_LocalTimeParameters,&expandedTime); + *vp = OBJECT_TO_JSVAL(js_NewDateObject(cx, expandedTime.tm_year, + expandedTime.tm_month, + expandedTime.tm_mday, + expandedTime.tm_hour, + expandedTime.tm_min, + expandedTime.tm_sec)); + break; + case FILE_MODIFIED: + SECURITY_CHECK(cx, NULL, "lastModified", file); + JSFILE_CHECK_NATIVE("lastModified"); + if(((file->isOpen)? + PR_GetOpenFileInfo(file->handle, &info): + PR_GetFileInfo(file->path, &info))!=PR_SUCCESS){ + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path); + goto out; + } + + PR_ExplodeTime(info.modifyTime, PR_LocalTimeParameters, &expandedTime); + *vp = OBJECT_TO_JSVAL(js_NewDateObject(cx, expandedTime.tm_year, + expandedTime.tm_month, + expandedTime.tm_mday, + expandedTime.tm_hour, + expandedTime.tm_min, + expandedTime.tm_sec)); + break; + case FILE_SIZE: + SECURITY_CHECK(cx, NULL, "size", file); + *vp = js_size(cx, file); + break; + case FILE_LENGTH: + SECURITY_CHECK(cx, NULL, "length", file); + JSFILE_CHECK_NATIVE("length"); + + if (js_isDirectory(cx, file)) { /* XXX debug me */ + PRDir *dir; + PRDirEntry *entry; + jsint count = 0; + + if(!(dir = PR_OpenDir(file->path))){ + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_CANNOT_OPEN_DIR, file->path); + goto out; + } + + while ((entry = PR_ReadDir(dir, PR_SKIP_BOTH))) { + count++; + } + + if(!PR_CloseDir(dir)){ + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_OP_FAILED, "close", file->path); + + goto out; + } + + *vp = INT_TO_JSVAL(count); + break; + }else{ + /* return file size */ + *vp = js_size(cx, file); + } + break; + case FILE_RANDOMACCESS: + SECURITY_CHECK(cx, NULL, "hasRandomAccess", file); + JSFILE_CHECK_OPEN("hasRandomAccess"); + *vp = BOOLEAN_TO_JSVAL(file->hasRandomAccess); + break; + case FILE_POSITION: + SECURITY_CHECK(cx, NULL, "position", file); + JSFILE_CHECK_NATIVE("position"); + JSFILE_CHECK_OPEN("position"); + + if(!file->hasRandomAccess){ + JS_ReportWarning(cx, "File %s doesn't support random access, can't report the position, proceeding"); + *vp = JSVAL_VOID; + break; + } + + if (file->isOpen && js_isFile(cx, file)) { + int pos = PR_Seek(file->handle, 0, PR_SEEK_CUR); + if(pos!=-1){ + *vp = INT_TO_JSVAL(pos); + }else{ + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_CANNOT_REPORT_POSITION, file->path); + goto out; + } + }else { + JS_ReportWarning(cx, "File %s is closed or not a plain file," + " can't report position, proceeding"); + goto out; + } + break; + default: + SECURITY_CHECK(cx, NULL, "file_access", file); + + /* this is some other property -- try to use the dir["file"] syntax */ + if (js_isDirectory(cx, file)) { + PRDir *dir = NULL; + PRDirEntry *entry = NULL; + char *prop_name; + + str = JS_ValueToString(cx, id); + if (!str) + return JS_FALSE; + + prop_name = JS_GetStringBytes(str); + + /* no native files past this point */ + dir = PR_OpenDir(file->path); + if(!dir) { + /* This is probably not a directory */ + JS_ReportWarning(cx, "Can't open directory %s", file->path); + return JS_FALSE; + } + + while ((entry = PR_ReadDir(dir, PR_SKIP_NONE)) != NULL) { + if (!strcmp(entry->name, prop_name)){ + bytes = js_combinePath(cx, file->path, prop_name); + *vp = OBJECT_TO_JSVAL(js_NewFileObject(cx, bytes)); + PR_CloseDir(dir); + cx->free(bytes); + return !JSVAL_IS_NULL(*vp); + } + } + PR_CloseDir(dir); + } + } + return JS_TRUE; + +out: + return JS_FALSE; +} + +static JSBool +file_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + jsint slot; + + if (JSVAL_IS_STRING(id)){ + return JS_TRUE; + } + + slot = JSVAL_TO_INT(id); + + switch (slot) { + /* File.position = 10 */ + case FILE_POSITION: + SECURITY_CHECK(cx, NULL, "set_position", file); + JSFILE_CHECK_NATIVE("set_position"); + + if(!file->hasRandomAccess){ + JS_ReportWarning(cx, "File %s doesn't support random access, can't " + "report the position, proceeding"); + goto out; + } + + if (file->isOpen && js_isFile(cx, file)) { + int32 pos; + int32 offset; + + if (!JS_ValueToInt32(cx, *vp, &offset)){ + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, "position", *vp); + goto out; + } + + pos = PR_Seek(file->handle, offset, PR_SEEK_SET); + + if(pos!=-1){ + *vp = INT_TO_JSVAL(pos); + }else{ + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_CANNOT_SET_POSITION, file->path); + goto out; + } + } else { + JS_ReportWarning(cx, "File %s is closed or not a file, can't set " + "position, proceeding", file->path); + goto out; + } + } + + return JS_TRUE; +out: + return JS_FALSE; +} + +/* + File.currentDir = new File("D:\") or File.currentDir = "D:\" +*/ +static JSBool +file_currentDirSetter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSFile *file; + + file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + + /* Look at the rhs and extract a file object from it */ + if (JSVAL_IS_OBJECT(*vp)) { + if (JS_InstanceOf(cx, obj, &js_FileClass, NULL)) { + /* Braindamaged rhs -- just return the old value */ + if (file && (!js_exists(cx, file) || !js_isDirectory(cx, file))) { + JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, vp); + return JS_FALSE; + } else { + chdir(file->path); + return JS_TRUE; + } + } else { + return JS_FALSE; + } + } else { + JSObject *rhsObject; + char *path; + + JSString *str = JS_ValueToString(cx, *vp); + if (!str) + return JS_FALSE; + + path = JS_GetStringBytes(str); + rhsObject = js_NewFileObject(cx, path); + if (!rhsObject) + return JS_FALSE; + + if (!file || !js_exists(cx, file) || !js_isDirectory(cx, file)){ + JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, vp); + } else { + *vp = OBJECT_TO_JSVAL(rhsObject); + chdir(path); + } + } + + return JS_TRUE; +} + +/* Declare class */ +JSClass js_FileClass = { + "File", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_File), + JS_PropertyStub, JS_PropertyStub, file_getProperty, file_setProperty, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, file_finalize +}; + +/* -------------------- Functions exposed to the outside -------------------- */ +JS_PUBLIC_API(JSObject*) +js_InitFileClass(JSContext *cx, JSObject* obj) +{ + JSObject *file, *ctor, *afile; + jsval vp; + char *currentdir; + char separator[2]; + + file = JS_InitClass(cx, obj, NULL, &js_FileClass, file_constructor, 1, + file_props, file_functions, NULL, NULL); + if (!file) { + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_INIT_FAILED); + return NULL; + } + + ctor = JS_GetConstructor(cx, file); + if (!ctor) return NULL; + + /* Define CURRENTDIR property. We are doing this to get a + slash at the end of the current dir */ + afile = js_NewFileObject(cx, CURRENT_DIR); + currentdir = cx->malloc(MAX_PATH_LENGTH); + currentdir = getcwd(currentdir, MAX_PATH_LENGTH); + afile = js_NewFileObject(cx, currentdir); + cx->free(currentdir); + vp = OBJECT_TO_JSVAL(afile); + JS_DefinePropertyWithTinyId(cx, ctor, CURRENTDIR_PROPERTY, 0, vp, + JS_PropertyStub, file_currentDirSetter, + JSPROP_ENUMERATE | JSPROP_READONLY ); + + /* Define input */ + vp = OBJECT_TO_JSVAL(js_NewFileObjectFromFILE(cx, stdin, + STDINPUT_NAME, PR_RDONLY, JS_TRUE, JS_FALSE)); + JS_SetProperty(cx, ctor, "input", &vp); + + /* Define output */ + vp = OBJECT_TO_JSVAL(js_NewFileObjectFromFILE(cx, stdout, + STDOUTPUT_NAME, PR_WRONLY, JS_TRUE, JS_FALSE)); + JS_SetProperty(cx, ctor, "output", &vp); + + /* Define error */ + vp = OBJECT_TO_JSVAL(js_NewFileObjectFromFILE(cx, stderr, + STDERROR_NAME, PR_WRONLY, JS_TRUE, JS_FALSE)); + JS_SetProperty(cx, ctor, "error", &vp); + + separator[0] = FILESEPARATOR; + separator[1] = '\0'; + vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, separator)); + JS_DefinePropertyWithTinyId(cx, ctor, SEPARATOR_PROPERTY, 0, vp, + JS_PropertyStub, JS_PropertyStub, + JSPROP_ENUMERATE | JSPROP_READONLY ); + return file; +} +#endif /* JS_HAS_FILE_OBJECT */ diff --git a/ape-server/deps/js/src/jsfile.h b/ape-server/deps/js/src/jsfile.h new file mode 100755 index 0000000..78707e8 --- /dev/null +++ b/ape-server/deps/js/src/jsfile.h @@ -0,0 +1,56 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _jsfile_h__ +#define _jsfile_h__ + +#if JS_HAS_FILE_OBJECT + +#include "jsobj.h" + +extern JS_PUBLIC_API(JSObject*) +js_InitFileClass(JSContext *cx, JSObject* obj); + +extern JS_PUBLIC_API(JSObject*) +js_NewFileObject(JSContext *cx, char *bytes); + +extern JSClass js_FileClass; + +#endif /* JS_HAS_FILE_OBJECT */ +#endif /* _jsfile_h__ */ diff --git a/ape-server/deps/js/src/jsfile.msg b/ape-server/deps/js/src/jsfile.msg new file mode 100755 index 0000000..137b35d --- /dev/null +++ b/ape-server/deps/js/src/jsfile.msg @@ -0,0 +1,90 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + Error messages for jsfile.c. See js.msg for format specification. +*/ + +MSG_DEF(JSFILEMSG_NOT_AN_ERROR, 0, 0, JSEXN_NONE, "") +MSG_DEF(JSFILEMSG_FILE_CONSTRUCTOR_UNDEFINED_ERROR, 1, 0, JSEXN_NONE, "File constructor is undefined") +MSG_DEF(JSFILEMSG_FILE_CURRENTDIR_UNDEFINED_ERROR, 2, 0, JSEXN_NONE, "File.currentDir is undefined") +MSG_DEF(JSFILEMSG_FIRST_ARGUMENT_OPEN_NOT_STRING_ERROR, 3, 1, JSEXN_NONE, "The first argument {0} to file.open must be a string") +MSG_DEF(JSFILEMSG_SECOND_ARGUMENT_OPEN_NOT_STRING_ERROR, 4, 0, JSEXN_NONE, "The second argument to file.open must be a string") +MSG_DEF(JSFILEMSG_CANNOT_COPY_FILE_OPEN_FOR_WRITING_ERROR, 5, 1, JSEXN_NONE, "Cannot copy file {0} open for writing") +MSG_DEF(JSFILEMSG_CANNOT_ACCESS_FILE_INFO_ERROR, 6, 1, JSEXN_NONE, "Cannot access file information for {0}") +MSG_DEF(JSFILEMSG_COPY_READ_ERROR, 7, 1, JSEXN_NONE, "An error occured while attempting to read a file {0} to copy") +MSG_DEF(JSFILEMSG_COPY_WRITE_ERROR, 8, 1, JSEXN_NONE, "An error occured while attempting to copy into file {0}") +MSG_DEF(JSFILEMSG_EXPECTS_ONE_ARG_ERROR, 9, 0, JSEXN_NONE, "Operation {0} expects one argument, not {1}") +MSG_DEF(JSFILEMSG_CANNOT_FLUSH_CLOSE_FILE_ERROR, 10, 1, JSEXN_NONE, "Cannot flush closed file {0}") +MSG_DEF(JSFILEMSG_CANNOT_OPEN_WRITING_ERROR, 11, 1, JSEXN_NONE, "Cannot open file {0} for writing") +MSG_DEF(JSFILEMSG_WRITEALL_EXPECTS_ONE_ARG_ERROR, 12, 0, JSEXN_NONE, "writeAll expects one argument") +MSG_DEF(JSFILEMSG_FIRST_ARGUMENT_WRITEALL_NOT_ARRAY_ERROR, 13, 0, JSEXN_NONE, "writeAll expects an array as an argument") +MSG_DEF(JSFILEMSG_UNUSED0, 14, 0, JSEXN_NONE, "Unused error message slot") +MSG_DEF(JSFILEMSG_CANNOT_OPEN_FILE_ERROR, 15, 1, JSEXN_NONE, "Cannot open file {0}") +MSG_DEF(JSFILEMSG_FIRST_ARGUMENT_CONSTRUCTOR_NOT_STRING_ERROR, 16, 1, JSEXN_NONE, "The argument to the File constructor {0} must be a string") +MSG_DEF(JSFILEMSG_BIDIRECTIONAL_PIPE_NOT_SUPPORTED, 17, 0, JSEXN_NONE, "Bidirectional pipes are not supported") +MSG_DEF(JSFILEMSG_OPEN_MODE_NOT_SUPPORTED_WITH_PIPES, 18, 2, JSEXN_NONE, "The opening mode you have chosen {0} is not supported by the pipe you are trying to open: {1}") +MSG_DEF(JSFILEMSG_OPEN_FAILED, 19, 1, JSEXN_NONE, "open on file {0} failed") +MSG_DEF(JSFILEMSG_CLOSE_FAILED, 20, 1, JSEXN_NONE, "close on file {0} failed") +MSG_DEF(JSFILEMSG_PCLOSE_FAILED, 21, 1, JSEXN_NONE, "pclose on file {0} failed") +MSG_DEF(JSFILEMSG_REMOVE_FAILED, 22, 1, JSEXN_NONE, "remove on file {0} failed") +MSG_DEF(JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, 23, 1, JSEXN_NONE, "Cannot access file status for {0}") +MSG_DEF(JSFILEMSG_RENAME_FAILED, 24, 2, JSEXN_NONE, "Cannot rename {0} to {1}") +MSG_DEF(JSFILEMSG_WRITE_FAILED, 25, 1, JSEXN_NONE, "Write failed on file {0}") +MSG_DEF(JSFILEMSG_READ_FAILED, 26, 1, JSEXN_NONE, "Read failed on file {0}") +MSG_DEF(JSFILEMSG_SKIP_FAILED, 27, 1, JSEXN_NONE, "Skip failed on file {0}") +MSG_DEF(JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_FUNCTION_OR_REGEX, 28, 1, JSEXN_NONE, "The first argument to file.list must be a function or a regex") +MSG_DEF(JSFILEMSG_CANNOT_DO_LIST_ON_A_FILE, 29, 1, JSEXN_NONE, "{0} must be a directory, cannot do list") +MSG_DEF(JSFILEMSG_NATIVE_OPERATION_IS_NOT_SUPPORTED, 30, 2, JSEXN_NONE, "Native operation {0} is not supported on {1}") +MSG_DEF(JSFILEMSG_CANNOT_SET_PRIVATE_FILE, 31, 1, JSEXN_NONE, "Cannot set private data for file {0}") +MSG_DEF(JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, 32, 2, JSEXN_NONE, "First argument to {0} must be a number, not {1}") +MSG_DEF(JSFILEMSG_CANNOT_WRITE, 33, 1, JSEXN_NONE, "Cannot write to {0}, file mode is different") +MSG_DEF(JSFILEMSG_CANNOT_READ, 34, 1, JSEXN_NONE, "Cannot read from {0}, file mode is different") +MSG_DEF(JSFILEMSG_CANNOT_FLUSH, 35, 1, JSEXN_NONE, "Flush failed on {0}") +MSG_DEF(JSFILEMSG_OP_FAILED, 36, 1, JSEXN_NONE, "File operation {0} failed") +MSG_DEF(JSFILEMSG_FILE_MUST_BE_OPEN, 37, 1, JSEXN_NONE, "File must be open for {0}") +MSG_DEF(JSFILEMSG_FILE_MUST_BE_CLOSED, 38, 1, JSEXN_NONE, "File must be closed for {0}") +MSG_DEF(JSFILEMSG_NO_RANDOM_ACCESS, 39, 1, JSEXN_NONE, "File {0} doesn't allow random access") +MSG_DEF(JSFILEMSG_OBJECT_CREATION_FAILED, 40, 1, JSEXN_NONE, "Couldn't create {0}") +MSG_DEF(JSFILEMSG_CANNOT_OPEN_DIR, 41, 1, JSEXN_NONE, "Couldn't open directory {0}") +MSG_DEF(JSFILEMSG_CANNOT_REPORT_POSITION, 42, 1, JSEXN_NONE, "Couldn't report position for {0}") +MSG_DEF(JSFILEMSG_CANNOT_SET_POSITION, 43, 1, JSEXN_NONE, "Couldn't set position for {0}") +MSG_DEF(JSFILEMSG_INIT_FAILED, 44, 0, JSEXN_NONE, "File class initialization failed") + + diff --git a/ape-server/deps/js/src/jsfun.cpp b/ape-server/deps/js/src/jsfun.cpp new file mode 100755 index 0000000..f0821d1 --- /dev/null +++ b/ape-server/deps/js/src/jsfun.cpp @@ -0,0 +1,3092 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=99: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * JS function support. + */ +#include +#include "jstypes.h" +#include "jsstdint.h" +#include "jsbit.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jsapi.h" +#include "jsarray.h" +#include "jsatom.h" +#include "jsbool.h" +#include "jsbuiltins.h" +#include "jscntxt.h" +#include "jsversion.h" +#include "jsdbgapi.h" +#include "jsemit.h" +#include "jsfun.h" +#include "jsgc.h" +#include "jsinterp.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsobj.h" +#include "jsopcode.h" +#include "jsparse.h" +#include "jsscan.h" +#include "jsscope.h" +#include "jsscript.h" +#include "jsstr.h" +#include "jsexn.h" +#include "jsstaticcheck.h" +#include "jstracer.h" + +#if JS_HAS_GENERATORS +# include "jsiter.h" +#endif + +#if JS_HAS_XDR +# include "jsxdrapi.h" +#endif + +#include "jsatominlines.h" + +static inline void +SetOverriddenArgsLength(JSObject *obj) +{ + JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_ArgumentsClass); + + jsval v = obj->fslots[JSSLOT_ARGS_LENGTH]; + v = INT_TO_JSVAL(JSVAL_TO_INT(v) | 1); + JS_ASSERT(JSVAL_IS_INT(v)); + obj->fslots[JSSLOT_ARGS_LENGTH] = v; +} + +static inline void +InitArgsLengthSlot(JSObject *obj, uint32 argc) +{ + JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_ArgumentsClass); + JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX); + JS_ASSERT(obj->fslots[JSSLOT_ARGS_LENGTH] == JSVAL_VOID); + obj->fslots[JSSLOT_ARGS_LENGTH] = INT_TO_JSVAL(argc << 1); + JS_ASSERT(!js_IsOverriddenArgsLength(obj)); +} + +static inline uint32 +GetArgsLength(JSObject *obj) +{ + JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_ArgumentsClass); + + uint32 argc = uint32(JSVAL_TO_INT(obj->fslots[JSSLOT_ARGS_LENGTH])) >> 1; + JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX); + return argc; +} + +static inline void +SetArgsPrivateNative(JSObject *argsobj, js_ArgsPrivateNative *apn) +{ + JS_ASSERT(STOBJ_GET_CLASS(argsobj) == &js_ArgumentsClass); + uintptr_t p = (uintptr_t) apn; + argsobj->setPrivate((void*) (p | 2)); +} + +JSBool +js_GetArgsValue(JSContext *cx, JSStackFrame *fp, jsval *vp) +{ + JSObject *argsobj; + + if (fp->flags & JSFRAME_OVERRIDE_ARGS) { + JS_ASSERT(fp->callobj); + jsid id = ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom); + return fp->callobj->getProperty(cx, id, vp); + } + argsobj = js_GetArgsObject(cx, fp); + if (!argsobj) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(argsobj); + return JS_TRUE; +} + +JSBool +js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, jsval *vp) +{ + if (fp->flags & JSFRAME_OVERRIDE_ARGS) { + JS_ASSERT(fp->callobj); + + jsid argumentsid = ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom); + jsval v; + if (!fp->callobj->getProperty(cx, argumentsid, &v)) + return false; + + JSObject *obj; + if (JSVAL_IS_PRIMITIVE(v)) { + obj = js_ValueToNonNullObject(cx, v); + if (!obj) + return false; + } else { + obj = JSVAL_TO_OBJECT(v); + } + return obj->getProperty(cx, id, vp); + } + + *vp = JSVAL_VOID; + if (JSID_IS_INT(id)) { + uint32 arg = uint32(JSID_TO_INT(id)); + JSObject *argsobj = JSVAL_TO_OBJECT(fp->argsobj); + if (arg < fp->argc) { + if (argsobj) { + jsval v = OBJ_GET_SLOT(cx, argsobj, JSSLOT_ARGS_COPY_START+arg); + if (v == JSVAL_HOLE) + return argsobj->getProperty(cx, id, vp); + } + *vp = fp->argv[arg]; + } else { + /* + * Per ECMA-262 Ed. 3, 10.1.8, last bulleted item, do not share + * storage between the formal parameter and arguments[k] for all + * fp->argc <= k && k < fp->fun->nargs. For example, in + * + * function f(x) { x = 42; return arguments[0]; } + * f(); + * + * the call to f should return undefined, not 42. If fp->argsobj + * is null at this point, as it would be in the example, return + * undefined in *vp. + */ + if (argsobj) + return argsobj->getProperty(cx, id, vp); + } + } else if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) { + JSObject *argsobj = JSVAL_TO_OBJECT(fp->argsobj); + if (argsobj && js_IsOverriddenArgsLength(argsobj)) + return argsobj->getProperty(cx, id, vp); + *vp = INT_TO_JSVAL(jsint(fp->argc)); + } + return true; +} + +static JSObject * +NewArguments(JSContext *cx, JSObject *parent, uint32 argc, JSObject *callee) +{ + JSObject *argsobj = js_NewObject(cx, &js_ArgumentsClass, NULL, parent, 0); + if (!argsobj || !js_EnsureReservedSlots(cx, argsobj, argc)) + return NULL; + + argsobj->fslots[JSSLOT_ARGS_CALLEE] = OBJECT_TO_JSVAL(callee); + InitArgsLengthSlot(argsobj, argc); + return argsobj; +} + +static void +PutArguments(JSContext *cx, JSObject *argsobj, jsval *args) +{ + uint32 argc = GetArgsLength(argsobj); + JS_LOCK_OBJ(cx, argsobj); + for (uint32 i = 0; i != argc; ++i) { + jsval v = STOBJ_GET_SLOT(argsobj, JSSLOT_ARGS_COPY_START + i); + if (v != JSVAL_HOLE) + STOBJ_SET_SLOT(argsobj, JSSLOT_ARGS_COPY_START + i, args[i]); + } + JS_UNLOCK_OBJ(cx, argsobj); +} + +JSObject * +js_GetArgsObject(JSContext *cx, JSStackFrame *fp) +{ + /* + * We must be in a function activation; the function must be lightweight + * or else fp must have a variable object. + */ + JS_ASSERT(fp->fun && (!(fp->fun->flags & JSFUN_HEAVYWEIGHT) || fp->varobj)); + + /* Skip eval and debugger frames. */ + while (fp->flags & JSFRAME_SPECIAL) + fp = fp->down; + + /* Create an arguments object for fp only if it lacks one. */ + JSObject *argsobj = JSVAL_TO_OBJECT(fp->argsobj); + if (argsobj) + return argsobj; + + /* + * Give arguments an intrinsic scope chain link to fp's global object. + * Since the arguments object lacks a prototype because js_ArgumentsClass + * is not initialized, js_NewObject won't assign a default parent to it. + * + * Therefore if arguments is used as the head of an eval scope chain (via + * a direct or indirect call to eval(program, arguments)), any reference + * to a standard class object in the program will fail to resolve due to + * js_GetClassPrototype not being able to find a global object containing + * the standard prototype by starting from arguments and following parent. + */ + JSObject *parent, *global = fp->scopeChain; + while ((parent = OBJ_GET_PARENT(cx, global)) != NULL) + global = parent; + + JS_ASSERT(fp->argv); + argsobj = NewArguments(cx, global, fp->argc, JSVAL_TO_OBJECT(fp->argv[-2])); + if (!argsobj) + return argsobj; + + /* Link the new object to fp so it can get actual argument values. */ + argsobj->setPrivate(fp); + fp->argsobj = OBJECT_TO_JSVAL(argsobj); + return argsobj; +} + +void +js_PutArgsObject(JSContext *cx, JSStackFrame *fp) +{ + JSObject *argsobj = JSVAL_TO_OBJECT(fp->argsobj); + JS_ASSERT(argsobj->getPrivate() == fp); + PutArguments(cx, argsobj, fp->argv); + argsobj->setPrivate(NULL); + fp->argsobj = JSVAL_NULL; +} + +/* + * Traced versions of js_GetArgsObject and js_PutArgsObject. + */ + +#ifdef JS_TRACER +JSObject * JS_FASTCALL +js_Arguments(JSContext *cx, JSObject *parent, uint32 argc, JSObject *callee, + double *argv, js_ArgsPrivateNative *apn) +{ + JSObject *argsobj = NewArguments(cx, parent, argc, callee); + if (!argsobj) + return NULL; + apn->argv = argv; + SetArgsPrivateNative(argsobj, apn); + return argsobj; +} +#endif + +JS_DEFINE_CALLINFO_6(extern, OBJECT, js_Arguments, CONTEXT, OBJECT, UINT32, OBJECT, + DOUBLEPTR, APNPTR, 0, 0) + +/* FIXME change the return type to void. */ +JSBool JS_FASTCALL +js_PutArguments(JSContext *cx, JSObject *argsobj, jsval *args) +{ + JS_ASSERT(js_GetArgsPrivateNative(argsobj)); + PutArguments(cx, argsobj, args); + argsobj->setPrivate(NULL); + return true; +} + +JS_DEFINE_CALLINFO_3(extern, BOOL, js_PutArguments, CONTEXT, OBJECT, JSVALPTR, 0, 0) + +static JSBool +args_delProperty(JSContext *cx, JSObject *obj, jsval idval, jsval *vp) +{ + JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_ArgumentsClass); + + if (JSVAL_IS_INT(idval)) { + uintN arg = uintN(JSVAL_TO_INT(idval)); + if (arg < GetArgsLength(obj)) + OBJ_SET_SLOT(cx, obj, JSSLOT_ARGS_COPY_START + arg, JSVAL_HOLE); + } else if (idval == ATOM_KEY(cx->runtime->atomState.lengthAtom)) { + SetOverriddenArgsLength(obj); + } else if (idval == ATOM_KEY(cx->runtime->atomState.calleeAtom)) { + obj->fslots[JSSLOT_ARGS_CALLEE] = JSVAL_HOLE; + } + return true; +} + +static JS_REQUIRES_STACK JSObject * +WrapEscapingClosure(JSContext *cx, JSStackFrame *fp, JSObject *funobj, JSFunction *fun) +{ + JS_ASSERT(GET_FUNCTION_PRIVATE(cx, funobj) == fun); + JS_ASSERT(fun->optimizedClosure()); + JS_ASSERT(!fun->u.i.wrapper); + + /* + * We do not attempt to reify Call and Block objects on demand for outer + * scopes. This could be done (see the "v8" patch in bug 494235) but it is + * fragile in the face of ongoing compile-time optimization. Instead, the + * _DBG* opcodes used by wrappers created here must cope with unresolved + * upvars and throw them as reference errors. Caveat debuggers! + */ + JSObject *scopeChain = js_GetScopeChain(cx, fp); + if (!scopeChain) + return NULL; + + JSObject *wfunobj = js_NewObjectWithGivenProto(cx, &js_FunctionClass, + funobj, scopeChain); + if (!wfunobj) + return NULL; + JSAutoTempValueRooter tvr(cx, wfunobj); + + JSFunction *wfun = (JSFunction *) wfunobj; + wfunobj->setPrivate(wfun); + wfun->nargs = 0; + wfun->flags = fun->flags | JSFUN_HEAVYWEIGHT; + wfun->u.i.nvars = 0; + wfun->u.i.nupvars = 0; + wfun->u.i.skipmin = fun->u.i.skipmin; + wfun->u.i.wrapper = true; + wfun->u.i.script = NULL; + wfun->u.i.names.taggedAtom = NULL; + wfun->atom = fun->atom; + + if (fun->hasLocalNames()) { + void *mark = JS_ARENA_MARK(&cx->tempPool); + jsuword *names = js_GetLocalNameArray(cx, fun, &cx->tempPool); + if (!names) + return NULL; + + JSBool ok = true; + for (uintN i = 0, n = fun->countLocalNames(); i != n; i++) { + jsuword name = names[i]; + JSAtom *atom = JS_LOCAL_NAME_TO_ATOM(name); + JSLocalKind localKind = (i < fun->nargs) + ? JSLOCAL_ARG + : (i < fun->countArgsAndVars()) + ? (JS_LOCAL_NAME_IS_CONST(name) + ? JSLOCAL_CONST + : JSLOCAL_VAR) + : JSLOCAL_UPVAR; + + ok = js_AddLocal(cx, wfun, atom, localKind); + if (!ok) + break; + } + + JS_ARENA_RELEASE(&cx->tempPool, mark); + if (!ok) + return NULL; + JS_ASSERT(wfun->nargs == fun->nargs); + JS_ASSERT(wfun->u.i.nvars == fun->u.i.nvars); + JS_ASSERT(wfun->u.i.nupvars == fun->u.i.nupvars); + js_FreezeLocalNames(cx, wfun); + } + + JSScript *script = fun->u.i.script; + jssrcnote *snbase = script->notes(); + jssrcnote *sn = snbase; + while (!SN_IS_TERMINATOR(sn)) + sn = SN_NEXT(sn); + uintN nsrcnotes = (sn - snbase) + 1; + + /* NB: GC must not occur before wscript is homed in wfun->u.i.script. */ + JSScript *wscript = js_NewScript(cx, script->length, nsrcnotes, + script->atomMap.length, + (script->objectsOffset != 0) + ? script->objects()->length + : 0, + fun->u.i.nupvars, + (script->regexpsOffset != 0) + ? script->regexps()->length + : 0, + (script->trynotesOffset != 0) + ? script->trynotes()->length + : 0); + if (!wscript) + return NULL; + + memcpy(wscript->code, script->code, script->length); + wscript->main = wscript->code + (script->main - script->code); + + memcpy(wscript->notes(), snbase, nsrcnotes * sizeof(jssrcnote)); + memcpy(wscript->atomMap.vector, script->atomMap.vector, + wscript->atomMap.length * sizeof(JSAtom *)); + if (script->objectsOffset != 0) { + memcpy(wscript->objects()->vector, script->objects()->vector, + wscript->objects()->length * sizeof(JSObject *)); + } + if (script->regexpsOffset != 0) { + memcpy(wscript->regexps()->vector, script->regexps()->vector, + wscript->regexps()->length * sizeof(JSObject *)); + } + if (script->trynotesOffset != 0) { + memcpy(wscript->trynotes()->vector, script->trynotes()->vector, + wscript->trynotes()->length * sizeof(JSTryNote)); + } + + if (wfun->u.i.nupvars != 0) { + JS_ASSERT(wfun->u.i.nupvars == wscript->upvars()->length); + memcpy(wscript->upvars()->vector, script->upvars()->vector, + wfun->u.i.nupvars * sizeof(uint32)); + } + + jsbytecode *pc = wscript->code; + while (*pc != JSOP_STOP) { + /* XYZZYbe should copy JSOP_TRAP? */ + JSOp op = js_GetOpcode(cx, wscript, pc); + const JSCodeSpec *cs = &js_CodeSpec[op]; + ptrdiff_t oplen = cs->length; + if (oplen < 0) + oplen = js_GetVariableBytecodeLength(pc); + + /* + * Rewrite JSOP_{GET,CALL}DSLOT as JSOP_{GET,CALL}UPVAR_DBG for the + * case where fun is an escaping flat closure. This works because the + * UPVAR and DSLOT ops by design have the same format: an upvar index + * immediate operand. + */ + switch (op) { + case JSOP_GETUPVAR: *pc = JSOP_GETUPVAR_DBG; break; + case JSOP_CALLUPVAR: *pc = JSOP_CALLUPVAR_DBG; break; + case JSOP_GETDSLOT: *pc = JSOP_GETUPVAR_DBG; break; + case JSOP_CALLDSLOT: *pc = JSOP_CALLUPVAR_DBG; break; + case JSOP_DEFFUN_FC: *pc = JSOP_DEFFUN_DBGFC; break; + case JSOP_DEFLOCALFUN_FC: *pc = JSOP_DEFLOCALFUN_DBGFC; break; + case JSOP_LAMBDA_FC: *pc = JSOP_LAMBDA_DBGFC; break; + default:; + } + pc += oplen; + } + + /* + * Fill in the rest of wscript. This means if you add members to JSScript + * you must update this code. FIXME: factor into JSScript::clone method. + */ + wscript->noScriptRval = script->noScriptRval; + wscript->savedCallerFun = script->savedCallerFun; + wscript->hasSharps = script->hasSharps; + wscript->strictModeCode = script->strictModeCode; + wscript->version = script->version; + wscript->nfixed = script->nfixed; + wscript->filename = script->filename; + wscript->lineno = script->lineno; + wscript->nslots = script->nslots; + wscript->staticLevel = script->staticLevel; + wscript->principals = script->principals; + if (wscript->principals) + JSPRINCIPALS_HOLD(cx, wscript->principals); +#ifdef CHECK_SCRIPT_OWNER + wscript->owner = script->owner; +#endif + + /* Deoptimize wfun from FUN_{FLAT,NULL}_CLOSURE to FUN_INTERPRETED. */ + FUN_SET_KIND(wfun, JSFUN_INTERPRETED); + wfun->u.i.script = wscript; + return wfunobj; +} + +static JSBool +ArgGetter(JSContext *cx, JSObject *obj, jsval idval, jsval *vp) +{ + if (!JS_InstanceOf(cx, obj, &js_ArgumentsClass, NULL)) + return true; + + if (JSVAL_IS_INT(idval)) { + /* + * arg can exceed the number of arguments if a script changed the + * prototype to point to another Arguments object with a bigger argc. + */ + uintN arg = uintN(JSVAL_TO_INT(idval)); + if (arg < GetArgsLength(obj)) { +#ifdef JS_TRACER + js_ArgsPrivateNative *argp = js_GetArgsPrivateNative(obj); + if (argp) { + if (js_NativeToValue(cx, *vp, argp->typemap()[arg], &argp->argv[arg])) + return true; + js_LeaveTrace(cx); + return false; + } +#endif + + JSStackFrame *fp = (JSStackFrame *) obj->getPrivate(); + if (fp) { + *vp = fp->argv[arg]; + } else { + jsval v = OBJ_GET_SLOT(cx, obj, JSSLOT_ARGS_COPY_START + arg); + if (v != JSVAL_HOLE) + *vp = v; + } + } + } else if (idval == ATOM_KEY(cx->runtime->atomState.lengthAtom)) { + if (!js_IsOverriddenArgsLength(obj)) + *vp = INT_TO_JSVAL(GetArgsLength(obj)); + } else { + JS_ASSERT(idval == ATOM_KEY(cx->runtime->atomState.calleeAtom)); + jsval v = obj->fslots[JSSLOT_ARGS_CALLEE]; + if (v != JSVAL_HOLE) { + /* + * If this function or one in it needs upvars that reach above it + * in the scope chain, it must not be a null closure (it could be a + * flat closure, or an unoptimized closure -- the latter itself not + * necessarily heavyweight). Rather than wrap here, we simply throw + * to reduce code size and tell debugger users the truth instead of + * passing off a fibbing wrapper. + */ + if (GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(v))->needsWrapper()) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_OPTIMIZED_CLOSURE_LEAK); + return false; + } + *vp = v; + } + } + return true; +} + +static JSBool +ArgSetter(JSContext *cx, JSObject *obj, jsval idval, jsval *vp) +{ +#ifdef JS_TRACER + // To be able to set a property here on trace, we would have to make + // sure any updates also get written back to the trace native stack. + // For simplicity, we just leave trace, since this is presumably not + // a common operation. + if (JS_ON_TRACE(cx)) { + js_DeepBail(cx); + return false; + } +#endif + + if (!JS_InstanceOf(cx, obj, &js_ArgumentsClass, NULL)) + return true; + + if (JSVAL_IS_INT(idval)) { + uintN arg = uintN(JSVAL_TO_INT(idval)); + if (arg < GetArgsLength(obj)) { + JSStackFrame *fp = (JSStackFrame *) obj->getPrivate(); + if (fp) { + fp->argv[arg] = *vp; + return true; + } + } + } else { + JS_ASSERT(idval == ATOM_KEY(cx->runtime->atomState.lengthAtom) || + idval == ATOM_KEY(cx->runtime->atomState.calleeAtom)); + } + + /* + * For simplicity we use delete/set to replace the property with one + * backed by the default Object getter and setter. Note the we rely on + * args_delete to clear the corresponding reserved slot so the GC can + * collect its value. + */ + jsid id; + if (!JS_ValueToId(cx, idval, &id)) + return false; + + JSAutoTempValueRooter tvr(cx); + return js_DeleteProperty(cx, obj, id, tvr.addr()) && + js_SetProperty(cx, obj, id, vp); +} + +static JSBool +args_resolve(JSContext *cx, JSObject *obj, jsval idval, uintN flags, + JSObject **objp) +{ + JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_ArgumentsClass); + + *objp = NULL; + jsid id = 0; + if (JSVAL_IS_INT(idval)) { + uint32 arg = uint32(JSVAL_TO_INT(idval)); + if (arg < GetArgsLength(obj) && + OBJ_GET_SLOT(cx, obj, JSSLOT_ARGS_COPY_START + arg) != JSVAL_HOLE) { + id = INT_JSVAL_TO_JSID(idval); + } + } else if (idval == ATOM_KEY(cx->runtime->atomState.lengthAtom)) { + if (!js_IsOverriddenArgsLength(obj)) + id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); + + } else if (idval == ATOM_KEY(cx->runtime->atomState.calleeAtom)) { + if (obj->fslots[JSSLOT_ARGS_CALLEE] != JSVAL_HOLE) + id = ATOM_TO_JSID(cx->runtime->atomState.calleeAtom); + } + + if (id != 0) { + /* + * XXX ECMA specs DontEnum even for indexed properties, contrary to + * other array-like objects. + */ + if (!js_DefineProperty(cx, obj, id, JSVAL_VOID, ArgGetter, ArgSetter, JSPROP_SHARED)) + return JS_FALSE; + *objp = obj; + } + return true; +} + +static JSBool +args_enumerate(JSContext *cx, JSObject *obj) +{ + JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_ArgumentsClass); + + /* + * Trigger reflection in args_resolve using a series of js_LookupProperty + * calls. + */ + int argc = int(GetArgsLength(obj)); + for (int i = -2; i != argc; i++) { + jsid id = (i == -2) + ? ATOM_TO_JSID(cx->runtime->atomState.lengthAtom) + : (i == -1) + ? ATOM_TO_JSID(cx->runtime->atomState.calleeAtom) + : INT_JSVAL_TO_JSID(INT_TO_JSVAL(i)); + + JSObject *pobj; + JSProperty *prop; + if (!js_LookupProperty(cx, obj, id, &pobj, &prop)) + return false; + + /* prop is null when the property was deleted. */ + if (prop) + pobj->dropProperty(cx, prop); + } + return true; +} + +#if JS_HAS_GENERATORS +/* + * If a generator-iterator's arguments or call object escapes, it needs to + * mark its generator object. + */ +static void +args_or_call_trace(JSTracer *trc, JSObject *obj) +{ + JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_ArgumentsClass || + STOBJ_GET_CLASS(obj) == &js_CallClass); + if (STOBJ_GET_CLASS(obj) == &js_ArgumentsClass && js_GetArgsPrivateNative(obj)) + return; + + JSStackFrame *fp = (JSStackFrame *) obj->getPrivate(); + if (fp && (fp->flags & JSFRAME_GENERATOR)) { + JS_CALL_OBJECT_TRACER(trc, FRAME_TO_GENERATOR(fp)->obj, + "FRAME_TO_GENERATOR(fp)->obj"); + } +} +#else +# define args_or_call_trace NULL +#endif + +static uint32 +args_reserveSlots(JSContext *cx, JSObject *obj) +{ + JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_ArgumentsClass); + return GetArgsLength(obj); +} + +/* + * The Arguments class is not initialized via JS_InitClass, and must not be, + * because its name is "Object". Per ECMA, that causes instances of it to + * delegate to the object named by Object.prototype. It also ensures that + * arguments.toString() returns "[object Object]". + * + * The JSClass functions below collaborate to lazily reflect and synchronize + * actual argument values, argument count, and callee function object stored + * in a JSStackFrame with their corresponding property values in the frame's + * arguments object. + */ +JSClass js_ArgumentsClass = { + js_Object_str, + JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | + JSCLASS_HAS_RESERVED_SLOTS(ARGS_CLASS_FIXED_RESERVED_SLOTS) | + JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Object), + JS_PropertyStub, args_delProperty, + JS_PropertyStub, JS_PropertyStub, + args_enumerate, (JSResolveOp) args_resolve, + JS_ConvertStub, NULL, + NULL, NULL, + NULL, NULL, + NULL, NULL, + JS_CLASS_TRACE(args_or_call_trace), args_reserveSlots +}; + +const uint32 JSSLOT_CALLEE = JSSLOT_PRIVATE + 1; +const uint32 JSSLOT_CALL_ARGUMENTS = JSSLOT_PRIVATE + 2; +const uint32 CALL_CLASS_FIXED_RESERVED_SLOTS = 2; + +/* + * A Declarative Environment object stores its active JSStackFrame pointer in + * its private slot, just as Call and Arguments objects do. + */ +JSClass js_DeclEnvClass = { + js_Object_str, + JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Object), + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +static JSBool +CheckForEscapingClosure(JSContext *cx, JSObject *obj, jsval *vp) +{ + JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_CallClass || + STOBJ_GET_CLASS(obj) == &js_DeclEnvClass); + + jsval v = *vp; + + if (VALUE_IS_FUNCTION(cx, v)) { + JSObject *funobj = JSVAL_TO_OBJECT(v); + JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj); + + /* + * Any escaping null or flat closure that reaches above itself or + * contains nested functions that reach above it must be wrapped. + * We can wrap only when this Call or Declarative Environment obj + * still has an active stack frame associated with it. + */ + if (fun->needsWrapper()) { + js_LeaveTrace(cx); + + JSStackFrame *fp = (JSStackFrame *) obj->getPrivate(); + if (fp) { + JSObject *wrapper = WrapEscapingClosure(cx, fp, funobj, fun); + if (!wrapper) + return false; + *vp = OBJECT_TO_JSVAL(wrapper); + return true; + } + + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_OPTIMIZED_CLOSURE_LEAK); + return false; + } + } + return true; +} + +static JSBool +CalleeGetter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + return CheckForEscapingClosure(cx, obj, vp); +} + +JSObject * +js_GetCallObject(JSContext *cx, JSStackFrame *fp) +{ + JSObject *callobj; + + /* Create a call object for fp only if it lacks one. */ + JS_ASSERT(fp->fun); + callobj = fp->callobj; + if (callobj) + return callobj; + +#ifdef DEBUG + /* A call object should be a frame's outermost scope chain element. */ + JSClass *classp = OBJ_GET_CLASS(cx, fp->scopeChain); + if (classp == &js_WithClass || classp == &js_BlockClass || classp == &js_CallClass) + JS_ASSERT(fp->scopeChain->getPrivate() != fp); +#endif + + /* + * Create the call object, using the frame's enclosing scope as its + * parent, and link the call to its stack frame. For a named function + * expression Call's parent points to an environment object holding + * function's name. + */ + JSAtom *lambdaName = (fp->fun->flags & JSFUN_LAMBDA) ? fp->fun->atom : NULL; + if (lambdaName) { + JSObject *env = js_NewObjectWithGivenProto(cx, &js_DeclEnvClass, NULL, + fp->scopeChain); + if (!env) + return NULL; + env->setPrivate(fp); + + /* Root env before js_DefineNativeProperty (-> JSClass.addProperty). */ + fp->scopeChain = env; + JS_ASSERT(fp->argv); + if (!js_DefineNativeProperty(cx, fp->scopeChain, ATOM_TO_JSID(lambdaName), + fp->calleeValue(), + CalleeGetter, NULL, + JSPROP_PERMANENT | JSPROP_READONLY, + 0, 0, NULL)) { + return NULL; + } + } + + callobj = js_NewObjectWithGivenProto(cx, &js_CallClass, NULL, fp->scopeChain); + if (!callobj || + !js_EnsureReservedSlots(cx, callobj, fp->fun->countArgsAndVars())) { + return NULL; + } + + callobj->setPrivate(fp); + JS_ASSERT(fp->argv); + JS_ASSERT(fp->fun == GET_FUNCTION_PRIVATE(cx, fp->calleeObject())); + STOBJ_SET_SLOT(callobj, JSSLOT_CALLEE, fp->calleeValue()); + fp->callobj = callobj; + + /* + * Push callobj on the top of the scope chain, and make it the + * variables object. + */ + fp->scopeChain = callobj; + fp->varobj = callobj; + return callobj; +} + +JSFunction * +js_GetCallObjectFunction(JSObject *obj) +{ + jsval v; + + JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_CallClass); + v = STOBJ_GET_SLOT(obj, JSSLOT_CALLEE); + if (JSVAL_IS_VOID(v)) { + /* Newborn or prototype object. */ + return NULL; + } + JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); + return GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(v)); +} + +void +js_PutCallObject(JSContext *cx, JSStackFrame *fp) +{ + JSObject *callobj = fp->callobj; + JS_ASSERT(callobj); + + /* Get the arguments object to snapshot fp's actual argument values. */ + if (fp->argsobj) { + if (!(fp->flags & JSFRAME_OVERRIDE_ARGS)) + STOBJ_SET_SLOT(callobj, JSSLOT_CALL_ARGUMENTS, fp->argsobj); + js_PutArgsObject(cx, fp); + } + + JSFunction *fun = fp->fun; + JS_ASSERT(fun == js_GetCallObjectFunction(callobj)); + uintN n = fun->countArgsAndVars(); + + /* + * Since for a call object all fixed slots happen to be taken, we can copy + * arguments and variables straight into JSObject.dslots. + */ + JS_STATIC_ASSERT(JS_INITIAL_NSLOTS - JSSLOT_PRIVATE == + 1 + CALL_CLASS_FIXED_RESERVED_SLOTS); + if (n != 0) { + JS_ASSERT(STOBJ_NSLOTS(callobj) >= JS_INITIAL_NSLOTS + n); + n += JS_INITIAL_NSLOTS; + JS_LOCK_OBJ(cx, callobj); + memcpy(callobj->dslots, fp->argv, fun->nargs * sizeof(jsval)); + memcpy(callobj->dslots + fun->nargs, fp->slots, + fun->u.i.nvars * sizeof(jsval)); + JS_UNLOCK_OBJ(cx, callobj); + } + + /* Clear private pointers to fp, which is about to go away (js_Invoke). */ + if ((fun->flags & JSFUN_LAMBDA) && fun->atom) { + JSObject *env = STOBJ_GET_PARENT(callobj); + + JS_ASSERT(STOBJ_GET_CLASS(env) == &js_DeclEnvClass); + JS_ASSERT(env->getPrivate() == fp); + env->setPrivate(NULL); + } + + callobj->setPrivate(NULL); + fp->callobj = NULL; +} + +static JSBool +call_enumerate(JSContext *cx, JSObject *obj) +{ + JSFunction *fun; + uintN n, i; + void *mark; + jsuword *names; + JSBool ok; + JSAtom *name; + JSObject *pobj; + JSProperty *prop; + + fun = js_GetCallObjectFunction(obj); + n = fun ? fun->countArgsAndVars() : 0; + if (n == 0) + return JS_TRUE; + + mark = JS_ARENA_MARK(&cx->tempPool); + + MUST_FLOW_THROUGH("out"); + names = js_GetLocalNameArray(cx, fun, &cx->tempPool); + if (!names) { + ok = JS_FALSE; + goto out; + } + + for (i = 0; i != n; ++i) { + name = JS_LOCAL_NAME_TO_ATOM(names[i]); + if (!name) + continue; + + /* + * Trigger reflection by looking up the name of the argument or + * variable. + */ + ok = js_LookupProperty(cx, obj, ATOM_TO_JSID(name), &pobj, &prop); + if (!ok) + goto out; + + /* + * The call object will always have a property corresponding to the + * argument or variable name because call_resolve creates the property + * using JSPROP_PERMANENT. + */ + JS_ASSERT(prop); + JS_ASSERT(pobj == obj); + pobj->dropProperty(cx, prop); + } + ok = JS_TRUE; + + out: + JS_ARENA_RELEASE(&cx->tempPool, mark); + return ok; +} + +typedef enum JSCallPropertyKind { + JSCPK_ARGUMENTS, + JSCPK_ARG, + JSCPK_VAR +} JSCallPropertyKind; + +static JSBool +CallPropertyOp(JSContext *cx, JSObject *obj, jsid id, jsval *vp, + JSCallPropertyKind kind, JSBool setter) +{ + JSFunction *fun; + JSStackFrame *fp; + uintN i; + jsval *array; + + if (STOBJ_GET_CLASS(obj) != &js_CallClass) + return JS_TRUE; + + fun = js_GetCallObjectFunction(obj); + fp = (JSStackFrame *) obj->getPrivate(); + + if (kind == JSCPK_ARGUMENTS) { + if (setter) { + if (fp) + fp->flags |= JSFRAME_OVERRIDE_ARGS; + STOBJ_SET_SLOT(obj, JSSLOT_CALL_ARGUMENTS, *vp); + } else { + if (fp && !(fp->flags & JSFRAME_OVERRIDE_ARGS)) { + JSObject *argsobj; + + argsobj = js_GetArgsObject(cx, fp); + if (!argsobj) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(argsobj); + } else { + *vp = STOBJ_GET_SLOT(obj, JSSLOT_CALL_ARGUMENTS); + } + } + return JS_TRUE; + } + + JS_ASSERT((int16) JSVAL_TO_INT(id) == JSVAL_TO_INT(id)); + i = (uint16) JSVAL_TO_INT(id); + JS_ASSERT_IF(kind == JSCPK_ARG, i < fun->nargs); + JS_ASSERT_IF(kind == JSCPK_VAR, i < fun->u.i.nvars); + + if (!fp) { + i += CALL_CLASS_FIXED_RESERVED_SLOTS; + if (kind == JSCPK_VAR) + i += fun->nargs; + else + JS_ASSERT(kind == JSCPK_ARG); + return setter + ? JS_SetReservedSlot(cx, obj, i, *vp) + : JS_GetReservedSlot(cx, obj, i, vp); + } + + if (kind == JSCPK_ARG) { + array = fp->argv; + } else { + JS_ASSERT(kind == JSCPK_VAR); + array = fp->slots; + } + if (setter) { + GC_POKE(cx, array[i]); + array[i] = *vp; + } else { + *vp = array[i]; + } + return JS_TRUE; +} + +static JSBool +GetCallArguments(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + return CallPropertyOp(cx, obj, id, vp, JSCPK_ARGUMENTS, JS_FALSE); +} + +static JSBool +SetCallArguments(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + return CallPropertyOp(cx, obj, id, vp, JSCPK_ARGUMENTS, JS_TRUE); +} + +JSBool +js_GetCallArg(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + return CallPropertyOp(cx, obj, id, vp, JSCPK_ARG, JS_FALSE); +} + +JSBool +SetCallArg(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + return CallPropertyOp(cx, obj, id, vp, JSCPK_ARG, JS_TRUE); +} + +JSBool +js_GetCallVar(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + return CallPropertyOp(cx, obj, id, vp, JSCPK_VAR, JS_FALSE); +} + +JSBool +js_GetCallVarChecked(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + if (!CallPropertyOp(cx, obj, id, vp, JSCPK_VAR, JS_FALSE)) + return JS_FALSE; + + return CheckForEscapingClosure(cx, obj, vp); +} + +JSBool +SetCallVar(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + return CallPropertyOp(cx, obj, id, vp, JSCPK_VAR, JS_TRUE); +} + +JSBool JS_FASTCALL +js_SetCallArg(JSContext *cx, JSObject *obj, jsid id, jsval v) +{ + return CallPropertyOp(cx, obj, id, &v, JSCPK_ARG, JS_TRUE); +} + +JSBool JS_FASTCALL +js_SetCallVar(JSContext *cx, JSObject *obj, jsid id, jsval v) +{ + return CallPropertyOp(cx, obj, id, &v, JSCPK_VAR, JS_TRUE); +} + +JS_DEFINE_CALLINFO_4(extern, BOOL, js_SetCallArg, CONTEXT, OBJECT, JSID, JSVAL, 0, 0) +JS_DEFINE_CALLINFO_4(extern, BOOL, js_SetCallVar, CONTEXT, OBJECT, JSID, JSVAL, 0, 0) + +static JSBool +call_resolve(JSContext *cx, JSObject *obj, jsval idval, uintN flags, + JSObject **objp) +{ + jsval callee; + JSFunction *fun; + jsid id; + JSLocalKind localKind; + JSPropertyOp getter, setter; + uintN slot, attrs; + + JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_CallClass); + JS_ASSERT(!STOBJ_GET_PROTO(obj)); + + if (!JSVAL_IS_STRING(idval)) + return JS_TRUE; + + callee = STOBJ_GET_SLOT(obj, JSSLOT_CALLEE); + if (JSVAL_IS_VOID(callee)) + return JS_TRUE; + fun = GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(callee)); + + if (!js_ValueToStringId(cx, idval, &id)) + return JS_FALSE; + + /* + * Check whether the id refers to a formal parameter, local variable or + * the arguments special name. + * + * We define all such names using JSDNP_DONT_PURGE to avoid an expensive + * shape invalidation in js_DefineNativeProperty. If such an id happens to + * shadow a global or upvar of the same name, any inner functions can + * never access the outer binding. Thus it cannot invalidate any property + * cache entries or derived trace guards for the outer binding. See also + * comments in js_PurgeScopeChainHelper from jsobj.cpp. + */ + localKind = js_LookupLocal(cx, fun, JSID_TO_ATOM(id), &slot); + if (localKind != JSLOCAL_NONE && localKind != JSLOCAL_UPVAR) { + JS_ASSERT((uint16) slot == slot); + + /* + * We follow 10.2.3 of ECMA 262 v3 and make argument and variable + * properties of the Call objects enumerable. + */ + attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED; + if (localKind == JSLOCAL_ARG) { + JS_ASSERT(slot < fun->nargs); + getter = js_GetCallArg; + setter = SetCallArg; + } else { + JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST); + JS_ASSERT(slot < fun->u.i.nvars); + getter = js_GetCallVar; + setter = SetCallVar; + if (localKind == JSLOCAL_CONST) + attrs |= JSPROP_READONLY; + + /* + * Use js_GetCallVarChecked if the local's value is a null closure. + * This way we penalize performance only slightly on first use of a + * null closure, not on every use. + */ + jsval v; + if (!CallPropertyOp(cx, obj, INT_TO_JSID((int16)slot), &v, JSCPK_VAR, JS_FALSE)) + return JS_FALSE; + if (VALUE_IS_FUNCTION(cx, v) && + GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(v))->needsWrapper()) { + getter = js_GetCallVarChecked; + } + } + if (!js_DefineNativeProperty(cx, obj, id, JSVAL_VOID, getter, setter, + attrs, SPROP_HAS_SHORTID, (int16) slot, + NULL, JSDNP_DONT_PURGE)) { + return JS_FALSE; + } + *objp = obj; + return JS_TRUE; + } + + /* + * Resolve arguments so that we never store a particular Call object's + * arguments object reference in a Call prototype's |arguments| slot. + */ + if (id == ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom)) { + if (!js_DefineNativeProperty(cx, obj, id, JSVAL_VOID, + GetCallArguments, SetCallArguments, + JSPROP_PERMANENT | JSPROP_SHARED, + 0, 0, NULL, JSDNP_DONT_PURGE)) { + return JS_FALSE; + } + *objp = obj; + return JS_TRUE; + } + + /* Control flow reaches here only if id was not resolved. */ + return JS_TRUE; +} + +static JSBool +call_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) +{ + if (type == JSTYPE_FUNCTION) { + JSStackFrame *fp = (JSStackFrame *) obj->getPrivate(); + if (fp) { + JS_ASSERT(fp->fun); + JS_ASSERT(fp->argv); + *vp = fp->calleeValue(); + } + } + return JS_TRUE; +} + +static uint32 +call_reserveSlots(JSContext *cx, JSObject *obj) +{ + JSFunction *fun; + + fun = js_GetCallObjectFunction(obj); + return fun->countArgsAndVars(); +} + +JS_FRIEND_DATA(JSClass) js_CallClass = { + "Call", + JSCLASS_HAS_PRIVATE | + JSCLASS_HAS_RESERVED_SLOTS(CALL_CLASS_FIXED_RESERVED_SLOTS) | + JSCLASS_NEW_RESOLVE | JSCLASS_IS_ANONYMOUS | JSCLASS_MARK_IS_TRACE, + JS_PropertyStub, JS_PropertyStub, + JS_PropertyStub, JS_PropertyStub, + call_enumerate, (JSResolveOp)call_resolve, + call_convert, NULL, + NULL, NULL, + NULL, NULL, + NULL, NULL, + JS_CLASS_TRACE(args_or_call_trace), call_reserveSlots +}; + +/* Generic function tinyids. */ +enum { + FUN_ARGUMENTS = -1, /* predefined arguments local variable */ + FUN_LENGTH = -2, /* number of actual args, arity if inactive */ + FUN_ARITY = -3, /* number of formal parameters; desired argc */ + FUN_NAME = -4, /* function name, "" if anonymous */ + FUN_CALLER = -5 /* Function.prototype.caller, backward compat */ +}; + +static JSBool +fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + jsint slot; + JSFunction *fun; + JSStackFrame *fp; + JSSecurityCallbacks *callbacks; + + if (!JSVAL_IS_INT(id)) + return JS_TRUE; + slot = JSVAL_TO_INT(id); + + /* + * Loop because getter and setter can be delegated from another class, + * but loop only for FUN_LENGTH because we must pretend that f.length + * is in each function instance f, per ECMA-262, instead of only in the + * Function.prototype object (we use JSPROP_PERMANENT with JSPROP_SHARED + * to make it appear so). + * + * This code couples tightly to the attributes for the function_props[] + * initializers above, and to js_SetProperty and js_HasOwnProperty. + * + * It's important to allow delegating objects, even though they inherit + * this getter (fun_getProperty), to override arguments, arity, caller, + * and name. If we didn't return early for slot != FUN_LENGTH, we would + * clobber *vp with the native property value, instead of letting script + * override that value in delegating objects. + * + * Note how that clobbering is what simulates JSPROP_READONLY for all of + * the non-standard properties when the directly addressed object (obj) + * is a function object (i.e., when this loop does not iterate). + */ + while (!(fun = (JSFunction *) + JS_GetInstancePrivate(cx, obj, &js_FunctionClass, NULL))) { + if (slot != FUN_LENGTH) + return JS_TRUE; + obj = OBJ_GET_PROTO(cx, obj); + if (!obj) + return JS_TRUE; + } + + /* Find fun's top-most activation record. */ + for (fp = js_GetTopStackFrame(cx); + fp && (fp->fun != fun || (fp->flags & JSFRAME_SPECIAL)); + fp = fp->down) { + continue; + } + + switch (slot) { + case FUN_ARGUMENTS: + /* Warn if strict about f.arguments or equivalent unqualified uses. */ + if (!JS_ReportErrorFlagsAndNumber(cx, + JSREPORT_WARNING | JSREPORT_STRICT, + js_GetErrorMessage, NULL, + JSMSG_DEPRECATED_USAGE, + js_arguments_str)) { + return JS_FALSE; + } + if (fp) { + if (!js_GetArgsValue(cx, fp, vp)) + return JS_FALSE; + } else { + *vp = JSVAL_NULL; + } + break; + + case FUN_LENGTH: + case FUN_ARITY: + *vp = INT_TO_JSVAL((jsint)fun->nargs); + break; + + case FUN_NAME: + *vp = fun->atom + ? ATOM_KEY(fun->atom) + : STRING_TO_JSVAL(cx->runtime->emptyString); + break; + + case FUN_CALLER: + if (fp && fp->down && fp->down->fun) { + JSFunction *caller = fp->down->fun; + /* + * See equivalent condition in args_getProperty for ARGS_CALLEE, + * but here we do not want to throw, since this escape can happen + * via foo.caller alone, without any debugger or indirect eval. And + * it seems foo.caller is still used on the Web. + */ + if (caller->needsWrapper()) { + JSObject *wrapper = WrapEscapingClosure(cx, fp->down, FUN_OBJECT(caller), caller); + if (!wrapper) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(wrapper); + return JS_TRUE; + } + + JS_ASSERT(fp->down->argv); + *vp = fp->down->calleeValue(); + } else { + *vp = JSVAL_NULL; + } + if (!JSVAL_IS_PRIMITIVE(*vp)) { + callbacks = JS_GetSecurityCallbacks(cx); + if (callbacks && callbacks->checkObjectAccess) { + id = ATOM_KEY(cx->runtime->atomState.callerAtom); + if (!callbacks->checkObjectAccess(cx, obj, id, JSACC_READ, vp)) + return JS_FALSE; + } + } + break; + + default: + /* XXX fun[0] and fun.arguments[0] are equivalent. */ + if (fp && fp->fun && (uintN)slot < fp->fun->nargs) + *vp = fp->argv[slot]; + break; + } + + return JS_TRUE; +} + +/* + * ECMA-262 specifies that length is a property of function object instances, + * but we can avoid that space cost by delegating to a prototype property that + * is JSPROP_PERMANENT and JSPROP_SHARED. Each fun_getProperty call computes + * a fresh length value based on the arity of the individual function object's + * private data. + * + * The extensions below other than length, i.e., the ones not in ECMA-262, + * are neither JSPROP_READONLY nor JSPROP_SHARED, because for compatibility + * with ECMA we must allow a delegating object to override them. Therefore to + * avoid entraining garbage in Function.prototype slots, they must be resolved + * in non-prototype function objects, wherefore the lazy_function_props table + * and fun_resolve's use of it. + */ +#define LENGTH_PROP_ATTRS (JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED) + +static JSPropertySpec function_props[] = { + {js_length_str, FUN_LENGTH, LENGTH_PROP_ATTRS, fun_getProperty, JS_PropertyStub}, + {0,0,0,0,0} +}; + +typedef struct LazyFunctionProp { + uint16 atomOffset; + int8 tinyid; + uint8 attrs; +} LazyFunctionProp; + +/* NB: no sentinel at the end -- use JS_ARRAY_LENGTH to bound loops. */ +static LazyFunctionProp lazy_function_props[] = { + {ATOM_OFFSET(arguments), FUN_ARGUMENTS, JSPROP_PERMANENT}, + {ATOM_OFFSET(arity), FUN_ARITY, JSPROP_PERMANENT}, + {ATOM_OFFSET(caller), FUN_CALLER, JSPROP_PERMANENT}, + {ATOM_OFFSET(name), FUN_NAME, JSPROP_PERMANENT}, +}; + +static JSBool +fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, + JSObject **objp) +{ + JSFunction *fun; + JSAtom *atom; + uintN i; + + if (!JSVAL_IS_STRING(id)) + return JS_TRUE; + + fun = GET_FUNCTION_PRIVATE(cx, obj); + + /* + * No need to reflect fun.prototype in 'fun.prototype = ... '. Assert that + * fun is not a compiler-created function object, which must never leak to + * script or embedding code and then be mutated. + */ + if (flags & JSRESOLVE_ASSIGNING) { + JS_ASSERT(!js_IsInternalFunctionObject(obj)); + return JS_TRUE; + } + + /* + * Ok, check whether id is 'prototype' and bootstrap the function object's + * prototype property. + */ + atom = cx->runtime->atomState.classPrototypeAtom; + if (id == ATOM_KEY(atom)) { + JS_ASSERT(!js_IsInternalFunctionObject(obj)); + + /* + * Beware of the wacky case of a user function named Object -- trying + * to find a prototype for that will recur back here _ad perniciem_. + */ + if (fun->atom == CLASS_ATOM(cx, Object)) + return JS_TRUE; + + /* + * Make the prototype object to have the same parent as the function + * object itself. + */ + JSObject *proto = + js_NewObject(cx, &js_ObjectClass, NULL, OBJ_GET_PARENT(cx, obj)); + if (!proto) + return JS_FALSE; + + /* + * ECMA (15.3.5.2) says that constructor.prototype is DontDelete for + * user-defined functions, but DontEnum | ReadOnly | DontDelete for + * native "system" constructors such as Object or Function. So lazily + * set the former here in fun_resolve, but eagerly define the latter + * in JS_InitClass, with the right attributes. + */ + if (!js_SetClassPrototype(cx, obj, proto, JSPROP_PERMANENT)) + return JS_FALSE; + + *objp = obj; + return JS_TRUE; + } + + for (i = 0; i < JS_ARRAY_LENGTH(lazy_function_props); i++) { + LazyFunctionProp *lfp = &lazy_function_props[i]; + + atom = OFFSET_TO_ATOM(cx->runtime, lfp->atomOffset); + if (id == ATOM_KEY(atom)) { + JS_ASSERT(!js_IsInternalFunctionObject(obj)); + + if (!js_DefineNativeProperty(cx, obj, + ATOM_TO_JSID(atom), JSVAL_VOID, + fun_getProperty, JS_PropertyStub, + lfp->attrs, SPROP_HAS_SHORTID, + lfp->tinyid, NULL)) { + return JS_FALSE; + } + *objp = obj; + return JS_TRUE; + } + } + + return JS_TRUE; +} + +static JSBool +fun_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) +{ + switch (type) { + case JSTYPE_FUNCTION: + *vp = OBJECT_TO_JSVAL(obj); + return JS_TRUE; + default: + return js_TryValueOf(cx, obj, type, vp); + } +} + +#if JS_HAS_XDR + +/* XXX store parent and proto, if defined */ +JSBool +js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp) +{ + JSContext *cx; + JSFunction *fun; + uint32 firstword; /* flag telling whether fun->atom is non-null, + plus for fun->u.i.skipmin, fun->u.i.wrapper, + and 14 bits reserved for future use */ + uintN nargs, nvars, nupvars, n; + uint32 localsword; /* word for argument and variable counts */ + uint32 flagsword; /* word for fun->u.i.nupvars and fun->flags */ + JSTempValueRooter tvr; + JSBool ok; + + cx = xdr->cx; + if (xdr->mode == JSXDR_ENCODE) { + fun = GET_FUNCTION_PRIVATE(cx, *objp); + if (!FUN_INTERPRETED(fun)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_NOT_SCRIPTED_FUNCTION, + JS_GetFunctionName(fun)); + return JS_FALSE; + } + if (fun->u.i.wrapper) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_XDR_CLOSURE_WRAPPER, + JS_GetFunctionName(fun)); + return JS_FALSE; + } + JS_ASSERT((fun->u.i.wrapper & ~1U) == 0); + firstword = (fun->u.i.skipmin << 2) | (fun->u.i.wrapper << 1) | !!fun->atom; + nargs = fun->nargs; + nvars = fun->u.i.nvars; + nupvars = fun->u.i.nupvars; + localsword = (nargs << 16) | nvars; + flagsword = (nupvars << 16) | fun->flags; + } else { + fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, NULL, NULL); + if (!fun) + return JS_FALSE; + STOBJ_CLEAR_PARENT(FUN_OBJECT(fun)); + STOBJ_CLEAR_PROTO(FUN_OBJECT(fun)); +#ifdef __GNUC__ + nvars = nargs = nupvars = 0; /* quell GCC uninitialized warning */ +#endif + } + + /* From here on, control flow must flow through label out. */ + MUST_FLOW_THROUGH("out"); + JS_PUSH_TEMP_ROOT_OBJECT(cx, FUN_OBJECT(fun), &tvr); + ok = JS_TRUE; + + if (!JS_XDRUint32(xdr, &firstword)) + goto bad; + if ((firstword & 1U) && !js_XDRStringAtom(xdr, &fun->atom)) + goto bad; + if (!JS_XDRUint32(xdr, &localsword) || + !JS_XDRUint32(xdr, &flagsword)) { + goto bad; + } + + if (xdr->mode == JSXDR_DECODE) { + nargs = localsword >> 16; + nvars = uint16(localsword); + JS_ASSERT((flagsword & JSFUN_KINDMASK) >= JSFUN_INTERPRETED); + nupvars = flagsword >> 16; + fun->flags = uint16(flagsword); + fun->u.i.skipmin = uint16(firstword >> 2); + fun->u.i.wrapper = JSPackedBool((firstword >> 1) & 1); + } + + /* do arguments and local vars */ + n = nargs + nvars + nupvars; + if (n != 0) { + void *mark; + uintN i; + uintN bitmapLength; + uint32 *bitmap; + jsuword *names; + JSAtom *name; + JSLocalKind localKind; + + mark = JS_ARENA_MARK(&xdr->cx->tempPool); + + /* + * From this point the control must flow via the label release_mark. + * + * To xdr the names we prefix the names with a bitmap descriptor and + * then xdr the names as strings. For argument names (indexes below + * nargs) the corresponding bit in the bitmap is unset when the name + * is null. Such null names are not encoded or decoded. For variable + * names (indexes starting from nargs) bitmap's bit is set when the + * name is declared as const, not as ordinary var. + * */ + MUST_FLOW_THROUGH("release_mark"); + bitmapLength = JS_HOWMANY(n, JS_BITS_PER_UINT32); + JS_ARENA_ALLOCATE_CAST(bitmap, uint32 *, &xdr->cx->tempPool, + bitmapLength * sizeof *bitmap); + if (!bitmap) { + js_ReportOutOfScriptQuota(xdr->cx); + ok = JS_FALSE; + goto release_mark; + } + if (xdr->mode == JSXDR_ENCODE) { + names = js_GetLocalNameArray(xdr->cx, fun, &xdr->cx->tempPool); + if (!names) { + ok = JS_FALSE; + goto release_mark; + } + memset(bitmap, 0, bitmapLength * sizeof *bitmap); + for (i = 0; i != n; ++i) { + if (i < fun->nargs + ? JS_LOCAL_NAME_TO_ATOM(names[i]) != NULL + : JS_LOCAL_NAME_IS_CONST(names[i])) { + bitmap[i >> JS_BITS_PER_UINT32_LOG2] |= + JS_BIT(i & (JS_BITS_PER_UINT32 - 1)); + } + } + } +#ifdef __GNUC__ + else { + names = NULL; /* quell GCC uninitialized warning */ + } +#endif + for (i = 0; i != bitmapLength; ++i) { + ok = JS_XDRUint32(xdr, &bitmap[i]); + if (!ok) + goto release_mark; + } + for (i = 0; i != n; ++i) { + if (i < nargs && + !(bitmap[i >> JS_BITS_PER_UINT32_LOG2] & + JS_BIT(i & (JS_BITS_PER_UINT32 - 1)))) { + if (xdr->mode == JSXDR_DECODE) { + ok = js_AddLocal(xdr->cx, fun, NULL, JSLOCAL_ARG); + if (!ok) + goto release_mark; + } else { + JS_ASSERT(!JS_LOCAL_NAME_TO_ATOM(names[i])); + } + continue; + } + if (xdr->mode == JSXDR_ENCODE) + name = JS_LOCAL_NAME_TO_ATOM(names[i]); + ok = js_XDRStringAtom(xdr, &name); + if (!ok) + goto release_mark; + if (xdr->mode == JSXDR_DECODE) { + localKind = (i < nargs) + ? JSLOCAL_ARG + : (i < nargs + nvars) + ? (bitmap[i >> JS_BITS_PER_UINT32_LOG2] & + JS_BIT(i & (JS_BITS_PER_UINT32 - 1)) + ? JSLOCAL_CONST + : JSLOCAL_VAR) + : JSLOCAL_UPVAR; + ok = js_AddLocal(xdr->cx, fun, name, localKind); + if (!ok) + goto release_mark; + } + } + ok = JS_TRUE; + + release_mark: + JS_ARENA_RELEASE(&xdr->cx->tempPool, mark); + if (!ok) + goto out; + + if (xdr->mode == JSXDR_DECODE) + js_FreezeLocalNames(cx, fun); + } + + if (!js_XDRScript(xdr, &fun->u.i.script, false, NULL)) + goto bad; + + if (xdr->mode == JSXDR_DECODE) { + *objp = FUN_OBJECT(fun); + if (fun->u.i.script != JSScript::emptyScript()) { +#ifdef CHECK_SCRIPT_OWNER + fun->u.i.script->owner = NULL; +#endif + js_CallNewScriptHook(cx, fun->u.i.script, fun); + } + } + +out: + JS_POP_TEMP_ROOT(cx, &tvr); + return ok; + +bad: + ok = JS_FALSE; + goto out; +} + +#else /* !JS_HAS_XDR */ + +#define js_XDRFunctionObject NULL + +#endif /* !JS_HAS_XDR */ + +/* + * [[HasInstance]] internal method for Function objects: fetch the .prototype + * property of its 'this' parameter, and walks the prototype chain of v (only + * if v is an object) returning true if .prototype is found. + */ +static JSBool +fun_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) +{ + jsval pval; + jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom); + if (!obj->getProperty(cx, id, &pval)) + return JS_FALSE; + + if (JSVAL_IS_PRIMITIVE(pval)) { + /* + * Throw a runtime error if instanceof is called on a function that + * has a non-object as its .prototype value. + */ + js_ReportValueError(cx, JSMSG_BAD_PROTOTYPE, + -1, OBJECT_TO_JSVAL(obj), NULL); + return JS_FALSE; + } + + return js_IsDelegate(cx, JSVAL_TO_OBJECT(pval), v, bp); +} + +static void +TraceLocalNames(JSTracer *trc, JSFunction *fun); + +static void +DestroyLocalNames(JSContext *cx, JSFunction *fun); + +static void +fun_trace(JSTracer *trc, JSObject *obj) +{ + /* A newborn function object may have a not yet initialized private slot. */ + JSFunction *fun = (JSFunction *) obj->getPrivate(); + if (!fun) + return; + + if (FUN_OBJECT(fun) != obj) { + /* obj is cloned function object, trace the original. */ + JS_CALL_TRACER(trc, FUN_OBJECT(fun), JSTRACE_OBJECT, "private"); + return; + } + if (fun->atom) + JS_CALL_STRING_TRACER(trc, ATOM_TO_STRING(fun->atom), "atom"); + if (FUN_INTERPRETED(fun)) { + if (fun->u.i.script) + js_TraceScript(trc, fun->u.i.script); + TraceLocalNames(trc, fun); + } +} + +static void +fun_finalize(JSContext *cx, JSObject *obj) +{ + /* Ignore newborn and cloned function objects. */ + JSFunction *fun = (JSFunction *) obj->getPrivate(); + if (!fun || FUN_OBJECT(fun) != obj) + return; + + /* + * Null-check of u.i.script is required since the parser sets interpreted + * very early. + */ + if (FUN_INTERPRETED(fun)) { + if (fun->u.i.script) + js_DestroyScript(cx, fun->u.i.script); + DestroyLocalNames(cx, fun); + } +} + +int +JSFunction::sharpSlotBase(JSContext *cx) +{ +#if JS_HAS_SHARP_VARS + JSAtom *name = js_Atomize(cx, "#array", 6, 0); + if (name) { + uintN index = uintN(-1); +#ifdef DEBUG + JSLocalKind kind = +#endif + js_LookupLocal(cx, this, name, &index); + JS_ASSERT(kind == JSLOCAL_VAR); + return int(index); + } +#endif + return -1; +} + +uint32 +JSFunction::countInterpretedReservedSlots() const +{ + JS_ASSERT(FUN_INTERPRETED(this)); + + uint32 nslots = (u.i.nupvars == 0) + ? 0 + : u.i.script->upvars()->length; + if (u.i.script->regexpsOffset != 0) + nslots += u.i.script->regexps()->length; + return nslots; +} + +static uint32 +fun_reserveSlots(JSContext *cx, JSObject *obj) +{ + /* + * We use getPrivate and not GET_FUNCTION_PRIVATE because during + * js_InitFunctionClass invocation the function is called before the + * private slot of the function object is set. + */ + JSFunction *fun = (JSFunction *) obj->getPrivate(); + return (fun && FUN_INTERPRETED(fun)) + ? fun->countInterpretedReservedSlots() + : 0; +} + +/* + * Reserve two slots in all function objects for XPConnect. Note that this + * does not bloat every instance, only those on which reserved slots are set, + * and those on which ad-hoc properties are defined. + */ +JS_FRIEND_DATA(JSClass) js_FunctionClass = { + js_Function_str, + JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(2) | + JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Function), + JS_PropertyStub, JS_PropertyStub, + JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, (JSResolveOp)fun_resolve, + fun_convert, fun_finalize, + NULL, NULL, + NULL, NULL, + js_XDRFunctionObject, fun_hasInstance, + JS_CLASS_TRACE(fun_trace), fun_reserveSlots +}; + +static JSBool +fun_toStringHelper(JSContext *cx, uint32 indent, uintN argc, jsval *vp) +{ + jsval fval; + JSObject *obj; + JSFunction *fun; + JSString *str; + + fval = JS_THIS(cx, vp); + if (JSVAL_IS_NULL(fval)) + return JS_FALSE; + + if (!VALUE_IS_FUNCTION(cx, fval)) { + /* + * If we don't have a function to start off with, try converting the + * object to a function. If that doesn't work, complain. + */ + if (!JSVAL_IS_PRIMITIVE(fval)) { + obj = JSVAL_TO_OBJECT(fval); + if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, JSTYPE_FUNCTION, + &fval)) { + return JS_FALSE; + } + vp[1] = fval; + } + if (!VALUE_IS_FUNCTION(cx, fval)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_INCOMPATIBLE_PROTO, + js_Function_str, js_toString_str, + JS_GetTypeName(cx, JS_TypeOfValue(cx, fval))); + return JS_FALSE; + } + } + + obj = JSVAL_TO_OBJECT(fval); + if (argc != 0) { + indent = js_ValueToECMAUint32(cx, &vp[2]); + if (JSVAL_IS_NULL(vp[2])) + return JS_FALSE; + } + + JS_ASSERT(JS_ObjectIsFunction(cx, obj)); + fun = GET_FUNCTION_PRIVATE(cx, obj); + if (!fun) + return JS_TRUE; + str = JS_DecompileFunction(cx, fun, (uintN)indent); + if (!str) + return JS_FALSE; + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +fun_toString(JSContext *cx, uintN argc, jsval *vp) +{ + return fun_toStringHelper(cx, 0, argc, vp); +} + +#if JS_HAS_TOSOURCE +static JSBool +fun_toSource(JSContext *cx, uintN argc, jsval *vp) +{ + return fun_toStringHelper(cx, JS_DONT_PRETTY_PRINT, argc, vp); +} +#endif + +JSBool +js_fun_call(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *obj; + jsval fval, *argv, *invokevp; + JSString *str; + void *mark; + JSBool ok; + + js_LeaveTrace(cx); + + obj = JS_THIS_OBJECT(cx, vp); + if (!obj || !obj->defaultValue(cx, JSTYPE_FUNCTION, &vp[1])) + return JS_FALSE; + fval = vp[1]; + + if (!VALUE_IS_FUNCTION(cx, fval)) { + str = JS_ValueToString(cx, fval); + if (str) { + const char *bytes = js_GetStringBytes(cx, str); + + if (bytes) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_INCOMPATIBLE_PROTO, + js_Function_str, js_call_str, + bytes); + } + } + return JS_FALSE; + } + + argv = vp + 2; + if (argc == 0) { + /* Call fun with its global object as the 'this' param if no args. */ + obj = NULL; + } else { + /* Otherwise convert the first arg to 'this' and skip over it. */ + if (!JSVAL_IS_PRIMITIVE(argv[0])) + obj = JSVAL_TO_OBJECT(argv[0]); + else if (!js_ValueToObject(cx, argv[0], &obj)) + return JS_FALSE; + argc--; + argv++; + } + + /* Allocate stack space for fval, obj, and the args. */ + invokevp = js_AllocStack(cx, 2 + argc, &mark); + if (!invokevp) + return JS_FALSE; + + /* Push fval, obj, and the args. */ + invokevp[0] = fval; + invokevp[1] = OBJECT_TO_JSVAL(obj); + memcpy(invokevp + 2, argv, argc * sizeof *argv); + + ok = js_Invoke(cx, argc, invokevp, 0); + *vp = *invokevp; + js_FreeStack(cx, mark); + return ok; +} + +JSBool +js_fun_apply(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *obj, *aobj; + jsval fval, *invokevp, *sp; + JSString *str; + jsuint length; + JSBool arraylike, ok; + void *mark; + uintN i; + + if (argc == 0) { + /* Will get globalObject as 'this' and no other arguments. */ + return js_fun_call(cx, argc, vp); + } + + js_LeaveTrace(cx); + + obj = JS_THIS_OBJECT(cx, vp); + if (!obj || !obj->defaultValue(cx, JSTYPE_FUNCTION, &vp[1])) + return JS_FALSE; + fval = vp[1]; + + if (!VALUE_IS_FUNCTION(cx, fval)) { + str = JS_ValueToString(cx, fval); + if (str) { + const char *bytes = js_GetStringBytes(cx, str); + + if (bytes) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_INCOMPATIBLE_PROTO, + js_Function_str, js_apply_str, + bytes); + } + } + return JS_FALSE; + } + + /* Quell GCC overwarnings. */ + aobj = NULL; + length = 0; + + if (argc >= 2) { + /* If the 2nd arg is null or void, call the function with 0 args. */ + if (JSVAL_IS_NULL(vp[3]) || JSVAL_IS_VOID(vp[3])) { + argc = 0; + } else { + /* The second arg must be an array (or arguments object). */ + arraylike = JS_FALSE; + if (!JSVAL_IS_PRIMITIVE(vp[3])) { + aobj = JSVAL_TO_OBJECT(vp[3]); + if (!js_IsArrayLike(cx, aobj, &arraylike, &length)) + return JS_FALSE; + } + if (!arraylike) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_APPLY_ARGS, js_apply_str); + return JS_FALSE; + } + } + } + + /* Convert the first arg to 'this' and skip over it. */ + if (!JSVAL_IS_PRIMITIVE(vp[2])) + obj = JSVAL_TO_OBJECT(vp[2]); + else if (!js_ValueToObject(cx, vp[2], &obj)) + return JS_FALSE; + + /* Allocate stack space for fval, obj, and the args. */ + argc = (uintN)JS_MIN(length, JS_ARGS_LENGTH_MAX); + invokevp = js_AllocStack(cx, 2 + argc, &mark); + if (!invokevp) + return JS_FALSE; + + /* Push fval, obj, and aobj's elements as args. */ + sp = invokevp; + *sp++ = fval; + *sp++ = OBJECT_TO_JSVAL(obj); + for (i = 0; i < argc; i++) { + ok = JS_GetElement(cx, aobj, (jsint)i, sp); + if (!ok) + goto out; + sp++; + } + + ok = js_Invoke(cx, argc, invokevp, 0); + *vp = *invokevp; +out: + js_FreeStack(cx, mark); + return ok; +} + +#ifdef NARCISSUS +static JS_REQUIRES_STACK JSBool +fun_applyConstructor(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *aobj; + uintN length, i; + void *mark; + jsval *invokevp, *sp; + JSBool ok; + + if (JSVAL_IS_PRIMITIVE(vp[2]) || + (aobj = JSVAL_TO_OBJECT(vp[2]), + OBJ_GET_CLASS(cx, aobj) != &js_ArrayClass && + OBJ_GET_CLASS(cx, aobj) != &js_ArgumentsClass)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_APPLY_ARGS, "__applyConstruct__"); + return JS_FALSE; + } + + if (!js_GetLengthProperty(cx, aobj, &length)) + return JS_FALSE; + + if (length > JS_ARGS_LENGTH_MAX) + length = JS_ARGS_LENGTH_MAX; + invokevp = js_AllocStack(cx, 2 + length, &mark); + if (!invokevp) + return JS_FALSE; + + sp = invokevp; + *sp++ = vp[1]; + *sp++ = JSVAL_NULL; /* this is filled automagically */ + for (i = 0; i < length; i++) { + ok = JS_GetElement(cx, aobj, (jsint)i, sp); + if (!ok) + goto out; + sp++; + } + + ok = js_InvokeConstructor(cx, length, JS_TRUE, invokevp); + *vp = *invokevp; +out: + js_FreeStack(cx, mark); + return ok; +} +#endif + +static JSFunctionSpec function_methods[] = { +#if JS_HAS_TOSOURCE + JS_FN(js_toSource_str, fun_toSource, 0,0), +#endif + JS_FN(js_toString_str, fun_toString, 0,0), + JS_FN(js_apply_str, js_fun_apply, 2,0), + JS_FN(js_call_str, js_fun_call, 1,0), +#ifdef NARCISSUS + JS_FN("__applyConstructor__", fun_applyConstructor, 1,0), +#endif + JS_FS_END +}; + +static JSBool +Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSFunction *fun; + JSObject *parent; + JSStackFrame *fp, *caller; + uintN i, n, lineno; + JSAtom *atom; + const char *filename; + JSBool ok; + JSString *str, *arg; + JSTokenStream ts(cx); + JSPrincipals *principals; + jschar *collected_args, *cp; + void *mark; + size_t arg_length, args_length, old_args_length; + JSTokenType tt; + + if (!JS_IsConstructing(cx)) { + obj = js_NewObject(cx, &js_FunctionClass, NULL, NULL); + if (!obj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + } else { + /* + * The constructor is called before the private slot is initialized so + * we must use getPrivate, not GET_FUNCTION_PRIVATE here. + */ + if (obj->getPrivate()) + return JS_TRUE; + } + + /* + * NB: (new Function) is not lexically closed by its caller, it's just an + * anonymous function in the top-level scope that its constructor inhabits. + * Thus 'var x = 42; f = new Function("return x"); print(f())' prints 42, + * and so would a call to f from another top-level's script or function. + * + * In older versions, before call objects, a new Function was adopted by + * its running context's globalObject, which might be different from the + * top-level reachable from scopeChain (in HTML frames, e.g.). + */ + parent = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(argv[-2])); + + fun = js_NewFunction(cx, obj, NULL, 0, JSFUN_LAMBDA | JSFUN_INTERPRETED, + parent, cx->runtime->atomState.anonymousAtom); + + if (!fun) + return JS_FALSE; + + /* + * Function is static and not called directly by other functions in this + * file, therefore it is callable only as a native function by js_Invoke. + * Find the scripted caller, possibly skipping other native frames such as + * are built for Function.prototype.call or .apply activations that invoke + * Function indirectly from a script. + */ + fp = js_GetTopStackFrame(cx); + JS_ASSERT(!fp->script && fp->fun && fp->fun->u.n.native == Function); + caller = js_GetScriptedCaller(cx, fp); + if (caller) { + principals = JS_EvalFramePrincipals(cx, fp, caller); + filename = js_ComputeFilename(cx, caller, principals, &lineno); + } else { + filename = NULL; + lineno = 0; + principals = NULL; + } + + /* Belt-and-braces: check that the caller has access to parent. */ + if (!js_CheckPrincipalsAccess(cx, parent, principals, + CLASS_ATOM(cx, Function))) { + return JS_FALSE; + } + + n = argc ? argc - 1 : 0; + if (n > 0) { + enum { OK, BAD, BAD_FORMAL } state; + + /* + * Collect the function-argument arguments into one string, separated + * by commas, then make a tokenstream from that string, and scan it to + * get the arguments. We need to throw the full scanner at the + * problem, because the argument string can legitimately contain + * comments and linefeeds. XXX It might be better to concatenate + * everything up into a function definition and pass it to the + * compiler, but doing it this way is less of a delta from the old + * code. See ECMA 15.3.2.1. + */ + state = BAD_FORMAL; + args_length = 0; + for (i = 0; i < n; i++) { + /* Collect the lengths for all the function-argument arguments. */ + arg = js_ValueToString(cx, argv[i]); + if (!arg) + return JS_FALSE; + argv[i] = STRING_TO_JSVAL(arg); + + /* + * Check for overflow. The < test works because the maximum + * JSString length fits in 2 fewer bits than size_t has. + */ + old_args_length = args_length; + args_length = old_args_length + arg->length(); + if (args_length < old_args_length) { + js_ReportAllocationOverflow(cx); + return JS_FALSE; + } + } + + /* Add 1 for each joining comma and check for overflow (two ways). */ + old_args_length = args_length; + args_length = old_args_length + n - 1; + if (args_length < old_args_length || + args_length >= ~(size_t)0 / sizeof(jschar)) { + js_ReportAllocationOverflow(cx); + return JS_FALSE; + } + + /* + * Allocate a string to hold the concatenated arguments, including room + * for a terminating 0. Mark cx->tempPool for later release, to free + * collected_args and its tokenstream in one swoop. + */ + mark = JS_ARENA_MARK(&cx->tempPool); + JS_ARENA_ALLOCATE_CAST(cp, jschar *, &cx->tempPool, + (args_length+1) * sizeof(jschar)); + if (!cp) { + js_ReportOutOfScriptQuota(cx); + return JS_FALSE; + } + collected_args = cp; + + /* + * Concatenate the arguments into the new string, separated by commas. + */ + for (i = 0; i < n; i++) { + arg = JSVAL_TO_STRING(argv[i]); + arg_length = arg->length(); + (void) js_strncpy(cp, arg->chars(), arg_length); + cp += arg_length; + + /* Add separating comma or terminating 0. */ + *cp++ = (i + 1 < n) ? ',' : 0; + } + + /* Initialize a tokenstream that reads from the given string. */ + if (!ts.init(cx, collected_args, args_length, NULL, filename, lineno)) { + JS_ARENA_RELEASE(&cx->tempPool, mark); + return JS_FALSE; + } + + /* The argument string may be empty or contain no tokens. */ + tt = js_GetToken(cx, &ts); + if (tt != TOK_EOF) { + for (;;) { + /* + * Check that it's a name. This also implicitly guards against + * TOK_ERROR, which was already reported. + */ + if (tt != TOK_NAME) + goto after_args; + + /* + * Get the atom corresponding to the name from the token + * stream; we're assured at this point that it's a valid + * identifier. + */ + atom = CURRENT_TOKEN(&ts).t_atom; + + /* Check for a duplicate parameter name. */ + if (js_LookupLocal(cx, fun, atom, NULL) != JSLOCAL_NONE) { + const char *name; + + name = js_AtomToPrintableString(cx, atom); + ok = name && + js_ReportCompileErrorNumber(cx, &ts, NULL, + JSREPORT_WARNING | + JSREPORT_STRICT, + JSMSG_DUPLICATE_FORMAL, + name); + if (!ok) + goto after_args; + } + if (!js_AddLocal(cx, fun, atom, JSLOCAL_ARG)) + goto after_args; + + /* + * Get the next token. Stop on end of stream. Otherwise + * insist on a comma, get another name, and iterate. + */ + tt = js_GetToken(cx, &ts); + if (tt == TOK_EOF) + break; + if (tt != TOK_COMMA) + goto after_args; + tt = js_GetToken(cx, &ts); + } + } + + state = OK; + after_args: + if (state == BAD_FORMAL && !(ts.flags & TSF_ERROR)) { + /* + * Report "malformed formal parameter" iff no illegal char or + * similar scanner error was already reported. + */ + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_FORMAL); + } + ts.close(cx); + JS_ARENA_RELEASE(&cx->tempPool, mark); + if (state != OK) + return JS_FALSE; + } + + if (argc) { + str = js_ValueToString(cx, argv[argc-1]); + if (!str) + return JS_FALSE; + argv[argc-1] = STRING_TO_JSVAL(str); + } else { + str = cx->runtime->emptyString; + } + + return JSCompiler::compileFunctionBody(cx, fun, principals, + str->chars(), str->length(), + filename, lineno); +} + +JSObject * +js_InitFunctionClass(JSContext *cx, JSObject *obj) +{ + JSObject *proto; + JSFunction *fun; + + proto = JS_InitClass(cx, obj, NULL, &js_FunctionClass, Function, 1, + function_props, function_methods, NULL, NULL); + if (!proto) + return NULL; + fun = js_NewFunction(cx, proto, NULL, 0, JSFUN_INTERPRETED, obj, NULL); + if (!fun) + return NULL; + fun->u.i.script = JSScript::emptyScript(); + return proto; +} + +JSFunction * +js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs, + uintN flags, JSObject *parent, JSAtom *atom) +{ + JSFunction *fun; + + if (funobj) { + JS_ASSERT(HAS_FUNCTION_CLASS(funobj)); + OBJ_SET_PARENT(cx, funobj, parent); + } else { + funobj = js_NewObject(cx, &js_FunctionClass, NULL, parent); + if (!funobj) + return NULL; + } + JS_ASSERT(!funobj->getPrivate()); + fun = (JSFunction *) funobj; + + /* Initialize all function members. */ + fun->nargs = nargs; + fun->flags = flags & (JSFUN_FLAGS_MASK | JSFUN_KINDMASK | JSFUN_TRCINFO); + if ((flags & JSFUN_KINDMASK) >= JSFUN_INTERPRETED) { + JS_ASSERT(!native); + JS_ASSERT(nargs == 0); + fun->u.i.nvars = 0; + fun->u.i.nupvars = 0; + fun->u.i.skipmin = 0; + fun->u.i.wrapper = false; + fun->u.i.script = NULL; +#ifdef DEBUG + fun->u.i.names.taggedAtom = 0; +#endif + } else { + fun->u.n.extra = 0; + fun->u.n.spare = 0; + fun->u.n.clasp = NULL; + if (flags & JSFUN_TRCINFO) { +#ifdef JS_TRACER + JSNativeTraceInfo *trcinfo = + JS_FUNC_TO_DATA_PTR(JSNativeTraceInfo *, native); + fun->u.n.native = (JSNative) trcinfo->native; + fun->u.n.trcinfo = trcinfo; +#else + fun->u.n.trcinfo = NULL; +#endif + } else { + fun->u.n.native = native; + fun->u.n.trcinfo = NULL; + } + JS_ASSERT(fun->u.n.native); + } + fun->atom = atom; + + /* Set private to self to indicate non-cloned fully initialized function. */ + FUN_OBJECT(fun)->setPrivate(fun); + return fun; +} + +JSObject * +js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent) +{ + /* + * The cloned function object does not need the extra JSFunction members + * beyond JSObject as it points to fun via the private slot. + */ + JSObject *clone = js_NewObject(cx, &js_FunctionClass, NULL, parent, sizeof(JSObject)); + if (!clone) + return NULL; + clone->setPrivate(fun); + return clone; +} + +/* + * Create a new flat closure, but don't initialize the imported upvar + * values. The tracer calls this function and then initializes the upvar + * slots on trace. + */ +JSObject * JS_FASTCALL +js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain) +{ + JS_ASSERT(FUN_FLAT_CLOSURE(fun)); + JS_ASSERT((fun->u.i.script->upvarsOffset + ? fun->u.i.script->upvars()->length + : 0) == fun->u.i.nupvars); + + JSObject *closure = js_CloneFunctionObject(cx, fun, scopeChain); + if (!closure) + return closure; + + uint32 nslots = fun->countInterpretedReservedSlots(); + if (!nslots) + return closure; + if (!js_EnsureReservedSlots(cx, closure, nslots)) + return NULL; + + return closure; +} + +JS_DEFINE_CALLINFO_3(extern, OBJECT, js_AllocFlatClosure, + CONTEXT, FUNCTION, OBJECT, 0, 0) + +JSObject * +js_NewFlatClosure(JSContext *cx, JSFunction *fun) +{ + JSObject *closure = js_AllocFlatClosure(cx, fun, cx->fp->scopeChain); + if (!closure || fun->u.i.nupvars == 0) + return closure; + + JSUpvarArray *uva = fun->u.i.script->upvars(); + JS_ASSERT(uva->length <= size_t(closure->dslots[-1])); + + uintN level = fun->u.i.script->staticLevel; + for (uint32 i = 0, n = uva->length; i < n; i++) + closure->dslots[i] = js_GetUpvar(cx, level, uva->vector[i]); + + return closure; +} + +JSObject * +js_NewDebuggableFlatClosure(JSContext *cx, JSFunction *fun) +{ + JS_ASSERT(cx->fp->fun->flags & JSFUN_HEAVYWEIGHT); + JS_ASSERT(!cx->fp->fun->optimizedClosure()); + + return WrapEscapingClosure(cx, cx->fp, FUN_OBJECT(fun), fun); +} + +JSFunction * +js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native, + uintN nargs, uintN attrs) +{ + JSPropertyOp gsop; + JSFunction *fun; + + if (attrs & JSFUN_STUB_GSOPS) { + /* + * JSFUN_STUB_GSOPS is a request flag only, not stored in fun->flags or + * the defined property's attributes. This allows us to encode another, + * internal flag using the same bit, JSFUN_EXPR_CLOSURE -- see jsfun.h + * for more on this. + */ + attrs &= ~JSFUN_STUB_GSOPS; + gsop = JS_PropertyStub; + } else { + gsop = NULL; + } + fun = js_NewFunction(cx, NULL, native, nargs, attrs, obj, atom); + if (!fun) + return NULL; + if (!obj->defineProperty(cx, ATOM_TO_JSID(atom), OBJECT_TO_JSVAL(FUN_OBJECT(fun)), + gsop, gsop, attrs & ~JSFUN_FLAGS_MASK)) { + return NULL; + } + return fun; +} + +#if (JSV2F_CONSTRUCT & JSV2F_SEARCH_STACK) +# error "JSINVOKE_CONSTRUCT and JSV2F_SEARCH_STACK are not disjoint!" +#endif + +JSFunction * +js_ValueToFunction(JSContext *cx, jsval *vp, uintN flags) +{ + jsval v; + JSObject *obj; + + v = *vp; + obj = NULL; + if (JSVAL_IS_OBJECT(v)) { + obj = JSVAL_TO_OBJECT(v); + if (obj && OBJ_GET_CLASS(cx, obj) != &js_FunctionClass) { + if (!obj->defaultValue(cx, JSTYPE_FUNCTION, &v)) + return NULL; + obj = VALUE_IS_FUNCTION(cx, v) ? JSVAL_TO_OBJECT(v) : NULL; + } + } + if (!obj) { + js_ReportIsNotFunction(cx, vp, flags); + return NULL; + } + return GET_FUNCTION_PRIVATE(cx, obj); +} + +JSObject * +js_ValueToFunctionObject(JSContext *cx, jsval *vp, uintN flags) +{ + JSFunction *fun; + JSStackFrame *caller; + JSPrincipals *principals; + + if (VALUE_IS_FUNCTION(cx, *vp)) + return JSVAL_TO_OBJECT(*vp); + + fun = js_ValueToFunction(cx, vp, flags); + if (!fun) + return NULL; + *vp = OBJECT_TO_JSVAL(FUN_OBJECT(fun)); + + caller = js_GetScriptedCaller(cx, NULL); + if (caller) { + principals = JS_StackFramePrincipals(cx, caller); + } else { + /* No scripted caller, don't allow access. */ + principals = NULL; + } + + if (!js_CheckPrincipalsAccess(cx, FUN_OBJECT(fun), principals, + fun->atom + ? fun->atom + : cx->runtime->atomState.anonymousAtom)) { + return NULL; + } + return FUN_OBJECT(fun); +} + +JSObject * +js_ValueToCallableObject(JSContext *cx, jsval *vp, uintN flags) +{ + JSObject *callable = JSVAL_IS_OBJECT(*vp) ? JSVAL_TO_OBJECT(*vp) : NULL; + + if (callable && js_IsCallable(callable, cx)) { + *vp = OBJECT_TO_JSVAL(callable); + return callable; + } + return js_ValueToFunctionObject(cx, vp, flags); +} + +void +js_ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags) +{ + JSStackFrame *fp; + uintN error; + const char *name, *source; + JSTempValueRooter tvr; + + for (fp = js_GetTopStackFrame(cx); fp && !fp->regs; fp = fp->down) + continue; + name = source = NULL; + JS_PUSH_TEMP_ROOT_STRING(cx, NULL, &tvr); + if (flags & JSV2F_ITERATOR) { + error = JSMSG_BAD_ITERATOR; + name = js_iterator_str; + JSString *src = js_ValueToSource(cx, *vp); + if (!src) + goto out; + tvr.u.value = STRING_TO_JSVAL(src); + JSString *qsrc = js_QuoteString(cx, src, 0); + if (!qsrc) + goto out; + tvr.u.value = STRING_TO_JSVAL(qsrc); + source = js_GetStringBytes(cx, qsrc); + if (!source) + goto out; + } else if (flags & JSV2F_CONSTRUCT) { + error = JSMSG_NOT_CONSTRUCTOR; + } else { + error = JSMSG_NOT_FUNCTION; + } + + js_ReportValueError3(cx, error, + (fp && fp->regs && + StackBase(fp) <= vp && vp < fp->regs->sp) + ? vp - fp->regs->sp + : (flags & JSV2F_SEARCH_STACK) + ? JSDVG_SEARCH_STACK + : JSDVG_IGNORE_STACK, + *vp, NULL, + name, source); + + out: + JS_POP_TEMP_ROOT(cx, &tvr); +} + +/* + * When a function has between 2 and MAX_ARRAY_LOCALS arguments and variables, + * their name are stored as the JSLocalNames.array. + */ +#define MAX_ARRAY_LOCALS 8 + +JS_STATIC_ASSERT(2 <= MAX_ARRAY_LOCALS); +JS_STATIC_ASSERT(MAX_ARRAY_LOCALS < JS_BITMASK(16)); + +/* + * We use the lowest bit of the string atom to distinguish const from var + * name when there is only single name or when names are stored as an array. + */ +JS_STATIC_ASSERT((JSVAL_STRING & 1) == 0); + +/* + * When we use a hash table to store the local names, we use a singly linked + * list to record the indexes of duplicated parameter names to preserve the + * duplicates for the decompiler. + */ +typedef struct JSNameIndexPair JSNameIndexPair; + +struct JSNameIndexPair { + JSAtom *name; + uint16 index; + JSNameIndexPair *link; +}; + +struct JSLocalNameMap { + JSDHashTable names; + JSNameIndexPair *lastdup; +}; + +typedef struct JSLocalNameHashEntry { + JSDHashEntryHdr hdr; + JSAtom *name; + uint16 index; + uint8 localKind; +} JSLocalNameHashEntry; + +static void +FreeLocalNameHash(JSContext *cx, JSLocalNameMap *map) +{ + JSNameIndexPair *dup, *next; + + for (dup = map->lastdup; dup; dup = next) { + next = dup->link; + cx->free(dup); + } + JS_DHashTableFinish(&map->names); + cx->free(map); +} + +static JSBool +HashLocalName(JSContext *cx, JSLocalNameMap *map, JSAtom *name, + JSLocalKind localKind, uintN index) +{ + JSLocalNameHashEntry *entry; + JSNameIndexPair *dup; + + JS_ASSERT(index <= JS_BITMASK(16)); +#if JS_HAS_DESTRUCTURING + if (!name) { + /* A destructuring pattern does not need a hash entry. */ + JS_ASSERT(localKind == JSLOCAL_ARG); + return JS_TRUE; + } +#endif + JS_ASSERT(ATOM_IS_STRING(name)); + entry = (JSLocalNameHashEntry *) + JS_DHashTableOperate(&map->names, name, JS_DHASH_ADD); + if (!entry) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + if (entry->name) { + JS_ASSERT(entry->name == name); + JS_ASSERT(entry->localKind == JSLOCAL_ARG); + dup = (JSNameIndexPair *) cx->malloc(sizeof *dup); + if (!dup) + return JS_FALSE; + dup->name = entry->name; + dup->index = entry->index; + dup->link = map->lastdup; + map->lastdup = dup; + } + entry->name = name; + entry->index = (uint16) index; + entry->localKind = (uint8) localKind; + return JS_TRUE; +} + +JSBool +js_AddLocal(JSContext *cx, JSFunction *fun, JSAtom *atom, JSLocalKind kind) +{ + jsuword taggedAtom; + uint16 *indexp; + uintN n, i; + jsuword *array; + JSLocalNameMap *map; + + JS_ASSERT(FUN_INTERPRETED(fun)); + JS_ASSERT(!fun->u.i.script); + JS_ASSERT(((jsuword) atom & 1) == 0); + taggedAtom = (jsuword) atom; + if (kind == JSLOCAL_ARG) { + indexp = &fun->nargs; + } else if (kind == JSLOCAL_UPVAR) { + indexp = &fun->u.i.nupvars; + } else { + indexp = &fun->u.i.nvars; + if (kind == JSLOCAL_CONST) + taggedAtom |= 1; + else + JS_ASSERT(kind == JSLOCAL_VAR); + } + n = fun->countLocalNames(); + if (n == 0) { + JS_ASSERT(fun->u.i.names.taggedAtom == 0); + fun->u.i.names.taggedAtom = taggedAtom; + } else if (n < MAX_ARRAY_LOCALS) { + if (n > 1) { + array = fun->u.i.names.array; + } else { + array = (jsuword *) cx->malloc(MAX_ARRAY_LOCALS * sizeof *array); + if (!array) + return JS_FALSE; + array[0] = fun->u.i.names.taggedAtom; + fun->u.i.names.array = array; + } + if (kind == JSLOCAL_ARG) { + /* + * A destructuring argument pattern adds variables, not arguments, + * so for the following arguments nvars != 0. + */ +#if JS_HAS_DESTRUCTURING + if (fun->u.i.nvars != 0) { + memmove(array + fun->nargs + 1, array + fun->nargs, + fun->u.i.nvars * sizeof *array); + } +#else + JS_ASSERT(fun->u.i.nvars == 0); +#endif + array[fun->nargs] = taggedAtom; + } else { + array[n] = taggedAtom; + } + } else if (n == MAX_ARRAY_LOCALS) { + array = fun->u.i.names.array; + map = (JSLocalNameMap *) cx->malloc(sizeof *map); + if (!map) + return JS_FALSE; + if (!JS_DHashTableInit(&map->names, JS_DHashGetStubOps(), + NULL, sizeof(JSLocalNameHashEntry), + JS_DHASH_DEFAULT_CAPACITY(MAX_ARRAY_LOCALS + * 2))) { + JS_ReportOutOfMemory(cx); + cx->free(map); + return JS_FALSE; + } + + map->lastdup = NULL; + for (i = 0; i != MAX_ARRAY_LOCALS; ++i) { + taggedAtom = array[i]; + uintN j = i; + JSLocalKind k = JSLOCAL_ARG; + if (j >= fun->nargs) { + j -= fun->nargs; + if (j < fun->u.i.nvars) { + k = (taggedAtom & 1) ? JSLOCAL_CONST : JSLOCAL_VAR; + } else { + j -= fun->u.i.nvars; + k = JSLOCAL_UPVAR; + } + } + if (!HashLocalName(cx, map, (JSAtom *) (taggedAtom & ~1), k, j)) { + FreeLocalNameHash(cx, map); + return JS_FALSE; + } + } + if (!HashLocalName(cx, map, atom, kind, *indexp)) { + FreeLocalNameHash(cx, map); + return JS_FALSE; + } + + /* + * At this point the entry is added and we cannot fail. It is time + * to replace fun->u.i.names with the built map. + */ + fun->u.i.names.map = map; + cx->free(array); + } else { + if (*indexp == JS_BITMASK(16)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + (kind == JSLOCAL_ARG) + ? JSMSG_TOO_MANY_FUN_ARGS + : JSMSG_TOO_MANY_LOCALS); + return JS_FALSE; + } + if (!HashLocalName(cx, fun->u.i.names.map, atom, kind, *indexp)) + return JS_FALSE; + } + + /* Update the argument or variable counter. */ + ++*indexp; + return JS_TRUE; +} + +JSLocalKind +js_LookupLocal(JSContext *cx, JSFunction *fun, JSAtom *atom, uintN *indexp) +{ + uintN n, i, upvar_base; + jsuword *array; + JSLocalNameHashEntry *entry; + + JS_ASSERT(FUN_INTERPRETED(fun)); + n = fun->countLocalNames(); + if (n == 0) + return JSLOCAL_NONE; + if (n <= MAX_ARRAY_LOCALS) { + array = (n == 1) ? &fun->u.i.names.taggedAtom : fun->u.i.names.array; + + /* Search from the tail to pick up the last duplicated name. */ + i = n; + upvar_base = fun->countArgsAndVars(); + do { + --i; + if (atom == JS_LOCAL_NAME_TO_ATOM(array[i])) { + if (i < fun->nargs) { + if (indexp) + *indexp = i; + return JSLOCAL_ARG; + } + if (i >= upvar_base) { + if (indexp) + *indexp = i - upvar_base; + return JSLOCAL_UPVAR; + } + if (indexp) + *indexp = i - fun->nargs; + return JS_LOCAL_NAME_IS_CONST(array[i]) + ? JSLOCAL_CONST + : JSLOCAL_VAR; + } + } while (i != 0); + } else { + entry = (JSLocalNameHashEntry *) + JS_DHashTableOperate(&fun->u.i.names.map->names, atom, + JS_DHASH_LOOKUP); + if (JS_DHASH_ENTRY_IS_BUSY(&entry->hdr)) { + JS_ASSERT(entry->localKind != JSLOCAL_NONE); + if (indexp) + *indexp = entry->index; + return (JSLocalKind) entry->localKind; + } + } + return JSLOCAL_NONE; +} + +typedef struct JSLocalNameEnumeratorArgs { + JSFunction *fun; + jsuword *names; +#ifdef DEBUG + uintN nCopiedArgs; + uintN nCopiedVars; +#endif +} JSLocalNameEnumeratorArgs; + +static JSDHashOperator +get_local_names_enumerator(JSDHashTable *table, JSDHashEntryHdr *hdr, + uint32 number, void *arg) +{ + JSLocalNameHashEntry *entry; + JSLocalNameEnumeratorArgs *args; + uint i; + jsuword constFlag; + + entry = (JSLocalNameHashEntry *) hdr; + args = (JSLocalNameEnumeratorArgs *) arg; + JS_ASSERT(entry->name); + if (entry->localKind == JSLOCAL_ARG) { + JS_ASSERT(entry->index < args->fun->nargs); + JS_ASSERT(args->nCopiedArgs++ < args->fun->nargs); + i = entry->index; + constFlag = 0; + } else { + JS_ASSERT(entry->localKind == JSLOCAL_VAR || + entry->localKind == JSLOCAL_CONST || + entry->localKind == JSLOCAL_UPVAR); + JS_ASSERT(entry->index < args->fun->u.i.nvars + args->fun->u.i.nupvars); + JS_ASSERT(args->nCopiedVars++ < unsigned(args->fun->u.i.nvars + args->fun->u.i.nupvars)); + i = args->fun->nargs; + if (entry->localKind == JSLOCAL_UPVAR) + i += args->fun->u.i.nvars; + i += entry->index; + constFlag = (entry->localKind == JSLOCAL_CONST); + } + args->names[i] = (jsuword) entry->name | constFlag; + return JS_DHASH_NEXT; +} + +JS_FRIEND_API(jsuword *) +js_GetLocalNameArray(JSContext *cx, JSFunction *fun, JSArenaPool *pool) +{ + uintN n; + jsuword *names; + JSLocalNameMap *map; + JSLocalNameEnumeratorArgs args; + JSNameIndexPair *dup; + + JS_ASSERT(fun->hasLocalNames()); + n = fun->countLocalNames(); + + if (n <= MAX_ARRAY_LOCALS) + return (n == 1) ? &fun->u.i.names.taggedAtom : fun->u.i.names.array; + + /* + * No need to check for overflow of the allocation size as we are making a + * copy of already allocated data. As such it must fit size_t. + */ + JS_ARENA_ALLOCATE_CAST(names, jsuword *, pool, (size_t) n * sizeof *names); + if (!names) { + js_ReportOutOfScriptQuota(cx); + return NULL; + } + +#if JS_HAS_DESTRUCTURING + /* Some parameter names can be NULL due to destructuring patterns. */ + memset(names, 0, fun->nargs * sizeof *names); +#endif + map = fun->u.i.names.map; + args.fun = fun; + args.names = names; +#ifdef DEBUG + args.nCopiedArgs = 0; + args.nCopiedVars = 0; +#endif + JS_DHashTableEnumerate(&map->names, get_local_names_enumerator, &args); + for (dup = map->lastdup; dup; dup = dup->link) { + JS_ASSERT(dup->index < fun->nargs); + JS_ASSERT(args.nCopiedArgs++ < fun->nargs); + names[dup->index] = (jsuword) dup->name; + } +#if !JS_HAS_DESTRUCTURING + JS_ASSERT(args.nCopiedArgs == fun->nargs); +#endif + JS_ASSERT(args.nCopiedVars == fun->u.i.nvars + fun->u.i.nupvars); + + return names; +} + +static JSDHashOperator +trace_local_names_enumerator(JSDHashTable *table, JSDHashEntryHdr *hdr, + uint32 number, void *arg) +{ + JSLocalNameHashEntry *entry; + JSTracer *trc; + + entry = (JSLocalNameHashEntry *) hdr; + JS_ASSERT(entry->name); + trc = (JSTracer *) arg; + JS_SET_TRACING_INDEX(trc, + entry->localKind == JSLOCAL_ARG ? "arg" : "var", + entry->index); + JS_CallTracer(trc, ATOM_TO_STRING(entry->name), JSTRACE_STRING); + return JS_DHASH_NEXT; +} + +static void +TraceLocalNames(JSTracer *trc, JSFunction *fun) +{ + uintN n, i; + JSAtom *atom; + jsuword *array; + + JS_ASSERT(FUN_INTERPRETED(fun)); + n = fun->countLocalNames(); + if (n == 0) + return; + if (n <= MAX_ARRAY_LOCALS) { + array = (n == 1) ? &fun->u.i.names.taggedAtom : fun->u.i.names.array; + i = n; + do { + --i; + atom = (JSAtom *) (array[i] & ~1); + if (atom) { + JS_SET_TRACING_INDEX(trc, + i < fun->nargs ? "arg" : "var", + i < fun->nargs ? i : i - fun->nargs); + JS_CallTracer(trc, ATOM_TO_STRING(atom), JSTRACE_STRING); + } + } while (i != 0); + } else { + JS_DHashTableEnumerate(&fun->u.i.names.map->names, + trace_local_names_enumerator, trc); + + /* + * No need to trace the list of duplicates in map->lastdup as the + * names there are traced when enumerating the hash table. + */ + } +} + +void +DestroyLocalNames(JSContext *cx, JSFunction *fun) +{ + uintN n; + + n = fun->countLocalNames(); + if (n <= 1) + return; + if (n <= MAX_ARRAY_LOCALS) + cx->free(fun->u.i.names.array); + else + FreeLocalNameHash(cx, fun->u.i.names.map); +} + +void +js_FreezeLocalNames(JSContext *cx, JSFunction *fun) +{ + uintN n; + jsuword *array; + + JS_ASSERT(FUN_INTERPRETED(fun)); + JS_ASSERT(!fun->u.i.script); + n = fun->nargs + fun->u.i.nvars + fun->u.i.nupvars; + if (2 <= n && n < MAX_ARRAY_LOCALS) { + /* Shrink over-allocated array ignoring realloc failures. */ + array = (jsuword *) cx->realloc(fun->u.i.names.array, + n * sizeof *array); + if (array) + fun->u.i.names.array = array; + } +#ifdef DEBUG + if (n > MAX_ARRAY_LOCALS) + JS_DHashMarkTableImmutable(&fun->u.i.names.map->names); +#endif +} + +extern JSAtom * +js_FindDuplicateFormal(JSFunction *fun) +{ + unsigned nargs = fun->nargs; + if (nargs <= 1) + return NULL; + + /* Function with two to MAX_ARRAY_LOCALS parameters use an aray. */ + if (nargs <= MAX_ARRAY_LOCALS) { + jsuword *array = fun->u.i.names.array; + /* Quadratic, but MAX_ARRAY_LOCALS is 8, so at most 28 comparisons. */ + for (unsigned i = 0; i < nargs; i++) { + for (unsigned j = i + 1; j < nargs; j++) { + if (array[i] == array[j]) + return JS_LOCAL_NAME_TO_ATOM(array[i]); + } + } + return NULL; + } + + /* + * Functions with more than MAX_ARRAY_LOCALS parameters use a hash + * table. Hashed local name maps have already made a list of any + * duplicate argument names for us. + */ + JSNameIndexPair *dup = fun->u.i.names.map->lastdup; + return dup ? dup->name : NULL; +} diff --git a/ape-server/deps/js/src/jsfun.h b/ape-server/deps/js/src/jsfun.h new file mode 100755 index 0000000..0df1360 --- /dev/null +++ b/ape-server/deps/js/src/jsfun.h @@ -0,0 +1,455 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsfun_h___ +#define jsfun_h___ +/* + * JS function definitions. + */ +#include "jsprvtd.h" +#include "jspubtd.h" +#include "jsobj.h" + +JS_BEGIN_EXTERN_C + +typedef struct JSLocalNameMap JSLocalNameMap; + +/* + * Depending on the number of arguments and variables in the function their + * names and attributes are stored either as a single atom or as an array of + * tagged atoms (when there are few locals) or as a hash-based map (when there + * are many locals). In the first 2 cases the lowest bit of the atom is used + * as a tag to distinguish const from var. See jsfun.c for details. + */ +typedef union JSLocalNames { + jsuword taggedAtom; + jsuword *array; + JSLocalNameMap *map; +} JSLocalNames; + +/* + * The high two bits of JSFunction.flags encode whether the function is native + * or interpreted, and if interpreted, what kind of optimized closure form (if + * any) it might be. + * + * 00 not interpreted + * 01 interpreted, neither flat nor null closure + * 10 interpreted, flat closure + * 11 interpreted, null closure + * + * FUN_FLAT_CLOSURE implies FUN_INTERPRETED and u.i.script->upvarsOffset != 0. + * FUN_NULL_CLOSURE implies FUN_INTERPRETED and u.i.script->upvarsOffset == 0. + * + * FUN_INTERPRETED but not FUN_FLAT_CLOSURE and u.i.script->upvarsOffset != 0 + * is an Algol-like function expression or nested function, i.e., a function + * that never escapes upward or downward (heapward), and is only ever called. + * + * Finally, FUN_INTERPRETED and u.i.script->upvarsOffset == 0 could be either + * a non-closure (a global function definition, or any function that uses no + * outer names), or a closure of an escaping function that uses outer names + * whose values can't be snapshot (because the outer names could be reassigned + * after the closure is formed, or because assignments could not be analyzed + * due to with or eval). + * + * Such a hard-case function must use JSOP_NAME, etc., and reify outer function + * activations' call objects, etc. if it's not a global function. + * + * NB: JSFUN_EXPR_CLOSURE reuses JSFUN_STUB_GSOPS, which is an API request flag + * bit only, never stored in fun->flags. + * + * If we need more bits in the future, all flags for FUN_INTERPRETED functions + * can move to u.i.script->flags. For now we use function flag bits to minimize + * pointer-chasing. + */ +#define JSFUN_EXPR_CLOSURE 0x1000 /* expression closure: function(x) x*x */ +#define JSFUN_TRCINFO 0x2000 /* when set, u.n.trcinfo is non-null, + JSFunctionSpec::call points to a + JSNativeTraceInfo. */ +#define JSFUN_INTERPRETED 0x4000 /* use u.i if kind >= this value else u.n */ +#define JSFUN_FLAT_CLOSURE 0x8000 /* flag (aka "display") closure */ +#define JSFUN_NULL_CLOSURE 0xc000 /* null closure entrains no scope chain */ +#define JSFUN_KINDMASK 0xc000 /* encode interp vs. native and closure + optimization level -- see above */ + +#define FUN_OBJECT(fun) (static_cast(fun)) +#define FUN_KIND(fun) ((fun)->flags & JSFUN_KINDMASK) +#define FUN_SET_KIND(fun,k) ((fun)->flags = ((fun)->flags & ~JSFUN_KINDMASK) | (k)) +#define FUN_INTERPRETED(fun) (FUN_KIND(fun) >= JSFUN_INTERPRETED) +#define FUN_FLAT_CLOSURE(fun)(FUN_KIND(fun) == JSFUN_FLAT_CLOSURE) +#define FUN_NULL_CLOSURE(fun)(FUN_KIND(fun) == JSFUN_NULL_CLOSURE) +#define FUN_SLOW_NATIVE(fun) (!FUN_INTERPRETED(fun) && !((fun)->flags & JSFUN_FAST_NATIVE)) +#define FUN_SCRIPT(fun) (FUN_INTERPRETED(fun) ? (fun)->u.i.script : NULL) +#define FUN_NATIVE(fun) (FUN_SLOW_NATIVE(fun) ? (fun)->u.n.native : NULL) +#define FUN_FAST_NATIVE(fun) (((fun)->flags & JSFUN_FAST_NATIVE) \ + ? (JSFastNative) (fun)->u.n.native \ + : NULL) +#define FUN_MINARGS(fun) (((fun)->flags & JSFUN_FAST_NATIVE) \ + ? 0 \ + : (fun)->nargs) +#define FUN_CLASP(fun) (JS_ASSERT(!FUN_INTERPRETED(fun)), \ + fun->u.n.clasp) +#define FUN_TRCINFO(fun) (JS_ASSERT(!FUN_INTERPRETED(fun)), \ + JS_ASSERT((fun)->flags & JSFUN_TRCINFO), \ + fun->u.n.trcinfo) + +struct JSFunction : public JSObject { + uint16 nargs; /* maximum number of specified arguments, + reflected as f.length/f.arity */ + uint16 flags; /* flags, see JSFUN_* below and in jsapi.h */ + union { + struct { + uint16 extra; /* number of arg slots for local GC roots */ + uint16 spare; /* reserved for future use */ + JSNative native; /* native method pointer or null */ + JSClass *clasp; /* class of objects constructed + by this function */ + JSNativeTraceInfo *trcinfo; + } n; + struct { + uint16 nvars; /* number of local variables */ + uint16 nupvars; /* number of upvars (computable from script + but here for faster access) */ + uint16 skipmin; /* net skip amount up (toward zero) from + script->staticLevel to nearest upvar, + including upvars in nested functions */ + JSPackedBool wrapper; /* true if this function is a wrapper that + rewrites bytecode optimized for a function + judged non-escaping by the compiler, which + then escaped via the debugger or a rogue + indirect eval; if true, then this function + object's proto is the wrapped object */ + JSScript *script; /* interpreted bytecode descriptor or null */ + JSLocalNames names; /* argument and variable names */ + } i; + } u; + JSAtom *atom; /* name for diagnostics and decompiling */ + + bool optimizedClosure() const { return FUN_KIND(this) > JSFUN_INTERPRETED; } + bool needsWrapper() const { return FUN_NULL_CLOSURE(this) && u.i.skipmin != 0; } + + uintN countArgsAndVars() const { + JS_ASSERT(FUN_INTERPRETED(this)); + return nargs + u.i.nvars; + } + + uintN countLocalNames() const { + JS_ASSERT(FUN_INTERPRETED(this)); + return countArgsAndVars() + u.i.nupvars; + } + + bool hasLocalNames() const { + JS_ASSERT(FUN_INTERPRETED(this)); + return countLocalNames() != 0; + } + + int sharpSlotBase(JSContext *cx); + + uint32 countInterpretedReservedSlots() const; +}; + +/* + * Trace-annotated native. This expands to a JSFunctionSpec initializer (like + * JS_FN in jsapi.h). fastcall is a JSFastNative; trcinfo is a + * JSNativeTraceInfo*. + */ +#ifdef JS_TRACER +/* MSVC demands the intermediate (void *) cast here. */ +# define JS_TN(name,fastcall,nargs,flags,trcinfo) \ + JS_FN(name, JS_DATA_TO_FUNC_PTR(JSNative, trcinfo), nargs, \ + (flags) | JSFUN_FAST_NATIVE | JSFUN_STUB_GSOPS | JSFUN_TRCINFO) +#else +# define JS_TN(name,fastcall,nargs,flags,trcinfo) \ + JS_FN(name, fastcall, nargs, flags) +#endif + +extern JSClass js_ArgumentsClass; +extern JS_FRIEND_DATA(JSClass) js_CallClass; +extern JSClass js_DeclEnvClass; + +/* JS_FRIEND_DATA so that VALUE_IS_FUNCTION is callable from the shell. */ +extern JS_FRIEND_DATA(JSClass) js_FunctionClass; + +#define HAS_FUNCTION_CLASS(obj) (STOBJ_GET_CLASS(obj) == &js_FunctionClass) + +/* + * NB: jsapi.h and jsobj.h must be included before any call to this macro. + */ +#define VALUE_IS_FUNCTION(cx, v) \ + (!JSVAL_IS_PRIMITIVE(v) && HAS_FUNCTION_CLASS(JSVAL_TO_OBJECT(v))) + +/* + * Macro to access the private slot of the function object after the slot is + * initialized. + */ +#define GET_FUNCTION_PRIVATE(cx, funobj) \ + (JS_ASSERT(HAS_FUNCTION_CLASS(funobj)), \ + (JSFunction *) (funobj)->getPrivate()) + +/* + * Return true if this is a compiler-created internal function accessed by + * its own object. Such a function object must not be accessible to script + * or embedding code. + */ +inline bool +js_IsInternalFunctionObject(JSObject *funobj) +{ + JS_ASSERT(HAS_FUNCTION_CLASS(funobj)); + JSFunction *fun = (JSFunction *) funobj->getPrivate(); + return funobj == fun && (fun->flags & JSFUN_LAMBDA) && !funobj->getParent(); +} + +struct js_ArgsPrivateNative; + +inline js_ArgsPrivateNative * +js_GetArgsPrivateNative(JSObject *argsobj) +{ + JS_ASSERT(STOBJ_GET_CLASS(argsobj) == &js_ArgumentsClass); + uintptr_t p = (uintptr_t) argsobj->getPrivate(); + return (js_ArgsPrivateNative *) (p & 2 ? p & ~2 : NULL); +} + +extern JSObject * +js_InitFunctionClass(JSContext *cx, JSObject *obj); + +extern JSObject * +js_InitArgumentsClass(JSContext *cx, JSObject *obj); + +extern JSFunction * +js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs, + uintN flags, JSObject *parent, JSAtom *atom); + +extern void +js_TraceFunction(JSTracer *trc, JSFunction *fun); + +extern void +js_FinalizeFunction(JSContext *cx, JSFunction *fun); + +extern JSObject * +js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent); + +extern JS_REQUIRES_STACK JSObject * +js_NewFlatClosure(JSContext *cx, JSFunction *fun); + +extern JS_REQUIRES_STACK JSObject * +js_NewDebuggableFlatClosure(JSContext *cx, JSFunction *fun); + +extern JSFunction * +js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native, + uintN nargs, uintN flags); + +/* + * Flags for js_ValueToFunction and js_ReportIsNotFunction. We depend on the + * fact that JSINVOKE_CONSTRUCT (aka JSFRAME_CONSTRUCTING) is 1, and test that + * with #if/#error in jsfun.c. + */ +#define JSV2F_CONSTRUCT JSINVOKE_CONSTRUCT +#define JSV2F_ITERATOR JSINVOKE_ITERATOR +#define JSV2F_SEARCH_STACK 0x10000 + +extern JSFunction * +js_ValueToFunction(JSContext *cx, jsval *vp, uintN flags); + +extern JSObject * +js_ValueToFunctionObject(JSContext *cx, jsval *vp, uintN flags); + +extern JSObject * +js_ValueToCallableObject(JSContext *cx, jsval *vp, uintN flags); + +extern void +js_ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags); + +extern JSObject * +js_GetCallObject(JSContext *cx, JSStackFrame *fp); + +extern void +js_PutCallObject(JSContext *cx, JSStackFrame *fp); + +extern JSFunction * +js_GetCallObjectFunction(JSObject *obj); + +extern JSBool +js_GetCallArg(JSContext *cx, JSObject *obj, jsid id, jsval *vp); + +extern JSBool +js_GetCallVar(JSContext *cx, JSObject *obj, jsid id, jsval *vp); + +extern JSBool +SetCallArg(JSContext *cx, JSObject *obj, jsid id, jsval *vp); + +extern JSBool +SetCallVar(JSContext *cx, JSObject *obj, jsid id, jsval *vp); + +/* + * js_SetCallArg and js_SetCallVar are extern fastcall copies of the setter + * functions. These versions are required in order to set call vars from traces. + * The normal versions must not be fastcall because they are stored in the + * property ops map. + */ +extern JSBool JS_FASTCALL +js_SetCallArg(JSContext *cx, JSObject *obj, jsid id, jsval v); + +extern JSBool JS_FASTCALL +js_SetCallVar(JSContext *cx, JSObject *obj, jsid id, jsval v); + +/* + * Slower version of js_GetCallVar used when call_resolve detects an attempt to + * leak an optimized closure via indirect or debugger eval. + */ +extern JSBool +js_GetCallVarChecked(JSContext *cx, JSObject *obj, jsid id, jsval *vp); + +extern JSBool +js_GetArgsValue(JSContext *cx, JSStackFrame *fp, jsval *vp); + +extern JSBool +js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, jsval *vp); + +extern JSObject * +js_GetArgsObject(JSContext *cx, JSStackFrame *fp); + +extern void +js_PutArgsObject(JSContext *cx, JSStackFrame *fp); + +/* + * Reserved slot structure for Arguments objects: + * + * JSSLOT_PRIVATE - the corresponding frame until the frame exits. + * JSSLOT_ARGS_LENGTH - the number of actual arguments and a flag indicating + * whether arguments.length was overwritten. + * JSSLOT_ARGS_CALLEE - the arguments.callee value or JSVAL_HOLE if that was + * overwritten. + * JSSLOT_ARGS_COPY_START .. - room to store the corresponding arguments after + * the frame exists. The slot's value will be JSVAL_HOLE + * if arguments[i] was deleted or overwritten. + */ +const uint32 JSSLOT_ARGS_LENGTH = JSSLOT_PRIVATE + 1; +const uint32 JSSLOT_ARGS_CALLEE = JSSLOT_PRIVATE + 2; +const uint32 JSSLOT_ARGS_COPY_START = JSSLOT_PRIVATE + 3; + +/* Number of extra fixed slots besides JSSLOT_PRIVATE. */ +const uint32 ARGS_CLASS_FIXED_RESERVED_SLOTS = JSSLOT_ARGS_COPY_START - + JSSLOT_ARGS_LENGTH; + +/* + * JSSLOT_ARGS_LENGTH stores ((argc << 1) | overwritten_flag) as int jsval. + * Thus (JS_ARGS_LENGTH_MAX << 1) | 1 must fit JSVAL_INT_MAX. To assert that + * we check first that the shift does not overflow uint32. + */ +JS_STATIC_ASSERT(JS_ARGS_LENGTH_MAX <= JS_BIT(30)); +JS_STATIC_ASSERT(jsval((JS_ARGS_LENGTH_MAX << 1) | 1) <= JSVAL_INT_MAX); + +JS_INLINE bool +js_IsOverriddenArgsLength(JSObject *obj) +{ + JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_ArgumentsClass); + + jsval v = obj->fslots[JSSLOT_ARGS_LENGTH]; + return (JSVAL_TO_INT(v) & 1) != 0; +} + +extern JSBool +js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp); + +typedef enum JSLocalKind { + JSLOCAL_NONE, + JSLOCAL_ARG, + JSLOCAL_VAR, + JSLOCAL_CONST, + JSLOCAL_UPVAR +} JSLocalKind; + +extern JSBool +js_AddLocal(JSContext *cx, JSFunction *fun, JSAtom *atom, JSLocalKind kind); + +/* + * Look up an argument or variable name returning its kind when found or + * JSLOCAL_NONE when no such name exists. When indexp is not null and the name + * exists, *indexp will receive the index of the corresponding argument or + * variable. + */ +extern JSLocalKind +js_LookupLocal(JSContext *cx, JSFunction *fun, JSAtom *atom, uintN *indexp); + +/* + * Functions to work with local names as an array of words. + * + * js_GetLocalNameArray returns the array, or null if we are out of memory. + * This function must be called only when fun->hasLocalNames(). + * + * The supplied pool is used to allocate the returned array, so the caller is + * obligated to mark and release to free it. + * + * The elements of the array with index less than fun->nargs correspond to the + * names of function formal parameters. An index >= fun->nargs addresses a var + * binding. Use JS_LOCAL_NAME_TO_ATOM to convert array's element to an atom + * pointer. This pointer can be null when the element is for a formal parameter + * corresponding to a destructuring pattern. + * + * If nameWord does not name a formal parameter, use JS_LOCAL_NAME_IS_CONST to + * check if nameWord corresponds to the const declaration. + */ +extern JS_FRIEND_API(jsuword *) +js_GetLocalNameArray(JSContext *cx, JSFunction *fun, struct JSArenaPool *pool); + +#define JS_LOCAL_NAME_TO_ATOM(nameWord) \ + ((JSAtom *) ((nameWord) & ~(jsuword) 1)) + +#define JS_LOCAL_NAME_IS_CONST(nameWord) \ + ((((nameWord) & (jsuword) 1)) != 0) + +extern void +js_FreezeLocalNames(JSContext *cx, JSFunction *fun); + +/* + * If fun's formal parameters include any duplicate names, return one + * of them (chosen arbitrarily). If they are all unique, return NULL. + */ +extern JSAtom * +js_FindDuplicateFormal(JSFunction *fun); + +extern JSBool +js_fun_apply(JSContext *cx, uintN argc, jsval *vp); + +extern JSBool +js_fun_call(JSContext *cx, uintN argc, jsval *vp); + + +JS_END_EXTERN_C + +#endif /* jsfun_h___ */ diff --git a/ape-server/deps/js/src/jsgc.cpp b/ape-server/deps/js/src/jsgc.cpp new file mode 100755 index 0000000..eb891ec --- /dev/null +++ b/ape-server/deps/js/src/jsgc.cpp @@ -0,0 +1,3341 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * JS Mark-and-Sweep Garbage Collector. + * + * This GC allocates fixed-sized things with sizes up to GC_NBYTES_MAX (see + * jsgc.h). It allocates from a special GC arena pool with each arena allocated + * using malloc. It uses an ideally parallel array of flag bytes to hold the + * mark bit, finalizer type index, etc. + * + * XXX swizzle page to freelist for better locality of reference + */ +#include /* for free */ +#include +#include /* for memset used when DEBUG */ +#include "jstypes.h" +#include "jsstdint.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jshash.h" /* Added by JSIFY */ +#include "jsbit.h" +#include "jsclist.h" +#include "jsprf.h" +#include "jsapi.h" +#include "jsatom.h" +#include "jscntxt.h" +#include "jsversion.h" +#include "jsdbgapi.h" +#include "jsexn.h" +#include "jsfun.h" +#include "jsgc.h" +#include "jsinterp.h" +#include "jsiter.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsobj.h" +#include "jsparse.h" +#include "jsscope.h" +#include "jsscript.h" +#include "jsstaticcheck.h" +#include "jsstr.h" +#include "jstask.h" +#include "jstracer.h" + +#if JS_HAS_XML_SUPPORT +#include "jsxml.h" +#endif + +#ifdef INCLUDE_MOZILLA_DTRACE +#include "jsdtracef.h" +#endif + +/* + * Include the headers for mmap. + */ +#if defined(XP_WIN) +# include +#endif +#if defined(XP_UNIX) || defined(XP_BEOS) +# include +# include +#endif +/* On Mac OS X MAP_ANONYMOUS is not defined. */ +#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) +# define MAP_ANONYMOUS MAP_ANON +#endif +#if !defined(MAP_ANONYMOUS) +# define MAP_ANONYMOUS 0 +#endif + +/* + * Check JSTempValueUnion has the size of jsval and void * so we can + * reinterpret jsval as void* GC-thing pointer and use JSTVU_SINGLE for + * different GC-things. + */ +JS_STATIC_ASSERT(sizeof(JSTempValueUnion) == sizeof(jsval)); +JS_STATIC_ASSERT(sizeof(JSTempValueUnion) == sizeof(void *)); + +/* + * Check that JSTRACE_XML follows JSTRACE_OBJECT, JSTRACE_DOUBLE and + * JSTRACE_STRING. + */ +JS_STATIC_ASSERT(JSTRACE_OBJECT == 0); +JS_STATIC_ASSERT(JSTRACE_DOUBLE == 1); +JS_STATIC_ASSERT(JSTRACE_STRING == 2); +JS_STATIC_ASSERT(JSTRACE_XML == 3); + +/* + * JS_IS_VALID_TRACE_KIND assumes that JSTRACE_STRING is the last non-xml + * trace kind when JS_HAS_XML_SUPPORT is false. + */ +JS_STATIC_ASSERT(JSTRACE_STRING + 1 == JSTRACE_XML); + +/* + * Check that we can use memset(p, 0, ...) to implement JS_CLEAR_WEAK_ROOTS. + */ +JS_STATIC_ASSERT(JSVAL_NULL == 0); + +/* + * Check consistency of external string constants from JSFinalizeGCThingKind. + */ +JS_STATIC_ASSERT(FINALIZE_EXTERNAL_STRING_LAST - FINALIZE_EXTERNAL_STRING0 == + JS_EXTERNAL_STRING_LIMIT - 1); + +/* + * A GC arena contains a fixed number of flag bits for each thing in its heap, + * and supports O(1) lookup of a flag given its thing's address. + * + * To implement this, we allocate things of the same size from a GC arena + * containing GC_ARENA_SIZE bytes aligned on GC_ARENA_SIZE boundary. The + * following picture shows arena's layout: + * + * +------------------------------+--------------------+---------------+ + * | allocation area for GC thing | flags of GC things | JSGCArenaInfo | + * +------------------------------+--------------------+---------------+ + * + * To find the flag bits for the thing we calculate the thing index counting + * from arena's start using: + * + * thingIndex = (thingAddress & GC_ARENA_MASK) / thingSize + * + * The details of flag's lookup depend on thing's kind. For all GC things + * except doubles we use one byte of flags where the 4 bits determine thing's + * type and the rest is used to implement GC marking, finalization and + * locking. We calculate the address of flag's byte using: + * + * flagByteAddress = + * (thingAddress | GC_ARENA_MASK) - sizeof(JSGCArenaInfo) - thingIndex + * + * where + * + * (thingAddress | GC_ARENA_MASK) - sizeof(JSGCArenaInfo) + * + * is the last byte of flags' area. + * + * This implies that the things are allocated from the start of their area and + * flags are allocated from the end. This arrangement avoids a relatively + * expensive calculation of the location of the boundary separating things and + * flags. The boundary's offset from the start of the arena is given by: + * + * thingsPerArena * thingSize + * + * where thingsPerArena is the number of things that the arena can hold: + * + * (GC_ARENA_SIZE - sizeof(JSGCArenaInfo)) / (thingSize + 1). + * + * To allocate doubles we use a specialized arena. It can contain only numbers + * so we do not need the type bits. Moreover, since the doubles do not require + * a finalizer and very few of them are locked via js_LockGCThing API, we use + * just one bit of flags per double to denote if it was marked during the + * marking phase of the GC. The locking is implemented via a hash table. Thus + * for doubles the flag area becomes a bitmap. + */ + +static const jsuword GC_ARENAS_PER_CHUNK = 16; +static const jsuword GC_ARENA_SHIFT = 12; +static const jsuword GC_ARENA_MASK = JS_BITMASK(GC_ARENA_SHIFT); +static const jsuword GC_ARENA_SIZE = JS_BIT(GC_ARENA_SHIFT); + +struct JSGCArenaInfo { + /* + * Allocation list for the arena or NULL if the arena holds double values. + */ + JSGCArenaList *list; + + /* + * Pointer to the previous arena in a linked list. The arena can either + * belong to one of JSContext.gcArenaList lists or, when it does not have + * any allocated GC things, to the list of free arenas in the chunk with + * head stored in JSGCChunkInfo.lastFreeArena. + */ + JSGCArenaInfo *prev; + + /* + * A link field for the list of arenas with marked but not yet traced + * things. The field is encoded as arena's page to share the space with + * firstArena and arenaIndex fields. + */ + jsuword prevUntracedPage : JS_BITS_PER_WORD - GC_ARENA_SHIFT; + + /* + * When firstArena is false, the index of arena in the chunk. When + * firstArena is true, the index of a free arena holding JSGCChunkInfo or + * NO_FREE_ARENAS if there are no free arenas in the chunk. + * + * GET_ARENA_INDEX and GET_CHUNK_INFO_INDEX are convenience macros to + * access either of indexes. + */ + jsuword arenaIndex : GC_ARENA_SHIFT - 1; + + /* Flag indicating if the arena is the first in the chunk. */ + jsuword firstArena : 1; + + union { + struct { + JSGCThing *freeList; + jsuword untracedThings; /* bitset for fast search of marked + but not yet traced things */ + } finalizable; + + bool hasMarkedDoubles; /* the arena has marked doubles */ + }; +}; + +/* GC flag definitions, must fit in 8 bits. */ +const uint8 GCF_MARK = JS_BIT(0); +const uint8 GCF_LOCK = JS_BIT(1); /* lock request bit in API */ +const uint8 GCF_CHILDREN = JS_BIT(2); /* GC things with children to be + marked later. */ + +/* + * The private JSGCThing struct, which describes a JSRuntime.gcFreeList element. + */ +struct JSGCThing { + JSGCThing *link; +}; + +/* + * Macros to convert between JSGCArenaInfo, the start address of the arena and + * arena's page defined as (start address) >> GC_ARENA_SHIFT. + */ +#define ARENA_INFO_OFFSET (GC_ARENA_SIZE - (uint32) sizeof(JSGCArenaInfo)) + +#define IS_ARENA_INFO_ADDRESS(arena) \ + (((jsuword) (arena) & GC_ARENA_MASK) == ARENA_INFO_OFFSET) + +#define ARENA_START_TO_INFO(arenaStart) \ + (JS_ASSERT(((arenaStart) & (jsuword) GC_ARENA_MASK) == 0), \ + (JSGCArenaInfo *) ((arenaStart) + (jsuword) ARENA_INFO_OFFSET)) + +#define ARENA_INFO_TO_START(arena) \ + (JS_ASSERT(IS_ARENA_INFO_ADDRESS(arena)), \ + (jsuword) (arena) & ~(jsuword) GC_ARENA_MASK) + +#define ARENA_PAGE_TO_INFO(arenaPage) \ + (JS_ASSERT(arenaPage != 0), \ + JS_ASSERT(!((jsuword)(arenaPage) >> (JS_BITS_PER_WORD-GC_ARENA_SHIFT))), \ + ARENA_START_TO_INFO((arenaPage) << GC_ARENA_SHIFT)) + +#define ARENA_INFO_TO_PAGE(arena) \ + (JS_ASSERT(IS_ARENA_INFO_ADDRESS(arena)), \ + ((jsuword) (arena) >> GC_ARENA_SHIFT)) + +#define GET_ARENA_INFO(chunk, index) \ + (JS_ASSERT((index) < GC_ARENAS_PER_CHUNK), \ + ARENA_START_TO_INFO(chunk + ((index) << GC_ARENA_SHIFT))) + +/* + * Definitions for allocating arenas in chunks. + * + * All chunks that have at least one free arena are put on the doubly-linked + * list with the head stored in JSRuntime.gcChunkList. JSGCChunkInfo contains + * the head of the chunk's free arena list together with the link fields for + * gcChunkList. + * + * Structure stored in one of chunk's free arenas. GET_CHUNK_INFO_INDEX gives + * the index of this arena. When all arenas in the chunk are used, it is + * removed from the list and the index is set to NO_FREE_ARENAS indicating + * that the chunk is not on gcChunkList and has no JSGCChunkInfo available. + */ + +struct JSGCChunkInfo { + JSGCChunkInfo **prevp; + JSGCChunkInfo *next; + JSGCArenaInfo *lastFreeArena; + uint32 numFreeArenas; +}; + +#define NO_FREE_ARENAS JS_BITMASK(GC_ARENA_SHIFT - 1) + +JS_STATIC_ASSERT(1 <= GC_ARENAS_PER_CHUNK && + GC_ARENAS_PER_CHUNK <= NO_FREE_ARENAS); + +#define GET_ARENA_CHUNK(arena, index) \ + (JS_ASSERT(GET_ARENA_INDEX(arena) == index), \ + ARENA_INFO_TO_START(arena) - ((index) << GC_ARENA_SHIFT)) + +#define GET_ARENA_INDEX(arena) \ + ((arena)->firstArena ? 0 : (uint32) (arena)->arenaIndex) + +#define GET_CHUNK_INFO_INDEX(chunk) \ + ((uint32) ARENA_START_TO_INFO(chunk)->arenaIndex) + +#define SET_CHUNK_INFO_INDEX(chunk, index) \ + (JS_ASSERT((index) < GC_ARENAS_PER_CHUNK || (index) == NO_FREE_ARENAS), \ + (void) (ARENA_START_TO_INFO(chunk)->arenaIndex = (jsuword) (index))) + +#define GET_CHUNK_INFO(chunk, infoIndex) \ + (JS_ASSERT(GET_CHUNK_INFO_INDEX(chunk) == (infoIndex)), \ + JS_ASSERT((uint32) (infoIndex) < GC_ARENAS_PER_CHUNK), \ + (JSGCChunkInfo *) ((chunk) + ((infoIndex) << GC_ARENA_SHIFT))) + +#define CHUNK_INFO_TO_INDEX(ci) \ + GET_ARENA_INDEX(ARENA_START_TO_INFO((jsuword)ci)) + +/* + * Macros for GC-thing operations. + */ +#define THINGS_PER_ARENA(thingSize) \ + ((GC_ARENA_SIZE - (uint32) sizeof(JSGCArenaInfo)) / ((thingSize) + 1U)) + +#define THING_TO_ARENA(thing) \ + (JS_ASSERT(!JSString::isStatic(thing)), \ + (JSGCArenaInfo *)(((jsuword) (thing) | GC_ARENA_MASK) \ + + 1 - sizeof(JSGCArenaInfo))) + +#define THING_TO_INDEX(thing, thingSize) \ + ((uint32) ((jsuword) (thing) & GC_ARENA_MASK) / (uint32) (thingSize)) + +#define THING_FLAGP(arena, thingIndex) \ + (JS_ASSERT((jsuword) (thingIndex) \ + < (jsuword) THINGS_PER_ARENA((arena)->list->thingSize)), \ + (uint8 *)(arena) - 1 - (thingIndex)) + +#define THING_TO_FLAGP(thing, thingSize) \ + THING_FLAGP(THING_TO_ARENA(thing), THING_TO_INDEX(thing, thingSize)) + +#define FLAGP_TO_ARENA(flagp) THING_TO_ARENA(flagp) + +#define FLAGP_TO_INDEX(flagp) \ + (JS_ASSERT(((jsuword) (flagp) & GC_ARENA_MASK) < ARENA_INFO_OFFSET), \ + (ARENA_INFO_OFFSET - 1 - (uint32) ((jsuword) (flagp) & GC_ARENA_MASK))) + +#define FLAGP_TO_THING(flagp, thingSize) \ + (JS_ASSERT(((jsuword) (flagp) & GC_ARENA_MASK) >= \ + (ARENA_INFO_OFFSET - THINGS_PER_ARENA(thingSize))), \ + (JSGCThing *)(((jsuword) (flagp) & ~GC_ARENA_MASK) + \ + (thingSize) * FLAGP_TO_INDEX(flagp))) + +static inline JSGCThing * +NextThing(JSGCThing *thing, size_t thingSize) +{ + return reinterpret_cast(reinterpret_cast(thing) + + thingSize); +} + +static inline JSGCThing * +MakeNewArenaFreeList(JSGCArenaInfo *a, unsigned thingSize, size_t nthings) +{ + JS_ASSERT(nthings * thingSize < GC_ARENA_SIZE - sizeof(JSGCArenaInfo)); + + jsuword thingsStart = ARENA_INFO_TO_START(a); + JSGCThing *first = reinterpret_cast(thingsStart); + JSGCThing *last = reinterpret_cast(thingsStart + + (nthings - 1) * thingSize); + for (JSGCThing *thing = first; thing != last;) { + JSGCThing *next = NextThing(thing, thingSize); + thing->link = next; + thing = next; + } + last->link = NULL; + return first; +} + +/* + * Macros for the specialized arena for doubles. + * + * DOUBLES_PER_ARENA defines the maximum number of doubles that the arena can + * hold. We find it as the following. Let n be the number of doubles in the + * arena. Together with the bitmap of flags and JSGCArenaInfo they should fit + * the arena. Hence DOUBLES_PER_ARENA or n_max is the maximum value of n for + * which the following holds: + * + * n*s + ceil(n/B) <= M (1) + * + * where "/" denotes normal real division, + * ceil(r) gives the least integer not smaller than the number r, + * s is the number of words in jsdouble, + * B is number of bits per word or B == JS_BITS_PER_WORD + * M is the number of words in the arena before JSGCArenaInfo or + * M == (GC_ARENA_SIZE - sizeof(JSGCArenaInfo)) / sizeof(jsuword). + * M == ARENA_INFO_OFFSET / sizeof(jsuword) + * + * We rewrite the inequality as + * + * n*B*s/B + ceil(n/B) <= M, + * ceil(n*B*s/B + n/B) <= M, + * ceil(n*(B*s + 1)/B) <= M (2) + * + * We define a helper function e(n, s, B), + * + * e(n, s, B) := ceil(n*(B*s + 1)/B) - n*(B*s + 1)/B, 0 <= e(n, s, B) < 1. + * + * It gives: + * + * n*(B*s + 1)/B + e(n, s, B) <= M, + * n + e*B/(B*s + 1) <= M*B/(B*s + 1) + * + * We apply the floor function to both sides of the last equation, where + * floor(r) gives the biggest integer not greater than r. As a consequence we + * have: + * + * floor(n + e*B/(B*s + 1)) <= floor(M*B/(B*s + 1)), + * n + floor(e*B/(B*s + 1)) <= floor(M*B/(B*s + 1)), + * n <= floor(M*B/(B*s + 1)), (3) + * + * where floor(e*B/(B*s + 1)) is zero as e*B/(B*s + 1) < B/(B*s + 1) < 1. + * Thus any n that satisfies the original constraint (1) or its equivalent (2), + * must also satisfy (3). That is, we got an upper estimate for the maximum + * value of n. Lets show that this upper estimate, + * + * floor(M*B/(B*s + 1)), (4) + * + * also satisfies (1) and, as such, gives the required maximum value. + * Substituting it into (2) gives: + * + * ceil(floor(M*B/(B*s + 1))*(B*s + 1)/B) == ceil(floor(M/X)*X) + * + * where X == (B*s + 1)/B > 1. But then floor(M/X)*X <= M/X*X == M and + * + * ceil(floor(M/X)*X) <= ceil(M) == M. + * + * Thus the value of (4) gives the maximum n satisfying (1). + * + * For the final result we observe that in (4) + * + * M*B == ARENA_INFO_OFFSET / sizeof(jsuword) * JS_BITS_PER_WORD + * == ARENA_INFO_OFFSET * JS_BITS_PER_BYTE + * + * and + * + * B*s == JS_BITS_PER_WORD * sizeof(jsdouble) / sizeof(jsuword) + * == JS_BITS_PER_DOUBLE. + */ +const size_t DOUBLES_PER_ARENA = + (ARENA_INFO_OFFSET * JS_BITS_PER_BYTE) / (JS_BITS_PER_DOUBLE + 1); + +/* + * Check that ARENA_INFO_OFFSET and sizeof(jsdouble) divides sizeof(jsuword). + */ +JS_STATIC_ASSERT(ARENA_INFO_OFFSET % sizeof(jsuword) == 0); +JS_STATIC_ASSERT(sizeof(jsdouble) % sizeof(jsuword) == 0); +JS_STATIC_ASSERT(sizeof(jsbitmap) == sizeof(jsuword)); + +const size_t DOUBLES_ARENA_BITMAP_WORDS = + JS_HOWMANY(DOUBLES_PER_ARENA, JS_BITS_PER_WORD); + +/* Check that DOUBLES_PER_ARENA indeed maximises (1). */ +JS_STATIC_ASSERT(DOUBLES_PER_ARENA * sizeof(jsdouble) + + DOUBLES_ARENA_BITMAP_WORDS * sizeof(jsuword) <= + ARENA_INFO_OFFSET); + +JS_STATIC_ASSERT((DOUBLES_PER_ARENA + 1) * sizeof(jsdouble) + + sizeof(jsuword) * + JS_HOWMANY((DOUBLES_PER_ARENA + 1), JS_BITS_PER_WORD) > + ARENA_INFO_OFFSET); + +/* + * When DOUBLES_PER_ARENA % BITS_PER_DOUBLE_FLAG_UNIT != 0, some bits in the + * last byte of the occupation bitmap are unused. + */ +const size_t UNUSED_DOUBLE_BITMAP_BITS = + DOUBLES_ARENA_BITMAP_WORDS * JS_BITS_PER_WORD - DOUBLES_PER_ARENA; + +JS_STATIC_ASSERT(UNUSED_DOUBLE_BITMAP_BITS < JS_BITS_PER_WORD); + +const size_t DOUBLES_ARENA_BITMAP_OFFSET = + ARENA_INFO_OFFSET - DOUBLES_ARENA_BITMAP_WORDS * sizeof(jsuword); + +#define CHECK_DOUBLE_ARENA_INFO(arenaInfo) \ + (JS_ASSERT(IS_ARENA_INFO_ADDRESS(arenaInfo)), \ + JS_ASSERT(!(arenaInfo)->list)) \ + +/* + * Get the start of the bitmap area containing double mark flags in the arena. + * To access the flag the code uses + * + * JS_TEST_BIT(bitmapStart, index) + * + * That is, compared with the case of arenas with non-double things, we count + * flags from the start of the bitmap area, not from the end. + */ +#define DOUBLE_ARENA_BITMAP(arenaInfo) \ + (CHECK_DOUBLE_ARENA_INFO(arenaInfo), \ + (jsbitmap *) arenaInfo - DOUBLES_ARENA_BITMAP_WORDS) + +#define DOUBLE_ARENA_BITMAP_END(arenaInfo) \ + (CHECK_DOUBLE_ARENA_INFO(arenaInfo), (jsbitmap *) (arenaInfo)) + +#define DOUBLE_THING_TO_INDEX(thing) \ + (CHECK_DOUBLE_ARENA_INFO(THING_TO_ARENA(thing)), \ + JS_ASSERT(((jsuword) (thing) & GC_ARENA_MASK) < \ + DOUBLES_ARENA_BITMAP_OFFSET), \ + ((uint32) (((jsuword) (thing) & GC_ARENA_MASK) / sizeof(jsdouble)))) + +static void +ClearDoubleArenaFlags(JSGCArenaInfo *a) +{ + /* + * When some high bits in the last byte of the double occupation bitmap + * are unused, we must set them. Otherwise TurnUsedArenaIntoDoubleList + * will assume that they corresponds to some free cells and tries to + * allocate them. + * + * Note that the code works correctly with UNUSED_DOUBLE_BITMAP_BITS == 0. + */ + jsbitmap *bitmap = DOUBLE_ARENA_BITMAP(a); + memset(bitmap, 0, (DOUBLES_ARENA_BITMAP_WORDS - 1) * sizeof *bitmap); + jsbitmap mask = ((jsbitmap) 1 << UNUSED_DOUBLE_BITMAP_BITS) - 1; + size_t nused = JS_BITS_PER_WORD - UNUSED_DOUBLE_BITMAP_BITS; + bitmap[DOUBLES_ARENA_BITMAP_WORDS - 1] = mask << nused; +} + +static JS_ALWAYS_INLINE JSBool +IsMarkedDouble(JSGCArenaInfo *a, uint32 index) +{ + jsbitmap *bitmap; + + JS_ASSERT(a->hasMarkedDoubles); + bitmap = DOUBLE_ARENA_BITMAP(a); + return JS_TEST_BIT(bitmap, index); +} + +JS_STATIC_ASSERT(sizeof(JSStackHeader) >= 2 * sizeof(jsval)); + +JS_STATIC_ASSERT(sizeof(JSGCThing) <= sizeof(JSString)); +JS_STATIC_ASSERT(sizeof(JSGCThing) <= sizeof(jsdouble)); + +/* We want to use all the available GC thing space for object's slots. */ +JS_STATIC_ASSERT(sizeof(JSObject) % sizeof(JSGCThing) == 0); + +#ifdef JS_GCMETER +# define METER(x) ((void) (x)) +# define METER_IF(condition, x) ((void) ((condition) && (x))) +#else +# define METER(x) ((void) 0) +# define METER_IF(condition, x) ((void) 0) +#endif + +#define METER_UPDATE_MAX(maxLval, rval) \ + METER_IF((maxLval) < (rval), (maxLval) = (rval)) + +static jsuword +NewGCChunk(void) +{ + void *p; + +#if defined(XP_WIN) + p = VirtualAlloc(NULL, GC_ARENAS_PER_CHUNK << GC_ARENA_SHIFT, + MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + return (jsuword) p; +#elif defined(XP_OS2) + if (DosAllocMem(&p, GC_ARENAS_PER_CHUNK << GC_ARENA_SHIFT, + OBJ_ANY | PAG_COMMIT | PAG_READ | PAG_WRITE)) { + if (DosAllocMem(&p, GC_ARENAS_PER_CHUNK << GC_ARENA_SHIFT, + PAG_COMMIT | PAG_READ | PAG_WRITE)) { + return 0; + } + } + return (jsuword) p; +#else + p = mmap(NULL, GC_ARENAS_PER_CHUNK << GC_ARENA_SHIFT, + PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + return (p == MAP_FAILED) ? 0 : (jsuword) p; +#endif +} + +static void +DestroyGCChunk(jsuword chunk) +{ + JS_ASSERT((chunk & GC_ARENA_MASK) == 0); +#if defined(XP_WIN) + VirtualFree((void *) chunk, 0, MEM_RELEASE); +#elif defined(XP_OS2) + DosFreeMem((void *) chunk); +#elif defined(SOLARIS) + munmap((char *) chunk, GC_ARENAS_PER_CHUNK << GC_ARENA_SHIFT); +#else + munmap((void *) chunk, GC_ARENAS_PER_CHUNK << GC_ARENA_SHIFT); +#endif +} + +static void +AddChunkToList(JSRuntime *rt, JSGCChunkInfo *ci) +{ + ci->prevp = &rt->gcChunkList; + ci->next = rt->gcChunkList; + if (rt->gcChunkList) { + JS_ASSERT(rt->gcChunkList->prevp == &rt->gcChunkList); + rt->gcChunkList->prevp = &ci->next; + } + rt->gcChunkList = ci; +} + +static void +RemoveChunkFromList(JSRuntime *rt, JSGCChunkInfo *ci) +{ + *ci->prevp = ci->next; + if (ci->next) { + JS_ASSERT(ci->next->prevp == &ci->next); + ci->next->prevp = ci->prevp; + } +} + +static JSGCArenaInfo * +NewGCArena(JSContext *cx) +{ + jsuword chunk; + JSGCArenaInfo *a; + + JSRuntime *rt = cx->runtime; + if (!JS_THREAD_DATA(cx)->waiveGCQuota && rt->gcBytes >= rt->gcMaxBytes) { + /* + * FIXME bug 524051 We cannot run a last-ditch GC on trace for now, so + * just pretend we are out of memory which will throw us off trace and + * we will re-try this code path from the interpreter. + */ + if (!JS_ON_TRACE(cx)) + return NULL; + js_TriggerGC(cx, true); + } + + JSGCChunkInfo *ci; + uint32 i; + JSGCArenaInfo *aprev; + + ci = rt->gcChunkList; + if (!ci) { + chunk = NewGCChunk(); + if (chunk == 0) + return NULL; + JS_ASSERT((chunk & GC_ARENA_MASK) == 0); + a = GET_ARENA_INFO(chunk, 0); + a->firstArena = JS_TRUE; + a->arenaIndex = 0; + aprev = NULL; + i = 0; + do { + a->prev = aprev; + aprev = a; + ++i; + a = GET_ARENA_INFO(chunk, i); + a->firstArena = JS_FALSE; + a->arenaIndex = i; + } while (i != GC_ARENAS_PER_CHUNK - 1); + ci = GET_CHUNK_INFO(chunk, 0); + ci->lastFreeArena = aprev; + ci->numFreeArenas = GC_ARENAS_PER_CHUNK - 1; + AddChunkToList(rt, ci); + } else { + JS_ASSERT(ci->prevp == &rt->gcChunkList); + a = ci->lastFreeArena; + aprev = a->prev; + if (!aprev) { + JS_ASSERT(ci->numFreeArenas == 1); + JS_ASSERT(ARENA_INFO_TO_START(a) == (jsuword) ci); + RemoveChunkFromList(rt, ci); + chunk = GET_ARENA_CHUNK(a, GET_ARENA_INDEX(a)); + SET_CHUNK_INFO_INDEX(chunk, NO_FREE_ARENAS); + } else { + JS_ASSERT(ci->numFreeArenas >= 2); + JS_ASSERT(ARENA_INFO_TO_START(a) != (jsuword) ci); + ci->lastFreeArena = aprev; + ci->numFreeArenas--; + } + } + + rt->gcBytes += GC_ARENA_SIZE; + a->prevUntracedPage = 0; + + return a; +} + +static void +DestroyGCArenas(JSRuntime *rt, JSGCArenaInfo *last) +{ + JSGCArenaInfo *a; + + while (last) { + a = last; + last = last->prev; + + METER(rt->gcStats.afree++); + JS_ASSERT(rt->gcBytes >= GC_ARENA_SIZE); + rt->gcBytes -= GC_ARENA_SIZE; + + uint32 arenaIndex; + jsuword chunk; + uint32 chunkInfoIndex; + JSGCChunkInfo *ci; +#ifdef DEBUG + jsuword firstArena; + + firstArena = a->firstArena; + arenaIndex = a->arenaIndex; + memset((void *) ARENA_INFO_TO_START(a), JS_FREE_PATTERN, GC_ARENA_SIZE); + a->firstArena = firstArena; + a->arenaIndex = arenaIndex; +#endif + arenaIndex = GET_ARENA_INDEX(a); + chunk = GET_ARENA_CHUNK(a, arenaIndex); + chunkInfoIndex = GET_CHUNK_INFO_INDEX(chunk); + if (chunkInfoIndex == NO_FREE_ARENAS) { + chunkInfoIndex = arenaIndex; + SET_CHUNK_INFO_INDEX(chunk, arenaIndex); + ci = GET_CHUNK_INFO(chunk, chunkInfoIndex); + a->prev = NULL; + ci->lastFreeArena = a; + ci->numFreeArenas = 1; + AddChunkToList(rt, ci); + } else { + JS_ASSERT(chunkInfoIndex != arenaIndex); + ci = GET_CHUNK_INFO(chunk, chunkInfoIndex); + JS_ASSERT(ci->numFreeArenas != 0); + JS_ASSERT(ci->lastFreeArena); + JS_ASSERT(a != ci->lastFreeArena); + if (ci->numFreeArenas == GC_ARENAS_PER_CHUNK - 1) { + RemoveChunkFromList(rt, ci); + DestroyGCChunk(chunk); + } else { + ++ci->numFreeArenas; + a->prev = ci->lastFreeArena; + ci->lastFreeArena = a; + } + } + } +} + +static inline size_t +GetFinalizableThingSize(unsigned thingKind) +{ + JS_STATIC_ASSERT(JS_EXTERNAL_STRING_LIMIT == 8); + + static const uint8 map[FINALIZE_LIMIT] = { + sizeof(JSObject), /* FINALIZE_OBJECT */ + sizeof(JSFunction), /* FINALIZE_FUNCTION */ +#if JS_HAS_XML_SUPPORT + sizeof(JSXML), /* FINALIZE_XML */ +#endif + sizeof(JSString), /* FINALIZE_STRING */ + sizeof(JSString), /* FINALIZE_EXTERNAL_STRING0 */ + sizeof(JSString), /* FINALIZE_EXTERNAL_STRING1 */ + sizeof(JSString), /* FINALIZE_EXTERNAL_STRING2 */ + sizeof(JSString), /* FINALIZE_EXTERNAL_STRING3 */ + sizeof(JSString), /* FINALIZE_EXTERNAL_STRING4 */ + sizeof(JSString), /* FINALIZE_EXTERNAL_STRING5 */ + sizeof(JSString), /* FINALIZE_EXTERNAL_STRING6 */ + sizeof(JSString), /* FINALIZE_EXTERNAL_STRING7 */ + }; + + JS_ASSERT(thingKind < FINALIZE_LIMIT); + return map[thingKind]; +} + +static inline size_t +GetFinalizableTraceKind(size_t thingKind) +{ + JS_STATIC_ASSERT(JS_EXTERNAL_STRING_LIMIT == 8); + + static const uint8 map[FINALIZE_LIMIT] = { + JSTRACE_OBJECT, /* FINALIZE_OBJECT */ + JSTRACE_OBJECT, /* FINALIZE_FUNCTION */ +#if JS_HAS_XML_SUPPORT /* FINALIZE_XML */ + JSTRACE_XML, +#endif /* FINALIZE_STRING */ + JSTRACE_STRING, + JSTRACE_STRING, /* FINALIZE_EXTERNAL_STRING0 */ + JSTRACE_STRING, /* FINALIZE_EXTERNAL_STRING1 */ + JSTRACE_STRING, /* FINALIZE_EXTERNAL_STRING2 */ + JSTRACE_STRING, /* FINALIZE_EXTERNAL_STRING3 */ + JSTRACE_STRING, /* FINALIZE_EXTERNAL_STRING4 */ + JSTRACE_STRING, /* FINALIZE_EXTERNAL_STRING5 */ + JSTRACE_STRING, /* FINALIZE_EXTERNAL_STRING6 */ + JSTRACE_STRING, /* FINALIZE_EXTERNAL_STRING7 */ + }; + + JS_ASSERT(thingKind < FINALIZE_LIMIT); + return map[thingKind]; +} + +static inline size_t +GetFinalizableArenaTraceKind(JSGCArenaInfo *a) +{ + JS_ASSERT(a->list); + return GetFinalizableTraceKind(a->list->thingKind); +} + +static void +InitGCArenaLists(JSRuntime *rt) +{ + for (unsigned i = 0; i != FINALIZE_LIMIT; ++i) { + JSGCArenaList *arenaList = &rt->gcArenaList[i]; + arenaList->head = NULL; + arenaList->cursor = NULL; + arenaList->thingKind = i; + arenaList->thingSize = GetFinalizableThingSize(i); + } + rt->gcDoubleArenaList.head = NULL; + rt->gcDoubleArenaList.cursor = NULL; +} + +static void +FinishGCArenaLists(JSRuntime *rt) +{ + for (unsigned i = 0; i < FINALIZE_LIMIT; i++) { + JSGCArenaList *arenaList = &rt->gcArenaList[i]; + DestroyGCArenas(rt, arenaList->head); + arenaList->head = NULL; + arenaList->cursor = NULL; + } + DestroyGCArenas(rt, rt->gcDoubleArenaList.head); + rt->gcDoubleArenaList.head = NULL; + rt->gcDoubleArenaList.cursor = NULL; + + rt->gcBytes = 0; + JS_ASSERT(rt->gcChunkList == 0); +} + +/* + * This function must not be called when thing is jsdouble. + */ +static uint8 * +GetGCThingFlags(void *thing) +{ + JSGCArenaInfo *a; + uint32 index; + + a = THING_TO_ARENA(thing); + index = THING_TO_INDEX(thing, a->list->thingSize); + return THING_FLAGP(a, index); +} + +intN +js_GetExternalStringGCType(JSString *str) +{ + JS_STATIC_ASSERT(FINALIZE_STRING + 1 == FINALIZE_EXTERNAL_STRING0); + JS_ASSERT(!JSString::isStatic(str)); + + unsigned thingKind = THING_TO_ARENA(str)->list->thingKind; + JS_ASSERT(IsFinalizableStringKind(thingKind)); + return intN(thingKind) - intN(FINALIZE_EXTERNAL_STRING0); +} + +JS_FRIEND_API(uint32) +js_GetGCThingTraceKind(void *thing) +{ + if (JSString::isStatic(thing)) + return JSTRACE_STRING; + + JSGCArenaInfo *a = THING_TO_ARENA(thing); + if (!a->list) + return JSTRACE_DOUBLE; + return GetFinalizableArenaTraceKind(a); +} + +JSRuntime* +js_GetGCStringRuntime(JSString *str) +{ + JSGCArenaList *list = THING_TO_ARENA(str)->list; + JS_ASSERT(list->thingSize == sizeof(JSString)); + + unsigned i = list->thingKind; + JS_ASSERT(i == FINALIZE_STRING || + (FINALIZE_EXTERNAL_STRING0 <= i && + i < FINALIZE_EXTERNAL_STRING0 + JS_EXTERNAL_STRING_LIMIT)); + return (JSRuntime *)((uint8 *)(list - i) - + offsetof(JSRuntime, gcArenaList)); +} + +JSBool +js_IsAboutToBeFinalized(JSContext *cx, void *thing) +{ + JSGCArenaInfo *a; + uint32 index, flags; + + a = THING_TO_ARENA(thing); + if (!a->list) { + /* + * Check if arena has no marked doubles. In that case the bitmap with + * the mark flags contains all garbage as it is initialized only when + * marking the first double in the arena. + */ + if (!a->hasMarkedDoubles) + return JS_TRUE; + index = DOUBLE_THING_TO_INDEX(thing); + return !IsMarkedDouble(a, index); + } + index = THING_TO_INDEX(thing, a->list->thingSize); + flags = *THING_FLAGP(a, index); + return !(flags & (GCF_MARK | GCF_LOCK)); +} + +/* This is compatible with JSDHashEntryStub. */ +typedef struct JSGCRootHashEntry { + JSDHashEntryHdr hdr; + void *root; + const char *name; +} JSGCRootHashEntry; + +/* Initial size of the gcRootsHash table (SWAG, small enough to amortize). */ +#define GC_ROOTS_SIZE 256 + +/* + * For a CPU with extremely large pages using them for GC things wastes + * too much memory. + */ +#define GC_ARENAS_PER_CPU_PAGE_LIMIT JS_BIT(18 - GC_ARENA_SHIFT) + +JS_STATIC_ASSERT(GC_ARENAS_PER_CPU_PAGE_LIMIT <= NO_FREE_ARENAS); + +JSBool +js_InitGC(JSRuntime *rt, uint32 maxbytes) +{ + InitGCArenaLists(rt); + if (!JS_DHashTableInit(&rt->gcRootsHash, JS_DHashGetStubOps(), NULL, + sizeof(JSGCRootHashEntry), GC_ROOTS_SIZE)) { + rt->gcRootsHash.ops = NULL; + return JS_FALSE; + } + rt->gcLocksHash = NULL; /* create lazily */ + + /* + * Separate gcMaxMallocBytes from gcMaxBytes but initialize to maxbytes + * for default backward API compatibility. + */ + rt->gcMaxBytes = maxbytes; + rt->setGCMaxMallocBytes(maxbytes); + + rt->gcEmptyArenaPoolLifespan = 30000; + + /* + * By default the trigger factor gets maximum possible value. This + * means that GC will not be triggered by growth of GC memory (gcBytes). + */ + rt->setGCTriggerFactor((uint32) -1); + + /* + * The assigned value prevents GC from running when GC memory is too low + * (during JS engine start). + */ + rt->setGCLastBytes(8192); + + METER(memset(&rt->gcStats, 0, sizeof rt->gcStats)); + return JS_TRUE; +} + +#ifdef JS_GCMETER + +static void +UpdateArenaStats(JSGCArenaStats *st, uint32 nlivearenas, uint32 nkilledArenas, + uint32 nthings) +{ + size_t narenas; + + narenas = nlivearenas + nkilledArenas; + JS_ASSERT(narenas >= st->livearenas); + + st->newarenas = narenas - st->livearenas; + st->narenas = narenas; + st->livearenas = nlivearenas; + if (st->maxarenas < narenas) + st->maxarenas = narenas; + st->totalarenas += narenas; + + st->nthings = nthings; + if (st->maxthings < nthings) + st->maxthings = nthings; + st->totalthings += nthings; +} + +JS_FRIEND_API(void) +js_DumpGCStats(JSRuntime *rt, FILE *fp) +{ + int i; + size_t sumArenas, sumTotalArenas; + size_t sumThings, sumMaxThings; + size_t sumThingSize, sumTotalThingSize; + size_t sumArenaCapacity, sumTotalArenaCapacity; + JSGCArenaStats *st; + size_t thingSize, thingsPerArena; + size_t sumAlloc, sumLocalAlloc, sumFail, sumRetry; + + fprintf(fp, "\nGC allocation statistics:\n"); + +#define UL(x) ((unsigned long)(x)) +#define ULSTAT(x) UL(rt->gcStats.x) +#define PERCENT(x,y) (100.0 * (double) (x) / (double) (y)) + + sumArenas = 0; + sumTotalArenas = 0; + sumThings = 0; + sumMaxThings = 0; + sumThingSize = 0; + sumTotalThingSize = 0; + sumArenaCapacity = 0; + sumTotalArenaCapacity = 0; + sumAlloc = 0; + sumLocalAlloc = 0; + sumFail = 0; + sumRetry = 0; + for (i = -1; i < (int) GC_NUM_FREELISTS; i++) { + if (i == -1) { + thingSize = sizeof(jsdouble); + thingsPerArena = DOUBLES_PER_ARENA; + st = &rt->gcStats.doubleArenaStats; + fprintf(fp, + "Arena list for double values (%lu doubles per arena):", + UL(thingsPerArena)); + } else { + thingSize = rt->gcArenaList[i].thingSize; + thingsPerArena = THINGS_PER_ARENA(thingSize); + st = &rt->gcStats.arenaStats[i]; + fprintf(fp, + "Arena list %d (thing size %lu, %lu things per arena):", + i, UL(GC_FREELIST_NBYTES(i)), UL(thingsPerArena)); + } + if (st->maxarenas == 0) { + fputs(" NEVER USED\n", fp); + continue; + } + putc('\n', fp); + fprintf(fp, " arenas before GC: %lu\n", UL(st->narenas)); + fprintf(fp, " new arenas before GC: %lu (%.1f%%)\n", + UL(st->newarenas), PERCENT(st->newarenas, st->narenas)); + fprintf(fp, " arenas after GC: %lu (%.1f%%)\n", + UL(st->livearenas), PERCENT(st->livearenas, st->narenas)); + fprintf(fp, " max arenas: %lu\n", UL(st->maxarenas)); + fprintf(fp, " things: %lu\n", UL(st->nthings)); + fprintf(fp, " GC cell utilization: %.1f%%\n", + PERCENT(st->nthings, thingsPerArena * st->narenas)); + fprintf(fp, " average cell utilization: %.1f%%\n", + PERCENT(st->totalthings, thingsPerArena * st->totalarenas)); + fprintf(fp, " max things: %lu\n", UL(st->maxthings)); + fprintf(fp, " alloc attempts: %lu\n", UL(st->alloc)); + fprintf(fp, " alloc without locks: %1u (%.1f%%)\n", + UL(st->localalloc), PERCENT(st->localalloc, st->alloc)); + sumArenas += st->narenas; + sumTotalArenas += st->totalarenas; + sumThings += st->nthings; + sumMaxThings += st->maxthings; + sumThingSize += thingSize * st->nthings; + sumTotalThingSize += thingSize * st->totalthings; + sumArenaCapacity += thingSize * thingsPerArena * st->narenas; + sumTotalArenaCapacity += thingSize * thingsPerArena * st->totalarenas; + sumAlloc += st->alloc; + sumLocalAlloc += st->localalloc; + sumFail += st->fail; + sumRetry += st->retry; + } + fprintf(fp, "TOTAL STATS:\n"); + fprintf(fp, " bytes allocated: %lu\n", UL(rt->gcBytes)); + fprintf(fp, " total GC arenas: %lu\n", UL(sumArenas)); + fprintf(fp, " total GC things: %lu\n", UL(sumThings)); + fprintf(fp, " max total GC things: %lu\n", UL(sumMaxThings)); + fprintf(fp, " GC cell utilization: %.1f%%\n", + PERCENT(sumThingSize, sumArenaCapacity)); + fprintf(fp, " average cell utilization: %.1f%%\n", + PERCENT(sumTotalThingSize, sumTotalArenaCapacity)); + fprintf(fp, "allocation retries after GC: %lu\n", UL(sumRetry)); + fprintf(fp, " alloc attempts: %lu\n", UL(sumAlloc)); + fprintf(fp, " alloc without locks: %1u (%.1f%%)\n", + UL(sumLocalAlloc), PERCENT(sumLocalAlloc, sumAlloc)); + fprintf(fp, " allocation failures: %lu\n", UL(sumFail)); + fprintf(fp, " things born locked: %lu\n", ULSTAT(lockborn)); + fprintf(fp, " valid lock calls: %lu\n", ULSTAT(lock)); + fprintf(fp, " valid unlock calls: %lu\n", ULSTAT(unlock)); + fprintf(fp, " mark recursion depth: %lu\n", ULSTAT(depth)); + fprintf(fp, " maximum mark recursion: %lu\n", ULSTAT(maxdepth)); + fprintf(fp, " mark C recursion depth: %lu\n", ULSTAT(cdepth)); + fprintf(fp, " maximum mark C recursion: %lu\n", ULSTAT(maxcdepth)); + fprintf(fp, " delayed tracing calls: %lu\n", ULSTAT(untraced)); +#ifdef DEBUG + fprintf(fp, " max trace later count: %lu\n", ULSTAT(maxuntraced)); +#endif + fprintf(fp, " maximum GC nesting level: %lu\n", ULSTAT(maxlevel)); + fprintf(fp, "potentially useful GC calls: %lu\n", ULSTAT(poke)); + fprintf(fp, " thing arenas freed so far: %lu\n", ULSTAT(afree)); + fprintf(fp, " stack segments scanned: %lu\n", ULSTAT(stackseg)); + fprintf(fp, "stack segment slots scanned: %lu\n", ULSTAT(segslots)); + fprintf(fp, "reachable closeable objects: %lu\n", ULSTAT(nclose)); + fprintf(fp, " max reachable closeable: %lu\n", ULSTAT(maxnclose)); + fprintf(fp, " scheduled close hooks: %lu\n", ULSTAT(closelater)); + fprintf(fp, " max scheduled close hooks: %lu\n", ULSTAT(maxcloselater)); + +#undef UL +#undef ULSTAT +#undef PERCENT + +#ifdef JS_ARENAMETER + JS_DumpArenaStats(fp); +#endif +} +#endif + +#ifdef DEBUG +static void +CheckLeakedRoots(JSRuntime *rt); +#endif + +void +js_FinishGC(JSRuntime *rt) +{ +#ifdef JS_ARENAMETER + JS_DumpArenaStats(stdout); +#endif +#ifdef JS_GCMETER + js_DumpGCStats(rt, stdout); +#endif + + rt->gcIteratorTable.clear(); + FinishGCArenaLists(rt); + + if (rt->gcRootsHash.ops) { +#ifdef DEBUG + CheckLeakedRoots(rt); +#endif + JS_DHashTableFinish(&rt->gcRootsHash); + rt->gcRootsHash.ops = NULL; + } + if (rt->gcLocksHash) { + JS_DHashTableDestroy(rt->gcLocksHash); + rt->gcLocksHash = NULL; + } +} + +JSBool +js_AddRoot(JSContext *cx, void *rp, const char *name) +{ + JSBool ok = js_AddRootRT(cx->runtime, rp, name); + if (!ok) + JS_ReportOutOfMemory(cx); + return ok; +} + +JSBool +js_AddRootRT(JSRuntime *rt, void *rp, const char *name) +{ + JSBool ok; + JSGCRootHashEntry *rhe; + + /* + * Due to the long-standing, but now removed, use of rt->gcLock across the + * bulk of js_GC, API users have come to depend on JS_AddRoot etc. locking + * properly with a racing GC, without calling JS_AddRoot from a request. + * We have to preserve API compatibility here, now that we avoid holding + * rt->gcLock across the mark phase (including the root hashtable mark). + */ + JS_LOCK_GC(rt); + js_WaitForGC(rt); + rhe = (JSGCRootHashEntry *) + JS_DHashTableOperate(&rt->gcRootsHash, rp, JS_DHASH_ADD); + if (rhe) { + rhe->root = rp; + rhe->name = name; + ok = JS_TRUE; + } else { + ok = JS_FALSE; + } + JS_UNLOCK_GC(rt); + return ok; +} + +JSBool +js_RemoveRoot(JSRuntime *rt, void *rp) +{ + /* + * Due to the JS_RemoveRootRT API, we may be called outside of a request. + * Same synchronization drill as above in js_AddRoot. + */ + JS_LOCK_GC(rt); + js_WaitForGC(rt); + (void) JS_DHashTableOperate(&rt->gcRootsHash, rp, JS_DHASH_REMOVE); + rt->gcPoke = JS_TRUE; + JS_UNLOCK_GC(rt); + return JS_TRUE; +} + +#ifdef DEBUG + +static JSDHashOperator +js_root_printer(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 i, void *arg) +{ + uint32 *leakedroots = (uint32 *)arg; + JSGCRootHashEntry *rhe = (JSGCRootHashEntry *)hdr; + + (*leakedroots)++; + fprintf(stderr, + "JS engine warning: leaking GC root \'%s\' at %p\n", + rhe->name ? (char *)rhe->name : "", rhe->root); + + return JS_DHASH_NEXT; +} + +static void +CheckLeakedRoots(JSRuntime *rt) +{ + uint32 leakedroots = 0; + + /* Warn (but don't assert) debug builds of any remaining roots. */ + JS_DHashTableEnumerate(&rt->gcRootsHash, js_root_printer, + &leakedroots); + if (leakedroots > 0) { + if (leakedroots == 1) { + fprintf(stderr, +"JS engine warning: 1 GC root remains after destroying the JSRuntime at %p.\n" +" This root may point to freed memory. Objects reachable\n" +" through it have not been finalized.\n", + (void *) rt); + } else { + fprintf(stderr, +"JS engine warning: %lu GC roots remain after destroying the JSRuntime at %p.\n" +" These roots may point to freed memory. Objects reachable\n" +" through them have not been finalized.\n", + (unsigned long) leakedroots, (void *) rt); + } + } +} + +typedef struct NamedRootDumpArgs { + void (*dump)(const char *name, void *rp, void *data); + void *data; +} NamedRootDumpArgs; + +static JSDHashOperator +js_named_root_dumper(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, + void *arg) +{ + NamedRootDumpArgs *args = (NamedRootDumpArgs *) arg; + JSGCRootHashEntry *rhe = (JSGCRootHashEntry *)hdr; + + if (rhe->name) + args->dump(rhe->name, rhe->root, args->data); + return JS_DHASH_NEXT; +} + +JS_BEGIN_EXTERN_C +void +js_DumpNamedRoots(JSRuntime *rt, + void (*dump)(const char *name, void *rp, void *data), + void *data) +{ + NamedRootDumpArgs args; + + args.dump = dump; + args.data = data; + JS_DHashTableEnumerate(&rt->gcRootsHash, js_named_root_dumper, &args); +} +JS_END_EXTERN_C + +#endif /* DEBUG */ + +typedef struct GCRootMapArgs { + JSGCRootMapFun map; + void *data; +} GCRootMapArgs; + +static JSDHashOperator +js_gcroot_mapper(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, + void *arg) +{ + GCRootMapArgs *args = (GCRootMapArgs *) arg; + JSGCRootHashEntry *rhe = (JSGCRootHashEntry *)hdr; + intN mapflags; + int op; + + mapflags = args->map(rhe->root, rhe->name, args->data); + +#if JS_MAP_GCROOT_NEXT == JS_DHASH_NEXT && \ + JS_MAP_GCROOT_STOP == JS_DHASH_STOP && \ + JS_MAP_GCROOT_REMOVE == JS_DHASH_REMOVE + op = (JSDHashOperator)mapflags; +#else + op = JS_DHASH_NEXT; + if (mapflags & JS_MAP_GCROOT_STOP) + op |= JS_DHASH_STOP; + if (mapflags & JS_MAP_GCROOT_REMOVE) + op |= JS_DHASH_REMOVE; +#endif + + return (JSDHashOperator) op; +} + +uint32 +js_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data) +{ + GCRootMapArgs args; + uint32 rv; + + args.map = map; + args.data = data; + JS_LOCK_GC(rt); + rv = JS_DHashTableEnumerate(&rt->gcRootsHash, js_gcroot_mapper, &args); + JS_UNLOCK_GC(rt); + return rv; +} + +JSBool +js_RegisterCloseableIterator(JSContext *cx, JSObject *obj) +{ + JSRuntime *rt; + JSBool ok; + + rt = cx->runtime; + JS_ASSERT(!rt->gcRunning); + + JS_LOCK_GC(rt); + ok = rt->gcIteratorTable.append(obj); + JS_UNLOCK_GC(rt); + return ok; +} + +static void +CloseNativeIterators(JSContext *cx) +{ + JSRuntime *rt = cx->runtime; + size_t length = rt->gcIteratorTable.length(); + JSObject **array = rt->gcIteratorTable.begin(); + + size_t newLength = 0; + for (size_t i = 0; i < length; ++i) { + JSObject *obj = array[i]; + if (js_IsAboutToBeFinalized(cx, obj)) + js_CloseNativeIterator(cx, obj); + else + array[newLength++] = obj; + } + rt->gcIteratorTable.resize(newLength); +} + +void +JSRuntime::setGCTriggerFactor(uint32 factor) +{ + JS_ASSERT(factor >= 100); + + gcTriggerFactor = factor; + setGCLastBytes(gcLastBytes); +} + +void +JSRuntime::setGCLastBytes(size_t lastBytes) +{ + gcLastBytes = lastBytes; + uint64 triggerBytes = uint64(lastBytes) * uint64(gcTriggerFactor / 100); + if (triggerBytes != size_t(triggerBytes)) + triggerBytes = size_t(-1); + gcTriggerBytes = size_t(triggerBytes); +} + +void +JSGCFreeLists::purge() +{ + /* + * Return the free list back to the arena so the GC finalization will not + * run the finalizers over unitialized bytes from free things. + */ + for (JSGCThing **p = finalizables; p != JS_ARRAY_END(finalizables); ++p) { + JSGCThing *freeListHead = *p; + if (freeListHead) { + JSGCArenaInfo *a = THING_TO_ARENA(freeListHead); + JS_ASSERT(!a->finalizable.freeList); + a->finalizable.freeList = freeListHead; + *p = NULL; + } + } + doubles = NULL; +} + +void +JSGCFreeLists::moveTo(JSGCFreeLists *another) +{ + *another = *this; + doubles = NULL; + memset(finalizables, 0, sizeof(finalizables)); + JS_ASSERT(isEmpty()); +} + +static inline bool +IsGCThresholdReached(JSRuntime *rt) +{ +#ifdef JS_GC_ZEAL + if (rt->gcZeal >= 1) + return true; +#endif + + /* + * Since the initial value of the gcLastBytes parameter is not equal to + * zero (see the js_InitGC function) the return value is false when + * the gcBytes value is close to zero at the JS engine start. + */ + return rt->isGCMallocLimitReached() || rt->gcBytes >= rt->gcTriggerBytes; +} + +static inline JSGCFreeLists * +GetGCFreeLists(JSContext *cx) +{ + JSThreadData *td = JS_THREAD_DATA(cx); + if (!td->localRootStack) + return &td->gcFreeLists; + JS_ASSERT(td->gcFreeLists.isEmpty()); + return &td->localRootStack->gcFreeLists; +} + +static JSGCThing * +RefillFinalizableFreeList(JSContext *cx, unsigned thingKind) +{ + JS_ASSERT(!GetGCFreeLists(cx)->finalizables[thingKind]); + JSRuntime *rt = cx->runtime; + JS_LOCK_GC(rt); + JS_ASSERT(!rt->gcRunning); + if (rt->gcRunning) { + METER(rt->gcStats.finalfail++); + JS_UNLOCK_GC(rt); + return NULL; + } + + METER(JSGCArenaStats *astats = &cx->runtime->gcStats.arenaStats[thingKind]); + bool canGC = !JS_ON_TRACE(cx) && !JS_THREAD_DATA(cx)->waiveGCQuota; + bool doGC = canGC && IsGCThresholdReached(rt); + JSGCArenaList *arenaList = &rt->gcArenaList[thingKind]; + JSGCArenaInfo *a; + for (;;) { + if (doGC) { + /* + * Keep rt->gcLock across the call into js_GC so we don't starve + * and lose to racing threads who deplete the heap just after + * js_GC has replenished it (or has synchronized with a racing + * GC that collected a bunch of garbage). This unfair scheduling + * can happen on certain operating systems. For the gory details, + * see bug 162779 at https://bugzilla.mozilla.org/. + */ + js_GC(cx, GC_LAST_DITCH); + METER(astats->retry++); + canGC = false; + + /* + * The JSGC_END callback can legitimately allocate new GC things + * and populate the free list. If that happens, just return that + * list head. + */ + JSGCThing *freeList = GetGCFreeLists(cx)->finalizables[thingKind]; + if (freeList) { + JS_UNLOCK_GC(rt); + return freeList; + } + } + + while ((a = arenaList->cursor) != NULL) { + arenaList->cursor = a->prev; + JSGCThing *freeList = a->finalizable.freeList; + if (freeList) { + a->finalizable.freeList = NULL; + JS_UNLOCK_GC(rt); + return freeList; + } + } + + a = NewGCArena(cx); + if (a) + break; + if (!canGC) { + METER(astats->fail++); + JS_UNLOCK_GC(rt); + return NULL; + } + doGC = true; + } + + /* + * Do only minimal initialization of the arena inside the GC lock. We + * can do the rest outside the lock because no other threads will see + * the arena until the GC is run. + */ + a->list = arenaList; + a->prev = arenaList->head; + a->prevUntracedPage = 0; + a->finalizable.untracedThings = 0; + a->finalizable.freeList = NULL; + arenaList->head = a; + JS_UNLOCK_GC(rt); + + unsigned nthings = THINGS_PER_ARENA(arenaList->thingSize); + uint8 *flagsStart = THING_FLAGP(a, nthings - 1); + memset(flagsStart, 0, nthings); + + /* Turn all things in the arena into a free list. */ + return MakeNewArenaFreeList(a, arenaList->thingSize, nthings); +} + +static inline void +CheckGCFreeListLink(JSGCThing *thing) +{ + /* + * The GC things on the free lists come from one arena and the things on + * the free list are linked in ascending address order. + */ + JS_ASSERT_IF(thing->link, + THING_TO_ARENA(thing) == THING_TO_ARENA(thing->link)); + JS_ASSERT_IF(thing->link, thing < thing->link); +} + +void * +js_NewFinalizableGCThing(JSContext *cx, unsigned thingKind) +{ + JS_ASSERT(thingKind < FINALIZE_LIMIT); +#ifdef JS_THREADSAFE + JS_ASSERT(cx->thread); +#endif + + /* Updates of metering counters here may not be thread-safe. */ + METER(cx->runtime->gcStats.arenaStats[thingKind].alloc++); + + JSGCThing **freeListp = + JS_THREAD_DATA(cx)->gcFreeLists.finalizables + thingKind; + JSGCThing *thing = *freeListp; + if (thing) { + JS_ASSERT(!JS_THREAD_DATA(cx)->localRootStack); + *freeListp = thing->link; + cx->weakRoots.finalizableNewborns[thingKind] = thing; + CheckGCFreeListLink(thing); + METER(astats->localalloc++); + return thing; + } + + /* + * To avoid for the local roots on each GC allocation when the local roots + * are not active we move the GC free lists from JSThreadData to lrs in + * JS_EnterLocalRootScope(). This way with inactive local roots we only + * check for non-null lrs only when we exhaust the free list. + */ + JSLocalRootStack *lrs = JS_THREAD_DATA(cx)->localRootStack; + for (;;) { + if (lrs) { + freeListp = lrs->gcFreeLists.finalizables + thingKind; + thing = *freeListp; + if (thing) { + *freeListp = thing->link; + METER(astats->localalloc++); + break; + } + } + + thing = RefillFinalizableFreeList(cx, thingKind); + if (thing) { + /* + * See comments in RefillFinalizableFreeList about a possibility + * of *freeListp == thing. + */ + JS_ASSERT(!*freeListp || *freeListp == thing); + *freeListp = thing->link; + break; + } + + js_ReportOutOfMemory(cx); + return NULL; + } + + CheckGCFreeListLink(thing); + if (lrs) { + /* + * If we're in a local root scope, don't set newborn[type] at all, to + * avoid entraining garbage from it for an unbounded amount of time + * on this context. A caller will leave the local root scope and pop + * this reference, allowing thing to be GC'd if it has no other refs. + * See JS_EnterLocalRootScope and related APIs. + */ + if (js_PushLocalRoot(cx, lrs, (jsval) thing) < 0) { + JS_ASSERT(thing->link == *freeListp); + *freeListp = thing; + return NULL; + } + } else { + /* + * No local root scope, so we're stuck with the old, fragile model of + * depending on a pigeon-hole newborn per type per context. + */ + cx->weakRoots.finalizableNewborns[thingKind] = thing; + } + + return thing; +} + +static JSGCThing * +TurnUsedArenaIntoDoubleList(JSGCArenaInfo *a) +{ + JSGCThing *head; + JSGCThing **tailp = &head; + jsuword thing = ARENA_INFO_TO_START(a); + + /* + * When m below points the last bitmap's word in the arena, its high bits + * corresponds to non-existing cells and thingptr is outside the space + * allocated for doubles. ClearDoubleArenaFlags sets such bits to 1. Thus + * even for this last word its bit is unset iff the corresponding cell + * exists and free. + */ + for (jsbitmap *m = DOUBLE_ARENA_BITMAP(a); + m != DOUBLE_ARENA_BITMAP_END(a); + ++m) { + JS_ASSERT(thing < reinterpret_cast(DOUBLE_ARENA_BITMAP(a))); + JS_ASSERT((thing - ARENA_INFO_TO_START(a)) % + (JS_BITS_PER_WORD * sizeof(jsdouble)) == 0); + + jsbitmap bits = *m; + if (bits == jsbitmap(-1)) { + thing += JS_BITS_PER_WORD * sizeof(jsdouble); + } else { + /* + * We have some zero bits. Turn corresponding cells into a list + * unrolling the loop for better performance. + */ + const unsigned unroll = 4; + const jsbitmap unrollMask = (jsbitmap(1) << unroll) - 1; + JS_STATIC_ASSERT((JS_BITS_PER_WORD & unrollMask) == 0); + + for (unsigned n = 0; n != JS_BITS_PER_WORD; n += unroll) { + jsbitmap bitsChunk = bits & unrollMask; + bits >>= unroll; + if (bitsChunk == unrollMask) { + thing += unroll * sizeof(jsdouble); + } else { +#define DO_BIT(bit) \ + if (!(bitsChunk & (jsbitmap(1) << (bit)))) { \ + JS_ASSERT(thing - ARENA_INFO_TO_START(a) <= \ + (DOUBLES_PER_ARENA - 1) * sizeof(jsdouble));\ + JSGCThing *t = reinterpret_cast(thing); \ + *tailp = t; \ + tailp = &t->link; \ + } \ + thing += sizeof(jsdouble); + DO_BIT(0); + DO_BIT(1); + DO_BIT(2); + DO_BIT(3); +#undef DO_BIT + } + } + } + } + *tailp = NULL; + return head; +} + +static JSGCThing * +RefillDoubleFreeList(JSContext *cx) +{ + JS_ASSERT(!GetGCFreeLists(cx)->doubles); + + JSRuntime *rt = cx->runtime; + JS_ASSERT(!rt->gcRunning); + + JS_LOCK_GC(rt); + + JSGCArenaInfo *a; + bool canGC = !JS_ON_TRACE(cx) && !JS_THREAD_DATA(cx)->waiveGCQuota; + bool doGC = canGC && IsGCThresholdReached(rt); + for (;;) { + if (doGC) { + js_GC(cx, GC_LAST_DITCH); + METER(rt->gcStats.doubleArenaStats.retry++); + canGC = false; + + /* See comments in RefillFinalizableFreeList. */ + JSGCThing *freeList = GetGCFreeLists(cx)->doubles; + if (freeList) { + JS_UNLOCK_GC(rt); + return freeList; + } + } + + /* + * Loop until we find arena with some free doubles. We turn arenas + * into free lists outside the lock to minimize contention between + * threads. + */ + while (!!(a = rt->gcDoubleArenaList.cursor)) { + JS_ASSERT(!a->hasMarkedDoubles); + rt->gcDoubleArenaList.cursor = a->prev; + JS_UNLOCK_GC(rt); + JSGCThing *list = TurnUsedArenaIntoDoubleList(a); + if (list) + return list; + JS_LOCK_GC(rt); + } + a = NewGCArena(cx); + if (a) + break; + if (!canGC) { + METER(rt->gcStats.doubleArenaStats.fail++); + JS_UNLOCK_GC(rt); + return NULL; + } + doGC = true; + } + + a->list = NULL; + a->hasMarkedDoubles = false; + a->prev = rt->gcDoubleArenaList.head; + rt->gcDoubleArenaList.head = a; + JS_UNLOCK_GC(rt); + return MakeNewArenaFreeList(a, sizeof(jsdouble), DOUBLES_PER_ARENA); +} + +JSBool +js_NewDoubleInRootedValue(JSContext *cx, jsdouble d, jsval *vp) +{ + /* Updates of metering counters here are not thread-safe. */ + METER(JSGCArenaStats *astats = &cx->runtime->gcStats.doubleArenaStats); + METER(astats->alloc++); + + JSGCThing **freeListp = &JS_THREAD_DATA(cx)->gcFreeLists.doubles; + JSGCThing *thing = *freeListp; + if (thing) { + METER(astats->localalloc++); + JS_ASSERT(!JS_THREAD_DATA(cx)->localRootStack); + CheckGCFreeListLink(thing); + *freeListp = thing->link; + + jsdouble *dp = reinterpret_cast(thing); + *dp = d; + *vp = DOUBLE_TO_JSVAL(dp); + return true; + } + + JSLocalRootStack *lrs = JS_THREAD_DATA(cx)->localRootStack; + for (;;) { + if (lrs) { + freeListp = &lrs->gcFreeLists.doubles; + thing = *freeListp; + if (thing) { + METER(astats->localalloc++); + break; + } + } + thing = RefillDoubleFreeList(cx); + if (thing) { + JS_ASSERT(!*freeListp || *freeListp == thing); + break; + } + + if (!JS_ON_TRACE(cx)) { + /* Trace code handle this on its own. */ + js_ReportOutOfMemory(cx); + METER(astats->fail++); + } + return false; + } + + CheckGCFreeListLink(thing); + *freeListp = thing->link; + + jsdouble *dp = reinterpret_cast(thing); + *dp = d; + *vp = DOUBLE_TO_JSVAL(dp); + return !lrs || js_PushLocalRoot(cx, lrs, *vp) >= 0; +} + +jsdouble * +js_NewWeaklyRootedDouble(JSContext *cx, jsdouble d) +{ + jsval v; + if (!js_NewDoubleInRootedValue(cx, d, &v)) + return NULL; + + jsdouble *dp = JSVAL_TO_DOUBLE(v); + cx->weakRoots.newbornDouble = dp; + return dp; +} + +/* + * Shallow GC-things can be locked just by setting the GCF_LOCK bit, because + * they have no descendants to mark during the GC. Currently the optimization + * is only used for non-dependant strings. + */ +static uint8 * +GetShallowGCThingFlag(void *thing) +{ + if (JSString::isStatic(thing)) + return NULL; + JSGCArenaInfo *a = THING_TO_ARENA(thing); + if (!a->list || !IsFinalizableStringKind(a->list->thingKind)) + return NULL; + + JS_ASSERT(sizeof(JSString) == a->list->thingSize); + JSString *str = (JSString *) thing; + if (str->isDependent()) + return NULL; + + uint32 index = THING_TO_INDEX(thing, sizeof(JSString)); + return THING_FLAGP(a, index); +} + +/* This is compatible with JSDHashEntryStub. */ +typedef struct JSGCLockHashEntry { + JSDHashEntryHdr hdr; + const void *thing; + uint32 count; +} JSGCLockHashEntry; + +JSBool +js_LockGCThingRT(JSRuntime *rt, void *thing) +{ + if (!thing) + return JS_TRUE; + + JS_LOCK_GC(rt); + + /* + * Avoid adding a rt->gcLocksHash entry for shallow things until someone + * nests a lock. + */ + uint8 *flagp = GetShallowGCThingFlag(thing); + JSBool ok; + JSGCLockHashEntry *lhe; + if (flagp && !(*flagp & GCF_LOCK)) { + *flagp |= GCF_LOCK; + METER(rt->gcStats.lock++); + ok = JS_TRUE; + goto out; + } + + if (!rt->gcLocksHash) { + rt->gcLocksHash = JS_NewDHashTable(JS_DHashGetStubOps(), NULL, + sizeof(JSGCLockHashEntry), + GC_ROOTS_SIZE); + if (!rt->gcLocksHash) { + ok = JS_FALSE; + goto out; + } + } + + lhe = (JSGCLockHashEntry *) + JS_DHashTableOperate(rt->gcLocksHash, thing, JS_DHASH_ADD); + if (!lhe) { + ok = JS_FALSE; + goto out; + } + if (!lhe->thing) { + lhe->thing = thing; + lhe->count = 1; + } else { + JS_ASSERT(lhe->count >= 1); + lhe->count++; + } + + METER(rt->gcStats.lock++); + ok = JS_TRUE; + out: + JS_UNLOCK_GC(rt); + return ok; +} + +JSBool +js_UnlockGCThingRT(JSRuntime *rt, void *thing) +{ + if (!thing) + return JS_TRUE; + + JS_LOCK_GC(rt); + + uint8 *flagp = GetShallowGCThingFlag(thing); + JSGCLockHashEntry *lhe; + if (flagp && !(*flagp & GCF_LOCK)) + goto out; + if (!rt->gcLocksHash || + (lhe = (JSGCLockHashEntry *) + JS_DHashTableOperate(rt->gcLocksHash, thing, + JS_DHASH_LOOKUP), + JS_DHASH_ENTRY_IS_FREE(&lhe->hdr))) { + /* Shallow entry is not in the hash -> clear its lock bit. */ + if (flagp) + *flagp &= ~GCF_LOCK; + else + goto out; + } else { + if (--lhe->count != 0) + goto out; + JS_DHashTableOperate(rt->gcLocksHash, thing, JS_DHASH_REMOVE); + } + + rt->gcPoke = JS_TRUE; + METER(rt->gcStats.unlock++); + out: + JS_UNLOCK_GC(rt); + return JS_TRUE; +} + +JS_PUBLIC_API(void) +JS_TraceChildren(JSTracer *trc, void *thing, uint32 kind) +{ + switch (kind) { + case JSTRACE_OBJECT: { + /* If obj has no map, it must be a newborn. */ + JSObject *obj = (JSObject *) thing; + if (!obj->map) + break; + obj->map->ops->trace(trc, obj); + break; + } + + case JSTRACE_STRING: { + JSString *str = (JSString *) thing; + if (str->isDependent()) + JS_CALL_STRING_TRACER(trc, str->dependentBase(), "base"); + break; + } + +#if JS_HAS_XML_SUPPORT + case JSTRACE_XML: + js_TraceXML(trc, (JSXML *)thing); + break; +#endif + } +} + +/* + * Number of things covered by a single bit of JSGCArenaInfo.untracedThings. + */ +#define THINGS_PER_UNTRACED_BIT(thingSize) \ + JS_HOWMANY(THINGS_PER_ARENA(thingSize), JS_BITS_PER_WORD) + +static void +DelayTracingChildren(JSRuntime *rt, uint8 *flagp) +{ + JSGCArenaInfo *a; + uint32 untracedBitIndex; + jsuword bit; + + JS_ASSERT(!(*flagp & GCF_CHILDREN)); + *flagp |= GCF_CHILDREN; + + METER(rt->gcStats.untraced++); +#ifdef DEBUG + ++rt->gcTraceLaterCount; + METER_UPDATE_MAX(rt->gcStats.maxuntraced, rt->gcTraceLaterCount); +#endif + + a = FLAGP_TO_ARENA(flagp); + untracedBitIndex = FLAGP_TO_INDEX(flagp) / + THINGS_PER_UNTRACED_BIT(a->list->thingSize); + JS_ASSERT(untracedBitIndex < JS_BITS_PER_WORD); + bit = (jsuword)1 << untracedBitIndex; + if (a->finalizable.untracedThings != 0) { + JS_ASSERT(rt->gcUntracedArenaStackTop); + if (a->finalizable.untracedThings & bit) { + /* bit already covers things with children to trace later. */ + return; + } + a->finalizable.untracedThings |= bit; + } else { + /* + * The thing is the first thing with not yet traced children in the + * whole arena, so push the arena on the stack of arenas with things + * to be traced later unless the arena has already been pushed. We + * detect that through checking prevUntracedPage as the field is 0 + * only for not yet pushed arenas. To ensure that + * prevUntracedPage != 0 + * even when the stack contains one element, we make prevUntracedPage + * for the arena at the bottom to point to itself. + * + * See comments in TraceDelayedChildren. + */ + a->finalizable.untracedThings = bit; + if (a->prevUntracedPage == 0) { + if (!rt->gcUntracedArenaStackTop) { + /* Stack was empty, mark the arena as the bottom element. */ + a->prevUntracedPage = ARENA_INFO_TO_PAGE(a); + } else { + JS_ASSERT(rt->gcUntracedArenaStackTop->prevUntracedPage != 0); + a->prevUntracedPage = + ARENA_INFO_TO_PAGE(rt->gcUntracedArenaStackTop); + } + rt->gcUntracedArenaStackTop = a; + } + } + JS_ASSERT(rt->gcUntracedArenaStackTop); +} + +static void +TraceDelayedChildren(JSTracer *trc) +{ + JSRuntime *rt; + JSGCArenaInfo *a, *aprev; + uint32 thingSize, traceKind; + uint32 thingsPerUntracedBit; + uint32 untracedBitIndex, thingIndex, indexLimit, endIndex; + JSGCThing *thing; + uint8 *flagp; + + rt = trc->context->runtime; + a = rt->gcUntracedArenaStackTop; + if (!a) { + JS_ASSERT(rt->gcTraceLaterCount == 0); + return; + } + + for (;;) { + /* + * The following assert verifies that the current arena belongs to the + * untraced stack, since DelayTracingChildren ensures that even for + * stack's bottom prevUntracedPage != 0 but rather points to itself. + */ + JS_ASSERT(a->prevUntracedPage != 0); + JS_ASSERT(rt->gcUntracedArenaStackTop->prevUntracedPage != 0); + thingSize = a->list->thingSize; + traceKind = GetFinalizableArenaTraceKind(a); + indexLimit = THINGS_PER_ARENA(thingSize); + thingsPerUntracedBit = THINGS_PER_UNTRACED_BIT(thingSize); + + /* + * We cannot use do-while loop here as a->untracedThings can be zero + * before the loop as a leftover from the previous iterations. See + * comments after the loop. + */ + while (a->finalizable.untracedThings != 0) { + untracedBitIndex = JS_FLOOR_LOG2W(a->finalizable.untracedThings); + a->finalizable.untracedThings &= + ~((jsuword)1 << untracedBitIndex); + thingIndex = untracedBitIndex * thingsPerUntracedBit; + endIndex = thingIndex + thingsPerUntracedBit; + + /* + * endIndex can go beyond the last allocated thing as the real + * limit can be "inside" the bit. + */ + if (endIndex > indexLimit) + endIndex = indexLimit; + JS_ASSERT(thingIndex < indexLimit); + + do { + /* + * Skip free or already traced things that share the bit + * with untraced ones. + */ + flagp = THING_FLAGP(a, thingIndex); + if (!(*flagp & GCF_CHILDREN)) + continue; + *flagp &= ~GCF_CHILDREN; +#ifdef DEBUG + JS_ASSERT(rt->gcTraceLaterCount != 0); + --rt->gcTraceLaterCount; +#endif + thing = FLAGP_TO_THING(flagp, thingSize); + JS_TraceChildren(trc, thing, traceKind); + } while (++thingIndex != endIndex); + } + + /* + * We finished tracing of all things in the the arena but we can only + * pop it from the stack if the arena is the stack's top. + * + * When JS_TraceChildren from the above calls JS_CallTracer that in + * turn on low C stack calls DelayTracingChildren and the latter + * pushes new arenas to the untraced stack, we have to skip popping + * of this arena until it becomes the top of the stack again. + */ + if (a == rt->gcUntracedArenaStackTop) { + aprev = ARENA_PAGE_TO_INFO(a->prevUntracedPage); + a->prevUntracedPage = 0; + if (a == aprev) { + /* + * prevUntracedPage points to itself and we reached the + * bottom of the stack. + */ + break; + } + rt->gcUntracedArenaStackTop = a = aprev; + } else { + a = rt->gcUntracedArenaStackTop; + } + } + JS_ASSERT(rt->gcUntracedArenaStackTop); + JS_ASSERT(rt->gcUntracedArenaStackTop->prevUntracedPage == 0); + rt->gcUntracedArenaStackTop = NULL; + JS_ASSERT(rt->gcTraceLaterCount == 0); +} + +JS_PUBLIC_API(void) +JS_CallTracer(JSTracer *trc, void *thing, uint32 kind) +{ + JSContext *cx; + JSRuntime *rt; + JSGCArenaInfo *a; + uintN index; + uint8 *flagp; + + JS_ASSERT(thing); + JS_ASSERT(JS_IS_VALID_TRACE_KIND(kind)); + JS_ASSERT(trc->debugPrinter || trc->debugPrintArg); + + if (!IS_GC_MARKING_TRACER(trc)) { + trc->callback(trc, thing, kind); + goto out; + } + + cx = trc->context; + rt = cx->runtime; + JS_ASSERT(rt->gcMarkingTracer == trc); + JS_ASSERT(rt->gcLevel > 0); + + /* + * Optimize for string and double as their size is known and their tracing + * is not recursive. + */ + switch (kind) { + case JSTRACE_DOUBLE: + a = THING_TO_ARENA(thing); + JS_ASSERT(!a->list); + if (!a->hasMarkedDoubles) { + ClearDoubleArenaFlags(a); + a->hasMarkedDoubles = true; + } + index = DOUBLE_THING_TO_INDEX(thing); + JS_SET_BIT(DOUBLE_ARENA_BITMAP(a), index); + goto out; + + case JSTRACE_STRING: + for (;;) { + if (JSString::isStatic(thing)) + goto out; + a = THING_TO_ARENA(thing); + flagp = THING_FLAGP(a, THING_TO_INDEX(thing, sizeof(JSString))); + JS_ASSERT(kind == GetFinalizableArenaTraceKind(a)); + if (!((JSString *) thing)->isDependent()) { + *flagp |= GCF_MARK; + goto out; + } + if (*flagp & GCF_MARK) + goto out; + *flagp |= GCF_MARK; + thing = ((JSString *) thing)->dependentBase(); + } + /* NOTREACHED */ + } + + JS_ASSERT(kind == GetFinalizableArenaTraceKind(THING_TO_ARENA(thing))); + flagp = GetGCThingFlags(thing); + if (*flagp & GCF_MARK) + goto out; + + *flagp |= GCF_MARK; + if (!cx->insideGCMarkCallback) { + /* + * With JS_GC_ASSUME_LOW_C_STACK defined the mark phase of GC always + * uses the non-recursive code that otherwise would be called only on + * a low C stack condition. + */ +#ifdef JS_GC_ASSUME_LOW_C_STACK +# define RECURSION_TOO_DEEP() JS_TRUE +#else + int stackDummy; +# define RECURSION_TOO_DEEP() (!JS_CHECK_STACK_SIZE(cx, stackDummy)) +#endif + if (RECURSION_TOO_DEEP()) + DelayTracingChildren(rt, flagp); + else + JS_TraceChildren(trc, thing, kind); + } else { + /* + * For API compatibility we allow for the callback to assume that + * after it calls JS_MarkGCThing for the last time, the callback can + * start to finalize its own objects that are only referenced by + * unmarked GC things. + * + * Since we do not know which call from inside the callback is the + * last, we ensure that children of all marked things are traced and + * call TraceDelayedChildren(trc) after tracing the thing. + * + * As TraceDelayedChildren unconditionally invokes JS_TraceChildren + * for the things with untraced children, calling DelayTracingChildren + * is useless here. Hence we always trace thing's children even with a + * low native stack. + */ + cx->insideGCMarkCallback = JS_FALSE; + JS_TraceChildren(trc, thing, kind); + TraceDelayedChildren(trc); + cx->insideGCMarkCallback = JS_TRUE; + } + + out: +#ifdef DEBUG + trc->debugPrinter = NULL; + trc->debugPrintArg = NULL; +#endif + return; /* to avoid out: right_curl when DEBUG is not defined */ +} + +void +js_CallValueTracerIfGCThing(JSTracer *trc, jsval v) +{ + void *thing; + uint32 kind; + + if (JSVAL_IS_DOUBLE(v) || JSVAL_IS_STRING(v)) { + thing = JSVAL_TO_TRACEABLE(v); + kind = JSVAL_TRACE_KIND(v); + JS_ASSERT(kind == js_GetGCThingTraceKind(JSVAL_TO_GCTHING(v))); + } else if (JSVAL_IS_OBJECT(v) && v != JSVAL_NULL) { + /* v can be an arbitrary GC thing reinterpreted as an object. */ + thing = JSVAL_TO_OBJECT(v); + kind = js_GetGCThingTraceKind(thing); + } else { + return; + } + JS_CallTracer(trc, thing, kind); +} + +static JSDHashOperator +gc_root_traversal(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 num, + void *arg) +{ + JSGCRootHashEntry *rhe = (JSGCRootHashEntry *)hdr; + JSTracer *trc = (JSTracer *)arg; + jsval *rp = (jsval *)rhe->root; + jsval v = *rp; + + /* Ignore null reference, scalar values, and static strings. */ + if (!JSVAL_IS_NULL(v) && + JSVAL_IS_GCTHING(v) && + !JSString::isStatic(JSVAL_TO_GCTHING(v))) { +#ifdef DEBUG + bool root_points_to_gcArenaList = false; + jsuword thing = (jsuword) JSVAL_TO_GCTHING(v); + JSRuntime *rt = trc->context->runtime; + for (unsigned i = 0; i != FINALIZE_LIMIT; i++) { + JSGCArenaList *arenaList = &rt->gcArenaList[i]; + size_t thingSize = arenaList->thingSize; + size_t limit = THINGS_PER_ARENA(thingSize) * thingSize; + for (JSGCArenaInfo *a = arenaList->head; a; a = a->prev) { + if (thing - ARENA_INFO_TO_START(a) < limit) { + root_points_to_gcArenaList = true; + break; + } + } + } + if (!root_points_to_gcArenaList) { + for (JSGCArenaInfo *a = rt->gcDoubleArenaList.head; a; a = a->prev) { + if (thing - ARENA_INFO_TO_START(a) < + DOUBLES_PER_ARENA * sizeof(jsdouble)) { + root_points_to_gcArenaList = true; + break; + } + } + } + if (!root_points_to_gcArenaList && rhe->name) { + fprintf(stderr, +"JS API usage error: the address passed to JS_AddNamedRoot currently holds an\n" +"invalid jsval. This is usually caused by a missing call to JS_RemoveRoot.\n" +"The root's name is \"%s\".\n", + rhe->name); + } + JS_ASSERT(root_points_to_gcArenaList); +#endif + JS_SET_TRACING_NAME(trc, rhe->name ? rhe->name : "root"); + js_CallValueTracerIfGCThing(trc, v); + } + + return JS_DHASH_NEXT; +} + +static JSDHashOperator +gc_lock_traversal(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 num, + void *arg) +{ + JSGCLockHashEntry *lhe = (JSGCLockHashEntry *)hdr; + void *thing = (void *)lhe->thing; + JSTracer *trc = (JSTracer *)arg; + uint32 traceKind; + + JS_ASSERT(lhe->count >= 1); + traceKind = js_GetGCThingTraceKind(thing); + JS_CALL_TRACER(trc, thing, traceKind, "locked object"); + return JS_DHASH_NEXT; +} + +#define TRACE_JSVALS(trc, len, vec, name) \ + JS_BEGIN_MACRO \ + jsval _v, *_vp, *_end; \ + \ + for (_vp = vec, _end = _vp + len; _vp < _end; _vp++) { \ + _v = *_vp; \ + if (JSVAL_IS_TRACEABLE(_v)) { \ + JS_SET_TRACING_INDEX(trc, name, _vp - (vec)); \ + JS_CallTracer(trc, JSVAL_TO_TRACEABLE(_v), \ + JSVAL_TRACE_KIND(_v)); \ + } \ + } \ + JS_END_MACRO + +void +js_TraceStackFrame(JSTracer *trc, JSStackFrame *fp) +{ + uintN nslots, minargs, skip; + + if (fp->callobj) + JS_CALL_OBJECT_TRACER(trc, fp->callobj, "call"); + if (fp->argsobj) + JS_CALL_OBJECT_TRACER(trc, JSVAL_TO_OBJECT(fp->argsobj), "arguments"); + if (fp->varobj) + JS_CALL_OBJECT_TRACER(trc, fp->varobj, "variables"); + if (fp->script) { + js_TraceScript(trc, fp->script); + + /* fp->slots is null for watch pseudo-frames, see js_watch_set. */ + if (fp->slots) { + /* + * Don't mark what has not been pushed yet, or what has been + * popped already. + */ + if (fp->regs && fp->regs->sp) { + nslots = (uintN) (fp->regs->sp - fp->slots); + JS_ASSERT(nslots >= fp->script->nfixed); + } else { + nslots = fp->script->nfixed; + } + TRACE_JSVALS(trc, nslots, fp->slots, "slot"); + } + } else { + JS_ASSERT(!fp->slots); + JS_ASSERT(!fp->regs); + } + + /* Allow for primitive this parameter due to JSFUN_THISP_* flags. */ + JS_CALL_VALUE_TRACER(trc, fp->thisv, "this"); + + if (fp->argv) { + JS_CALL_VALUE_TRACER(trc, fp->calleeValue(), "callee"); + nslots = fp->argc; + skip = 0; + if (fp->fun) { + minargs = FUN_MINARGS(fp->fun); + if (minargs > nslots) + nslots = minargs; + if (!FUN_INTERPRETED(fp->fun)) { + JS_ASSERT(!(fp->fun->flags & JSFUN_FAST_NATIVE)); + nslots += fp->fun->u.n.extra; + } + if (fp->fun->flags & JSFRAME_ROOTED_ARGV) + skip = 2 + fp->argc; + } + TRACE_JSVALS(trc, 2 + nslots - skip, fp->argv - 2 + skip, "operand"); + } + + JS_CALL_VALUE_TRACER(trc, fp->rval, "rval"); + if (fp->scopeChain) + JS_CALL_OBJECT_TRACER(trc, fp->scopeChain, "scope chain"); +} + +void +JSWeakRoots::mark(JSTracer *trc) +{ +#ifdef DEBUG + const char * const newbornNames[] = { + "newborn_object", /* FINALIZE_OBJECT */ + "newborn_function", /* FINALIZE_FUNCTION */ +#if JS_HAS_XML_SUPPORT + "newborn_xml", /* FINALIZE_XML */ +#endif + "newborn_string", /* FINALIZE_STRING */ + "newborn_external_string0", /* FINALIZE_EXTERNAL_STRING0 */ + "newborn_external_string1", /* FINALIZE_EXTERNAL_STRING1 */ + "newborn_external_string2", /* FINALIZE_EXTERNAL_STRING2 */ + "newborn_external_string3", /* FINALIZE_EXTERNAL_STRING3 */ + "newborn_external_string4", /* FINALIZE_EXTERNAL_STRING4 */ + "newborn_external_string5", /* FINALIZE_EXTERNAL_STRING5 */ + "newborn_external_string6", /* FINALIZE_EXTERNAL_STRING6 */ + "newborn_external_string7", /* FINALIZE_EXTERNAL_STRING7 */ + }; +#endif + for (size_t i = 0; i != JS_ARRAY_LENGTH(finalizableNewborns); ++i) { + void *newborn = finalizableNewborns[i]; + if (newborn) { + JS_CALL_TRACER(trc, newborn, GetFinalizableTraceKind(i), + newbornNames[i]); + } + } + if (newbornDouble) + JS_CALL_DOUBLE_TRACER(trc, newbornDouble, "newborn_double"); + JS_CALL_VALUE_TRACER(trc, lastAtom, "lastAtom"); + JS_SET_TRACING_NAME(trc, "lastInternalResult"); + js_CallValueTracerIfGCThing(trc, lastInternalResult); +} + +JS_REQUIRES_STACK JS_FRIEND_API(void) +js_TraceContext(JSTracer *trc, JSContext *acx) +{ + JSStackFrame *fp, *nextChain; + JSStackHeader *sh; + JSTempValueRooter *tvr; + + if (IS_GC_MARKING_TRACER(trc)) { + +#define FREE_OLD_ARENAS(pool) \ + JS_BEGIN_MACRO \ + int64 _age; \ + JSArena * _a = (pool).current; \ + if (_a == (pool).first.next && \ + _a->avail == _a->base + sizeof(int64)) { \ + _age = JS_Now() - *(int64 *) _a->base; \ + if (_age > (int64) acx->runtime->gcEmptyArenaPoolLifespan * \ + 1000) \ + JS_FreeArenaPool(&(pool)); \ + } \ + JS_END_MACRO + + /* + * Release the stackPool's arenas if the stackPool has existed for + * longer than the limit specified by gcEmptyArenaPoolLifespan. + */ + FREE_OLD_ARENAS(acx->stackPool); + + /* + * Release the regexpPool's arenas based on the same criterion as for + * the stackPool. + */ + FREE_OLD_ARENAS(acx->regexpPool); + } + + /* + * Iterate frame chain and dormant chains. + * + * (NB: see comment on this whole "dormant" thing in js_Execute.) + * + * Since js_GetTopStackFrame needs to dereference cx->thread to check for + * JIT frames, we check for non-null thread here and avoid null checks + * there. See bug 471197. + */ +#ifdef JS_THREADSAFE + if (acx->thread) +#endif + { + fp = js_GetTopStackFrame(acx); + nextChain = acx->dormantFrameChain; + if (!fp) + goto next_chain; + + /* The top frame must not be dormant. */ + JS_ASSERT(!fp->dormantNext); + for (;;) { + do { + js_TraceStackFrame(trc, fp); + } while ((fp = fp->down) != NULL); + + next_chain: + if (!nextChain) + break; + fp = nextChain; + nextChain = nextChain->dormantNext; + } + } + + /* Mark other roots-by-definition in acx. */ + if (acx->globalObject && !JS_HAS_OPTION(acx, JSOPTION_UNROOTED_GLOBAL)) + JS_CALL_OBJECT_TRACER(trc, acx->globalObject, "global object"); + acx->weakRoots.mark(trc); + if (acx->throwing) { + JS_CALL_VALUE_TRACER(trc, acx->exception, "exception"); + } else { + /* Avoid keeping GC-ed junk stored in JSContext.exception. */ + acx->exception = JSVAL_NULL; + } + + for (sh = acx->stackHeaders; sh; sh = sh->down) { + METER(trc->context->runtime->gcStats.stackseg++); + METER(trc->context->runtime->gcStats.segslots += sh->nslots); + TRACE_JSVALS(trc, sh->nslots, JS_STACK_SEGMENT(sh), "stack"); + } + + for (tvr = acx->tempValueRooters; tvr; tvr = tvr->down) { + switch (tvr->count) { + case JSTVU_SINGLE: + JS_SET_TRACING_NAME(trc, "tvr->u.value"); + js_CallValueTracerIfGCThing(trc, tvr->u.value); + break; + case JSTVU_TRACE: + tvr->u.trace(trc, tvr); + break; + case JSTVU_SPROP: + tvr->u.sprop->trace(trc); + break; + case JSTVU_WEAK_ROOTS: + tvr->u.weakRoots->mark(trc); + break; + case JSTVU_COMPILER: + tvr->u.compiler->trace(trc); + break; + case JSTVU_SCRIPT: + js_TraceScript(trc, tvr->u.script); + break; + case JSTVU_ENUMERATOR: + static_cast(tvr)->mark(trc); + break; + default: + JS_ASSERT(tvr->count >= 0); + TRACE_JSVALS(trc, tvr->count, tvr->u.array, "tvr->u.array"); + } + } + + if (acx->sharpObjectMap.depth > 0) + js_TraceSharpMap(trc, &acx->sharpObjectMap); + + js_TraceRegExpStatics(trc, acx); + +#ifdef JS_TRACER + InterpState* state = acx->interpState; + while (state) { + if (state->nativeVp) + TRACE_JSVALS(trc, state->nativeVpLen, state->nativeVp, "nativeVp"); + state = state->prev; + } +#endif +} + +JS_REQUIRES_STACK void +js_TraceRuntime(JSTracer *trc, JSBool allAtoms) +{ + JSRuntime *rt = trc->context->runtime; + JSContext *iter, *acx; + + JS_DHashTableEnumerate(&rt->gcRootsHash, gc_root_traversal, trc); + if (rt->gcLocksHash) + JS_DHashTableEnumerate(rt->gcLocksHash, gc_lock_traversal, trc); + js_TraceAtomState(trc, allAtoms); + js_TraceRuntimeNumberState(trc); + js_MarkTraps(trc); + + iter = NULL; + while ((acx = js_ContextIterator(rt, JS_TRUE, &iter)) != NULL) + js_TraceContext(trc, acx); + + js_TraceThreads(rt, trc); + + if (rt->gcExtraRootsTraceOp) + rt->gcExtraRootsTraceOp(trc, rt->gcExtraRootsData); + +#ifdef JS_TRACER + for (int i = 0; i < JSBUILTIN_LIMIT; i++) { + if (rt->builtinFunctions[i]) + JS_CALL_OBJECT_TRACER(trc, rt->builtinFunctions[i], "builtin function"); + } +#endif +} + +void +js_TriggerGC(JSContext *cx, JSBool gcLocked) +{ + JSRuntime *rt = cx->runtime; + +#ifdef JS_THREADSAFE + JS_ASSERT(cx->requestDepth > 0); +#endif + JS_ASSERT(!rt->gcRunning); + if (rt->gcIsNeeded) + return; + + js_TriggerAllOperationCallbacks(rt, gcLocked); +} + +static void +ProcessSetSlotRequest(JSContext *cx, JSSetSlotRequest *ssr) +{ + JSObject *obj = ssr->obj; + JSObject *pobj = ssr->pobj; + uint32 slot = ssr->slot; + + while (pobj) { + pobj = js_GetWrappedObject(cx, pobj); + if (pobj == obj) { + ssr->cycle = true; + return; + } + pobj = JSVAL_TO_OBJECT(STOBJ_GET_SLOT(pobj, slot)); + } + + pobj = ssr->pobj; + if (slot == JSSLOT_PROTO) { + obj->setProto(pobj); + } else { + JS_ASSERT(slot == JSSLOT_PARENT); + obj->setParent(pobj); + } +} + +void +js_DestroyScriptsToGC(JSContext *cx, JSThreadData *data) +{ + JSScript **listp, *script; + + for (size_t i = 0; i != JS_ARRAY_LENGTH(data->scriptsToGC); ++i) { + listp = &data->scriptsToGC[i]; + while ((script = *listp) != NULL) { + *listp = script->u.nextToGC; + script->u.nextToGC = NULL; + js_DestroyScript(cx, script); + } + } +} + +static inline void +FinalizeGCThing(JSContext *cx, JSObject *obj, unsigned thingKind) +{ + JS_ASSERT(thingKind == FINALIZE_FUNCTION || thingKind == FINALIZE_OBJECT); + + /* Cope with stillborn objects that have no map. */ + if (!obj->map) + return; + + if (JS_UNLIKELY(cx->debugHooks->objectHook != NULL)) { + cx->debugHooks->objectHook(cx, obj, JS_FALSE, + cx->debugHooks->objectHookData); + } + + /* Finalize obj first, in case it needs map and slots. */ + JSClass *clasp = STOBJ_GET_CLASS(obj); + if (clasp->finalize) + clasp->finalize(cx, obj); + +#ifdef INCLUDE_MOZILLA_DTRACE + if (JAVASCRIPT_OBJECT_FINALIZE_ENABLED()) + jsdtrace_object_finalize(obj); +#endif + + if (JS_LIKELY(OBJ_IS_NATIVE(obj))) + OBJ_SCOPE(obj)->drop(cx, obj); + js_FreeSlots(cx, obj); +} + +static inline void +FinalizeGCThing(JSContext *cx, JSFunction *fun, unsigned thingKind) +{ + JS_ASSERT(thingKind == FINALIZE_FUNCTION); + ::FinalizeGCThing(cx, FUN_OBJECT(fun), thingKind); +} + +#if JS_HAS_XML_SUPPORT +static inline void +FinalizeGCThing(JSContext *cx, JSXML *xml, unsigned thingKind) +{ + js_FinalizeXML(cx, xml); +} +#endif + +JS_STATIC_ASSERT(JS_EXTERNAL_STRING_LIMIT == 8); +static JSStringFinalizeOp str_finalizers[JS_EXTERNAL_STRING_LIMIT] = { + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL +}; + +intN +js_ChangeExternalStringFinalizer(JSStringFinalizeOp oldop, + JSStringFinalizeOp newop) +{ + for (uintN i = 0; i != JS_ARRAY_LENGTH(str_finalizers); i++) { + if (str_finalizers[i] == oldop) { + str_finalizers[i] = newop; + return intN(i); + } + } + return -1; +} + +/* + * cx is NULL when we are called from js_FinishAtomState to force the + * finalization of the permanently interned strings. + */ +static void +FinalizeString(JSRuntime *rt, JSString *str, unsigned thingKind, JSContext *cx) +{ + jschar *chars; + JSBool valid; + JSStringFinalizeOp finalizer; + + JS_RUNTIME_UNMETER(rt, liveStrings); + JS_ASSERT(!JSString::isStatic(str)); + JS_ASSERT(IsFinalizableStringKind(thingKind)); + if (str->isDependent()) { + /* A dependent string can not be external and must be valid. */ + JS_ASSERT(thingKind == FINALIZE_STRING); + JS_ASSERT(str->dependentBase()); + JS_RUNTIME_UNMETER(rt, liveDependentStrings); + valid = JS_TRUE; + } else { + /* A stillborn string has null chars, so is not valid. */ + chars = str->flatChars(); + valid = (chars != NULL); + if (valid) { + if (thingKind == FINALIZE_STRING) { + if (cx) + cx->free(chars); + else + rt->free(chars); + } else { + unsigned type = thingKind - FINALIZE_EXTERNAL_STRING0; + JS_ASSERT(type < JS_ARRAY_LENGTH(str_finalizers)); + finalizer = str_finalizers[type]; + if (finalizer) { + /* + * Assume that the finalizer for the permanently interned + * string knows how to deal with null context. + */ + finalizer(cx, str); + } + } + } + } + if (valid && str->isDeflated()) + js_PurgeDeflatedStringCache(rt, str); +} + +static inline void +FinalizeGCThing(JSContext *cx, JSString *str, unsigned thingKind) +{ + return FinalizeString(cx->runtime, str, thingKind, cx); +} + +void +js_FinalizeStringRT(JSRuntime *rt, JSString *str) +{ + JS_ASSERT(!JSString::isStatic(str)); + + unsigned thingKind = THING_TO_ARENA(str)->list->thingKind; + FinalizeString(rt, str, thingKind, NULL); +} + +template +static void +FinalizeArenaList(JSContext *cx, unsigned thingKind, + JSGCArenaInfo **emptyArenas) +{ + JSGCArenaList *arenaList = &cx->runtime->gcArenaList[thingKind]; + JS_ASSERT(sizeof(T) == arenaList->thingSize); + + JSGCArenaInfo **ap = &arenaList->head; + JSGCArenaInfo *a = *ap; + if (!a) + return; + +#ifdef JS_GCMETER + uint32 nlivearenas = 0, nkilledarenas = 0, nthings = 0; +#endif + for (;;) { + JS_ASSERT(a->list == arenaList); + JS_ASSERT(a->prevUntracedPage == 0); + JS_ASSERT(a->finalizable.untracedThings == 0); + + JSGCThing *freeList = NULL; + JSGCThing **tailp = &freeList; + bool allClear = true; + uint8 *flagp = THING_FLAGP(a, 0); + JSGCThing *thing = + reinterpret_cast(ARENA_INFO_TO_START(a)); + JSGCThing *thingsEnd = + reinterpret_cast(ARENA_INFO_TO_START(a) + + THINGS_PER_ARENA(sizeof(T)) * + sizeof(T)); + JSGCThing* nextFree = a->finalizable.freeList + ? a->finalizable.freeList + : thingsEnd; + for (;; thing = NextThing(thing, sizeof(T)), --flagp) { + if (thing == nextFree) { + if (thing == thingsEnd) + break; + JS_ASSERT(!*flagp); + nextFree = nextFree->link; + if (!nextFree) { + nextFree = thingsEnd; + } else { + JS_ASSERT(thing < nextFree); + JS_ASSERT(nextFree < thingsEnd); + } + } else { + JS_ASSERT(!(*flagp & ~(GCF_MARK | GCF_LOCK))); + if (*flagp) { + *flagp &= ~GCF_MARK; + allClear = false; + METER(nthings++); + continue; + } + + /* + * Call the finalizer with cleared flags so + * js_IsAboutToBeFinalized will be false. + */ + *flagp = 0; + ::FinalizeGCThing(cx, reinterpret_cast(thing), thingKind); +#ifdef DEBUG + memset(thing, JS_FREE_PATTERN, sizeof(T)); +#endif + } + *tailp = thing; + tailp = &thing->link; + } + +#ifdef DEBUG + /* Check that the free list is consistent. */ + unsigned nfree = 0; + if (freeList) { + JS_ASSERT(tailp != &freeList); + JSGCThing *thing = freeList; + for (;;) { + ++nfree; + if (&thing->link == tailp) + break; + JS_ASSERT(thing < thing->link); + thing = thing->link; + } + } +#endif + if (allClear) { + /* + * Forget just assembled free list head for the arena and + * add the arena itself to the destroy list. + */ + JS_ASSERT(nfree == THINGS_PER_ARENA(sizeof(T))); + *ap = a->prev; + a->prev = *emptyArenas; + *emptyArenas = a; + METER(nkilledarenas++); + } else { + JS_ASSERT(nfree < THINGS_PER_ARENA(sizeof(T))); + *tailp = NULL; + a->finalizable.freeList = freeList; + ap = &a->prev; + METER(nlivearenas++); + } + if (!(a = *ap)) + break; + } + arenaList->cursor = arenaList->head; + + METER(UpdateArenaStats(&rt->gcStats.arenaStats[thingKind], + nlivearenas, nkilledarenas, nthings)); +} + +/* + * The gckind flag bit GC_LOCK_HELD indicates a call from js_NewGCThing with + * rt->gcLock already held, so the lock should be kept on return. + */ +void +js_GC(JSContext *cx, JSGCInvocationKind gckind) +{ + JSRuntime *rt; + JSBool keepAtoms; + JSGCCallback callback; + JSTracer trc; + JSGCArenaInfo *emptyArenas, *a, **ap; +#ifdef JS_THREADSAFE + uint32 requestDebit; +#endif +#ifdef JS_GCMETER + uint32 nlivearenas, nkilledarenas, nthings; +#endif + + JS_ASSERT_IF(gckind == GC_LAST_DITCH, !JS_ON_TRACE(cx)); + rt = cx->runtime; + +#ifdef JS_THREADSAFE + /* + * We allow js_GC calls outside a request but the context must be bound + * to the current thread. + */ + JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread)); + + /* Avoid deadlock. */ + JS_ASSERT(!JS_IS_RUNTIME_LOCKED(rt)); +#endif + + if (gckind & GC_KEEP_ATOMS) { + /* + * The set slot request and last ditch GC kinds preserve all atoms and + * weak roots. + */ + keepAtoms = JS_TRUE; + } else { + /* Keep atoms when a suspended compile is running on another context. */ + keepAtoms = (rt->gcKeepAtoms != 0); + JS_CLEAR_WEAK_ROOTS(&cx->weakRoots); + } + + /* + * Don't collect garbage if the runtime isn't up, and cx is not the last + * context in the runtime. The last context must force a GC, and nothing + * should suppress that final collection or there may be shutdown leaks, + * or runtime bloat until the next context is created. + */ + if (rt->state != JSRTS_UP && gckind != GC_LAST_CONTEXT) + return; + + restart_at_beginning: + /* + * Let the API user decide to defer a GC if it wants to (unless this + * is the last context). Invoke the callback regardless. Sample the + * callback in case we are freely racing with a JS_SetGCCallback{,RT} on + * another thread. + */ + if (gckind != GC_SET_SLOT_REQUEST && (callback = rt->gcCallback)) { + JSBool ok; + + if (gckind & GC_LOCK_HELD) + JS_UNLOCK_GC(rt); + ok = callback(cx, JSGC_BEGIN); + if (gckind & GC_LOCK_HELD) + JS_LOCK_GC(rt); + if (!ok && gckind != GC_LAST_CONTEXT) { + /* + * It's possible that we've looped back to this code from the 'goto + * restart_at_beginning' below in the GC_SET_SLOT_REQUEST code and + * that rt->gcLevel is now 0. Don't return without notifying! + */ + if (rt->gcLevel == 0 && (gckind & GC_LOCK_HELD)) + JS_NOTIFY_GC_DONE(rt); + return; + } + } + + /* Lock out other GC allocator and collector invocations. */ + if (!(gckind & GC_LOCK_HELD)) + JS_LOCK_GC(rt); + + METER(rt->gcStats.poke++); + rt->gcPoke = JS_FALSE; + +#ifdef JS_THREADSAFE + /* + * Check if the GC is already running on this or another thread and + * delegate the job to it. + */ + if (rt->gcLevel > 0) { + JS_ASSERT(rt->gcThread); + + /* Bump gcLevel to restart the current GC, so it finds new garbage. */ + rt->gcLevel++; + METER_UPDATE_MAX(rt->gcStats.maxlevel, rt->gcLevel); + + /* + * If the GC runs on another thread, temporarily suspend the current + * request and wait until the GC is done. + */ + if (rt->gcThread != cx->thread) { + requestDebit = js_DiscountRequestsForGC(cx); + js_RecountRequestsAfterGC(rt, requestDebit); + } + if (!(gckind & GC_LOCK_HELD)) + JS_UNLOCK_GC(rt); + return; + } + + /* No other thread is in GC, so indicate that we're now in GC. */ + rt->gcLevel = 1; + rt->gcThread = cx->thread; + + /* + * Notify all operation callbacks, which will give them a chance to + * yield their current request. Contexts that are not currently + * executing will perform their callback at some later point, + * which then will be unnecessary, but harmless. + */ + js_NudgeOtherContexts(cx); + + /* + * Discount all the requests on the current thread from contributing + * to rt->requestCount before we wait for all other requests to finish. + * JS_NOTIFY_REQUEST_DONE, which will wake us up, is only called on + * rt->requestCount transitions to 0. + */ + requestDebit = js_CountThreadRequests(cx); + JS_ASSERT_IF(cx->requestDepth != 0, requestDebit >= 1); + rt->requestCount -= requestDebit; + while (rt->requestCount > 0) + JS_AWAIT_REQUEST_DONE(rt); + rt->requestCount += requestDebit; + +#else /* !JS_THREADSAFE */ + + /* Bump gcLevel and return rather than nest; the outer gc will restart. */ + rt->gcLevel++; + METER_UPDATE_MAX(rt->gcStats.maxlevel, rt->gcLevel); + if (rt->gcLevel > 1) + return; + +#endif /* !JS_THREADSAFE */ + + /* + * Set rt->gcRunning here within the GC lock, and after waiting for any + * active requests to end, so that new requests that try to JS_AddRoot, + * JS_RemoveRoot, or JS_RemoveRootRT block in JS_BeginRequest waiting for + * rt->gcLevel to drop to zero, while request-less calls to the *Root* + * APIs block in js_AddRoot or js_RemoveRoot (see above in this file), + * waiting for GC to finish. + */ + rt->gcRunning = JS_TRUE; + + if (gckind == GC_SET_SLOT_REQUEST) { + JSSetSlotRequest *ssr; + + while ((ssr = rt->setSlotRequests) != NULL) { + rt->setSlotRequests = ssr->next; + JS_UNLOCK_GC(rt); + ssr->next = NULL; + ProcessSetSlotRequest(cx, ssr); + JS_LOCK_GC(rt); + } + + /* + * We assume here that killing links to parent and prototype objects + * does not create garbage (such objects typically are long-lived and + * widely shared, e.g. global objects, Function.prototype, etc.). We + * collect garbage only if a racing thread attempted GC and is waiting + * for us to finish (gcLevel > 1) or if someone already poked us. + */ + if (rt->gcLevel == 1 && !rt->gcPoke && !rt->gcIsNeeded) + goto done_running; + + rt->gcLevel = 0; + rt->gcPoke = JS_FALSE; + rt->gcRunning = JS_FALSE; +#ifdef JS_THREADSAFE + rt->gcThread = NULL; +#endif + gckind = GC_LOCK_HELD; + goto restart_at_beginning; + } + + JS_UNLOCK_GC(rt); + +#ifdef JS_TRACER + if (JS_ON_TRACE(cx)) + goto out; +#endif + VOUCH_HAVE_STACK(); + + /* Clear gcIsNeeded now, when we are about to start a normal GC cycle. */ + rt->gcIsNeeded = JS_FALSE; + + /* Reset malloc counter. */ + rt->resetGCMallocBytes(); + +#ifdef JS_DUMP_SCOPE_METERS + { extern void js_DumpScopeMeters(JSRuntime *rt); + js_DumpScopeMeters(rt); + } +#endif + +#ifdef JS_TRACER + js_PurgeJITOracle(); +#endif + + restart: + rt->gcNumber++; + JS_ASSERT(!rt->gcUntracedArenaStackTop); + JS_ASSERT(rt->gcTraceLaterCount == 0); + + /* + * Reset the property cache's type id generator so we can compress ids. + * Same for the protoHazardShape proxy-shape standing in for all object + * prototypes having readonly or setter properties. + */ + if (rt->shapeGen & SHAPE_OVERFLOW_BIT +#ifdef JS_GC_ZEAL + || rt->gcZeal >= 1 +#endif + ) { + rt->gcRegenShapes = true; + rt->gcRegenShapesScopeFlag ^= JSScope::SHAPE_REGEN; + rt->shapeGen = 0; + rt->protoHazardShape = 0; + } + + js_PurgeThreads(cx); +#ifdef JS_TRACER + if (gckind == GC_LAST_CONTEXT) { + /* Clear builtin functions, which are recreated on demand. */ + memset(rt->builtinFunctions, 0, sizeof rt->builtinFunctions); + } +#endif + + /* + * Mark phase. + */ + JS_TRACER_INIT(&trc, cx, NULL); + rt->gcMarkingTracer = &trc; + JS_ASSERT(IS_GC_MARKING_TRACER(&trc)); + +#ifdef DEBUG + for (a = rt->gcDoubleArenaList.head; a; a = a->prev) + JS_ASSERT(!a->hasMarkedDoubles); +#endif + + js_TraceRuntime(&trc, keepAtoms); + js_MarkScriptFilenames(rt, keepAtoms); + + /* + * Mark children of things that caused too deep recursion during the above + * tracing. + */ + TraceDelayedChildren(&trc); + + JS_ASSERT(!cx->insideGCMarkCallback); + if (rt->gcCallback) { + cx->insideGCMarkCallback = JS_TRUE; + (void) rt->gcCallback(cx, JSGC_MARK_END); + JS_ASSERT(cx->insideGCMarkCallback); + cx->insideGCMarkCallback = JS_FALSE; + } + JS_ASSERT(rt->gcTraceLaterCount == 0); + + rt->gcMarkingTracer = NULL; + +#ifdef JS_THREADSAFE + cx->createDeallocatorTask(); +#endif + + /* + * Sweep phase. + * + * Finalize as we sweep, outside of rt->gcLock but with rt->gcRunning set + * so that any attempt to allocate a GC-thing from a finalizer will fail, + * rather than nest badly and leave the unmarked newborn to be swept. + * + * We first sweep atom state so we can use js_IsAboutToBeFinalized on + * JSString or jsdouble held in a hashtable to check if the hashtable + * entry can be freed. Note that even after the entry is freed, JSObject + * finalizers can continue to access the corresponding jsdouble* and + * JSString* assuming that they are unique. This works since the + * atomization API must not be called during GC. + */ + js_SweepAtomState(cx); + + /* Finalize iterator states before the objects they iterate over. */ + CloseNativeIterators(cx); + + /* Finalize watch points associated with unreachable objects. */ + js_SweepWatchPoints(cx); + +#ifdef DEBUG + /* Save the pre-sweep count of scope-mapped properties. */ + rt->liveScopePropsPreSweep = rt->liveScopeProps; +#endif + + /* + * We finalize JSObject instances before JSString, double and other GC + * things to ensure that object's finalizer can access them even if they + * will be freed. + * + * To minimize the number of checks per each to be freed object and + * function we use separated list finalizers when a debug hook is + * installed. + */ + emptyArenas = NULL; + FinalizeArenaList(cx, FINALIZE_OBJECT, &emptyArenas); + FinalizeArenaList(cx, FINALIZE_FUNCTION, &emptyArenas); +#if JS_HAS_XML_SUPPORT + FinalizeArenaList(cx, FINALIZE_XML, &emptyArenas); +#endif + FinalizeArenaList(cx, FINALIZE_STRING, &emptyArenas); + for (unsigned i = FINALIZE_EXTERNAL_STRING0; + i <= FINALIZE_EXTERNAL_STRING_LAST; + ++i) { + FinalizeArenaList(cx, i, &emptyArenas); + } + + ap = &rt->gcDoubleArenaList.head; + METER((nlivearenas = 0, nkilledarenas = 0, nthings = 0)); + while ((a = *ap) != NULL) { + if (!a->hasMarkedDoubles) { + /* No marked double values in the arena. */ + *ap = a->prev; + a->prev = emptyArenas; + emptyArenas = a; + METER(nkilledarenas++); + } else { + a->hasMarkedDoubles = false; + ap = &a->prev; +#ifdef JS_GCMETER + for (size_t i = 0; i != DOUBLES_PER_ARENA; ++i) { + if (IsMarkedDouble(a, index)) + METER(nthings++); + } + METER(nlivearenas++); +#endif + } + } + METER(UpdateArenaStats(&rt->gcStats.doubleArenaStats, + nlivearenas, nkilledarenas, nthings)); + rt->gcDoubleArenaList.cursor = rt->gcDoubleArenaList.head; + + /* + * Sweep the runtime's property tree after finalizing objects, in case any + * had watchpoints referencing tree nodes. + */ + js_SweepScopeProperties(cx); + + /* + * Sweep script filenames after sweeping functions in the generic loop + * above. In this way when a scripted function's finalizer destroys the + * script and calls rt->destroyScriptHook, the hook can still access the + * script's filename. See bug 323267. + */ + js_SweepScriptFilenames(rt); + + /* + * Destroy arenas after we finished the sweeping so finalizers can safely + * use js_IsAboutToBeFinalized(). + */ + DestroyGCArenas(rt, emptyArenas); + +#ifdef JS_THREADSAFE + cx->submitDeallocatorTask(); +#endif + + if (rt->gcCallback) + (void) rt->gcCallback(cx, JSGC_FINALIZE_END); +#ifdef DEBUG_srcnotesize + { extern void DumpSrcNoteSizeHist(); + DumpSrcNoteSizeHist(); + printf("GC HEAP SIZE %lu\n", (unsigned long)rt->gcBytes); + } +#endif + +#ifdef JS_SCOPE_DEPTH_METER + { static FILE *fp; + if (!fp) + fp = fopen("/tmp/scopedepth.stats", "w"); + + if (fp) { + JS_DumpBasicStats(&rt->protoLookupDepthStats, "proto-lookup depth", fp); + JS_DumpBasicStats(&rt->scopeSearchDepthStats, "scope-search depth", fp); + JS_DumpBasicStats(&rt->hostenvScopeDepthStats, "hostenv scope depth", fp); + JS_DumpBasicStats(&rt->lexicalScopeDepthStats, "lexical scope depth", fp); + + putc('\n', fp); + fflush(fp); + } + } +#endif /* JS_SCOPE_DEPTH_METER */ + +#ifdef JS_DUMP_LOOP_STATS + { static FILE *lsfp; + if (!lsfp) + lsfp = fopen("/tmp/loopstats", "w"); + if (lsfp) { + JS_DumpBasicStats(&rt->loopStats, "loops", lsfp); + fflush(lsfp); + } + } +#endif /* JS_DUMP_LOOP_STATS */ + +#ifdef JS_TRACER +out: +#endif + JS_LOCK_GC(rt); + + /* + * We want to restart GC if js_GC was called recursively or if any of the + * finalizers called js_RemoveRoot or js_UnlockGCThingRT. + */ + if (!JS_ON_TRACE(cx) && (rt->gcLevel > 1 || rt->gcPoke)) { + VOUCH_HAVE_STACK(); + rt->gcLevel = 1; + rt->gcPoke = JS_FALSE; + JS_UNLOCK_GC(rt); + goto restart; + } + + rt->setGCLastBytes(rt->gcBytes); + done_running: + rt->gcLevel = 0; + rt->gcRunning = rt->gcRegenShapes = false; + +#ifdef JS_THREADSAFE + rt->gcThread = NULL; + JS_NOTIFY_GC_DONE(rt); + + /* + * Unlock unless we have GC_LOCK_HELD which requires locked GC on return. + */ + if (!(gckind & GC_LOCK_HELD)) + JS_UNLOCK_GC(rt); +#endif + + /* + * Execute JSGC_END callback outside the lock. Again, sample the callback + * pointer in case it changes, since we are outside of the GC vs. requests + * interlock mechanism here. + */ + if (gckind != GC_SET_SLOT_REQUEST && (callback = rt->gcCallback)) { + JSWeakRoots savedWeakRoots; + JSTempValueRooter tvr; + + if (gckind & GC_KEEP_ATOMS) { + /* + * We allow JSGC_END implementation to force a full GC or allocate + * new GC things. Thus we must protect the weak roots from garbage + * collection and overwrites. + */ + savedWeakRoots = cx->weakRoots; + JS_PUSH_TEMP_ROOT_WEAK_COPY(cx, &savedWeakRoots, &tvr); + JS_KEEP_ATOMS(rt); + JS_UNLOCK_GC(rt); + } + + (void) callback(cx, JSGC_END); + + if (gckind & GC_KEEP_ATOMS) { + JS_LOCK_GC(rt); + JS_UNKEEP_ATOMS(rt); + JS_POP_TEMP_ROOT(cx, &tvr); + } else if (gckind == GC_LAST_CONTEXT && rt->gcPoke) { + /* + * On shutdown iterate until JSGC_END callback stops creating + * garbage. + */ + goto restart_at_beginning; + } + } +} diff --git a/ape-server/deps/js/src/jsgc.h b/ape-server/deps/js/src/jsgc.h new file mode 100755 index 0000000..0ec449d --- /dev/null +++ b/ape-server/deps/js/src/jsgc.h @@ -0,0 +1,451 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsgc_h___ +#define jsgc_h___ +/* + * JS Garbage Collector. + */ +#include "jsprvtd.h" +#include "jspubtd.h" +#include "jsdhash.h" +#include "jsbit.h" +#include "jsutil.h" +#include "jstask.h" + +JS_BEGIN_EXTERN_C + +#define JSTRACE_XML 3 + +/* + * One past the maximum trace kind. + */ +#define JSTRACE_LIMIT 4 + +const uintN JS_EXTERNAL_STRING_LIMIT = 8; + +/* + * Get the type of the external string or -1 if the string was not created + * with JS_NewExternalString. + */ +extern intN +js_GetExternalStringGCType(JSString *str); + +extern JS_FRIEND_API(uint32) +js_GetGCThingTraceKind(void *thing); + +/* + * The sole purpose of the function is to preserve public API compatibility + * in JS_GetStringBytes which takes only single JSString* argument. + */ +JSRuntime* +js_GetGCStringRuntime(JSString *str); + +#if 1 +/* + * Since we're forcing a GC from JS_GC anyway, don't bother wasting cycles + * loading oldval. XXX remove implied force, fix jsinterp.c's "second arg + * ignored", etc. + */ +#define GC_POKE(cx, oldval) ((cx)->runtime->gcPoke = JS_TRUE) +#else +#define GC_POKE(cx, oldval) ((cx)->runtime->gcPoke = JSVAL_IS_GCTHING(oldval)) +#endif + +extern JSBool +js_InitGC(JSRuntime *rt, uint32 maxbytes); + +extern void +js_FinishGC(JSRuntime *rt); + +extern intN +js_ChangeExternalStringFinalizer(JSStringFinalizeOp oldop, + JSStringFinalizeOp newop); + +extern JSBool +js_AddRoot(JSContext *cx, void *rp, const char *name); + +extern JSBool +js_AddRootRT(JSRuntime *rt, void *rp, const char *name); + +extern JSBool +js_RemoveRoot(JSRuntime *rt, void *rp); + +#ifdef DEBUG +extern void +js_DumpNamedRoots(JSRuntime *rt, + void (*dump)(const char *name, void *rp, void *data), + void *data); +#endif + +extern uint32 +js_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data); + +/* Table of pointers with count valid members. */ +typedef struct JSPtrTable { + size_t count; + void **array; +} JSPtrTable; + +extern JSBool +js_RegisterCloseableIterator(JSContext *cx, JSObject *obj); + +/* + * Allocate a new double jsval and store the result in *vp. vp must be a root. + * The function does not copy the result into any weak root. + */ +extern JSBool +js_NewDoubleInRootedValue(JSContext *cx, jsdouble d, jsval *vp); + +/* + * Return a pointer to a new GC-allocated and weakly-rooted jsdouble number, + * or null when the allocation fails. + */ +extern jsdouble * +js_NewWeaklyRootedDouble(JSContext *cx, jsdouble d); + +#ifdef JS_TRACER +extern JSBool +js_ReserveObjects(JSContext *cx, size_t nobjects); +#endif + +extern JSBool +js_LockGCThingRT(JSRuntime *rt, void *thing); + +extern JSBool +js_UnlockGCThingRT(JSRuntime *rt, void *thing); + +extern JSBool +js_IsAboutToBeFinalized(JSContext *cx, void *thing); + +/* + * Macro to test if a traversal is the marking phase of GC to avoid exposing + * ScriptFilenameEntry to traversal implementations. + */ +#define IS_GC_MARKING_TRACER(trc) ((trc)->callback == NULL) + +#if JS_HAS_XML_SUPPORT +# define JS_IS_VALID_TRACE_KIND(kind) ((uint32)(kind) < JSTRACE_LIMIT) +#else +# define JS_IS_VALID_TRACE_KIND(kind) ((uint32)(kind) <= JSTRACE_STRING) +#endif + +/* + * Trace jsval when JSVAL_IS_OBJECT(v) can be a GC thing pointer tagged as a + * jsval. NB: punning an arbitrary JSString * as an untagged (object-tagged) + * jsval no longer works due to static int and unit strings! + */ +extern void +js_CallValueTracerIfGCThing(JSTracer *trc, jsval v); + +extern void +js_TraceStackFrame(JSTracer *trc, JSStackFrame *fp); + +extern JS_REQUIRES_STACK void +js_TraceRuntime(JSTracer *trc, JSBool allAtoms); + +extern JS_REQUIRES_STACK JS_FRIEND_API(void) +js_TraceContext(JSTracer *trc, JSContext *acx); + +/* + * Schedule the GC call at a later safe point. + */ +#ifndef JS_THREADSAFE +# define js_TriggerGC(cx, gcLocked) js_TriggerGC (cx) +#endif + +extern void +js_TriggerGC(JSContext *cx, JSBool gcLocked); + +/* + * Kinds of js_GC invocation. + */ +typedef enum JSGCInvocationKind { + /* Normal invocation. */ + GC_NORMAL = 0, + + /* + * Called from js_DestroyContext for last JSContext in a JSRuntime, when + * it is imperative that rt->gcPoke gets cleared early in js_GC. + */ + GC_LAST_CONTEXT = 1, + + /* + * Flag bit telling js_GC that the caller has already acquired rt->gcLock. + * Currently, this flag is set for the invocation kinds that also preserve + * atoms and weak roots, so we don't need another bit for GC_KEEP_ATOMS. + */ + GC_LOCK_HELD = 0x10, + GC_KEEP_ATOMS = GC_LOCK_HELD, + + /* + * Called from js_SetProtoOrParent with a request to set an object's proto + * or parent slot inserted on rt->setSlotRequests. + */ + GC_SET_SLOT_REQUEST = GC_LOCK_HELD | 1, + + /* + * Called from js_NewGCThing as a last-ditch GC attempt. See comments in + * jsgc.c just before js_GC's definition for details. + */ + GC_LAST_DITCH = GC_LOCK_HELD | 2 +} JSGCInvocationKind; + +extern void +js_GC(JSContext *cx, JSGCInvocationKind gckind); + +/* + * The kind of GC thing with a finalizer. The external strings follow the + * ordinary string to simplify js_GetExternalStringGCType. + */ +enum JSFinalizeGCThingKind { + FINALIZE_OBJECT, + FINALIZE_FUNCTION, +#if JS_HAS_XML_SUPPORT + FINALIZE_XML, +#endif + FINALIZE_STRING, + FINALIZE_EXTERNAL_STRING0, + FINALIZE_EXTERNAL_STRING1, + FINALIZE_EXTERNAL_STRING2, + FINALIZE_EXTERNAL_STRING3, + FINALIZE_EXTERNAL_STRING4, + FINALIZE_EXTERNAL_STRING5, + FINALIZE_EXTERNAL_STRING6, + FINALIZE_EXTERNAL_STRING7, + FINALIZE_EXTERNAL_STRING_LAST = FINALIZE_EXTERNAL_STRING7, + FINALIZE_LIMIT +}; + +static inline bool +IsFinalizableStringKind(unsigned thingKind) +{ + return unsigned(FINALIZE_STRING) <= thingKind && + thingKind <= unsigned(FINALIZE_EXTERNAL_STRING_LAST); +} + +/* + * Allocates a new GC thing. After a successful allocation the caller must + * fully initialize the thing before calling any function that can potentially + * trigger GC. This will ensure that GC tracing never sees junk values stored + * in the partially initialized thing. + */ +extern void * +js_NewFinalizableGCThing(JSContext *cx, unsigned thingKind); + +static inline JSObject * +js_NewGCObject(JSContext *cx) +{ + return (JSObject *) js_NewFinalizableGCThing(cx, FINALIZE_OBJECT); +} + +static inline JSString * +js_NewGCString(JSContext *cx) +{ + return (JSString *) js_NewFinalizableGCThing(cx, FINALIZE_STRING); +} + +static inline JSString * +js_NewGCExternalString(JSContext *cx, uintN type) +{ + JS_ASSERT(type < JS_EXTERNAL_STRING_LIMIT); + type += FINALIZE_EXTERNAL_STRING0; + return (JSString *) js_NewFinalizableGCThing(cx, type); +} + +static inline JSFunction* +js_NewGCFunction(JSContext *cx) +{ + return (JSFunction *) js_NewFinalizableGCThing(cx, FINALIZE_FUNCTION); +} + +#if JS_HAS_XML_SUPPORT +static inline JSXML * +js_NewGCXML(JSContext *cx) +{ + return (JSXML *) js_NewFinalizableGCThing(cx, FINALIZE_XML); +} +#endif + +struct JSGCArenaInfo; +struct JSGCChunkInfo; + +struct JSGCArenaList { + JSGCArenaInfo *head; /* list start */ + JSGCArenaInfo *cursor; /* arena with free things */ + uint32 thingKind; /* one of JSFinalizeGCThingKind */ + uint32 thingSize; /* size of things to allocate on this list + */ +}; + +struct JSGCDoubleArenaList { + JSGCArenaInfo *head; /* list start */ + JSGCArenaInfo *cursor; /* next arena with free cells */ +}; + +struct JSGCFreeLists { + JSGCThing *doubles; + JSGCThing *finalizables[FINALIZE_LIMIT]; + + void purge(); + void moveTo(JSGCFreeLists * another); + +#ifdef DEBUG + bool isEmpty() const { + if (doubles) + return false; + for (size_t i = 0; i != JS_ARRAY_LENGTH(finalizables); ++i) { + if (finalizables[i]) + return false; + } + return true; + } +#endif +}; + +extern void +js_DestroyScriptsToGC(JSContext *cx, JSThreadData *data); + +struct JSWeakRoots { + /* Most recently created things by type, members of the GC's root set. */ + void *finalizableNewborns[FINALIZE_LIMIT]; + jsdouble *newbornDouble; + + /* Atom root for the last-looked-up atom on this context. */ + jsval lastAtom; + + /* Root for the result of the most recent js_InternalInvoke call. */ + jsval lastInternalResult; + + void mark(JSTracer *trc); +}; + +#define JS_CLEAR_WEAK_ROOTS(wr) (memset((wr), 0, sizeof(JSWeakRoots))) + +#ifdef JS_THREADSAFE +class JSFreePointerListTask : public JSBackgroundTask { + void *head; + public: + JSFreePointerListTask() : head(NULL) {} + + void add(void* ptr) { + *(void**)ptr = head; + head = ptr; + } + + void run() { + void *ptr = head; + while (ptr) { + void *next = *(void **)ptr; + js_free(ptr); + ptr = next; + } + } +}; +#endif + +extern void +js_FinalizeStringRT(JSRuntime *rt, JSString *str); + +#ifdef DEBUG_notme +#define JS_GCMETER 1 +#endif + +#ifdef JS_GCMETER + +typedef struct JSGCArenaStats { + uint32 alloc; /* allocation attempts */ + uint32 localalloc; /* allocations from local lists */ + uint32 retry; /* allocation retries after running the GC */ + uint32 fail; /* allocation failures */ + uint32 nthings; /* live GC things */ + uint32 maxthings; /* maximum of live GC cells */ + double totalthings; /* live GC things the GC scanned so far */ + uint32 narenas; /* number of arena in list before the GC */ + uint32 newarenas; /* new arenas allocated before the last GC */ + uint32 livearenas; /* number of live arenas after the last GC */ + uint32 maxarenas; /* maximum of allocated arenas */ + uint32 totalarenas; /* total number of arenas with live things that + GC scanned so far */ +} JSGCArenaStats; + +typedef struct JSGCStats { + uint32 finalfail; /* finalizer calls allocator failures */ + uint32 lockborn; /* things born locked */ + uint32 lock; /* valid lock calls */ + uint32 unlock; /* valid unlock calls */ + uint32 depth; /* mark tail recursion depth */ + uint32 maxdepth; /* maximum mark tail recursion depth */ + uint32 cdepth; /* mark recursion depth of C functions */ + uint32 maxcdepth; /* maximum mark recursion depth of C functions */ + uint32 untraced; /* number of times tracing of GC thing's children were + delayed due to a low C stack */ +#ifdef DEBUG + uint32 maxuntraced;/* maximum number of things with children to trace + later */ +#endif + uint32 maxlevel; /* maximum GC nesting (indirect recursion) level */ + uint32 poke; /* number of potentially useful GC calls */ + uint32 afree; /* thing arenas freed so far */ + uint32 stackseg; /* total extraordinary stack segments scanned */ + uint32 segslots; /* total stack segment jsval slots scanned */ + uint32 nclose; /* number of objects with close hooks */ + uint32 maxnclose; /* max number of objects with close hooks */ + uint32 closelater; /* number of close hooks scheduled to run */ + uint32 maxcloselater; /* max number of close hooks scheduled to run */ + + JSGCArenaStats arenaStats[FINALIZE_LIST_LIMIT]; + JSGCArenaStats doubleArenaStats; +} JSGCStats; + +extern JS_FRIEND_API(void) +js_DumpGCStats(JSRuntime *rt, FILE *fp); + +#endif /* JS_GCMETER */ + +/* + * This function is defined in jsdbgapi.cpp but is declared here to avoid + * polluting jsdbgapi.h, a public API header, with internal functions. + */ +extern void +js_MarkTraps(JSTracer *trc); + +JS_END_EXTERN_C + +#endif /* jsgc_h___ */ diff --git a/ape-server/deps/js/src/jshash.cpp b/ape-server/deps/js/src/jshash.cpp new file mode 100755 index 0000000..e347744 --- /dev/null +++ b/ape-server/deps/js/src/jshash.cpp @@ -0,0 +1,477 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * PR hash table package. + */ +#include +#include +#include "jstypes.h" +#include "jsstdint.h" +#include "jsbit.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jshash.h" /* Added by JSIFY */ + +/* Compute the number of buckets in ht */ +#define NBUCKETS(ht) JS_BIT(JS_HASH_BITS - (ht)->shift) + +/* The smallest table has 16 buckets */ +#define MINBUCKETSLOG2 4 +#define MINBUCKETS JS_BIT(MINBUCKETSLOG2) + +/* Compute the maximum entries given n buckets that we will tolerate, ~90% */ +#define OVERLOADED(n) ((n) - ((n) >> 3)) + +/* Compute the number of entries below which we shrink the table by half */ +#define UNDERLOADED(n) (((n) > MINBUCKETS) ? ((n) >> 2) : 0) + +/* +** Stubs for default hash allocator ops. +*/ +static void * +DefaultAllocTable(void *pool, size_t size) +{ + return malloc(size); +} + +static void +DefaultFreeTable(void *pool, void *item, size_t size) +{ + js_free(item); +} + +static JSHashEntry * +DefaultAllocEntry(void *pool, const void *key) +{ + return (JSHashEntry*) malloc(sizeof(JSHashEntry)); +} + +static void +DefaultFreeEntry(void *pool, JSHashEntry *he, uintN flag) +{ + if (flag == HT_FREE_ENTRY) + js_free(he); +} + +static JSHashAllocOps defaultHashAllocOps = { + DefaultAllocTable, DefaultFreeTable, + DefaultAllocEntry, DefaultFreeEntry +}; + +JS_PUBLIC_API(JSHashTable *) +JS_NewHashTable(uint32 n, JSHashFunction keyHash, + JSHashComparator keyCompare, JSHashComparator valueCompare, + JSHashAllocOps *allocOps, void *allocPriv) +{ + JSHashTable *ht; + size_t nb; + + if (n <= MINBUCKETS) { + n = MINBUCKETSLOG2; + } else { + n = JS_CeilingLog2(n); + if ((int32)n < 0) + return NULL; + } + + if (!allocOps) allocOps = &defaultHashAllocOps; + + ht = (JSHashTable*) allocOps->allocTable(allocPriv, sizeof *ht); + if (!ht) + return NULL; + memset(ht, 0, sizeof *ht); + ht->shift = JS_HASH_BITS - n; + n = JS_BIT(n); + nb = n * sizeof(JSHashEntry *); + ht->buckets = (JSHashEntry**) allocOps->allocTable(allocPriv, nb); + if (!ht->buckets) { + allocOps->freeTable(allocPriv, ht, nb); + return NULL; + } + memset(ht->buckets, 0, nb); + + ht->keyHash = keyHash; + ht->keyCompare = keyCompare; + ht->valueCompare = valueCompare; + ht->allocOps = allocOps; + ht->allocPriv = allocPriv; + return ht; +} + +JS_PUBLIC_API(void) +JS_HashTableDestroy(JSHashTable *ht) +{ + uint32 i, n; + JSHashEntry *he, **hep; + JSHashAllocOps *allocOps = ht->allocOps; + void *allocPriv = ht->allocPriv; + + n = NBUCKETS(ht); + for (i = 0; i < n; i++) { + hep = &ht->buckets[i]; + while ((he = *hep) != NULL) { + *hep = he->next; + allocOps->freeEntry(allocPriv, he, HT_FREE_ENTRY); + } + } +#ifdef DEBUG + memset(ht->buckets, 0xDB, n * sizeof ht->buckets[0]); +#endif + allocOps->freeTable(allocPriv, ht->buckets, n * sizeof ht->buckets[0]); +#ifdef DEBUG + memset(ht, 0xDB, sizeof *ht); +#endif + allocOps->freeTable(allocPriv, ht, sizeof *ht); +} + +/* + * Multiplicative hash, from Knuth 6.4. + */ +#define BUCKET_HEAD(ht, keyHash) \ + (&(ht)->buckets[((keyHash) * JS_GOLDEN_RATIO) >> (ht)->shift]) + +JS_PUBLIC_API(JSHashEntry **) +JS_HashTableRawLookup(JSHashTable *ht, JSHashNumber keyHash, const void *key) +{ + JSHashEntry *he, **hep, **hep0; + +#ifdef JS_HASHMETER + ht->nlookups++; +#endif + hep = hep0 = BUCKET_HEAD(ht, keyHash); + while ((he = *hep) != NULL) { + if (he->keyHash == keyHash && ht->keyCompare(key, he->key)) { + /* Move to front of chain if not already there */ + if (hep != hep0) { + *hep = he->next; + he->next = *hep0; + *hep0 = he; + } + return hep0; + } + hep = &he->next; +#ifdef JS_HASHMETER + ht->nsteps++; +#endif + } + return hep; +} + +static JSBool +Resize(JSHashTable *ht, uint32 newshift) +{ + size_t nb, nentries, i; + JSHashEntry **oldbuckets, *he, *next, **hep; + size_t nold = NBUCKETS(ht); + + JS_ASSERT(newshift < JS_HASH_BITS); + + nb = (size_t)1 << (JS_HASH_BITS - newshift); + + /* Integer overflow protection. */ + if (nb > (size_t)-1 / sizeof(JSHashEntry*)) + return JS_FALSE; + nb *= sizeof(JSHashEntry*); + + oldbuckets = ht->buckets; + ht->buckets = (JSHashEntry**)ht->allocOps->allocTable(ht->allocPriv, nb); + if (!ht->buckets) { + ht->buckets = oldbuckets; + return JS_FALSE; + } + memset(ht->buckets, 0, nb); + + ht->shift = newshift; + nentries = ht->nentries; + + for (i = 0; nentries != 0; i++) { + for (he = oldbuckets[i]; he; he = next) { + JS_ASSERT(nentries != 0); + --nentries; + next = he->next; + hep = BUCKET_HEAD(ht, he->keyHash); + + /* + * We do not require unique entries, instead appending he to the + * chain starting at hep. + */ + while (*hep) + hep = &(*hep)->next; + he->next = NULL; + *hep = he; + } + } +#ifdef DEBUG + memset(oldbuckets, 0xDB, nold * sizeof oldbuckets[0]); +#endif + ht->allocOps->freeTable(ht->allocPriv, oldbuckets, + nold * sizeof oldbuckets[0]); + return JS_TRUE; +} + +JS_PUBLIC_API(JSHashEntry *) +JS_HashTableRawAdd(JSHashTable *ht, JSHashEntry **&hep, + JSHashNumber keyHash, const void *key, void *value) +{ + uint32 n; + JSHashEntry *he; + + /* Grow the table if it is overloaded */ + n = NBUCKETS(ht); + if (ht->nentries >= OVERLOADED(n)) { + if (!Resize(ht, ht->shift - 1)) + return NULL; +#ifdef JS_HASHMETER + ht->ngrows++; +#endif + hep = JS_HashTableRawLookup(ht, keyHash, key); + } + + /* Make a new key value entry */ + he = ht->allocOps->allocEntry(ht->allocPriv, key); + if (!he) + return NULL; + he->keyHash = keyHash; + he->key = key; + he->value = value; + he->next = *hep; + *hep = he; + ht->nentries++; + return he; +} + +JS_PUBLIC_API(JSHashEntry *) +JS_HashTableAdd(JSHashTable *ht, const void *key, void *value) +{ + JSHashNumber keyHash; + JSHashEntry *he, **hep; + + keyHash = ht->keyHash(key); + hep = JS_HashTableRawLookup(ht, keyHash, key); + if ((he = *hep) != NULL) { + /* Hit; see if values match */ + if (ht->valueCompare(he->value, value)) { + /* key,value pair is already present in table */ + return he; + } + if (he->value) + ht->allocOps->freeEntry(ht->allocPriv, he, HT_FREE_VALUE); + he->value = value; + return he; + } + return JS_HashTableRawAdd(ht, hep, keyHash, key, value); +} + +JS_PUBLIC_API(void) +JS_HashTableRawRemove(JSHashTable *ht, JSHashEntry **hep, JSHashEntry *he) +{ + uint32 n; + + *hep = he->next; + ht->allocOps->freeEntry(ht->allocPriv, he, HT_FREE_ENTRY); + + /* Shrink table if it's underloaded */ + n = NBUCKETS(ht); + if (--ht->nentries < UNDERLOADED(n)) { + Resize(ht, ht->shift + 1); +#ifdef JS_HASHMETER + ht->nshrinks++; +#endif + } +} + +JS_PUBLIC_API(JSBool) +JS_HashTableRemove(JSHashTable *ht, const void *key) +{ + JSHashNumber keyHash; + JSHashEntry *he, **hep; + + keyHash = ht->keyHash(key); + hep = JS_HashTableRawLookup(ht, keyHash, key); + if ((he = *hep) == NULL) + return JS_FALSE; + + /* Hit; remove element */ + JS_HashTableRawRemove(ht, hep, he); + return JS_TRUE; +} + +JS_PUBLIC_API(void *) +JS_HashTableLookup(JSHashTable *ht, const void *key) +{ + JSHashNumber keyHash; + JSHashEntry *he, **hep; + + keyHash = ht->keyHash(key); + hep = JS_HashTableRawLookup(ht, keyHash, key); + if ((he = *hep) != NULL) { + return he->value; + } + return NULL; +} + +/* +** Iterate over the entries in the hash table calling func for each +** entry found. Stop if "f" says to (return value & JS_ENUMERATE_STOP). +** Return a count of the number of elements scanned. +*/ +JS_PUBLIC_API(int) +JS_HashTableEnumerateEntries(JSHashTable *ht, JSHashEnumerator f, void *arg) +{ + JSHashEntry *he, **hep, **bucket; + uint32 nlimit, n, nbuckets, newlog2; + int rv; + + nlimit = ht->nentries; + n = 0; + for (bucket = ht->buckets; n != nlimit; ++bucket) { + hep = bucket; + while ((he = *hep) != NULL) { + JS_ASSERT(n < nlimit); + rv = f(he, n, arg); + n++; + if (rv & HT_ENUMERATE_REMOVE) { + *hep = he->next; + ht->allocOps->freeEntry(ht->allocPriv, he, HT_FREE_ENTRY); + --ht->nentries; + } else { + hep = &he->next; + } + if (rv & HT_ENUMERATE_STOP) { + goto out; + } + } + } + +out: + /* Shrink table if removal of entries made it underloaded */ + if (ht->nentries != nlimit) { + JS_ASSERT(ht->nentries < nlimit); + nbuckets = NBUCKETS(ht); + if (MINBUCKETS < nbuckets && ht->nentries < UNDERLOADED(nbuckets)) { + newlog2 = JS_CeilingLog2(ht->nentries); + if (newlog2 < MINBUCKETSLOG2) + newlog2 = MINBUCKETSLOG2; + + /* Check that we really shrink the table. */ + JS_ASSERT(JS_HASH_BITS - ht->shift > newlog2); + Resize(ht, JS_HASH_BITS - newlog2); + } + } + return (int)n; +} + +#ifdef JS_HASHMETER +#include + +JS_PUBLIC_API(void) +JS_HashTableDumpMeter(JSHashTable *ht, JSHashEnumerator dump, FILE *fp) +{ + double sqsum, mean, sigma; + uint32 nchains, nbuckets; + uint32 i, n, maxChain, maxChainLen; + JSHashEntry *he; + + sqsum = 0; + nchains = 0; + maxChain = maxChainLen = 0; + nbuckets = NBUCKETS(ht); + for (i = 0; i < nbuckets; i++) { + he = ht->buckets[i]; + if (!he) + continue; + nchains++; + for (n = 0; he; he = he->next) + n++; + sqsum += n * n; + if (n > maxChainLen) { + maxChainLen = n; + maxChain = i; + } + } + + mean = JS_MeanAndStdDev(nchains, ht->nentries, sqsum, &sigma); + + fprintf(fp, "\nHash table statistics:\n"); + fprintf(fp, " number of lookups: %u\n", ht->nlookups); + fprintf(fp, " number of entries: %u\n", ht->nentries); + fprintf(fp, " number of grows: %u\n", ht->ngrows); + fprintf(fp, " number of shrinks: %u\n", ht->nshrinks); + fprintf(fp, " mean steps per hash: %g\n", (double)ht->nsteps + / ht->nlookups); + fprintf(fp, "mean hash chain length: %g\n", mean); + fprintf(fp, " standard deviation: %g\n", sigma); + fprintf(fp, " max hash chain length: %u\n", maxChainLen); + fprintf(fp, " max hash chain: [%u]\n", maxChain); + + for (he = ht->buckets[maxChain], i = 0; he; he = he->next, i++) + if (dump(he, i, fp) != HT_ENUMERATE_NEXT) + break; +} +#endif /* JS_HASHMETER */ + +JS_PUBLIC_API(int) +JS_HashTableDump(JSHashTable *ht, JSHashEnumerator dump, FILE *fp) +{ + int count; + + count = JS_HashTableEnumerateEntries(ht, dump, fp); +#ifdef JS_HASHMETER + JS_HashTableDumpMeter(ht, dump, fp); +#endif + return count; +} + +JS_PUBLIC_API(JSHashNumber) +JS_HashString(const void *key) +{ + JSHashNumber h; + const unsigned char *s; + + h = 0; + for (s = (const unsigned char *)key; *s; s++) + h = JS_ROTATE_LEFT32(h, 4) ^ *s; + return h; +} + +JS_PUBLIC_API(int) +JS_CompareValues(const void *v1, const void *v2) +{ + return v1 == v2; +} diff --git a/ape-server/deps/js/src/jshash.h b/ape-server/deps/js/src/jshash.h new file mode 100755 index 0000000..a365a51 --- /dev/null +++ b/ape-server/deps/js/src/jshash.h @@ -0,0 +1,153 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jshash_h___ +#define jshash_h___ +/* + * API to portable hash table code. + */ +#include +#include +#include "jstypes.h" +#include "jscompat.h" + +JS_BEGIN_EXTERN_C + +typedef uint32 JSHashNumber; +typedef struct JSHashEntry JSHashEntry; +typedef struct JSHashTable JSHashTable; + +#define JS_HASH_BITS 32 +#define JS_GOLDEN_RATIO 0x9E3779B9U + +typedef JSHashNumber (* JSHashFunction)(const void *key); +typedef intN (* JSHashComparator)(const void *v1, const void *v2); +typedef intN (* JSHashEnumerator)(JSHashEntry *he, intN i, void *arg); + +/* Flag bits in JSHashEnumerator's return value */ +#define HT_ENUMERATE_NEXT 0 /* continue enumerating entries */ +#define HT_ENUMERATE_STOP 1 /* stop enumerating entries */ +#define HT_ENUMERATE_REMOVE 2 /* remove and free the current entry */ + +typedef struct JSHashAllocOps { + void * (*allocTable)(void *pool, size_t size); + void (*freeTable)(void *pool, void *item, size_t size); + JSHashEntry * (*allocEntry)(void *pool, const void *key); + void (*freeEntry)(void *pool, JSHashEntry *he, uintN flag); +} JSHashAllocOps; + +#define HT_FREE_VALUE 0 /* just free the entry's value */ +#define HT_FREE_ENTRY 1 /* free value and entire entry */ + +struct JSHashEntry { + JSHashEntry *next; /* hash chain linkage */ + JSHashNumber keyHash; /* key hash function result */ + const void *key; /* ptr to opaque key */ + void *value; /* ptr to opaque value */ +}; + +struct JSHashTable { + JSHashEntry **buckets; /* vector of hash buckets */ + uint32 nentries; /* number of entries in table */ + uint32 shift; /* multiplicative hash shift */ + JSHashFunction keyHash; /* key hash function */ + JSHashComparator keyCompare; /* key comparison function */ + JSHashComparator valueCompare; /* value comparison function */ + JSHashAllocOps *allocOps; /* allocation operations */ + void *allocPriv; /* allocation private data */ +#ifdef JS_HASHMETER + uint32 nlookups; /* total number of lookups */ + uint32 nsteps; /* number of hash chains traversed */ + uint32 ngrows; /* number of table expansions */ + uint32 nshrinks; /* number of table contractions */ +#endif +}; + +/* + * Create a new hash table. + * If allocOps is null, use default allocator ops built on top of malloc(). + */ +extern JS_PUBLIC_API(JSHashTable *) +JS_NewHashTable(uint32 n, JSHashFunction keyHash, + JSHashComparator keyCompare, JSHashComparator valueCompare, + JSHashAllocOps *allocOps, void *allocPriv); + +extern JS_PUBLIC_API(void) +JS_HashTableDestroy(JSHashTable *ht); + +/* Low level access methods */ +extern JS_PUBLIC_API(JSHashEntry **) +JS_HashTableRawLookup(JSHashTable *ht, JSHashNumber keyHash, const void *key); + +#ifdef __cplusplus +extern JS_PUBLIC_API(JSHashEntry *) +JS_HashTableRawAdd(JSHashTable *ht, JSHashEntry **&hep, JSHashNumber keyHash, + const void *key, void *value); +#endif + +extern JS_PUBLIC_API(void) +JS_HashTableRawRemove(JSHashTable *ht, JSHashEntry **hep, JSHashEntry *he); + +/* Higher level access methods */ +extern JS_PUBLIC_API(JSHashEntry *) +JS_HashTableAdd(JSHashTable *ht, const void *key, void *value); + +extern JS_PUBLIC_API(JSBool) +JS_HashTableRemove(JSHashTable *ht, const void *key); + +extern JS_PUBLIC_API(intN) +JS_HashTableEnumerateEntries(JSHashTable *ht, JSHashEnumerator f, void *arg); + +extern JS_PUBLIC_API(void *) +JS_HashTableLookup(JSHashTable *ht, const void *key); + +extern JS_PUBLIC_API(intN) +JS_HashTableDump(JSHashTable *ht, JSHashEnumerator dump, FILE *fp); + +/* General-purpose C string hash function. */ +extern JS_PUBLIC_API(JSHashNumber) +JS_HashString(const void *key); + +/* Stub function just returns v1 == v2 */ +extern JS_PUBLIC_API(intN) +JS_CompareValues(const void *v1, const void *v2); + +JS_END_EXTERN_C + +#endif /* jshash_h___ */ diff --git a/ape-server/deps/js/src/jsinterp.cpp b/ape-server/deps/js/src/jsinterp.cpp new file mode 100755 index 0000000..a87fdef --- /dev/null +++ b/ape-server/deps/js/src/jsinterp.cpp @@ -0,0 +1,3326 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=99: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * JavaScript bytecode interpreter. + */ +#include +#include +#include +#include "jstypes.h" +#include "jsstdint.h" +#include "jsarena.h" /* Added by JSIFY */ +#include "jsutil.h" /* Added by JSIFY */ +#include "jsprf.h" +#include "jsapi.h" +#include "jsarray.h" +#include "jsatom.h" +#include "jsbool.h" +#include "jscntxt.h" +#include "jsdate.h" +#include "jsversion.h" +#include "jsdbgapi.h" +#include "jsfun.h" +#include "jsgc.h" +#include "jsinterp.h" +#include "jsiter.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsobj.h" +#include "jsopcode.h" +#include "jsscan.h" +#include "jsscope.h" +#include "jsscript.h" +#include "jsstr.h" +#include "jsstaticcheck.h" +#include "jstracer.h" +#include "jslibmath.h" +#include "jsvector.h" + +#include "jsatominlines.h" +#include "jsscopeinlines.h" +#include "jsscriptinlines.h" +#include "jsstrinlines.h" + +#ifdef INCLUDE_MOZILLA_DTRACE +#include "jsdtracef.h" +#endif + +#if JS_HAS_XML_SUPPORT +#include "jsxml.h" +#endif + +#include "jsautooplen.h" + +/* jsinvoke_cpp___ indicates inclusion from jsinvoke.cpp. */ +#if !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___ + +JS_REQUIRES_STACK JSPropCacheEntry * +js_FillPropertyCache(JSContext *cx, JSObject *obj, + uintN scopeIndex, uintN protoIndex, JSObject *pobj, + JSScopeProperty *sprop, JSBool adding) +{ + JSPropertyCache *cache; + jsbytecode *pc; + JSScope *scope; + jsuword kshape, vshape, khash; + JSOp op; + const JSCodeSpec *cs; + jsuword vword; + ptrdiff_t pcoff; + JSAtom *atom; + JSPropCacheEntry *entry; + + JS_ASSERT(!cx->runtime->gcRunning); + cache = &JS_PROPERTY_CACHE(cx); + + /* FIXME bug 489098: consider enabling the property cache for eval. */ + if (js_IsPropertyCacheDisabled(cx) || (cx->fp->flags & JSFRAME_EVAL)) { + PCMETER(cache->disfills++); + return JS_NO_PROP_CACHE_FILL; + } + + /* + * Check for fill from js_SetPropertyHelper where the setter removed sprop + * from pobj's scope (via unwatch or delete, e.g.). + */ + scope = OBJ_SCOPE(pobj); + if (!scope->hasProperty(sprop)) { + PCMETER(cache->oddfills++); + return JS_NO_PROP_CACHE_FILL; + } + + /* + * Check for overdeep scope and prototype chain. Because resolve, getter, + * and setter hooks can change the prototype chain using JS_SetPrototype + * after js_LookupPropertyWithFlags has returned the nominal protoIndex, + * we have to validate protoIndex if it is non-zero. If it is zero, then + * we know thanks to the scope->hasProperty test above, combined with the + * fact that obj == pobj, that protoIndex is invariant. + * + * The scopeIndex can't be wrong. We require JS_SetParent calls to happen + * before any running script might consult a parent-linked scope chain. If + * this requirement is not satisfied, the fill in progress will never hit, + * but vcap vs. scope shape tests ensure nothing malfunctions. + */ + JS_ASSERT_IF(scopeIndex == 0 && protoIndex == 0, obj == pobj); + + if (protoIndex != 0) { + JSObject *tmp = obj; + + for (uintN i = 0; i != scopeIndex; i++) + tmp = OBJ_GET_PARENT(cx, tmp); + JS_ASSERT(tmp != pobj); + + protoIndex = 1; + for (;;) { + tmp = OBJ_GET_PROTO(cx, tmp); + + /* + * We cannot cache properties coming from native objects behind + * non-native ones on the prototype chain. The non-natives can + * mutate in arbitrary way without changing any shapes. + */ + if (!tmp || !OBJ_IS_NATIVE(tmp)) { + PCMETER(cache->noprotos++); + return JS_NO_PROP_CACHE_FILL; + } + if (tmp == pobj) + break; + ++protoIndex; + } + } + + if (scopeIndex > PCVCAP_SCOPEMASK || protoIndex > PCVCAP_PROTOMASK) { + PCMETER(cache->longchains++); + return JS_NO_PROP_CACHE_FILL; + } + + /* + * Optimize the cached vword based on our parameters and the current pc's + * opcode format flags. + */ + pc = cx->fp->regs->pc; + op = js_GetOpcode(cx, cx->fp->script, pc); + cs = &js_CodeSpec[op]; + kshape = 0; + + do { + /* + * Check for a prototype "plain old method" callee computation. What + * is a plain old method? It's a function-valued property with stub + * getter, so get of a function is idempotent. + */ + if (cs->format & JOF_CALLOP) { + jsval v; + + if (sprop->isMethod()) { + /* + * A compiler-created function object, AKA a method, already + * memoized in the property tree. + */ + JS_ASSERT(scope->hasMethodBarrier()); + v = sprop->methodValue(); + JS_ASSERT(VALUE_IS_FUNCTION(cx, v)); + JS_ASSERT(v == LOCKED_OBJ_GET_SLOT(pobj, sprop->slot)); + vword = JSVAL_OBJECT_TO_PCVAL(v); + break; + } + + if (SPROP_HAS_STUB_GETTER(sprop) && + SPROP_HAS_VALID_SLOT(sprop, scope)) { + v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot); + if (VALUE_IS_FUNCTION(cx, v)) { + /* + * Great, we have a function-valued prototype property + * where the getter is JS_PropertyStub. The type id in + * pobj's scope does not evolve with changes to property + * values, however. + * + * So here, on first cache fill for this method, we brand + * the scope with a new shape and set the JSScope::BRANDED + * flag. Once this flag is set, any property assignment + * that changes the value from or to a different function + * object will result in shape being regenerated. + */ + if (!scope->branded()) { + PCMETER(cache->brandfills++); +#ifdef DEBUG_notme + fprintf(stderr, + "branding %p (%s) for funobj %p (%s), shape %lu\n", + pobj, pobj->getClass()->name, + JSVAL_TO_OBJECT(v), + JS_GetFunctionName(GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(v))), + OBJ_SHAPE(obj)); +#endif + scope->brandingShapeChange(cx, sprop->slot, v); + if (js_IsPropertyCacheDisabled(cx)) /* check for rt->shapeGen overflow */ + return JS_NO_PROP_CACHE_FILL; + scope->setBranded(); + } + vword = JSVAL_OBJECT_TO_PCVAL(v); + break; + } + } + } + + /* If getting a value via a stub getter, we can cache the slot. */ + if (!(cs->format & (JOF_SET | JOF_INCDEC | JOF_FOR)) && + SPROP_HAS_STUB_GETTER(sprop) && + SPROP_HAS_VALID_SLOT(sprop, scope)) { + /* Great, let's cache sprop's slot and use it on cache hit. */ + vword = SLOT_TO_PCVAL(sprop->slot); + } else { + /* Best we can do is to cache sprop (still a nice speedup). */ + vword = SPROP_TO_PCVAL(sprop); + if (adding && + sprop == scope->lastProperty() && + scope->shape == sprop->shape) { + /* + * Our caller added a new property. We also know that a setter + * that js_NativeSet could have run has not mutated the scope, + * so the added property is still the last one added, and the + * scope is not branded. + * + * We want to cache under scope's shape before the property + * addition to bias for the case when the mutator opcode + * always adds the same property. This allows us to optimize + * periodic execution of object initializers or other explicit + * initialization sequences such as + * + * obj = {}; obj.x = 1; obj.y = 2; + * + * We assume that on average the win from this optimization is + * greater than the cost of an extra mismatch per loop owing to + * the bias for the following case: + * + * obj = {}; ... for (...) { ... obj.x = ... } + * + * On the first iteration of such a for loop, JSOP_SETPROP + * fills the cache with the shape of the newly created object + * obj, not the shape of obj after obj.x has been assigned. + * That mismatches obj's shape on the second iteration. Note + * that on the third and subsequent iterations the cache will + * be hit because the shape is no longer updated. + */ + JS_ASSERT(scope->owned()); + if (sprop->parent) { + kshape = sprop->parent->shape; + } else { + /* + * If obj had its own empty scope before, with a unique + * shape, that is lost. Here we only attempt to find a + * matching empty scope. In unusual cases involving + * __proto__ assignment we may not find one. + */ + JSObject *proto = STOBJ_GET_PROTO(obj); + if (!proto || !OBJ_IS_NATIVE(proto)) + return JS_NO_PROP_CACHE_FILL; + JSScope *protoscope = OBJ_SCOPE(proto); + if (!protoscope->emptyScope || + protoscope->emptyScope->clasp != obj->getClass()) { + return JS_NO_PROP_CACHE_FILL; + } + kshape = protoscope->emptyScope->shape; + } + + /* + * When adding we predict no prototype object will later gain a + * readonly property or setter. + */ + vshape = cx->runtime->protoHazardShape; + } + } + } while (0); + + if (kshape == 0) { + kshape = OBJ_SHAPE(obj); + vshape = scope->shape; + } + JS_ASSERT(kshape < SHAPE_OVERFLOW_BIT); + + khash = PROPERTY_CACHE_HASH_PC(pc, kshape); + if (obj == pobj) { + JS_ASSERT(scopeIndex == 0 && protoIndex == 0); + } else { + if (op == JSOP_LENGTH) { + atom = cx->runtime->atomState.lengthAtom; + } else { + pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? SLOTNO_LEN : 0; + GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom); + } + +#ifdef DEBUG + if (scopeIndex == 0) { + JS_ASSERT(protoIndex != 0); + JS_ASSERT((protoIndex == 1) == (OBJ_GET_PROTO(cx, obj) == pobj)); + } +#endif + + if (scopeIndex != 0 || protoIndex != 1) { + khash = PROPERTY_CACHE_HASH_ATOM(atom, obj); + PCMETER(if (PCVCAP_TAG(cache->table[khash].vcap) <= 1) + cache->pcrecycles++); + pc = (jsbytecode *) atom; + kshape = (jsuword) obj; + + /* + * Make sure that a later shadowing assignment will enter + * PurgeProtoChain and invalidate this entry, bug 479198. + * + * This is thread-safe even though obj is not locked. Only the + * DELEGATE bit of obj->classword can change at runtime, given that + * obj is native; and the bit is only set, never cleared. And on + * platforms where another CPU can fail to see this write, it's OK + * because the property cache and JIT cache are thread-local. + */ + obj->setDelegate(); + } + } + JS_ASSERT(vshape < SHAPE_OVERFLOW_BIT); + + entry = &cache->table[khash]; + PCMETER(PCVAL_IS_NULL(entry->vword) || cache->recycles++); + entry->kpc = pc; + entry->kshape = kshape; + entry->vcap = PCVCAP_MAKE(vshape, scopeIndex, protoIndex); + entry->vword = vword; + + cache->empty = JS_FALSE; + PCMETER(cache->fills++); + + /* + * The modfills counter is not exact. It increases if a getter or setter + * recurse into the interpreter. + */ + PCMETER(entry == cache->pctestentry || cache->modfills++); + PCMETER(cache->pctestentry = NULL); + return entry; +} + +JS_REQUIRES_STACK JSAtom * +js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc, + JSObject **objp, JSObject **pobjp, + JSPropCacheEntry **entryp) +{ + JSOp op; + const JSCodeSpec *cs; + ptrdiff_t pcoff; + JSAtom *atom; + JSObject *obj, *pobj, *tmp; + JSPropCacheEntry *entry; + uint32 vcap; + + JS_ASSERT(uintN((cx->fp->imacpc ? cx->fp->imacpc : pc) - cx->fp->script->code) + < cx->fp->script->length); + + op = js_GetOpcode(cx, cx->fp->script, pc); + cs = &js_CodeSpec[op]; + if (op == JSOP_LENGTH) { + atom = cx->runtime->atomState.lengthAtom; + } else { + pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? SLOTNO_LEN : 0; + GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom); + } + + obj = *objp; + JS_ASSERT(OBJ_IS_NATIVE(obj)); + entry = &JS_PROPERTY_CACHE(cx).table[PROPERTY_CACHE_HASH_ATOM(atom, obj)]; + *entryp = entry; + vcap = entry->vcap; + + if (entry->kpc != (jsbytecode *) atom) { + PCMETER(JS_PROPERTY_CACHE(cx).idmisses++); + +#ifdef DEBUG_notme + entry = &JS_PROPERTY_CACHE(cx).table[PROPERTY_CACHE_HASH_PC(pc, OBJ_SHAPE(obj))]; + fprintf(stderr, + "id miss for %s from %s:%u" + " (pc %u, kpc %u, kshape %u, shape %u)\n", + js_AtomToPrintableString(cx, atom), + cx->fp->script->filename, + js_PCToLineNumber(cx, cx->fp->script, pc), + pc - cx->fp->script->code, + entry->kpc - cx->fp->script->code, + entry->kshape, + OBJ_SHAPE(obj)); + js_Disassemble1(cx, cx->fp->script, pc, + pc - cx->fp->script->code, + JS_FALSE, stderr); +#endif + + return atom; + } + + if (entry->kshape != (jsuword) obj) { + PCMETER(JS_PROPERTY_CACHE(cx).komisses++); + return atom; + } + + pobj = obj; + + if (JOF_MODE(cs->format) == JOF_NAME) { + while (vcap & (PCVCAP_SCOPEMASK << PCVCAP_PROTOBITS)) { + tmp = OBJ_GET_PARENT(cx, pobj); + if (!tmp || !OBJ_IS_NATIVE(tmp)) + break; + pobj = tmp; + vcap -= PCVCAP_PROTOSIZE; + } + + *objp = pobj; + } + + while (vcap & PCVCAP_PROTOMASK) { + tmp = OBJ_GET_PROTO(cx, pobj); + if (!tmp || !OBJ_IS_NATIVE(tmp)) + break; + pobj = tmp; + --vcap; + } + + if (JS_LOCK_OBJ_IF_SHAPE(cx, pobj, PCVCAP_SHAPE(vcap))) { +#ifdef DEBUG + jsid id = ATOM_TO_JSID(atom); + + id = js_CheckForStringIndex(id); + JS_ASSERT(OBJ_SCOPE(pobj)->lookup(id)); + JS_ASSERT_IF(OBJ_SCOPE(pobj)->object, OBJ_SCOPE(pobj)->object == pobj); +#endif + *pobjp = pobj; + return NULL; + } + + PCMETER(JS_PROPERTY_CACHE(cx).vcmisses++); + return atom; +} + +#ifdef DEBUG +#define ASSERT_CACHE_IS_EMPTY(cache) \ + JS_BEGIN_MACRO \ + JSPropertyCache *cache_ = (cache); \ + uintN i_; \ + JS_ASSERT(cache_->empty); \ + for (i_ = 0; i_ < PROPERTY_CACHE_SIZE; i_++) { \ + JS_ASSERT(!cache_->table[i_].kpc); \ + JS_ASSERT(!cache_->table[i_].kshape); \ + JS_ASSERT(!cache_->table[i_].vcap); \ + JS_ASSERT(!cache_->table[i_].vword); \ + } \ + JS_END_MACRO +#else +#define ASSERT_CACHE_IS_EMPTY(cache) ((void)0) +#endif + +JS_STATIC_ASSERT(PCVAL_NULL == 0); + +void +js_PurgePropertyCache(JSContext *cx, JSPropertyCache *cache) +{ + if (cache->empty) { + ASSERT_CACHE_IS_EMPTY(cache); + return; + } + + memset(cache->table, 0, sizeof cache->table); + cache->empty = JS_TRUE; + +#ifdef JS_PROPERTY_CACHE_METERING + { static FILE *fp; + if (!fp) + fp = fopen("/tmp/propcache.stats", "w"); + if (fp) { + fputs("Property cache stats for ", fp); +#ifdef JS_THREADSAFE + fprintf(fp, "thread %lu, ", (unsigned long) cx->thread->id); +#endif + fprintf(fp, "GC %u\n", cx->runtime->gcNumber); + +# define P(mem) fprintf(fp, "%11s %10lu\n", #mem, (unsigned long)cache->mem) + P(fills); + P(nofills); + P(rofills); + P(disfills); + P(oddfills); + P(modfills); + P(brandfills); + P(noprotos); + P(longchains); + P(recycles); + P(pcrecycles); + P(tests); + P(pchits); + P(protopchits); + P(initests); + P(inipchits); + P(inipcmisses); + P(settests); + P(addpchits); + P(setpchits); + P(setpcmisses); + P(slotchanges); + P(setmisses); + P(idmisses); + P(komisses); + P(vcmisses); + P(misses); + P(flushes); + P(pcpurges); +# undef P + + fprintf(fp, "hit rates: pc %g%% (proto %g%%), set %g%%, ini %g%%, full %g%%\n", + (100. * cache->pchits) / cache->tests, + (100. * cache->protopchits) / cache->tests, + (100. * (cache->addpchits + cache->setpchits)) + / cache->settests, + (100. * cache->inipchits) / cache->initests, + (100. * (cache->tests - cache->misses)) / cache->tests); + fflush(fp); + } + } +#endif + + PCMETER(cache->flushes++); +} + +void +js_PurgePropertyCacheForScript(JSContext *cx, JSScript *script) +{ + JSPropertyCache *cache; + JSPropCacheEntry *entry; + + cache = &JS_PROPERTY_CACHE(cx); + for (entry = cache->table; entry < cache->table + PROPERTY_CACHE_SIZE; + entry++) { + if (JS_UPTRDIFF(entry->kpc, script->code) < script->length) { + entry->kpc = NULL; + entry->kshape = 0; +#ifdef DEBUG + entry->vcap = entry->vword = 0; +#endif + } + } +} + +/* + * Check if the current arena has enough space to fit nslots after sp and, if + * so, reserve the necessary space. + */ +static JS_REQUIRES_STACK JSBool +AllocateAfterSP(JSContext *cx, jsval *sp, uintN nslots) +{ + uintN surplus; + jsval *sp2; + + JS_ASSERT((jsval *) cx->stackPool.current->base <= sp); + JS_ASSERT(sp <= (jsval *) cx->stackPool.current->avail); + surplus = (jsval *) cx->stackPool.current->avail - sp; + if (nslots <= surplus) + return JS_TRUE; + + /* + * No room before current->avail, check if the arena has enough space to + * fit the missing slots before the limit. + */ + if (nslots > (size_t) ((jsval *) cx->stackPool.current->limit - sp)) + return JS_FALSE; + + JS_ARENA_ALLOCATE_CAST(sp2, jsval *, &cx->stackPool, + (nslots - surplus) * sizeof(jsval)); + JS_ASSERT(sp2 == sp + surplus); + return JS_TRUE; +} + +JS_STATIC_INTERPRET JS_REQUIRES_STACK jsval * +js_AllocRawStack(JSContext *cx, uintN nslots, void **markp) +{ + jsval *sp; + + JS_ASSERT(nslots != 0); + JS_ASSERT_NOT_ON_TRACE(cx); + + if (!cx->stackPool.first.next) { + int64 *timestamp; + + JS_ARENA_ALLOCATE_CAST(timestamp, int64 *, + &cx->stackPool, sizeof *timestamp); + if (!timestamp) { + js_ReportOutOfScriptQuota(cx); + return NULL; + } + *timestamp = JS_Now(); + } + + if (markp) + *markp = JS_ARENA_MARK(&cx->stackPool); + JS_ARENA_ALLOCATE_CAST(sp, jsval *, &cx->stackPool, nslots * sizeof(jsval)); + if (!sp) + js_ReportOutOfScriptQuota(cx); + return sp; +} + +JS_STATIC_INTERPRET JS_REQUIRES_STACK void +js_FreeRawStack(JSContext *cx, void *mark) +{ + JS_ARENA_RELEASE(&cx->stackPool, mark); +} + +JS_REQUIRES_STACK JS_FRIEND_API(jsval *) +js_AllocStack(JSContext *cx, uintN nslots, void **markp) +{ + jsval *sp; + JSArena *a; + JSStackHeader *sh; + + /* Callers don't check for zero nslots: we do to avoid empty segments. */ + if (nslots == 0) { + *markp = NULL; + return (jsval *) JS_ARENA_MARK(&cx->stackPool); + } + + /* Allocate 2 extra slots for the stack segment header we'll likely need. */ + sp = js_AllocRawStack(cx, 2 + nslots, markp); + if (!sp) + return NULL; + + /* Try to avoid another header if we can piggyback on the last segment. */ + a = cx->stackPool.current; + sh = cx->stackHeaders; + if (sh && JS_STACK_SEGMENT(sh) + sh->nslots == sp) { + /* Extend the last stack segment, give back the 2 header slots. */ + sh->nslots += nslots; + a->avail -= 2 * sizeof(jsval); + } else { + /* + * Need a new stack segment, so allocate and push a stack segment + * header from the 2 extra slots. + */ + sh = (JSStackHeader *)sp; + sh->nslots = nslots; + sh->down = cx->stackHeaders; + cx->stackHeaders = sh; + sp += 2; + } + + /* + * Store JSVAL_NULL using memset, to let compilers optimize as they see + * fit, in case a caller allocates and pushes GC-things one by one, which + * could nest a last-ditch GC that will scan this segment. + */ + memset(sp, 0, nslots * sizeof(jsval)); + return sp; +} + +JS_REQUIRES_STACK JS_FRIEND_API(void) +js_FreeStack(JSContext *cx, void *mark) +{ + JSStackHeader *sh; + jsuword slotdiff; + + /* Check for zero nslots allocation special case. */ + if (!mark) + return; + + /* We can assert because js_FreeStack always balances js_AllocStack. */ + sh = cx->stackHeaders; + JS_ASSERT(sh); + + /* If mark is in the current segment, reduce sh->nslots, else pop sh. */ + slotdiff = JS_UPTRDIFF(mark, JS_STACK_SEGMENT(sh)) / sizeof(jsval); + if (slotdiff < (jsuword)sh->nslots) + sh->nslots = slotdiff; + else + cx->stackHeaders = sh->down; + + /* Release the stackPool space allocated since mark was set. */ + JS_ARENA_RELEASE(&cx->stackPool, mark); +} + +JSObject * +js_GetScopeChain(JSContext *cx, JSStackFrame *fp) +{ + JSObject *sharedBlock = fp->blockChain; + + if (!sharedBlock) { + /* + * Don't force a call object for a lightweight function call, but do + * insist that there is a call object for a heavyweight function call. + */ + JS_ASSERT(!fp->fun || + !(fp->fun->flags & JSFUN_HEAVYWEIGHT) || + fp->callobj); + JS_ASSERT(fp->scopeChain); + return fp->scopeChain; + } + + /* We don't handle cloning blocks on trace. */ + js_LeaveTrace(cx); + + /* + * We have one or more lexical scopes to reflect into fp->scopeChain, so + * make sure there's a call object at the current head of the scope chain, + * if this frame is a call frame. + * + * Also, identify the innermost compiler-allocated block we needn't clone. + */ + JSObject *limitBlock, *limitClone; + if (fp->fun && !fp->callobj) { + JS_ASSERT(OBJ_GET_CLASS(cx, fp->scopeChain) != &js_BlockClass || + fp->scopeChain->getPrivate() != fp); + if (!js_GetCallObject(cx, fp)) + return NULL; + + /* We know we must clone everything on blockChain. */ + limitBlock = limitClone = NULL; + } else { + /* + * scopeChain includes all blocks whose static scope we're within that + * have already been cloned. Find the innermost such block. Its + * prototype should appear on blockChain; we'll clone blockChain up + * to, but not including, that prototype. + */ + limitClone = fp->scopeChain; + while (OBJ_GET_CLASS(cx, limitClone) == &js_WithClass) + limitClone = OBJ_GET_PARENT(cx, limitClone); + JS_ASSERT(limitClone); + + /* + * It may seem like we don't know enough about limitClone to be able + * to just grab its prototype as we do here, but it's actually okay. + * + * If limitClone is a block object belonging to this frame, then its + * prototype is the innermost entry in blockChain that we have already + * cloned, and is thus the place to stop when we clone below. + * + * Otherwise, there are no blocks for this frame on scopeChain, and we + * need to clone the whole blockChain. In this case, limitBlock can + * point to any object known not to be on blockChain, since we simply + * loop until we hit limitBlock or NULL. If limitClone is a block, it + * isn't a block from this function, since blocks can't be nested + * within themselves on scopeChain (recursion is dynamic nesting, not + * static nesting). If limitClone isn't a block, its prototype won't + * be a block either. So we can just grab limitClone's prototype here + * regardless of its type or which frame it belongs to. + */ + limitBlock = OBJ_GET_PROTO(cx, limitClone); + + /* If the innermost block has already been cloned, we are done. */ + if (limitBlock == sharedBlock) + return fp->scopeChain; + } + + /* + * Special-case cloning the innermost block; this doesn't have enough in + * common with subsequent steps to include in the loop. + * + * js_CloneBlockObject leaves the clone's parent slot uninitialized. We + * populate it below. + */ + JSObject *innermostNewChild = js_CloneBlockObject(cx, sharedBlock, fp); + if (!innermostNewChild) + return NULL; + JSAutoTempValueRooter tvr(cx, innermostNewChild); + + /* + * Clone our way towards outer scopes until we reach the innermost + * enclosing function, or the innermost block we've already cloned. + */ + JSObject *newChild = innermostNewChild; + for (;;) { + JS_ASSERT(OBJ_GET_PROTO(cx, newChild) == sharedBlock); + sharedBlock = OBJ_GET_PARENT(cx, sharedBlock); + + /* Sometimes limitBlock will be NULL, so check that first. */ + if (sharedBlock == limitBlock || !sharedBlock) + break; + + /* As in the call above, we don't know the real parent yet. */ + JSObject *clone + = js_CloneBlockObject(cx, sharedBlock, fp); + if (!clone) + return NULL; + + /* + * Avoid OBJ_SET_PARENT overhead as newChild cannot escape to + * other threads. + */ + STOBJ_SET_PARENT(newChild, clone); + newChild = clone; + } + STOBJ_SET_PARENT(newChild, fp->scopeChain); + + + /* + * If we found a limit block belonging to this frame, then we should have + * found it in blockChain. + */ + JS_ASSERT_IF(limitBlock && + OBJ_GET_CLASS(cx, limitBlock) == &js_BlockClass && + limitClone->getPrivate() == fp, + sharedBlock); + + /* Place our newly cloned blocks at the head of the scope chain. */ + fp->scopeChain = innermostNewChild; + return fp->scopeChain; +} + +JSBool +js_GetPrimitiveThis(JSContext *cx, jsval *vp, JSClass *clasp, jsval *thisvp) +{ + jsval v; + JSObject *obj; + + v = vp[1]; + if (JSVAL_IS_OBJECT(v)) { + obj = JS_THIS_OBJECT(cx, vp); + if (!JS_InstanceOf(cx, obj, clasp, vp + 2)) + return JS_FALSE; + v = obj->fslots[JSSLOT_PRIMITIVE_THIS]; + } + *thisvp = v; + return JS_TRUE; +} + +/* Some objects (e.g., With) delegate 'this' to another object. */ +static inline JSObject * +CallThisObjectHook(JSContext *cx, JSObject *obj, jsval *argv) +{ + JSObject *thisp = obj->thisObject(cx); + if (!thisp) + return NULL; + argv[-1] = OBJECT_TO_JSVAL(thisp); + return thisp; +} + +/* + * ECMA requires "the global object", but in embeddings such as the browser, + * which have multiple top-level objects (windows, frames, etc. in the DOM), + * we prefer fun's parent. An example that causes this code to run: + * + * // in window w1 + * function f() { return this } + * function g() { return f } + * + * // in window w2 + * var h = w1.g() + * alert(h() == w1) + * + * The alert should display "true". + */ +JS_STATIC_INTERPRET JSObject * +js_ComputeGlobalThis(JSContext *cx, JSBool lazy, jsval *argv) +{ + JSObject *thisp; + + if (JSVAL_IS_PRIMITIVE(argv[-2]) || + !OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(argv[-2]))) { + thisp = cx->globalObject; + } else { + JSStackFrame *fp; + jsid id; + jsval v; + uintN attrs; + JSBool ok; + JSObject *parent; + + /* + * Walk up the parent chain, first checking that the running script + * has access to the callee's parent object. Note that if lazy, the + * running script whose principals we want to check is the script + * associated with fp->down, not with fp. + * + * FIXME: 417851 -- this access check should not be required, as it + * imposes a performance penalty on all js_ComputeGlobalThis calls, + * and it represents a maintenance hazard. + */ + fp = js_GetTopStackFrame(cx); /* quell GCC overwarning */ + if (lazy) { + JS_ASSERT(fp->argv == argv); + fp->dormantNext = cx->dormantFrameChain; + cx->dormantFrameChain = fp; + cx->fp = fp->down; + fp->down = NULL; + } + thisp = JSVAL_TO_OBJECT(argv[-2]); + id = ATOM_TO_JSID(cx->runtime->atomState.parentAtom); + + ok = thisp->checkAccess(cx, id, JSACC_PARENT, &v, &attrs); + if (lazy) { + cx->dormantFrameChain = fp->dormantNext; + fp->dormantNext = NULL; + fp->down = cx->fp; + cx->fp = fp; + } + if (!ok) + return NULL; + + thisp = JSVAL_IS_VOID(v) + ? OBJ_GET_PARENT(cx, thisp) + : JSVAL_TO_OBJECT(v); + while ((parent = OBJ_GET_PARENT(cx, thisp)) != NULL) + thisp = parent; + } + + return CallThisObjectHook(cx, thisp, argv); +} + +static JSObject * +ComputeThis(JSContext *cx, JSBool lazy, jsval *argv) +{ + JSObject *thisp; + + JS_ASSERT(!JSVAL_IS_NULL(argv[-1])); + if (!JSVAL_IS_OBJECT(argv[-1])) { + if (!js_PrimitiveToObject(cx, &argv[-1])) + return NULL; + thisp = JSVAL_TO_OBJECT(argv[-1]); + return thisp; + } + + thisp = JSVAL_TO_OBJECT(argv[-1]); + if (OBJ_GET_CLASS(cx, thisp) == &js_CallClass || OBJ_GET_CLASS(cx, thisp) == &js_BlockClass) + return js_ComputeGlobalThis(cx, lazy, argv); + + return CallThisObjectHook(cx, thisp, argv); +} + +JSObject * +js_ComputeThis(JSContext *cx, JSBool lazy, jsval *argv) +{ + JS_ASSERT(argv[-1] != JSVAL_HOLE); // check for SynthesizeFrame poisoning + if (JSVAL_IS_NULL(argv[-1])) + return js_ComputeGlobalThis(cx, lazy, argv); + return ComputeThis(cx, lazy, argv); +} + +#if JS_HAS_NO_SUCH_METHOD + +const uint32 JSSLOT_FOUND_FUNCTION = JSSLOT_PRIVATE; +const uint32 JSSLOT_SAVED_ID = JSSLOT_PRIVATE + 1; + +JSClass js_NoSuchMethodClass = { + "NoSuchMethod", + JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL +}; + +/* + * When JSOP_CALLPROP or JSOP_CALLELEM does not find the method property of + * the base object, we search for the __noSuchMethod__ method in the base. + * If it exists, we store the method and the property's id into an object of + * NoSuchMethod class and store this object into the callee's stack slot. + * Later, js_Invoke will recognise such an object and transfer control to + * NoSuchMethod that invokes the method like: + * + * this.__noSuchMethod__(id, args) + * + * where id is the name of the method that this invocation attempted to + * call by name, and args is an Array containing this invocation's actual + * parameters. + */ +JS_STATIC_INTERPRET JSBool +js_OnUnknownMethod(JSContext *cx, jsval *vp) +{ + JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1])); + + JSObject *obj = JSVAL_TO_OBJECT(vp[1]); + jsid id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom); + JSAutoTempValueRooter tvr(cx, JSVAL_NULL); + if (!js_GetMethod(cx, obj, id, JSGET_NO_METHOD_BARRIER, tvr.addr())) + return false; + if (JSVAL_IS_PRIMITIVE(tvr.value())) { + vp[0] = tvr.value(); + } else { +#if JS_HAS_XML_SUPPORT + /* Extract the function name from function::name qname. */ + if (!JSVAL_IS_PRIMITIVE(vp[0])) { + obj = JSVAL_TO_OBJECT(vp[0]); + if (!js_IsFunctionQName(cx, obj, &id)) + return false; + if (id != 0) + vp[0] = ID_TO_VALUE(id); + } +#endif + obj = js_NewObjectWithGivenProto(cx, &js_NoSuchMethodClass, + NULL, NULL); + if (!obj) + return false; + obj->fslots[JSSLOT_FOUND_FUNCTION] = tvr.value(); + obj->fslots[JSSLOT_SAVED_ID] = vp[0]; + vp[0] = OBJECT_TO_JSVAL(obj); + } + return true; +} + +static JS_REQUIRES_STACK JSBool +NoSuchMethod(JSContext *cx, uintN argc, jsval *vp, uint32 flags) +{ + jsval *invokevp; + void *mark; + JSBool ok; + JSObject *obj, *argsobj; + + invokevp = js_AllocStack(cx, 2 + 2, &mark); + if (!invokevp) + return JS_FALSE; + + JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[0])); + JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1])); + obj = JSVAL_TO_OBJECT(vp[0]); + JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_NoSuchMethodClass); + + invokevp[0] = obj->fslots[JSSLOT_FOUND_FUNCTION]; + invokevp[1] = vp[1]; + invokevp[2] = obj->fslots[JSSLOT_SAVED_ID]; + argsobj = js_NewArrayObject(cx, argc, vp + 2); + if (!argsobj) { + ok = JS_FALSE; + } else { + invokevp[3] = OBJECT_TO_JSVAL(argsobj); + ok = (flags & JSINVOKE_CONSTRUCT) + ? js_InvokeConstructor(cx, 2, JS_TRUE, invokevp) + : js_Invoke(cx, 2, invokevp, flags); + vp[0] = invokevp[0]; + } + js_FreeStack(cx, mark); + return ok; +} + +#endif /* JS_HAS_NO_SUCH_METHOD */ + +/* + * We check if the function accepts a primitive value as |this|. For that we + * use a table that maps value's tag into the corresponding function flag. + */ +JS_STATIC_ASSERT(JSVAL_INT == 1); +JS_STATIC_ASSERT(JSVAL_DOUBLE == 2); +JS_STATIC_ASSERT(JSVAL_STRING == 4); +JS_STATIC_ASSERT(JSVAL_SPECIAL == 6); + +const uint16 js_PrimitiveTestFlags[] = { + JSFUN_THISP_NUMBER, /* INT */ + JSFUN_THISP_NUMBER, /* DOUBLE */ + JSFUN_THISP_NUMBER, /* INT */ + JSFUN_THISP_STRING, /* STRING */ + JSFUN_THISP_NUMBER, /* INT */ + JSFUN_THISP_BOOLEAN, /* BOOLEAN */ + JSFUN_THISP_NUMBER /* INT */ +}; + +/* + * Find a function reference and its 'this' object implicit first parameter + * under argc arguments on cx's stack, and call the function. Push missing + * required arguments, allocate declared local variables, and pop everything + * when done. Then push the return value. + */ +JS_REQUIRES_STACK JS_FRIEND_API(JSBool) +js_Invoke(JSContext *cx, uintN argc, jsval *vp, uintN flags) +{ + void *mark; + JSStackFrame frame; + jsval *sp, *argv, *newvp; + jsval v; + JSObject *funobj, *parent; + JSBool ok; + JSClass *clasp; + const JSObjectOps *ops; + JSNative native; + JSFunction *fun; + JSScript *script; + uintN nslots, i; + uint32 rootedArgsFlag; + JSInterpreterHook hook; + void *hookData; + + JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX); + + /* [vp .. vp + 2 + argc) must belong to the last JS stack arena. */ + JS_ASSERT((jsval *) cx->stackPool.current->base <= vp); + JS_ASSERT(vp + 2 + argc <= (jsval *) cx->stackPool.current->avail); + + /* Mark the top of stack and load frequently-used registers. */ + mark = JS_ARENA_MARK(&cx->stackPool); + MUST_FLOW_THROUGH("out2"); + v = *vp; + + if (JSVAL_IS_PRIMITIVE(v)) + goto bad; + + funobj = JSVAL_TO_OBJECT(v); + parent = OBJ_GET_PARENT(cx, funobj); + clasp = OBJ_GET_CLASS(cx, funobj); + if (clasp != &js_FunctionClass) { +#if JS_HAS_NO_SUCH_METHOD + if (clasp == &js_NoSuchMethodClass) { + ok = NoSuchMethod(cx, argc, vp, flags); + goto out2; + } +#endif + + /* Function is inlined, all other classes use object ops. */ + ops = funobj->map->ops; + + /* + * XXX this makes no sense -- why convert to function if clasp->call? + * XXX better to call that hook without converting + * + * FIXME bug 408416: try converting to function, for API compatibility + * if there is a call op defined. + */ + if ((ops == &js_ObjectOps) ? clasp->call : ops->call) { + ok = clasp->convert(cx, funobj, JSTYPE_FUNCTION, &v); + if (!ok) + goto out2; + + if (VALUE_IS_FUNCTION(cx, v)) { + /* Make vp refer to funobj to keep it available as argv[-2]. */ + *vp = v; + funobj = JSVAL_TO_OBJECT(v); + parent = OBJ_GET_PARENT(cx, funobj); + goto have_fun; + } + } + fun = NULL; + script = NULL; + nslots = 0; + + /* Try a call or construct native object op. */ + if (flags & JSINVOKE_CONSTRUCT) { + if (!JSVAL_IS_OBJECT(vp[1])) { + ok = js_PrimitiveToObject(cx, &vp[1]); + if (!ok) + goto out2; + } + native = ops->construct; + } else { + native = ops->call; + } + if (!native) + goto bad; + } else { +have_fun: + /* Get private data and set derived locals from it. */ + fun = GET_FUNCTION_PRIVATE(cx, funobj); + nslots = FUN_MINARGS(fun); + nslots = (nslots > argc) ? nslots - argc : 0; + if (FUN_INTERPRETED(fun)) { + native = NULL; + script = fun->u.i.script; + JS_ASSERT(script); + + if (script->isEmpty()) { + if (flags & JSINVOKE_CONSTRUCT) { + JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1])); + *vp = vp[1]; + } else { + *vp = JSVAL_VOID; + } + ok = JS_TRUE; + goto out2; + } + } else { + native = fun->u.n.native; + script = NULL; + nslots += fun->u.n.extra; + } + + if (JSFUN_BOUND_METHOD_TEST(fun->flags)) { + /* Handle bound method special case. */ + vp[1] = OBJECT_TO_JSVAL(parent); + } else if (!JSVAL_IS_OBJECT(vp[1])) { + JS_ASSERT(!(flags & JSINVOKE_CONSTRUCT)); + if (PRIMITIVE_THIS_TEST(fun, vp[1])) + goto start_call; + } + } + + if (flags & JSINVOKE_CONSTRUCT) { + JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1])); + } else { + /* + * We must call js_ComputeThis in case we are not called from the + * interpreter, where a prior bytecode has computed an appropriate + * |this| already. + * + * But we need to compute |this| eagerly only for so-called "slow" + * (i.e., not fast) native functions. Fast natives must use either + * JS_THIS or JS_THIS_OBJECT, and scripted functions will go through + * the appropriate this-computing bytecode, e.g., JSOP_THIS. + */ + if (native && (!fun || !(fun->flags & JSFUN_FAST_NATIVE))) { + if (!js_ComputeThis(cx, JS_FALSE, vp + 2)) { + ok = JS_FALSE; + goto out2; + } + flags |= JSFRAME_COMPUTED_THIS; + } + } + + start_call: + if (native && fun && (fun->flags & JSFUN_FAST_NATIVE)) { +#ifdef DEBUG_NOT_THROWING + JSBool alreadyThrowing = cx->throwing; +#endif + JS_ASSERT(nslots == 0); + ok = ((JSFastNative) native)(cx, argc, vp); + JS_RUNTIME_METER(cx->runtime, nativeCalls); +#ifdef DEBUG_NOT_THROWING + if (ok && !alreadyThrowing) + ASSERT_NOT_THROWING(cx); +#endif + goto out2; + } + + argv = vp + 2; + sp = argv + argc; + + rootedArgsFlag = JSFRAME_ROOTED_ARGV; + if (nslots != 0) { + /* + * The extra slots required by the function continue with argument + * slots. Thus, when the last stack pool arena does not have room to + * fit nslots right after sp and AllocateAfterSP fails, we have to copy + * [vp..vp+2+argc) slots and clear rootedArgsFlag to root the copy. + */ + if (!AllocateAfterSP(cx, sp, nslots)) { + rootedArgsFlag = 0; + newvp = js_AllocRawStack(cx, 2 + argc + nslots, NULL); + if (!newvp) { + ok = JS_FALSE; + goto out2; + } + memcpy(newvp, vp, (2 + argc) * sizeof(jsval)); + argv = newvp + 2; + sp = argv + argc; + } + + /* Push void to initialize missing args. */ + i = nslots; + do { + *sp++ = JSVAL_VOID; + } while (--i != 0); + } + + /* Allocate space for local variables and stack of interpreted function. */ + if (script && script->nslots != 0) { + if (!AllocateAfterSP(cx, sp, script->nslots)) { + /* NB: Discontinuity between argv and slots, stack slots. */ + sp = js_AllocRawStack(cx, script->nslots, NULL); + if (!sp) { + ok = JS_FALSE; + goto out2; + } + } + + /* Push void to initialize local variables. */ + for (jsval *end = sp + fun->u.i.nvars; sp != end; ++sp) + *sp = JSVAL_VOID; + } + + /* + * Initialize the frame. + */ + frame.thisv = vp[1]; + frame.varobj = NULL; + frame.callobj = NULL; + frame.argsobj = NULL; + frame.script = script; + frame.fun = fun; + frame.argc = argc; + frame.argv = argv; + + /* Default return value for a constructor is the new object. */ + frame.rval = (flags & JSINVOKE_CONSTRUCT) ? vp[1] : JSVAL_VOID; + frame.down = cx->fp; + frame.annotation = NULL; + frame.scopeChain = NULL; /* set below for real, after cx->fp is set */ + frame.blockChain = NULL; + frame.regs = NULL; + frame.imacpc = NULL; + frame.slots = NULL; + frame.flags = flags | rootedArgsFlag; + frame.dormantNext = NULL; + frame.displaySave = NULL; + + MUST_FLOW_THROUGH("out"); + cx->fp = &frame; + + /* Init these now in case we goto out before first hook call. */ + hook = cx->debugHooks->callHook; + hookData = NULL; + + if (native) { + /* If native, use caller varobj and scopeChain for eval. */ + JS_ASSERT(!frame.varobj); + JS_ASSERT(!frame.scopeChain); + if (frame.down) { + frame.varobj = frame.down->varobj; + frame.scopeChain = frame.down->scopeChain; + } + + /* But ensure that we have a scope chain. */ + if (!frame.scopeChain) + frame.scopeChain = parent; + } else { + /* Use parent scope so js_GetCallObject can find the right "Call". */ + frame.scopeChain = parent; + if (JSFUN_HEAVYWEIGHT_TEST(fun->flags)) { + /* Scope with a call object parented by the callee's parent. */ + if (!js_GetCallObject(cx, &frame)) { + ok = JS_FALSE; + goto out; + } + } + frame.slots = sp - fun->u.i.nvars; + } + + /* Call the hook if present after we fully initialized the frame. */ + if (hook) + hookData = hook(cx, &frame, JS_TRUE, 0, cx->debugHooks->callHookData); + +#ifdef INCLUDE_MOZILLA_DTRACE + /* DTrace function entry, non-inlines */ + if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED()) + jsdtrace_function_entry(cx, &frame, fun); + if (JAVASCRIPT_FUNCTION_INFO_ENABLED()) + jsdtrace_function_info(cx, &frame, frame.down, fun); + if (JAVASCRIPT_FUNCTION_ARGS_ENABLED()) + jsdtrace_function_args(cx, &frame, fun, frame.argc, frame.argv); +#endif + + /* Call the function, either a native method or an interpreted script. */ + if (native) { +#ifdef DEBUG_NOT_THROWING + JSBool alreadyThrowing = cx->throwing; +#endif + /* Primitive |this| should not be passed to slow natives. */ + JSObject *thisp = JSVAL_TO_OBJECT(frame.thisv); + ok = native(cx, thisp, argc, frame.argv, &frame.rval); + JS_RUNTIME_METER(cx->runtime, nativeCalls); +#ifdef DEBUG_NOT_THROWING + if (ok && !alreadyThrowing) + ASSERT_NOT_THROWING(cx); +#endif + } else { + JS_ASSERT(script); + ok = js_Interpret(cx); + } + +#ifdef INCLUDE_MOZILLA_DTRACE + /* DTrace function return, non-inlines */ + if (JAVASCRIPT_FUNCTION_RVAL_ENABLED()) + jsdtrace_function_rval(cx, &frame, fun, &frame.rval); + if (JAVASCRIPT_FUNCTION_RETURN_ENABLED()) + jsdtrace_function_return(cx, &frame, fun); +#endif + +out: + if (hookData) { + hook = cx->debugHooks->callHook; + if (hook) + hook(cx, &frame, JS_FALSE, &ok, hookData); + } + + frame.putActivationObjects(cx); + + *vp = frame.rval; + + /* Restore cx->fp now that we're done releasing frame objects. */ + cx->fp = frame.down; + +out2: + /* Pop everything we may have allocated off the stack. */ + JS_ARENA_RELEASE(&cx->stackPool, mark); + if (!ok) + *vp = JSVAL_NULL; + return ok; + +bad: + js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_FUNFLAGS); + ok = JS_FALSE; + goto out2; +} + +JSBool +js_InternalInvoke(JSContext *cx, JSObject *obj, jsval fval, uintN flags, + uintN argc, jsval *argv, jsval *rval) +{ + jsval *invokevp; + void *mark; + JSBool ok; + + js_LeaveTrace(cx); + invokevp = js_AllocStack(cx, 2 + argc, &mark); + if (!invokevp) + return JS_FALSE; + + invokevp[0] = fval; + invokevp[1] = OBJECT_TO_JSVAL(obj); + memcpy(invokevp + 2, argv, argc * sizeof *argv); + + ok = js_Invoke(cx, argc, invokevp, flags); + if (ok) { + /* + * Store *rval in the a scoped local root if a scope is open, else in + * the lastInternalResult pigeon-hole GC root, solely so users of + * js_InternalInvoke and its direct and indirect (js_ValueToString for + * example) callers do not need to manage roots for local, temporary + * references to such results. + */ + *rval = *invokevp; + if (JSVAL_IS_GCTHING(*rval) && *rval != JSVAL_NULL) { + JSLocalRootStack *lrs = JS_THREAD_DATA(cx)->localRootStack; + if (lrs) { + if (js_PushLocalRoot(cx, lrs, *rval) < 0) + ok = JS_FALSE; + } else { + cx->weakRoots.lastInternalResult = *rval; + } + } + } + + js_FreeStack(cx, mark); + return ok; +} + +JSBool +js_InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, jsval fval, + JSAccessMode mode, uintN argc, jsval *argv, jsval *rval) +{ + js_LeaveTrace(cx); + + /* + * js_InternalInvoke could result in another try to get or set the same id + * again, see bug 355497. + */ + JS_CHECK_RECURSION(cx, return JS_FALSE); + + return js_InternalCall(cx, obj, fval, argc, argv, rval); +} + +JSBool +js_Execute(JSContext *cx, JSObject *chain, JSScript *script, + JSStackFrame *down, uintN flags, jsval *result) +{ + JSInterpreterHook hook; + void *hookData, *mark; + JSStackFrame *oldfp, frame; + JSObject *obj, *tmp; + JSBool ok; + + if (script->isEmpty()) { + if (result) + *result = JSVAL_VOID; + return JS_TRUE; + } + + js_LeaveTrace(cx); + +#ifdef INCLUDE_MOZILLA_DTRACE + if (JAVASCRIPT_EXECUTE_START_ENABLED()) + jsdtrace_execute_start(script); +#endif + + hook = cx->debugHooks->executeHook; + hookData = mark = NULL; + oldfp = js_GetTopStackFrame(cx); + frame.script = script; + if (down) { + /* Propagate arg state for eval and the debugger API. */ + frame.callobj = down->callobj; + frame.argsobj = down->argsobj; + frame.varobj = down->varobj; + frame.fun = (script->staticLevel > 0) ? down->fun : NULL; + frame.thisv = down->thisv; + if (down->flags & JSFRAME_COMPUTED_THIS) + flags |= JSFRAME_COMPUTED_THIS; + frame.argc = down->argc; + frame.argv = down->argv; + frame.annotation = down->annotation; + } else { + frame.callobj = NULL; + frame.argsobj = NULL; + obj = chain; + if (cx->options & JSOPTION_VAROBJFIX) { + while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL) + obj = tmp; + } + frame.varobj = obj; + frame.fun = NULL; + frame.thisv = OBJECT_TO_JSVAL(chain); + frame.argc = 0; + frame.argv = NULL; + frame.annotation = NULL; + } + + frame.imacpc = NULL; + if (script->nslots != 0) { + frame.slots = js_AllocRawStack(cx, script->nslots, &mark); + if (!frame.slots) { + ok = JS_FALSE; + goto out; + } + memset(frame.slots, 0, script->nfixed * sizeof(jsval)); + +#if JS_HAS_SHARP_VARS + JS_STATIC_ASSERT(SHARP_NSLOTS == 2); + + if (script->hasSharps) { + JS_ASSERT(script->nfixed >= SHARP_NSLOTS); + jsval *sharps = &frame.slots[script->nfixed - SHARP_NSLOTS]; + + if (down && down->script && down->script->hasSharps) { + JS_ASSERT(down->script->nfixed >= SHARP_NSLOTS); + int base = (down->fun && !(down->flags & JSFRAME_SPECIAL)) + ? down->fun->sharpSlotBase(cx) + : down->script->nfixed - SHARP_NSLOTS; + if (base < 0) { + ok = JS_FALSE; + goto out; + } + sharps[0] = down->slots[base]; + sharps[1] = down->slots[base + 1]; + } else { + sharps[0] = sharps[1] = JSVAL_VOID; + } + } +#endif + } else { + frame.slots = NULL; + } + + frame.rval = JSVAL_VOID; + frame.down = down; + frame.scopeChain = chain; + frame.regs = NULL; + frame.flags = flags; + frame.dormantNext = NULL; + frame.blockChain = NULL; + + /* + * Here we wrap the call to js_Interpret with code to (conditionally) + * save and restore the old stack frame chain into a chain of 'dormant' + * frame chains. Since we are replacing cx->fp, we were running into + * the problem that if GC was called under this frame, some of the GC + * things associated with the old frame chain (available here only in + * the C variable 'oldfp') were not rooted and were being collected. + * + * So, now we preserve the links to these 'dormant' frame chains in cx + * before calling js_Interpret and cleanup afterwards. The GC walks + * these dormant chains and marks objects in the same way that it marks + * objects in the primary cx->fp chain. + */ + if (oldfp && oldfp != down) { + JS_ASSERT(!oldfp->dormantNext); + oldfp->dormantNext = cx->dormantFrameChain; + cx->dormantFrameChain = oldfp; + } + + cx->fp = &frame; + if (!down) { + OBJ_TO_INNER_OBJECT(cx, chain); + if (!chain) + return JS_FALSE; + frame.scopeChain = chain; + + JSObject *thisp = JSVAL_TO_OBJECT(frame.thisv)->thisObject(cx); + if (!thisp) { + ok = JS_FALSE; + goto out2; + } + frame.thisv = OBJECT_TO_JSVAL(thisp); + frame.flags |= JSFRAME_COMPUTED_THIS; + } + + if (hook) { + hookData = hook(cx, &frame, JS_TRUE, 0, + cx->debugHooks->executeHookData); + } + + ok = js_Interpret(cx); + if (result) + *result = frame.rval; + + if (hookData) { + hook = cx->debugHooks->executeHook; + if (hook) + hook(cx, &frame, JS_FALSE, &ok, hookData); + } + +out2: + if (mark) + js_FreeRawStack(cx, mark); + cx->fp = oldfp; + + if (oldfp && oldfp != down) { + JS_ASSERT(cx->dormantFrameChain == oldfp); + cx->dormantFrameChain = oldfp->dormantNext; + oldfp->dormantNext = NULL; + } + +out: +#ifdef INCLUDE_MOZILLA_DTRACE + if (JAVASCRIPT_EXECUTE_DONE_ENABLED()) + jsdtrace_execute_done(script); +#endif + return ok; +} + +JSBool +js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs, + JSObject **objp, JSProperty **propp) +{ + JSObject *obj2; + JSProperty *prop; + uintN oldAttrs, report; + bool isFunction; + jsval value; + const char *type, *name; + + /* + * Both objp and propp must be either null or given. When given, *propp + * must be null. This way we avoid an extra "if (propp) *propp = NULL" for + * the common case of a non-existing property. + */ + JS_ASSERT(!objp == !propp); + JS_ASSERT_IF(propp, !*propp); + + /* The JSPROP_INITIALIZER case below may generate a warning. Since we must + * drop the property before reporting it, we insists on !propp to avoid + * looking up the property again after the reporting is done. + */ + JS_ASSERT_IF(attrs & JSPROP_INITIALIZER, attrs == JSPROP_INITIALIZER); + JS_ASSERT_IF(attrs == JSPROP_INITIALIZER, !propp); + + if (!obj->lookupProperty(cx, id, &obj2, &prop)) + return JS_FALSE; + if (!prop) + return JS_TRUE; + + /* Use prop as a speedup hint to obj->getAttributes. */ + if (!obj2->getAttributes(cx, id, prop, &oldAttrs)) { + obj2->dropProperty(cx, prop); + return JS_FALSE; + } + + /* + * If our caller doesn't want prop, drop it (we don't need it any longer). + */ + if (!propp) { + obj2->dropProperty(cx, prop); + prop = NULL; + } else { + *objp = obj2; + *propp = prop; + } + + if (attrs == JSPROP_INITIALIZER) { + /* Allow the new object to override properties. */ + if (obj2 != obj) + return JS_TRUE; + + /* The property must be dropped already. */ + JS_ASSERT(!prop); + report = JSREPORT_WARNING | JSREPORT_STRICT; + +#ifdef __GNUC__ + isFunction = false; /* suppress bogus gcc warnings */ +#endif + } else { + /* We allow redeclaring some non-readonly properties. */ + if (((oldAttrs | attrs) & JSPROP_READONLY) == 0) { + /* Allow redeclaration of variables and functions. */ + if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER))) + return JS_TRUE; + + /* + * Allow adding a getter only if a property already has a setter + * but no getter and similarly for adding a setter. That is, we + * allow only the following transitions: + * + * no-property --> getter --> getter + setter + * no-property --> setter --> getter + setter + */ + if ((~(oldAttrs ^ attrs) & (JSPROP_GETTER | JSPROP_SETTER)) == 0) + return JS_TRUE; + + /* + * Allow redeclaration of an impermanent property (in which case + * anyone could delete it and redefine it, willy-nilly). + */ + if (!(oldAttrs & JSPROP_PERMANENT)) + return JS_TRUE; + } + if (prop) + obj2->dropProperty(cx, prop); + + report = JSREPORT_ERROR; + isFunction = (oldAttrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0; + if (!isFunction) { + if (!obj->getProperty(cx, id, &value)) + return JS_FALSE; + isFunction = VALUE_IS_FUNCTION(cx, value); + } + } + + type = (attrs == JSPROP_INITIALIZER) + ? "property" + : (oldAttrs & attrs & JSPROP_GETTER) + ? js_getter_str + : (oldAttrs & attrs & JSPROP_SETTER) + ? js_setter_str + : (oldAttrs & JSPROP_READONLY) + ? js_const_str + : isFunction + ? js_function_str + : js_var_str; + name = js_ValueToPrintableString(cx, ID_TO_VALUE(id)); + if (!name) + return JS_FALSE; + return JS_ReportErrorFlagsAndNumber(cx, report, + js_GetErrorMessage, NULL, + JSMSG_REDECLARED_VAR, + type, name); +} + +JSBool +js_StrictlyEqual(JSContext *cx, jsval lval, jsval rval) +{ + jsval ltag = JSVAL_TAG(lval), rtag = JSVAL_TAG(rval); + jsdouble ld, rd; + + if (ltag == rtag) { + if (ltag == JSVAL_STRING) { + JSString *lstr = JSVAL_TO_STRING(lval), + *rstr = JSVAL_TO_STRING(rval); + return js_EqualStrings(lstr, rstr); + } + if (ltag == JSVAL_DOUBLE) { + ld = *JSVAL_TO_DOUBLE(lval); + rd = *JSVAL_TO_DOUBLE(rval); + return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE); + } + if (ltag == JSVAL_OBJECT && + lval != rval && + !JSVAL_IS_NULL(lval) && + !JSVAL_IS_NULL(rval)) { + JSObject *lobj, *robj; + + lobj = js_GetWrappedObject(cx, JSVAL_TO_OBJECT(lval)); + robj = js_GetWrappedObject(cx, JSVAL_TO_OBJECT(rval)); + lval = OBJECT_TO_JSVAL(lobj); + rval = OBJECT_TO_JSVAL(robj); + } + return lval == rval; + } + if (ltag == JSVAL_DOUBLE && JSVAL_IS_INT(rval)) { + ld = *JSVAL_TO_DOUBLE(lval); + rd = JSVAL_TO_INT(rval); + return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE); + } + if (JSVAL_IS_INT(lval) && rtag == JSVAL_DOUBLE) { + ld = JSVAL_TO_INT(lval); + rd = *JSVAL_TO_DOUBLE(rval); + return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE); + } + return lval == rval; +} + +static inline bool +IsNegativeZero(jsval v) +{ + return JSVAL_IS_DOUBLE(v) && JSDOUBLE_IS_NEGZERO(*JSVAL_TO_DOUBLE(v)); +} + +static inline bool +IsNaN(jsval v) +{ + return JSVAL_IS_DOUBLE(v) && JSDOUBLE_IS_NaN(*JSVAL_TO_DOUBLE(v)); +} + +JSBool +js_SameValue(jsval v1, jsval v2, JSContext *cx) +{ + if (IsNegativeZero(v1)) + return IsNegativeZero(v2); + if (IsNegativeZero(v2)) + return JS_FALSE; + if (IsNaN(v1) && IsNaN(v2)) + return JS_TRUE; + return js_StrictlyEqual(cx, v1, v2); +} + +JS_REQUIRES_STACK JSBool +js_InvokeConstructor(JSContext *cx, uintN argc, JSBool clampReturn, jsval *vp) +{ + JSFunction *fun, *fun2; + JSObject *obj, *obj2, *proto, *parent; + jsval lval, rval; + JSClass *clasp; + + fun = NULL; + obj2 = NULL; + lval = *vp; + if (!JSVAL_IS_OBJECT(lval) || + (obj2 = JSVAL_TO_OBJECT(lval)) == NULL || + /* XXX clean up to avoid special cases above ObjectOps layer */ + OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass || + !obj2->map->ops->construct) + { + fun = js_ValueToFunction(cx, vp, JSV2F_CONSTRUCT); + if (!fun) + return JS_FALSE; + } + + clasp = &js_ObjectClass; + if (!obj2) { + proto = parent = NULL; + fun = NULL; + } else { + /* + * Get the constructor prototype object for this function. + * Use the nominal 'this' parameter slot, vp[1], as a local + * root to protect this prototype, in case it has no other + * strong refs. + */ + if (!obj2->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), + &vp[1])) { + return JS_FALSE; + } + rval = vp[1]; + proto = JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : NULL; + parent = OBJ_GET_PARENT(cx, obj2); + + if (OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass) { + fun2 = GET_FUNCTION_PRIVATE(cx, obj2); + if (!FUN_INTERPRETED(fun2) && fun2->u.n.clasp) + clasp = fun2->u.n.clasp; + } + } + obj = js_NewObject(cx, clasp, proto, parent); + if (!obj) + return JS_FALSE; + + /* Now we have an object with a constructor method; call it. */ + vp[1] = OBJECT_TO_JSVAL(obj); + if (!js_Invoke(cx, argc, vp, JSINVOKE_CONSTRUCT)) + return JS_FALSE; + + /* Check the return value and if it's primitive, force it to be obj. */ + rval = *vp; + if (clampReturn && JSVAL_IS_PRIMITIVE(rval)) { + if (!fun) { + /* native [[Construct]] returning primitive is error */ + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_NEW_RESULT, + js_ValueToPrintableString(cx, rval)); + return JS_FALSE; + } + *vp = OBJECT_TO_JSVAL(obj); + } + + JS_RUNTIME_METER(cx->runtime, constructs); + return JS_TRUE; +} + +JSBool +js_InternNonIntElementId(JSContext *cx, JSObject *obj, jsval idval, jsid *idp) +{ + JS_ASSERT(!JSVAL_IS_INT(idval)); + +#if JS_HAS_XML_SUPPORT + if (!JSVAL_IS_PRIMITIVE(idval)) { + if (OBJECT_IS_XML(cx, obj)) { + *idp = OBJECT_JSVAL_TO_JSID(idval); + return JS_TRUE; + } + if (!js_IsFunctionQName(cx, JSVAL_TO_OBJECT(idval), idp)) + return JS_FALSE; + if (*idp != 0) + return JS_TRUE; + } +#endif + + return js_ValueToStringId(cx, idval, idp); +} + +/* + * Enter the new with scope using an object at sp[-1] and associate the depth + * of the with block with sp + stackIndex. + */ +JS_STATIC_INTERPRET JS_REQUIRES_STACK JSBool +js_EnterWith(JSContext *cx, jsint stackIndex) +{ + JSStackFrame *fp; + jsval *sp; + JSObject *obj, *parent, *withobj; + + fp = cx->fp; + sp = fp->regs->sp; + JS_ASSERT(stackIndex < 0); + JS_ASSERT(StackBase(fp) <= sp + stackIndex); + + if (!JSVAL_IS_PRIMITIVE(sp[-1])) { + obj = JSVAL_TO_OBJECT(sp[-1]); + } else { + obj = js_ValueToNonNullObject(cx, sp[-1]); + if (!obj) + return JS_FALSE; + sp[-1] = OBJECT_TO_JSVAL(obj); + } + + parent = js_GetScopeChain(cx, fp); + if (!parent) + return JS_FALSE; + + OBJ_TO_INNER_OBJECT(cx, obj); + if (!obj) + return JS_FALSE; + + withobj = js_NewWithObject(cx, obj, parent, + sp + stackIndex - StackBase(fp)); + if (!withobj) + return JS_FALSE; + + fp->scopeChain = withobj; + return JS_TRUE; +} + +JS_STATIC_INTERPRET JS_REQUIRES_STACK void +js_LeaveWith(JSContext *cx) +{ + JSObject *withobj; + + withobj = cx->fp->scopeChain; + JS_ASSERT(OBJ_GET_CLASS(cx, withobj) == &js_WithClass); + JS_ASSERT(withobj->getPrivate() == cx->fp); + JS_ASSERT(OBJ_BLOCK_DEPTH(cx, withobj) >= 0); + cx->fp->scopeChain = OBJ_GET_PARENT(cx, withobj); + withobj->setPrivate(NULL); +} + +JS_REQUIRES_STACK JSClass * +js_IsActiveWithOrBlock(JSContext *cx, JSObject *obj, int stackDepth) +{ + JSClass *clasp; + + clasp = OBJ_GET_CLASS(cx, obj); + if ((clasp == &js_WithClass || clasp == &js_BlockClass) && + obj->getPrivate() == cx->fp && + OBJ_BLOCK_DEPTH(cx, obj) >= stackDepth) { + return clasp; + } + return NULL; +} + +/* + * Unwind block and scope chains to match the given depth. The function sets + * fp->sp on return to stackDepth. + */ +JS_REQUIRES_STACK JSBool +js_UnwindScope(JSContext *cx, JSStackFrame *fp, jsint stackDepth, + JSBool normalUnwind) +{ + JSObject *obj; + JSClass *clasp; + + JS_ASSERT(stackDepth >= 0); + JS_ASSERT(StackBase(fp) + stackDepth <= fp->regs->sp); + + for (obj = fp->blockChain; obj; obj = OBJ_GET_PARENT(cx, obj)) { + JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass); + if (OBJ_BLOCK_DEPTH(cx, obj) < stackDepth) + break; + } + fp->blockChain = obj; + + for (;;) { + obj = fp->scopeChain; + clasp = js_IsActiveWithOrBlock(cx, obj, stackDepth); + if (!clasp) + break; + if (clasp == &js_BlockClass) { + /* Don't fail until after we've updated all stacks. */ + normalUnwind &= js_PutBlockObject(cx, normalUnwind); + } else { + js_LeaveWith(cx); + } + } + + fp->regs->sp = StackBase(fp) + stackDepth; + return normalUnwind; +} + +JS_STATIC_INTERPRET JSBool +js_DoIncDec(JSContext *cx, const JSCodeSpec *cs, jsval *vp, jsval *vp2) +{ + jsval v; + jsdouble d; + + v = *vp; + if (JSVAL_IS_DOUBLE(v)) { + d = *JSVAL_TO_DOUBLE(v); + } else if (JSVAL_IS_INT(v)) { + d = JSVAL_TO_INT(v); + } else { + d = js_ValueToNumber(cx, vp); + if (JSVAL_IS_NULL(*vp)) + return JS_FALSE; + JS_ASSERT(JSVAL_IS_NUMBER(*vp) || *vp == JSVAL_TRUE); + + /* Store the result of v conversion back in vp for post increments. */ + if ((cs->format & JOF_POST) && + *vp == JSVAL_TRUE + && !js_NewNumberInRootedValue(cx, d, vp)) { + return JS_FALSE; + } + } + + (cs->format & JOF_INC) ? d++ : d--; + if (!js_NewNumberInRootedValue(cx, d, vp2)) + return JS_FALSE; + + if (!(cs->format & JOF_POST)) + *vp = *vp2; + return JS_TRUE; +} + +jsval& +js_GetUpvar(JSContext *cx, uintN level, uintN cookie) +{ + level -= UPVAR_FRAME_SKIP(cookie); + JS_ASSERT(level < JS_DISPLAY_SIZE); + + JSStackFrame *fp = cx->display[level]; + JS_ASSERT(fp->script); + + uintN slot = UPVAR_FRAME_SLOT(cookie); + jsval *vp; + + if (!fp->fun) { + vp = fp->slots + fp->script->nfixed; + } else if (slot < fp->fun->nargs) { + vp = fp->argv; + } else if (slot == CALLEE_UPVAR_SLOT) { + vp = &fp->argv[-2]; + slot = 0; + } else { + slot -= fp->fun->nargs; + JS_ASSERT(slot < fp->script->nslots); + vp = fp->slots; + } + + return vp[slot]; +} + +#ifdef DEBUG + +JS_STATIC_INTERPRET JS_REQUIRES_STACK void +js_TraceOpcode(JSContext *cx) +{ + FILE *tracefp; + JSStackFrame *fp; + JSFrameRegs *regs; + intN ndefs, n, nuses; + jsval *siter; + JSString *str; + JSOp op; + + tracefp = (FILE *) cx->tracefp; + JS_ASSERT(tracefp); + fp = cx->fp; + regs = fp->regs; + + /* + * Operations in prologues don't produce interesting values, and + * js_DecompileValueGenerator isn't set up to handle them anyway. + */ + if (cx->tracePrevPc && regs->pc >= fp->script->main) { + JSOp tracePrevOp = JSOp(*cx->tracePrevPc); + ndefs = js_GetStackDefs(cx, &js_CodeSpec[tracePrevOp], tracePrevOp, + fp->script, cx->tracePrevPc); + + /* + * If there aren't that many elements on the stack, then we have + * probably entered a new frame, and printing output would just be + * misleading. + */ + if (ndefs != 0 && + ndefs < regs->sp - fp->slots) { + for (n = -ndefs; n < 0; n++) { + char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n], + NULL); + if (bytes) { + fprintf(tracefp, "%s %s", + (n == -ndefs) ? " output:" : ",", + bytes); + cx->free(bytes); + } + } + fprintf(tracefp, " @ %u\n", (uintN) (regs->sp - StackBase(fp))); + } + fprintf(tracefp, " stack: "); + for (siter = StackBase(fp); siter < regs->sp; siter++) { + str = js_ValueToString(cx, *siter); + if (!str) + fputs("", tracefp); + else + js_FileEscapedString(tracefp, str, 0); + fputc(' ', tracefp); + } + fputc('\n', tracefp); + } + + fprintf(tracefp, "%4u: ", + js_PCToLineNumber(cx, fp->script, fp->imacpc ? fp->imacpc : regs->pc)); + js_Disassemble1(cx, fp->script, regs->pc, + regs->pc - fp->script->code, + JS_FALSE, tracefp); + op = (JSOp) *regs->pc; + nuses = js_GetStackUses(&js_CodeSpec[op], op, regs->pc); + if (nuses != 0) { + for (n = -nuses; n < 0; n++) { + char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n], + NULL); + if (bytes) { + fprintf(tracefp, "%s %s", + (n == -nuses) ? " inputs:" : ",", + bytes); + cx->free(bytes); + } + } + fprintf(tracefp, " @ %u\n", (uintN) (regs->sp - StackBase(fp))); + } + cx->tracePrevPc = regs->pc; + + /* It's nice to have complete traces when debugging a crash. */ + fflush(tracefp); +} + +#endif /* DEBUG */ + +#ifdef JS_OPMETER + +# include + +# define HIST_NSLOTS 8 + +/* + * The second dimension is hardcoded at 256 because we know that many bits fit + * in a byte, and mainly to optimize away multiplying by JSOP_LIMIT to address + * any particular row. + */ +static uint32 succeeds[JSOP_LIMIT][256]; +static uint32 slot_ops[JSOP_LIMIT][HIST_NSLOTS]; + +JS_STATIC_INTERPRET void +js_MeterOpcodePair(JSOp op1, JSOp op2) +{ + if (op1 != JSOP_STOP) + ++succeeds[op1][op2]; +} + +JS_STATIC_INTERPRET void +js_MeterSlotOpcode(JSOp op, uint32 slot) +{ + if (slot < HIST_NSLOTS) + ++slot_ops[op][slot]; +} + +typedef struct Edge { + const char *from; + const char *to; + uint32 count; +} Edge; + +static int +compare_edges(const void *a, const void *b) +{ + const Edge *ea = (const Edge *) a; + const Edge *eb = (const Edge *) b; + + return (int32)eb->count - (int32)ea->count; +} + +void +js_DumpOpMeters() +{ + const char *name, *from, *style; + FILE *fp; + uint32 total, count; + uint32 i, j, nedges; + Edge *graph; + + name = getenv("JS_OPMETER_FILE"); + if (!name) + name = "/tmp/ops.dot"; + fp = fopen(name, "w"); + if (!fp) { + perror(name); + return; + } + + total = nedges = 0; + for (i = 0; i < JSOP_LIMIT; i++) { + for (j = 0; j < JSOP_LIMIT; j++) { + count = succeeds[i][j]; + if (count != 0) { + total += count; + ++nedges; + } + } + } + +# define SIGNIFICANT(count,total) (200. * (count) >= (total)) + + graph = (Edge *) js_calloc(nedges * sizeof graph[0]); + for (i = nedges = 0; i < JSOP_LIMIT; i++) { + from = js_CodeName[i]; + for (j = 0; j < JSOP_LIMIT; j++) { + count = succeeds[i][j]; + if (count != 0 && SIGNIFICANT(count, total)) { + graph[nedges].from = from; + graph[nedges].to = js_CodeName[j]; + graph[nedges].count = count; + ++nedges; + } + } + } + qsort(graph, nedges, sizeof(Edge), compare_edges); + +# undef SIGNIFICANT + + fputs("digraph {\n", fp); + for (i = 0, style = NULL; i < nedges; i++) { + JS_ASSERT(i == 0 || graph[i-1].count >= graph[i].count); + if (!style || graph[i-1].count != graph[i].count) { + style = (i > nedges * .75) ? "dotted" : + (i > nedges * .50) ? "dashed" : + (i > nedges * .25) ? "solid" : "bold"; + } + fprintf(fp, " %s -> %s [label=\"%lu\" style=%s]\n", + graph[i].from, graph[i].to, + (unsigned long)graph[i].count, style); + } + js_free(graph); + fputs("}\n", fp); + fclose(fp); + + name = getenv("JS_OPMETER_HIST"); + if (!name) + name = "/tmp/ops.hist"; + fp = fopen(name, "w"); + if (!fp) { + perror(name); + return; + } + fputs("bytecode", fp); + for (j = 0; j < HIST_NSLOTS; j++) + fprintf(fp, " slot %1u", (unsigned)j); + putc('\n', fp); + fputs("========", fp); + for (j = 0; j < HIST_NSLOTS; j++) + fputs(" =======", fp); + putc('\n', fp); + for (i = 0; i < JSOP_LIMIT; i++) { + for (j = 0; j < HIST_NSLOTS; j++) { + if (slot_ops[i][j] != 0) { + /* Reuse j in the next loop, since we break after. */ + fprintf(fp, "%-8.8s", js_CodeName[i]); + for (j = 0; j < HIST_NSLOTS; j++) + fprintf(fp, " %7lu", (unsigned long)slot_ops[i][j]); + putc('\n', fp); + break; + } + } + } + fclose(fp); +} + +#endif /* JS_OPSMETER */ + +#endif /* !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___ */ + +#ifndef jsinvoke_cpp___ + +#define PUSH(v) (*regs.sp++ = (v)) +#define PUSH_OPND(v) PUSH(v) +#define STORE_OPND(n,v) (regs.sp[n] = (v)) +#define POP() (*--regs.sp) +#define POP_OPND() POP() +#define FETCH_OPND(n) (regs.sp[n]) + +/* + * Push the jsdouble d using sp from the lexical environment. Try to convert d + * to a jsint that fits in a jsval, otherwise GC-alloc space for it and push a + * reference. + */ +#define STORE_NUMBER(cx, n, d) \ + JS_BEGIN_MACRO \ + jsint i_; \ + \ + if (JSDOUBLE_IS_INT(d, i_) && INT_FITS_IN_JSVAL(i_)) \ + regs.sp[n] = INT_TO_JSVAL(i_); \ + else if (!js_NewDoubleInRootedValue(cx, d, ®s.sp[n])) \ + goto error; \ + JS_END_MACRO + +#define STORE_INT(cx, n, i) \ + JS_BEGIN_MACRO \ + if (INT_FITS_IN_JSVAL(i)) \ + regs.sp[n] = INT_TO_JSVAL(i); \ + else if (!js_NewDoubleInRootedValue(cx, (jsdouble) (i), ®s.sp[n])) \ + goto error; \ + JS_END_MACRO + +#define STORE_UINT(cx, n, u) \ + JS_BEGIN_MACRO \ + if ((u) <= JSVAL_INT_MAX) \ + regs.sp[n] = INT_TO_JSVAL(u); \ + else if (!js_NewDoubleInRootedValue(cx, (jsdouble) (u), ®s.sp[n])) \ + goto error; \ + JS_END_MACRO + +#define FETCH_NUMBER(cx, n, d) \ + JS_BEGIN_MACRO \ + jsval v_; \ + \ + v_ = FETCH_OPND(n); \ + VALUE_TO_NUMBER(cx, n, v_, d); \ + JS_END_MACRO + +#define FETCH_INT(cx, n, i) \ + JS_BEGIN_MACRO \ + jsval v_; \ + \ + v_= FETCH_OPND(n); \ + if (JSVAL_IS_INT(v_)) { \ + i = JSVAL_TO_INT(v_); \ + } else { \ + i = js_ValueToECMAInt32(cx, ®s.sp[n]); \ + if (JSVAL_IS_NULL(regs.sp[n])) \ + goto error; \ + } \ + JS_END_MACRO + +#define FETCH_UINT(cx, n, ui) \ + JS_BEGIN_MACRO \ + jsval v_; \ + \ + v_= FETCH_OPND(n); \ + if (JSVAL_IS_INT(v_)) { \ + ui = (uint32) JSVAL_TO_INT(v_); \ + } else { \ + ui = js_ValueToECMAUint32(cx, ®s.sp[n]); \ + if (JSVAL_IS_NULL(regs.sp[n])) \ + goto error; \ + } \ + JS_END_MACRO + +/* + * Optimized conversion macros that test for the desired type in v before + * homing sp and calling a conversion function. + */ +#define VALUE_TO_NUMBER(cx, n, v, d) \ + JS_BEGIN_MACRO \ + JS_ASSERT(v == regs.sp[n]); \ + if (JSVAL_IS_INT(v)) { \ + d = (jsdouble)JSVAL_TO_INT(v); \ + } else if (JSVAL_IS_DOUBLE(v)) { \ + d = *JSVAL_TO_DOUBLE(v); \ + } else { \ + d = js_ValueToNumber(cx, ®s.sp[n]); \ + if (JSVAL_IS_NULL(regs.sp[n])) \ + goto error; \ + JS_ASSERT(JSVAL_IS_NUMBER(regs.sp[n]) || \ + regs.sp[n] == JSVAL_TRUE); \ + } \ + JS_END_MACRO + +#define POP_BOOLEAN(cx, v, b) \ + JS_BEGIN_MACRO \ + v = FETCH_OPND(-1); \ + if (v == JSVAL_NULL) { \ + b = JS_FALSE; \ + } else if (JSVAL_IS_BOOLEAN(v)) { \ + b = JSVAL_TO_BOOLEAN(v); \ + } else { \ + b = js_ValueToBoolean(v); \ + } \ + regs.sp--; \ + JS_END_MACRO + +#define VALUE_TO_OBJECT(cx, n, v, obj) \ + JS_BEGIN_MACRO \ + if (!JSVAL_IS_PRIMITIVE(v)) { \ + obj = JSVAL_TO_OBJECT(v); \ + } else { \ + obj = js_ValueToNonNullObject(cx, v); \ + if (!obj) \ + goto error; \ + STORE_OPND(n, OBJECT_TO_JSVAL(obj)); \ + } \ + JS_END_MACRO + +#define FETCH_OBJECT(cx, n, v, obj) \ + JS_BEGIN_MACRO \ + v = FETCH_OPND(n); \ + VALUE_TO_OBJECT(cx, n, v, obj); \ + JS_END_MACRO + +#define DEFAULT_VALUE(cx, n, hint, v) \ + JS_BEGIN_MACRO \ + JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); \ + JS_ASSERT(v == regs.sp[n]); \ + if (!JSVAL_TO_OBJECT(v)->defaultValue(cx, hint, ®s.sp[n])) \ + goto error; \ + v = regs.sp[n]; \ + JS_END_MACRO + +/* + * Quickly test if v is an int from the [-2**29, 2**29) range, that is, when + * the lowest bit of v is 1 and the bits 30 and 31 are both either 0 or 1. For + * such v we can do increment or decrement via adding or subtracting two + * without checking that the result overflows JSVAL_INT_MIN or JSVAL_INT_MAX. + */ +#define CAN_DO_FAST_INC_DEC(v) (((((v) << 1) ^ v) & 0x80000001) == 1) + +JS_STATIC_ASSERT(JSVAL_INT == 1); +JS_STATIC_ASSERT(!CAN_DO_FAST_INC_DEC(INT_TO_JSVAL_CONSTEXPR(JSVAL_INT_MIN))); +JS_STATIC_ASSERT(!CAN_DO_FAST_INC_DEC(INT_TO_JSVAL_CONSTEXPR(JSVAL_INT_MAX))); + +/* + * Conditional assert to detect failure to clear a pending exception that is + * suppressed (or unintentional suppression of a wanted exception). + */ +#if defined DEBUG_brendan || defined DEBUG_mrbkap || defined DEBUG_shaver +# define DEBUG_NOT_THROWING 1 +#endif + +#ifdef DEBUG_NOT_THROWING +# define ASSERT_NOT_THROWING(cx) JS_ASSERT(!(cx)->throwing) +#else +# define ASSERT_NOT_THROWING(cx) /* nothing */ +#endif + +/* + * Define JS_OPMETER to instrument bytecode succession, generating a .dot file + * on shutdown that shows the graph of significant predecessor/successor pairs + * executed, where the edge labels give the succession counts. The .dot file + * is named by the JS_OPMETER_FILE envariable, and defaults to /tmp/ops.dot. + * + * Bonus feature: JS_OPMETER also enables counters for stack-addressing ops + * such as JSOP_GETLOCAL, JSOP_INCARG, via METER_SLOT_OP. The resulting counts + * are written to JS_OPMETER_HIST, defaulting to /tmp/ops.hist. + */ +#ifndef JS_OPMETER +# define METER_OP_INIT(op) /* nothing */ +# define METER_OP_PAIR(op1,op2) /* nothing */ +# define METER_SLOT_OP(op,slot) /* nothing */ +#else + +/* + * The second dimension is hardcoded at 256 because we know that many bits fit + * in a byte, and mainly to optimize away multiplying by JSOP_LIMIT to address + * any particular row. + */ +# define METER_OP_INIT(op) ((op) = JSOP_STOP) +# define METER_OP_PAIR(op1,op2) (js_MeterOpcodePair(op1, op2)) +# define METER_SLOT_OP(op,slot) (js_MeterSlotOpcode(op, slot)) + +#endif + +/* + * Threaded interpretation via computed goto appears to be well-supported by + * GCC 3 and higher. IBM's C compiler when run with the right options (e.g., + * -qlanglvl=extended) also supports threading. Ditto the SunPro C compiler. + * Currently it's broken for JS_VERSION < 160, though this isn't worth fixing. + * Add your compiler support macros here. + */ +#ifndef JS_THREADED_INTERP +# if JS_VERSION >= 160 && ( \ + __GNUC__ >= 3 || \ + (__IBMC__ >= 700 && defined __IBM_COMPUTED_GOTO) || \ + __SUNPRO_C >= 0x570) +# define JS_THREADED_INTERP 1 +# else +# define JS_THREADED_INTERP 0 +# endif +#endif + +/* + * Deadlocks or else bad races are likely if JS_THREADSAFE, so we must rely on + * single-thread DEBUG js shell testing to verify property cache hits. + */ +#if defined DEBUG && !defined JS_THREADSAFE + +# define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry) \ + JS_BEGIN_MACRO \ + if (!AssertValidPropertyCacheHit(cx, script, regs, pcoff, obj, pobj, \ + entry)) { \ + goto error; \ + } \ + JS_END_MACRO + +static bool +AssertValidPropertyCacheHit(JSContext *cx, JSScript *script, JSFrameRegs& regs, + ptrdiff_t pcoff, JSObject *start, JSObject *found, + JSPropCacheEntry *entry) +{ + uint32 sample = cx->runtime->gcNumber; + + JSAtom *atom; + if (pcoff >= 0) + GET_ATOM_FROM_BYTECODE(script, regs.pc, pcoff, atom); + else + atom = cx->runtime->atomState.lengthAtom; + + JSObject *obj, *pobj; + JSProperty *prop; + JSBool ok; + + if (JOF_OPMODE(*regs.pc) == JOF_NAME) { + ok = js_FindProperty(cx, ATOM_TO_JSID(atom), &obj, &pobj, &prop); + } else { + obj = start; + ok = js_LookupProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop); + } + if (!ok) + return false; + if (cx->runtime->gcNumber != sample || + PCVCAP_SHAPE(entry->vcap) != OBJ_SHAPE(pobj)) { + pobj->dropProperty(cx, prop); + return true; + } + JS_ASSERT(prop); + JS_ASSERT(pobj == found); + + JSScopeProperty *sprop = (JSScopeProperty *) prop; + if (PCVAL_IS_SLOT(entry->vword)) { + JS_ASSERT(PCVAL_TO_SLOT(entry->vword) == sprop->slot); + JS_ASSERT(!sprop->isMethod()); + } else if (PCVAL_IS_SPROP(entry->vword)) { + JS_ASSERT(PCVAL_TO_SPROP(entry->vword) == sprop); + JS_ASSERT_IF(sprop->isMethod(), + sprop->methodValue() == LOCKED_OBJ_GET_SLOT(pobj, sprop->slot)); + } else { + jsval v; + JS_ASSERT(PCVAL_IS_OBJECT(entry->vword)); + JS_ASSERT(entry->vword != PCVAL_NULL); + JS_ASSERT(OBJ_SCOPE(pobj)->brandedOrHasMethodBarrier()); + JS_ASSERT(SPROP_HAS_STUB_GETTER_OR_IS_METHOD(sprop)); + JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))); + v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot); + JS_ASSERT(VALUE_IS_FUNCTION(cx, v)); + JS_ASSERT(PCVAL_TO_OBJECT(entry->vword) == JSVAL_TO_OBJECT(v)); + + if (sprop->isMethod()) { + JS_ASSERT(js_CodeSpec[*regs.pc].format & JOF_CALLOP); + JS_ASSERT(sprop->methodValue() == v); + } + } + + pobj->dropProperty(cx, prop); + return true; +} + +#else +# define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry) ((void) 0) +#endif + +/* + * Ensure that the intrepreter switch can close call-bytecode cases in the + * same way as non-call bytecodes. + */ +JS_STATIC_ASSERT(JSOP_NAME_LENGTH == JSOP_CALLNAME_LENGTH); +JS_STATIC_ASSERT(JSOP_GETGVAR_LENGTH == JSOP_CALLGVAR_LENGTH); +JS_STATIC_ASSERT(JSOP_GETUPVAR_LENGTH == JSOP_CALLUPVAR_LENGTH); +JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH == JSOP_CALLUPVAR_DBG_LENGTH); +JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH == JSOP_GETUPVAR_LENGTH); +JS_STATIC_ASSERT(JSOP_GETDSLOT_LENGTH == JSOP_CALLDSLOT_LENGTH); +JS_STATIC_ASSERT(JSOP_GETARG_LENGTH == JSOP_CALLARG_LENGTH); +JS_STATIC_ASSERT(JSOP_GETLOCAL_LENGTH == JSOP_CALLLOCAL_LENGTH); +JS_STATIC_ASSERT(JSOP_XMLNAME_LENGTH == JSOP_CALLXMLNAME_LENGTH); + +/* + * Same for debuggable flat closures defined at top level in another function + * or program fragment. + */ +JS_STATIC_ASSERT(JSOP_DEFFUN_FC_LENGTH == JSOP_DEFFUN_DBGFC_LENGTH); + +/* + * Same for JSOP_SETNAME and JSOP_SETPROP, which differ only slightly but + * remain distinct for the decompiler. Likewise for JSOP_INIT{PROP,METHOD}. + */ +JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETPROP_LENGTH); +JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETMETHOD_LENGTH); +JS_STATIC_ASSERT(JSOP_INITPROP_LENGTH == JSOP_INITMETHOD_LENGTH); + +/* See TRY_BRANCH_AFTER_COND. */ +JS_STATIC_ASSERT(JSOP_IFNE_LENGTH == JSOP_IFEQ_LENGTH); +JS_STATIC_ASSERT(JSOP_IFNE == JSOP_IFEQ + 1); + +/* For the fastest case inder JSOP_INCNAME, etc. */ +JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_DECNAME_LENGTH); +JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEINC_LENGTH); +JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEDEC_LENGTH); + +#ifdef JS_TRACER +# define ABORT_RECORDING(cx, reason) \ + JS_BEGIN_MACRO \ + if (TRACE_RECORDER(cx)) \ + js_AbortRecording(cx, reason); \ + JS_END_MACRO +#else +# define ABORT_RECORDING(cx, reason) ((void) 0) +#endif + +JS_REQUIRES_STACK JSBool +js_Interpret(JSContext *cx) +{ +#ifdef MOZ_TRACEVIS + TraceVisStateObj tvso(cx, S_INTERP); +#endif + + JSRuntime *rt; + JSStackFrame *fp; + JSScript *script; + uintN inlineCallCount; + JSAtom **atoms; + JSVersion currentVersion, originalVersion; + JSFrameRegs regs; + JSObject *obj, *obj2, *parent; + JSBool ok, cond; + jsint len; + jsbytecode *endpc, *pc2; + JSOp op, op2; + jsatomid index; + JSAtom *atom; + uintN argc, attrs, flags; + uint32 slot; + jsval *vp, lval, rval, ltmp, rtmp; + jsid id; + JSProperty *prop; + JSScopeProperty *sprop; + JSString *str, *str2; + jsint i, j; + jsdouble d, d2; + JSClass *clasp; + JSFunction *fun; + JSType type; + jsint low, high, off, npairs; + JSBool match; +#if JS_HAS_GETTER_SETTER + JSPropertyOp getter, setter; +#endif + JSAutoResolveFlags rf(cx, JSRESOLVE_INFER); + +# ifdef DEBUG + /* + * We call this macro from BEGIN_CASE in threaded interpreters, + * and before entering the switch in non-threaded interpreters. + * However, reaching such points doesn't mean we've actually + * fetched an OP from the instruction stream: some opcodes use + * 'op=x; DO_OP()' to let another opcode's implementation finish + * their work, and many opcodes share entry points with a run of + * consecutive BEGIN_CASEs. + * + * Take care to trace OP only when it is the opcode fetched from + * the instruction stream, so the trace matches what one would + * expect from looking at the code. (We do omit POPs after SETs; + * unfortunate, but not worth fixing.) + */ +# define TRACE_OPCODE(OP) JS_BEGIN_MACRO \ + if (JS_UNLIKELY(cx->tracefp != NULL) && \ + (OP) == *regs.pc) \ + js_TraceOpcode(cx); \ + JS_END_MACRO +# else +# define TRACE_OPCODE(OP) ((void) 0) +# endif + +#if JS_THREADED_INTERP + static void *const normalJumpTable[] = { +# define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ + JS_EXTENSION &&L_##op, +# include "jsopcode.tbl" +# undef OPDEF + }; + + static void *const interruptJumpTable[] = { +# define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ + JS_EXTENSION &&interrupt, +# include "jsopcode.tbl" +# undef OPDEF + }; + + register void * const *jumpTable = normalJumpTable; + + METER_OP_INIT(op); /* to nullify first METER_OP_PAIR */ + +# define ENABLE_INTERRUPTS() ((void) (jumpTable = interruptJumpTable)) + +# ifdef JS_TRACER +# define CHECK_RECORDER() \ + JS_ASSERT_IF(TRACE_RECORDER(cx), jumpTable == interruptJumpTable) +# else +# define CHECK_RECORDER() ((void)0) +# endif + +# define DO_OP() JS_BEGIN_MACRO \ + CHECK_RECORDER(); \ + JS_EXTENSION_(goto *jumpTable[op]); \ + JS_END_MACRO +# define DO_NEXT_OP(n) JS_BEGIN_MACRO \ + METER_OP_PAIR(op, regs.pc[n]); \ + op = (JSOp) *(regs.pc += (n)); \ + DO_OP(); \ + JS_END_MACRO + +# define BEGIN_CASE(OP) L_##OP: TRACE_OPCODE(OP); CHECK_RECORDER(); +# define END_CASE(OP) DO_NEXT_OP(OP##_LENGTH); +# define END_VARLEN_CASE DO_NEXT_OP(len); +# define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP) \ + JS_ASSERT(js_CodeSpec[OP].length == 1); \ + op = (JSOp) *++regs.pc; \ + DO_OP(); + +# define END_EMPTY_CASES + +#else /* !JS_THREADED_INTERP */ + + register intN switchMask = 0; + intN switchOp; + +# define ENABLE_INTERRUPTS() ((void) (switchMask = -1)) + +# ifdef JS_TRACER +# define CHECK_RECORDER() \ + JS_ASSERT_IF(TRACE_RECORDER(cx), switchMask == -1) +# else +# define CHECK_RECORDER() ((void)0) +# endif + +# define DO_OP() goto do_op +# define DO_NEXT_OP(n) JS_BEGIN_MACRO \ + JS_ASSERT((n) == len); \ + goto advance_pc; \ + JS_END_MACRO + +# define BEGIN_CASE(OP) case OP: CHECK_RECORDER(); +# define END_CASE(OP) END_CASE_LEN(OP##_LENGTH) +# define END_CASE_LEN(n) END_CASE_LENX(n) +# define END_CASE_LENX(n) END_CASE_LEN##n + +/* + * To share the code for all len == 1 cases we use the specialized label with + * code that falls through to advance_pc: . + */ +# define END_CASE_LEN1 goto advance_pc_by_one; +# define END_CASE_LEN2 len = 2; goto advance_pc; +# define END_CASE_LEN3 len = 3; goto advance_pc; +# define END_CASE_LEN4 len = 4; goto advance_pc; +# define END_CASE_LEN5 len = 5; goto advance_pc; +# define END_VARLEN_CASE goto advance_pc; +# define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP) +# define END_EMPTY_CASES goto advance_pc_by_one; + +#endif /* !JS_THREADED_INTERP */ + + /* Check for too deep of a native thread stack. */ + JS_CHECK_RECURSION(cx, return JS_FALSE); + + rt = cx->runtime; + + /* Set registerized frame pointer and derived script pointer. */ + fp = cx->fp; + script = fp->script; + JS_ASSERT(!script->isEmpty()); + JS_ASSERT(script->length > 1); + + /* Count of JS function calls that nest in this C js_Interpret frame. */ + inlineCallCount = 0; + + /* + * Initialize the index segment register used by LOAD_ATOM and + * GET_FULL_INDEX macros below. As a register we use a pointer based on + * the atom map to turn frequently executed LOAD_ATOM into simple array + * access. For less frequent object and regexp loads we have to recover + * the segment from atoms pointer first. + */ + atoms = script->atomMap.vector; + +#define LOAD_ATOM(PCOFF) \ + JS_BEGIN_MACRO \ + JS_ASSERT(fp->imacpc \ + ? atoms == COMMON_ATOMS_START(&rt->atomState) && \ + GET_INDEX(regs.pc + PCOFF) < js_common_atom_count \ + : (size_t)(atoms - script->atomMap.vector) < \ + (size_t)(script->atomMap.length - \ + GET_INDEX(regs.pc + PCOFF))); \ + atom = atoms[GET_INDEX(regs.pc + PCOFF)]; \ + JS_END_MACRO + +#define GET_FULL_INDEX(PCOFF) \ + (atoms - script->atomMap.vector + GET_INDEX(regs.pc + PCOFF)) + +#define LOAD_OBJECT(PCOFF) \ + (obj = script->getObject(GET_FULL_INDEX(PCOFF))) + +#define LOAD_FUNCTION(PCOFF) \ + (fun = script->getFunction(GET_FULL_INDEX(PCOFF))) + +#ifdef JS_TRACER + +#ifdef MOZ_TRACEVIS +#if JS_THREADED_INTERP +#define MONITOR_BRANCH_TRACEVIS \ + JS_BEGIN_MACRO \ + if (jumpTable != interruptJumpTable) \ + js_EnterTraceVisState(cx, S_RECORD, R_NONE); \ + JS_END_MACRO +#else /* !JS_THREADED_INTERP */ +#define MONITOR_BRANCH_TRACEVIS \ + JS_BEGIN_MACRO \ + js_EnterTraceVisState(cx, S_RECORD, R_NONE); \ + JS_END_MACRO +#endif +#else +#define MONITOR_BRANCH_TRACEVIS +#endif + +#define RESTORE_INTERP_VARS() \ + JS_BEGIN_MACRO \ + fp = cx->fp; \ + script = fp->script; \ + atoms = FrameAtomBase(cx, fp); \ + currentVersion = (JSVersion) script->version; \ + JS_ASSERT(fp->regs == ®s); \ + if (cx->throwing) \ + goto error; \ + JS_END_MACRO + +#define MONITOR_BRANCH(reason) \ + JS_BEGIN_MACRO \ + if (TRACING_ENABLED(cx)) { \ + if (js_MonitorLoopEdge(cx, inlineCallCount, reason)) { \ + JS_ASSERT(TRACE_RECORDER(cx)); \ + MONITOR_BRANCH_TRACEVIS; \ + ENABLE_INTERRUPTS(); \ + } \ + RESTORE_INTERP_VARS(); \ + } \ + JS_END_MACRO + +#else /* !JS_TRACER */ + +#define MONITOR_BRANCH(reason) ((void) 0) + +#endif /* !JS_TRACER */ + + /* + * Prepare to call a user-supplied branch handler, and abort the script + * if it returns false. + */ +#define CHECK_BRANCH() \ + JS_BEGIN_MACRO \ + if (!JS_CHECK_OPERATION_LIMIT(cx)) \ + goto error; \ + JS_END_MACRO + +#ifndef TRACE_RECORDER +#define TRACE_RECORDER(cx) (false) +#endif + +#define BRANCH(n) \ + JS_BEGIN_MACRO \ + regs.pc += (n); \ + op = (JSOp) *regs.pc; \ + if ((n) <= 0) { \ + CHECK_BRANCH(); \ + if (op == JSOP_NOP) { \ + if (TRACE_RECORDER(cx)) { \ + MONITOR_BRANCH(Record_Branch); \ + op = (JSOp) *regs.pc; \ + } else { \ + op = (JSOp) *++regs.pc; \ + } \ + } else if (op == JSOP_TRACE) { \ + MONITOR_BRANCH(Record_Branch); \ + op = (JSOp) *regs.pc; \ + } \ + } \ + DO_OP(); \ + JS_END_MACRO + + MUST_FLOW_THROUGH("exit"); + ++cx->interpLevel; + + /* + * Optimized Get and SetVersion for proper script language versioning. + * + * If any native method or JSClass/JSObjectOps hook calls js_SetVersion + * and changes cx->version, the effect will "stick" and we will stop + * maintaining currentVersion. This is relied upon by testsuites, for + * the most part -- web browsers select version before compiling and not + * at run-time. + */ + currentVersion = (JSVersion) script->version; + originalVersion = (JSVersion) cx->version; + if (currentVersion != originalVersion) + js_SetVersion(cx, currentVersion); + + /* Update the static-link display. */ + if (script->staticLevel < JS_DISPLAY_SIZE) { + JSStackFrame **disp = &cx->display[script->staticLevel]; + fp->displaySave = *disp; + *disp = fp; + } + +# define CHECK_INTERRUPT_HANDLER() \ + JS_BEGIN_MACRO \ + if (cx->debugHooks->interruptHandler) \ + ENABLE_INTERRUPTS(); \ + JS_END_MACRO + + /* + * Load the debugger's interrupt hook here and after calling out to native + * functions (but not to getters, setters, or other native hooks), so we do + * not have to reload it each time through the interpreter loop -- we hope + * the compiler can keep it in a register when it is non-null. + */ + CHECK_INTERRUPT_HANDLER(); + +#if !JS_HAS_GENERATORS + JS_ASSERT(!fp->regs); +#else + /* Initialize the pc and sp registers unless we're resuming a generator. */ + if (JS_LIKELY(!fp->regs)) { +#endif + ASSERT_NOT_THROWING(cx); + regs.pc = script->code; + regs.sp = StackBase(fp); + fp->regs = ®s; +#if JS_HAS_GENERATORS + } else { + JSGenerator *gen; + + JS_ASSERT(fp->flags & JSFRAME_GENERATOR); + gen = FRAME_TO_GENERATOR(fp); + JS_ASSERT(fp->regs == &gen->savedRegs); + regs = gen->savedRegs; + fp->regs = ®s; + JS_ASSERT((size_t) (regs.pc - script->code) <= script->length); + JS_ASSERT((size_t) (regs.sp - StackBase(fp)) <= StackDepth(script)); + + /* + * To support generator_throw and to catch ignored exceptions, + * fail if cx->throwing is set. + */ + if (cx->throwing) { +#ifdef DEBUG_NOT_THROWING + if (cx->exception != JSVAL_ARETURN) { + printf("JS INTERPRETER CALLED WITH PENDING EXCEPTION %lx\n", + (unsigned long) cx->exception); + } +#endif + goto error; + } + } +#endif /* JS_HAS_GENERATORS */ + +#ifdef JS_TRACER + /* + * We cannot reenter the interpreter while recording; wait to abort until + * after cx->fp->regs is set. + */ + if (TRACE_RECORDER(cx)) + js_AbortRecording(cx, "attempt to reenter interpreter while recording"); +#endif + + /* + * It is important that "op" be initialized before calling DO_OP because + * it is possible for "op" to be specially assigned during the normal + * processing of an opcode while looping. We rely on DO_NEXT_OP to manage + * "op" correctly in all other cases. + */ + len = 0; + DO_NEXT_OP(len); + +#if JS_THREADED_INTERP + /* + * This is a loop, but it does not look like a loop. The loop-closing + * jump is distributed throughout goto *jumpTable[op] inside of DO_OP. + * When interrupts are enabled, jumpTable is set to interruptJumpTable + * where all jumps point to the interrupt label. The latter, after + * calling the interrupt handler, dispatches through normalJumpTable to + * continue the normal bytecode processing. + */ + +#else /* !JS_THREADED_INTERP */ + for (;;) { + advance_pc_by_one: + JS_ASSERT(js_CodeSpec[op].length == 1); + len = 1; + advance_pc: + regs.pc += len; + op = (JSOp) *regs.pc; + + do_op: + CHECK_RECORDER(); + TRACE_OPCODE(op); + switchOp = intN(op) | switchMask; + do_switch: + switch (switchOp) { +#endif + +/********************** Here we include the operations ***********************/ +#include "jsops.cpp" +/*****************************************************************************/ + +#if !JS_THREADED_INTERP + default: +#endif +#ifndef JS_TRACER + bad_opcode: +#endif + { + char numBuf[12]; + JS_snprintf(numBuf, sizeof numBuf, "%d", op); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_BYTECODE, numBuf); + goto error; + } + +#if !JS_THREADED_INTERP + } /* switch (op) */ + } /* for (;;) */ +#endif /* !JS_THREADED_INTERP */ + + error: + if (fp->imacpc && cx->throwing) { + // To keep things simple, we hard-code imacro exception handlers here. + if (*fp->imacpc == JSOP_NEXTITER && js_ValueIsStopIteration(cx->exception)) { + // pc may point to JSOP_DUP here due to bug 474854. + JS_ASSERT(*regs.pc == JSOP_CALL || *regs.pc == JSOP_DUP || *regs.pc == JSOP_TRUE); + cx->throwing = JS_FALSE; + cx->exception = JSVAL_VOID; + regs.sp[-1] = JSVAL_HOLE; + PUSH(JSVAL_FALSE); + goto end_imacro; + } + + // Handle other exceptions as if they came from the imacro-calling pc. + regs.pc = fp->imacpc; + fp->imacpc = NULL; + atoms = script->atomMap.vector; + } + + JS_ASSERT((size_t)((fp->imacpc ? fp->imacpc : regs.pc) - script->code) < script->length); + +#ifdef JS_TRACER + /* + * This abort could be weakened to permit tracing through exceptions that + * are thrown and caught within a loop, with the co-operation of the tracer. + * For now just bail on any sign of trouble. + */ + if (TRACE_RECORDER(cx)) + js_AbortRecording(cx, "error or exception while recording"); +#endif + + if (!cx->throwing) { + /* This is an error, not a catchable exception, quit the frame ASAP. */ + ok = JS_FALSE; + } else { + JSTrapHandler handler; + JSTryNote *tn, *tnlimit; + uint32 offset; + + /* Call debugger throw hook if set. */ + handler = cx->debugHooks->throwHook; + if (handler) { + switch (handler(cx, script, regs.pc, &rval, + cx->debugHooks->throwHookData)) { + case JSTRAP_ERROR: + cx->throwing = JS_FALSE; + goto error; + case JSTRAP_RETURN: + cx->throwing = JS_FALSE; + fp->rval = rval; + ok = JS_TRUE; + goto forced_return; + case JSTRAP_THROW: + cx->exception = rval; + case JSTRAP_CONTINUE: + default:; + } + CHECK_INTERRUPT_HANDLER(); + } + + /* + * Look for a try block in script that can catch this exception. + */ + if (script->trynotesOffset == 0) + goto no_catch; + + offset = (uint32)(regs.pc - script->main); + tn = script->trynotes()->vector; + tnlimit = tn + script->trynotes()->length; + do { + if (offset - tn->start >= tn->length) + continue; + + /* + * We have a note that covers the exception pc but we must check + * whether the interpreter has already executed the corresponding + * handler. This is possible when the executed bytecode + * implements break or return from inside a for-in loop. + * + * In this case the emitter generates additional [enditer] and + * [gosub] opcodes to close all outstanding iterators and execute + * the finally blocks. If such an [enditer] throws an exception, + * its pc can still be inside several nested for-in loops and + * try-finally statements even if we have already closed the + * corresponding iterators and invoked the finally blocks. + * + * To address this, we make [enditer] always decrease the stack + * even when its implementation throws an exception. Thus already + * executed [enditer] and [gosub] opcodes will have try notes + * with the stack depth exceeding the current one and this + * condition is what we use to filter them out. + */ + if (tn->stackDepth > regs.sp - StackBase(fp)) + continue; + + /* + * Set pc to the first bytecode after the the try note to point + * to the beginning of catch or finally or to [enditer] closing + * the for-in loop. + */ + regs.pc = (script)->main + tn->start + tn->length; + + ok = js_UnwindScope(cx, fp, tn->stackDepth, JS_TRUE); + JS_ASSERT(fp->regs->sp == StackBase(fp) + tn->stackDepth); + if (!ok) { + /* + * Restart the handler search with updated pc and stack depth + * to properly notify the debugger. + */ + goto error; + } + + switch (tn->kind) { + case JSTRY_CATCH: + JS_ASSERT(js_GetOpcode(cx, fp->script, regs.pc) == JSOP_ENTERBLOCK); + +#if JS_HAS_GENERATORS + /* Catch cannot intercept the closing of a generator. */ + if (JS_UNLIKELY(cx->exception == JSVAL_ARETURN)) + break; +#endif + + /* + * Don't clear cx->throwing to save cx->exception from GC + * until it is pushed to the stack via [exception] in the + * catch block. + */ + len = 0; + DO_NEXT_OP(len); + + case JSTRY_FINALLY: + /* + * Push (true, exception) pair for finally to indicate that + * [retsub] should rethrow the exception. + */ + PUSH(JSVAL_TRUE); + PUSH(cx->exception); + cx->throwing = JS_FALSE; + len = 0; + DO_NEXT_OP(len); + + case JSTRY_ITER: + /* + * This is similar to JSOP_ENDITER in the interpreter loop, + * except the code now uses the stack slot normally used by + * JSOP_NEXTITER, namely regs.sp[-1] before the regs.sp -= 2 + * adjustment and regs.sp[1] after, to save and restore the + * pending exception. + */ + JS_ASSERT(js_GetOpcode(cx, fp->script, regs.pc) == JSOP_ENDITER); + regs.sp[-1] = cx->exception; + cx->throwing = JS_FALSE; + ok = js_CloseIterator(cx, regs.sp[-2]); + regs.sp -= 2; + if (!ok) + goto error; + cx->throwing = JS_TRUE; + cx->exception = regs.sp[1]; + } + } while (++tn != tnlimit); + + no_catch: + /* + * Propagate the exception or error to the caller unless the exception + * is an asynchronous return from a generator. + */ + ok = JS_FALSE; +#if JS_HAS_GENERATORS + if (JS_UNLIKELY(cx->throwing && cx->exception == JSVAL_ARETURN)) { + cx->throwing = JS_FALSE; + ok = JS_TRUE; + fp->rval = JSVAL_VOID; + } +#endif + } + + forced_return: + /* + * Unwind the scope making sure that ok stays false even when UnwindScope + * returns true. + * + * When a trap handler returns JSTRAP_RETURN, we jump here with ok set to + * true bypassing any finally blocks. + */ + ok &= js_UnwindScope(cx, fp, 0, ok || cx->throwing); + JS_ASSERT(regs.sp == StackBase(fp)); + +#ifdef DEBUG + cx->tracePrevPc = NULL; +#endif + + if (inlineCallCount) + goto inline_return; + + exit: + /* + * At this point we are inevitably leaving an interpreted function or a + * top-level script, and returning to one of: + * (a) an "out of line" call made through js_Invoke; + * (b) a js_Execute activation; + * (c) a generator (SendToGenerator, jsiter.c). + * + * We must not be in an inline frame. The check above ensures that for the + * error case and for a normal return, the code jumps directly to parent's + * frame pc. + */ + JS_ASSERT(inlineCallCount == 0); + JS_ASSERT(fp->regs == ®s); +#ifdef JS_TRACER + if (TRACE_RECORDER(cx)) + js_AbortRecording(cx, "recording out of js_Interpret"); +#endif +#if JS_HAS_GENERATORS + if (JS_UNLIKELY(fp->flags & JSFRAME_YIELDING)) { + JSGenerator *gen; + + gen = FRAME_TO_GENERATOR(fp); + gen->savedRegs = regs; + gen->frame.regs = &gen->savedRegs; + } else +#endif /* JS_HAS_GENERATORS */ + { + JS_ASSERT(!fp->blockChain); + JS_ASSERT(!js_IsActiveWithOrBlock(cx, fp->scopeChain, 0)); + fp->regs = NULL; + } + + /* Undo the remaining effects committed on entry to js_Interpret. */ + if (script->staticLevel < JS_DISPLAY_SIZE) + cx->display[script->staticLevel] = fp->displaySave; + if (cx->version == currentVersion && currentVersion != originalVersion) + js_SetVersion(cx, originalVersion); + --cx->interpLevel; + + return ok; + + atom_not_defined: + { + const char *printable; + + printable = js_AtomToPrintableString(cx, atom); + if (printable) + js_ReportIsNotDefined(cx, printable); + goto error; + } +} + +#endif /* !defined jsinvoke_cpp___ */ diff --git a/ape-server/deps/js/src/jsinterp.h b/ape-server/deps/js/src/jsinterp.h new file mode 100755 index 0000000..86c75bc --- /dev/null +++ b/ape-server/deps/js/src/jsinterp.h @@ -0,0 +1,680 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsinterp_h___ +#define jsinterp_h___ +/* + * JS interpreter interface. + */ +#include "jsprvtd.h" +#include "jspubtd.h" +#include "jsfun.h" +#include "jsopcode.h" +#include "jsscript.h" + +JS_BEGIN_EXTERN_C + +typedef struct JSFrameRegs { + jsbytecode *pc; /* program counter */ + jsval *sp; /* stack pointer */ +} JSFrameRegs; + +/* + * JS stack frame, may be allocated on the C stack by native callers. Always + * allocated on cx->stackPool for calls from the interpreter to an interpreted + * function. + * + * NB: This struct is manually initialized in jsinterp.c and jsiter.c. If you + * add new members, update both files. But first, try to remove members. The + * sharp* and xml* members should be moved onto the stack as local variables + * with well-known slots, if possible. + */ +struct JSStackFrame { + JSFrameRegs *regs; + jsbytecode *imacpc; /* null or interpreter macro call pc */ + jsval *slots; /* variables, locals and operand stack */ + JSObject *callobj; /* lazily created Call object */ + jsval argsobj; /* lazily created arguments object, must be + JSVAL_OBJECT */ + JSObject *varobj; /* variables object, where vars go */ + JSScript *script; /* script being interpreted */ + JSFunction *fun; /* function being called or null */ + jsval thisv; /* "this" pointer if in method */ + uintN argc; /* actual argument count */ + jsval *argv; /* base of argument stack slots */ + jsval rval; /* function return value */ + JSStackFrame *down; /* previous frame */ + void *annotation; /* used by Java security */ + + /* + * We can't determine in advance which local variables can live on + * the stack and be freed when their dynamic scope ends, and which + * will be closed over and need to live in the heap. So we place + * variables on the stack initially, note when they are closed + * over, and copy those that are out to the heap when we leave + * their dynamic scope. + * + * The bytecode compiler produces a tree of block objects + * accompanying each JSScript representing those lexical blocks in + * the script that have let-bound variables associated with them. + * These block objects are never modified, and never become part + * of any function's scope chain. Their parent slots point to the + * innermost block that encloses them, or are NULL in the + * outermost blocks within a function or in eval or global code. + * + * When we are in the static scope of such a block, blockChain + * points to its compiler-allocated block object; otherwise, it is + * NULL. + * + * scopeChain is the current scope chain, including 'call' and + * 'block' objects for those function calls and lexical blocks + * whose static scope we are currently executing in, and 'with' + * objects for with statements; the chain is typically terminated + * by a global object. However, as an optimization, the young end + * of the chain omits block objects we have not yet cloned. To + * create a closure, we clone the missing blocks from blockChain + * (which is always current), place them at the head of + * scopeChain, and use that for the closure's scope chain. If we + * never close over a lexical block, we never place a mutable + * clone of it on scopeChain. + * + * This lazy cloning is implemented in js_GetScopeChain, which is + * also used in some other cases --- entering 'with' blocks, for + * example. + */ + JSObject *scopeChain; + JSObject *blockChain; + + uint32 flags; /* frame flags -- see below */ + JSStackFrame *dormantNext; /* next dormant frame chain */ + JSStackFrame *displaySave; /* previous value of display entry for + script->staticLevel */ + + inline void assertValidStackDepth(uintN depth); + + void putActivationObjects(JSContext *cx) { + /* + * The order of calls here is important as js_PutCallObject needs to + * access argsobj. + */ + if (callobj) { + js_PutCallObject(cx, this); + JS_ASSERT(!argsobj); + } else if (argsobj) { + js_PutArgsObject(cx, this); + } + } + + jsval calleeValue() { + JS_ASSERT(argv); + return argv[-2]; + } + + JSObject *calleeObject() { + JS_ASSERT(argv); + return JSVAL_TO_OBJECT(argv[-2]); + } + + JSObject *callee() { + return argv ? JSVAL_TO_OBJECT(argv[-2]) : NULL; + } +}; + +#ifdef __cplusplus +static JS_INLINE uintN +FramePCOffset(JSStackFrame* fp) +{ + return uintN((fp->imacpc ? fp->imacpc : fp->regs->pc) - fp->script->code); +} +#endif + +static JS_INLINE jsval * +StackBase(JSStackFrame *fp) +{ + return fp->slots + fp->script->nfixed; +} + +void +JSStackFrame::assertValidStackDepth(uintN depth) +{ + JS_ASSERT(0 <= regs->sp - StackBase(this)); + JS_ASSERT(depth <= uintptr_t(regs->sp - StackBase(this))); +} + +static JS_INLINE uintN +GlobalVarCount(JSStackFrame *fp) +{ + uintN n; + + JS_ASSERT(!fp->fun); + n = fp->script->nfixed; + if (fp->script->regexpsOffset != 0) + n -= fp->script->regexps()->length; + return n; +} + +typedef struct JSInlineFrame { + JSStackFrame frame; /* base struct */ + JSFrameRegs callerRegs; /* parent's frame registers */ + void *mark; /* mark before inline frame */ + void *hookData; /* debugger call hook data */ + JSVersion callerVersion; /* dynamic version of calling script */ +} JSInlineFrame; + +/* JS stack frame flags. */ +#define JSFRAME_CONSTRUCTING 0x01 /* frame is for a constructor invocation */ +#define JSFRAME_COMPUTED_THIS 0x02 /* frame.thisv was computed already and + JSVAL_IS_OBJECT(thisv) */ +#define JSFRAME_ASSIGNING 0x04 /* a complex (not simplex JOF_ASSIGNING) op + is currently assigning to a property */ +#define JSFRAME_DEBUGGER 0x08 /* frame for JS_EvaluateInStackFrame */ +#define JSFRAME_EVAL 0x10 /* frame for obj_eval */ +#define JSFRAME_ROOTED_ARGV 0x20 /* frame.argv is rooted by the caller */ +#define JSFRAME_YIELDING 0x40 /* js_Interpret dispatched JSOP_YIELD */ +#define JSFRAME_ITERATOR 0x80 /* trying to get an iterator for for-in */ +#define JSFRAME_GENERATOR 0x200 /* frame belongs to generator-iterator */ +#define JSFRAME_OVERRIDE_ARGS 0x400 /* overridden arguments local variable */ + +#define JSFRAME_SPECIAL (JSFRAME_DEBUGGER | JSFRAME_EVAL) + +/* + * Property cache with structurally typed capabilities for invalidation, for + * polymorphic callsite method/get/set speedups. For details, see + * . + */ +#define PROPERTY_CACHE_LOG2 12 +#define PROPERTY_CACHE_SIZE JS_BIT(PROPERTY_CACHE_LOG2) +#define PROPERTY_CACHE_MASK JS_BITMASK(PROPERTY_CACHE_LOG2) + +/* + * Add kshape rather than xor it to avoid collisions between nearby bytecode + * that are evolving an object by setting successive properties, incrementing + * the object's scope->shape on each set. + */ +#define PROPERTY_CACHE_HASH(pc,kshape) \ + (((((jsuword)(pc) >> PROPERTY_CACHE_LOG2) ^ (jsuword)(pc)) + (kshape)) & \ + PROPERTY_CACHE_MASK) + +#define PROPERTY_CACHE_HASH_PC(pc,kshape) \ + PROPERTY_CACHE_HASH(pc, kshape) + +#define PROPERTY_CACHE_HASH_ATOM(atom,obj) \ + PROPERTY_CACHE_HASH((jsuword)(atom) >> 2, OBJ_SHAPE(obj)) + +/* + * Property cache value capability macros. + */ +#define PCVCAP_PROTOBITS 4 +#define PCVCAP_PROTOSIZE JS_BIT(PCVCAP_PROTOBITS) +#define PCVCAP_PROTOMASK JS_BITMASK(PCVCAP_PROTOBITS) + +#define PCVCAP_SCOPEBITS 4 +#define PCVCAP_SCOPESIZE JS_BIT(PCVCAP_SCOPEBITS) +#define PCVCAP_SCOPEMASK JS_BITMASK(PCVCAP_SCOPEBITS) + +#define PCVCAP_TAGBITS (PCVCAP_PROTOBITS + PCVCAP_SCOPEBITS) +#define PCVCAP_TAGMASK JS_BITMASK(PCVCAP_TAGBITS) +#define PCVCAP_TAG(t) ((t) & PCVCAP_TAGMASK) + +#define PCVCAP_MAKE(t,s,p) ((uint32(t) << PCVCAP_TAGBITS) | \ + ((s) << PCVCAP_PROTOBITS) | \ + (p)) +#define PCVCAP_SHAPE(t) ((t) >> PCVCAP_TAGBITS) + +#define SHAPE_OVERFLOW_BIT JS_BIT(32 - PCVCAP_TAGBITS) + +struct JSPropCacheEntry { + jsbytecode *kpc; /* pc if vcap tag is <= 1, else atom */ + jsuword kshape; /* key shape if pc, else obj for atom */ + jsuword vcap; /* value capability, see above */ + jsuword vword; /* value word, see PCVAL_* below */ + + bool adding() const { + return PCVCAP_TAG(vcap) == 0 && kshape != PCVCAP_SHAPE(vcap); + } + + bool directHit() const { + return PCVCAP_TAG(vcap) == 0 && kshape == PCVCAP_SHAPE(vcap); + } +}; + +/* + * Special value for functions returning JSPropCacheEntry * to distinguish + * between failure and no no-cache-fill cases. + */ +#define JS_NO_PROP_CACHE_FILL ((JSPropCacheEntry *) NULL + 1) + +#if defined DEBUG_brendan || defined DEBUG_brendaneich +#define JS_PROPERTY_CACHE_METERING 1 +#endif + +typedef struct JSPropertyCache { + JSPropCacheEntry table[PROPERTY_CACHE_SIZE]; + JSBool empty; +#ifdef JS_PROPERTY_CACHE_METERING + JSPropCacheEntry *pctestentry; /* entry of the last PC-based test */ + uint32 fills; /* number of cache entry fills */ + uint32 nofills; /* couldn't fill (e.g. default get) */ + uint32 rofills; /* set on read-only prop can't fill */ + uint32 disfills; /* fill attempts on disabled cache */ + uint32 oddfills; /* fill attempt after setter deleted */ + uint32 modfills; /* fill that rehashed to a new entry */ + uint32 brandfills; /* scope brandings to type structural + method fills */ + uint32 noprotos; /* resolve-returned non-proto pobj */ + uint32 longchains; /* overlong scope and/or proto chain */ + uint32 recycles; /* cache entries recycled by fills */ + uint32 pcrecycles; /* pc-keyed entries recycled by atom- + keyed fills */ + uint32 tests; /* cache probes */ + uint32 pchits; /* fast-path polymorphic op hits */ + uint32 protopchits; /* pchits hitting immediate prototype */ + uint32 initests; /* cache probes from JSOP_INITPROP */ + uint32 inipchits; /* init'ing next property pchit case */ + uint32 inipcmisses; /* init'ing next property pc misses */ + uint32 settests; /* cache probes from JOF_SET opcodes */ + uint32 addpchits; /* adding next property pchit case */ + uint32 setpchits; /* setting existing property pchit */ + uint32 setpcmisses; /* setting/adding property pc misses */ + uint32 slotchanges; /* clasp->reserveSlots result variance- + induced slot changes */ + uint32 setmisses; /* JSOP_SET{NAME,PROP} total misses */ + uint32 idmisses; /* slow-path key id == atom misses */ + uint32 komisses; /* slow-path key object misses */ + uint32 vcmisses; /* value capability misses */ + uint32 misses; /* cache misses */ + uint32 flushes; /* cache flushes */ + uint32 pcpurges; /* shadowing purges on proto chain */ +# define PCMETER(x) x +#else +# define PCMETER(x) ((void)0) +#endif +} JSPropertyCache; + +/* + * Property cache value tagging/untagging macros. + */ +#define PCVAL_OBJECT 0 +#define PCVAL_SLOT 1 +#define PCVAL_SPROP 2 + +#define PCVAL_TAGBITS 2 +#define PCVAL_TAGMASK JS_BITMASK(PCVAL_TAGBITS) +#define PCVAL_TAG(v) ((v) & PCVAL_TAGMASK) +#define PCVAL_CLRTAG(v) ((v) & ~(jsuword)PCVAL_TAGMASK) +#define PCVAL_SETTAG(v,t) ((jsuword)(v) | (t)) + +#define PCVAL_NULL 0 +#define PCVAL_IS_NULL(v) ((v) == PCVAL_NULL) + +#define PCVAL_IS_OBJECT(v) (PCVAL_TAG(v) == PCVAL_OBJECT) +#define PCVAL_TO_OBJECT(v) ((JSObject *) (v)) +#define OBJECT_TO_PCVAL(obj) ((jsuword) (obj)) + +#define PCVAL_OBJECT_TO_JSVAL(v) OBJECT_TO_JSVAL(PCVAL_TO_OBJECT(v)) +#define JSVAL_OBJECT_TO_PCVAL(v) OBJECT_TO_PCVAL(JSVAL_TO_OBJECT(v)) + +#define PCVAL_IS_SLOT(v) ((v) & PCVAL_SLOT) +#define PCVAL_TO_SLOT(v) ((jsuint)(v) >> 1) +#define SLOT_TO_PCVAL(i) (((jsuword)(i) << 1) | PCVAL_SLOT) + +#define PCVAL_IS_SPROP(v) (PCVAL_TAG(v) == PCVAL_SPROP) +#define PCVAL_TO_SPROP(v) ((JSScopeProperty *) PCVAL_CLRTAG(v)) +#define SPROP_TO_PCVAL(sprop) PCVAL_SETTAG(sprop, PCVAL_SPROP) + +/* + * Fill property cache entry for key cx->fp->pc, optimized value word computed + * from obj and sprop, and entry capability forged from 24-bit OBJ_SHAPE(obj), + * 4-bit scopeIndex, and 4-bit protoIndex. + * + * Return the filled cache entry or JS_NO_PROP_CACHE_FILL if caching was not + * possible. + */ +extern JS_REQUIRES_STACK JSPropCacheEntry * +js_FillPropertyCache(JSContext *cx, JSObject *obj, + uintN scopeIndex, uintN protoIndex, JSObject *pobj, + JSScopeProperty *sprop, JSBool adding); + +/* + * Property cache lookup macros. PROPERTY_CACHE_TEST is designed to inline the + * fast path in js_Interpret, so it makes "just-so" restrictions on parameters, + * e.g. pobj and obj should not be the same variable, since for JOF_PROP-mode + * opcodes, obj must not be changed because of a cache miss. + * + * On return from PROPERTY_CACHE_TEST, if atom is null then obj points to the + * scope chain element in which the property was found, pobj is locked, and + * entry is valid. If atom is non-null then no object is locked but entry is + * still set correctly for use, e.g., by js_FillPropertyCache and atom should + * be used as the id to find. + * + * We must lock pobj on a hit in order to close races with threads that might + * be deleting a property from its scope, or otherwise invalidating property + * caches (on all threads) by re-generating scope->shape. + */ +#define PROPERTY_CACHE_TEST(cx, pc, obj, pobj, entry, atom) \ + do { \ + JSPropertyCache *cache_ = &JS_PROPERTY_CACHE(cx); \ + uint32 kshape_ = (JS_ASSERT(OBJ_IS_NATIVE(obj)), OBJ_SHAPE(obj)); \ + entry = &cache_->table[PROPERTY_CACHE_HASH_PC(pc, kshape_)]; \ + PCMETER(cache_->pctestentry = entry); \ + PCMETER(cache_->tests++); \ + JS_ASSERT(&obj != &pobj); \ + if (entry->kpc == pc && entry->kshape == kshape_) { \ + JSObject *tmp_; \ + pobj = obj; \ + JS_ASSERT(PCVCAP_TAG(entry->vcap) <= 1); \ + if (PCVCAP_TAG(entry->vcap) == 1 && \ + (tmp_ = OBJ_GET_PROTO(cx, pobj)) != NULL) { \ + pobj = tmp_; \ + } \ + \ + if (JS_LOCK_OBJ_IF_SHAPE(cx, pobj, PCVCAP_SHAPE(entry->vcap))) { \ + PCMETER(cache_->pchits++); \ + PCMETER(!PCVCAP_TAG(entry->vcap) || cache_->protopchits++); \ + atom = NULL; \ + break; \ + } \ + } \ + atom = js_FullTestPropertyCache(cx, pc, &obj, &pobj, &entry); \ + if (atom) \ + PCMETER(cache_->misses++); \ + } while (0) + +extern JS_REQUIRES_STACK JSAtom * +js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc, + JSObject **objp, JSObject **pobjp, + JSPropCacheEntry **entryp); + +/* The property cache does not need a destructor. */ +#define js_FinishPropertyCache(cache) ((void) 0) + +extern void +js_PurgePropertyCache(JSContext *cx, JSPropertyCache *cache); + +extern void +js_PurgePropertyCacheForScript(JSContext *cx, JSScript *script); + +/* + * Interpreter stack arena-pool alloc and free functions. + */ +extern JS_REQUIRES_STACK JS_FRIEND_API(jsval *) +js_AllocStack(JSContext *cx, uintN nslots, void **markp); + +extern JS_REQUIRES_STACK JS_FRIEND_API(void) +js_FreeStack(JSContext *cx, void *mark); + +/* + * Refresh and return fp->scopeChain. It may be stale if block scopes are + * active but not yet reflected by objects in the scope chain. If a block + * scope contains a with, eval, XML filtering predicate, or similar such + * dynamically scoped construct, then compile-time block scope at fp->blocks + * must reflect at runtime. + */ +extern JSObject * +js_GetScopeChain(JSContext *cx, JSStackFrame *fp); + +/* + * Given a context and a vector of [callee, this, args...] for a function that + * was specified with a JSFUN_THISP_PRIMITIVE flag, get the primitive value of + * |this| into *thisvp. In doing so, if |this| is an object, insist it is an + * instance of clasp and extract its private slot value to return via *thisvp. + * + * NB: this function loads and uses *vp before storing *thisvp, so the two may + * alias the same jsval. + */ +extern JSBool +js_GetPrimitiveThis(JSContext *cx, jsval *vp, JSClass *clasp, jsval *thisvp); + +/* + * For a call with arguments argv including argv[-1] (nominal |this|) and + * argv[-2] (callee) replace null |this| with callee's parent, replace + * primitive values with the equivalent wrapper objects and censor activation + * objects as, per ECMA-262, they may not be referred to by |this|. argv[-1] + * must not be a JSVAL_VOID. + */ +extern JSObject * +js_ComputeThis(JSContext *cx, JSBool lazy, jsval *argv); + +extern const uint16 js_PrimitiveTestFlags[]; + +#define PRIMITIVE_THIS_TEST(fun,thisv) \ + (JS_ASSERT(!JSVAL_IS_VOID(thisv)), \ + JSFUN_THISP_TEST(JSFUN_THISP_FLAGS((fun)->flags), \ + js_PrimitiveTestFlags[JSVAL_TAG(thisv) - 1])) + +#ifdef __cplusplus /* Aargh, libgjs, bug 492720. */ +static JS_INLINE JSObject * +js_ComputeThisForFrame(JSContext *cx, JSStackFrame *fp) +{ + if (fp->flags & JSFRAME_COMPUTED_THIS) + return JSVAL_TO_OBJECT(fp->thisv); /* JSVAL_COMPUTED_THIS invariant */ + JSObject* obj = js_ComputeThis(cx, JS_TRUE, fp->argv); + if (!obj) + return NULL; + fp->thisv = OBJECT_TO_JSVAL(obj); + fp->flags |= JSFRAME_COMPUTED_THIS; + return obj; +} +#endif + +/* + * NB: js_Invoke requires that cx is currently running JS (i.e., that cx->fp + * is non-null), and that vp points to the callee, |this| parameter, and + * actual arguments of the call. [vp .. vp + 2 + argc) must belong to the last + * JS stack segment that js_AllocStack allocated. The function may use the + * space available after vp + 2 + argc in the stack segment for temporaries, + * so the caller should not use that space for values that must be preserved + * across the call. + */ +extern JS_REQUIRES_STACK JS_FRIEND_API(JSBool) +js_Invoke(JSContext *cx, uintN argc, jsval *vp, uintN flags); + +/* + * Consolidated js_Invoke flags simply rename certain JSFRAME_* flags, so that + * we can share bits stored in JSStackFrame.flags and passed to: + * + * js_Invoke + * js_InternalInvoke + * js_ValueToFunction + * js_ValueToFunctionObject + * js_ValueToCallableObject + * js_ReportIsNotFunction + * + * See jsfun.h for the latter four and flag renaming macros. + */ +#define JSINVOKE_CONSTRUCT JSFRAME_CONSTRUCTING +#define JSINVOKE_ITERATOR JSFRAME_ITERATOR + +/* + * Mask to isolate construct and iterator flags for use with jsfun.h functions. + */ +#define JSINVOKE_FUNFLAGS (JSINVOKE_CONSTRUCT | JSINVOKE_ITERATOR) + +/* + * "Internal" calls may come from C or C++ code using a JSContext on which no + * JS is running (!cx->fp), so they may need to push a dummy JSStackFrame. + */ +#define js_InternalCall(cx,obj,fval,argc,argv,rval) \ + js_InternalInvoke(cx, obj, fval, 0, argc, argv, rval) + +#define js_InternalConstruct(cx,obj,fval,argc,argv,rval) \ + js_InternalInvoke(cx, obj, fval, JSINVOKE_CONSTRUCT, argc, argv, rval) + +extern JSBool +js_InternalInvoke(JSContext *cx, JSObject *obj, jsval fval, uintN flags, + uintN argc, jsval *argv, jsval *rval); + +extern JSBool +js_InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, jsval fval, + JSAccessMode mode, uintN argc, jsval *argv, jsval *rval); + +extern JS_FORCES_STACK JSBool +js_Execute(JSContext *cx, JSObject *chain, JSScript *script, + JSStackFrame *down, uintN flags, jsval *result); + +extern JS_REQUIRES_STACK JSBool +js_InvokeConstructor(JSContext *cx, uintN argc, JSBool clampReturn, jsval *vp); + +extern JS_REQUIRES_STACK JSBool +js_Interpret(JSContext *cx); + +#define JSPROP_INITIALIZER 0x100 /* NB: Not a valid property attribute. */ + +extern JSBool +js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs, + JSObject **objp, JSProperty **propp); + +extern JSBool +js_StrictlyEqual(JSContext *cx, jsval lval, jsval rval); + +/* === except that NaN is the same as NaN and -0 is not the same as +0. */ +extern JSBool +js_SameValue(jsval v1, jsval v2, JSContext *cx); + +extern JSBool +js_InternNonIntElementId(JSContext *cx, JSObject *obj, jsval idval, jsid *idp); + +/* + * Given an active context, a static scope level, and an upvar cookie, return + * the value of the upvar. + */ +extern jsval& +js_GetUpvar(JSContext *cx, uintN level, uintN cookie); + +/* + * JS_LONE_INTERPRET indicates that the compiler should see just the code for + * the js_Interpret function when compiling jsinterp.cpp. The rest of the code + * from the file should be visible only when compiling jsinvoke.cpp. It allows + * platform builds to optimize selectively js_Interpret when the granularity + * of the optimizations with the given compiler is a compilation unit. + * + * JS_STATIC_INTERPRET is the modifier for functions defined in jsinterp.cpp + * that only js_Interpret calls. When JS_LONE_INTERPRET is true all such + * functions are declared below. + */ +#ifndef JS_LONE_INTERPRET +# ifdef _MSC_VER +# define JS_LONE_INTERPRET 0 +# else +# define JS_LONE_INTERPRET 1 +# endif +#endif + +#define JS_MAX_INLINE_CALL_COUNT 3000 + +#if !JS_LONE_INTERPRET +# define JS_STATIC_INTERPRET static +#else +# define JS_STATIC_INTERPRET + +extern JS_REQUIRES_STACK jsval * +js_AllocRawStack(JSContext *cx, uintN nslots, void **markp); + +extern JS_REQUIRES_STACK void +js_FreeRawStack(JSContext *cx, void *mark); + +/* + * ECMA requires "the global object", but in embeddings such as the browser, + * which have multiple top-level objects (windows, frames, etc. in the DOM), + * we prefer fun's parent. An example that causes this code to run: + * + * // in window w1 + * function f() { return this } + * function g() { return f } + * + * // in window w2 + * var h = w1.g() + * alert(h() == w1) + * + * The alert should display "true". + */ +extern JSObject * +js_ComputeGlobalThis(JSContext *cx, JSBool lazy, jsval *argv); + +extern JS_REQUIRES_STACK JSBool +js_EnterWith(JSContext *cx, jsint stackIndex); + +extern JS_REQUIRES_STACK void +js_LeaveWith(JSContext *cx); + +extern JS_REQUIRES_STACK JSClass * +js_IsActiveWithOrBlock(JSContext *cx, JSObject *obj, int stackDepth); + +/* + * Unwind block and scope chains to match the given depth. The function sets + * fp->sp on return to stackDepth. + */ +extern JS_REQUIRES_STACK JSBool +js_UnwindScope(JSContext *cx, JSStackFrame *fp, jsint stackDepth, + JSBool normalUnwind); + +extern JSBool +js_OnUnknownMethod(JSContext *cx, jsval *vp); + +/* + * Find the results of incrementing or decrementing *vp. For pre-increments, + * both *vp and *vp2 will contain the result on return. For post-increments, + * vp will contain the original value converted to a number and vp2 will get + * the result. Both vp and vp2 must be roots. + */ +extern JSBool +js_DoIncDec(JSContext *cx, const JSCodeSpec *cs, jsval *vp, jsval *vp2); + +/* + * Opcode tracing helper. When len is not 0, cx->fp->regs->pc[-len] gives the + * previous opcode. + */ +extern JS_REQUIRES_STACK void +js_TraceOpcode(JSContext *cx); + +/* + * JS_OPMETER helper functions. + */ +extern void +js_MeterOpcodePair(JSOp op1, JSOp op2); + +extern void +js_MeterSlotOpcode(JSOp op, uint32 slot); + +#endif /* JS_LONE_INTERPRET */ + +JS_END_EXTERN_C + +#endif /* jsinterp_h___ */ diff --git a/ape-server/deps/js/src/jsinttypes.h b/ape-server/deps/js/src/jsinttypes.h new file mode 100755 index 0000000..7e06b3e --- /dev/null +++ b/ape-server/deps/js/src/jsinttypes.h @@ -0,0 +1,139 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released + * May 28, 2008. + * + * The Initial Developer of the Original Code is + * Jim Blandy + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsinttypes_h___ +#define jsinttypes_h___ + +#include "js-config.h" + +/* + * Types: + * JSInt, JSUint (for = 8, 16, 32, and 64) + * JSIntPtr, JSUIntPtr + * + * JSInt and JSUint are signed and unsigned types known to be + * bits long. Note that neither JSInt8 nor JSUInt8 is necessarily + * equivalent to a plain "char". + * + * JSIntPtr and JSUintPtr are signed and unsigned types capable of + * holding an object pointer. + * + * Use these types in public SpiderMonkey header files, not the + * corresponding types from the C standard header. Windows + * doesn't support , and Microsoft says it has no plans to + * do so in the future; this means that projects that embed + * SpiderMonkey often take matters into their own hands and define the + * standard types themselves. If we define them in our public + * headers, our definitions may conflict with embedders' (see bug + * 479258). The JS* types are in our namespace, and can be used + * without troubling anyone. + * + * Internal SpiderMonkey code wishing to use the type names ISO C + * defines in the standard header can #include + * "jsstdint.h", which provides those types regardless of whether + * itself is available. + */ + +#if defined(JS_HAVE_STDINT_H) + +#include + +typedef int8_t JSInt8; +typedef int16_t JSInt16; +typedef int32_t JSInt32; +typedef int64_t JSInt64; +typedef intptr_t JSIntPtr; + +typedef uint8_t JSUint8; +typedef uint16_t JSUint16; +typedef uint32_t JSUint32; +typedef uint64_t JSUint64; +typedef uintptr_t JSUintPtr; + +#else + +#if defined(JS_HAVE___INTN) + +typedef __int8 JSInt8; +typedef __int16 JSInt16; +typedef __int32 JSInt32; +typedef __int64 JSInt64; + +typedef unsigned __int8 JSUint8; +typedef unsigned __int16 JSUint16; +typedef unsigned __int32 JSUint32; +typedef unsigned __int64 JSUint64; + +#elif defined(JS_INT8_TYPE) + +typedef signed JS_INT8_TYPE JSInt8; +typedef signed JS_INT16_TYPE JSInt16; +typedef signed JS_INT32_TYPE JSInt32; +typedef signed JS_INT64_TYPE JSInt64; + +typedef unsigned JS_INT8_TYPE JSUint8; +typedef unsigned JS_INT16_TYPE JSUint16; +typedef unsigned JS_INT32_TYPE JSUint32; +typedef unsigned JS_INT64_TYPE JSUint64; + +#else +#error "couldn't find exact-width integer types" +#endif + +/* Microsoft Visual C/C++ defines intptr_t and uintptr_t in . */ +#if defined(JS_STDDEF_H_HAS_INTPTR_T) +#include +typedef intptr_t JSIntPtr; +typedef uintptr_t JSUintPtr; + +/* Windows Mobile defines intptr_t and uintptr_t in . Why not? */ +#elif defined(JS_CRTDEFS_H_HAS_INTPTR_T) +#include +typedef intptr_t JSIntPtr; +typedef uintptr_t JSUintPtr; + +/* Failing that, the configure script will have found something. */ +#elif defined(JS_INTPTR_TYPE) +typedef signed JS_INTPTR_TYPE JSIntPtr; +typedef unsigned JS_INTPTR_TYPE JSUintPtr; + +#else +#error "couldn't find pointer-sized integer types" +#endif + +#endif /* JS_HAVE_STDINT_H */ + +#endif /* jsinttypes_h___ */ diff --git a/ape-server/deps/js/src/jsinvoke.cpp b/ape-server/deps/js/src/jsinvoke.cpp new file mode 100755 index 0000000..5857e41 --- /dev/null +++ b/ape-server/deps/js/src/jsinvoke.cpp @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla SpiderMonkey JavaScript 1.8 code, released + * March 4, 2008. + * + * The Initial Developer of the Original Code is + * Igor Bukanov + * + * Contributor(s): + * Brendan Eich /* for memcpy */ +#include "jstypes.h" +#include "jsstdint.h" +#include "jsutil.h" +#include "jsarena.h" +#include "jsapi.h" +#include "jsarray.h" +#include "jsatom.h" +#include "jsbool.h" +#include "jsbuiltins.h" +#include "jscntxt.h" +#include "jsversion.h" +#include "jsexn.h" +#include "jsfun.h" +#include "jsgc.h" +#include "jsinterp.h" +#include "jsiter.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsobj.h" +#include "jsopcode.h" +#include "jsscan.h" +#include "jsscope.h" +#include "jsscript.h" +#include "jsstaticcheck.h" +#include "jstracer.h" + +#if JS_HAS_XML_SUPPORT +#include "jsxml.h" +#endif + +JS_STATIC_ASSERT(JSSLOT_ITER_FLAGS < JS_INITIAL_NSLOTS); + +#if JS_HAS_GENERATORS + +static JS_REQUIRES_STACK JSBool +CloseGenerator(JSContext *cx, JSObject *genobj); + +#endif + +/* + * Shared code to close iterator's state either through an explicit call or + * when GC detects that the iterator is no longer reachable. + */ +void +js_CloseNativeIterator(JSContext *cx, JSObject *iterobj) +{ + jsval state; + JSObject *iterable; + + JS_ASSERT(STOBJ_GET_CLASS(iterobj) == &js_IteratorClass); + + /* Avoid double work if js_CloseNativeIterator was called on obj. */ + state = STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_STATE); + if (JSVAL_IS_NULL(state)) + return; + + /* Protect against failure to fully initialize obj. */ + iterable = iterobj->getParent(); + if (iterable) { +#if JS_HAS_XML_SUPPORT + uintN flags = JSVAL_TO_INT(STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_FLAGS)); + if ((flags & JSITER_FOREACH) && OBJECT_IS_XML(cx, iterable)) { + js_EnumerateXMLValues(cx, iterable, JSENUMERATE_DESTROY, &state, + NULL, NULL); + } else +#endif + iterable->enumerate(cx, JSENUMERATE_DESTROY, &state, NULL); + } + STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, JSVAL_NULL); +} + +static void +iterator_trace(JSTracer *trc, JSObject *obj) +{ + /* + * The GC marks iter_state during the normal slot scanning if + * JSVAL_IS_TRACEABLE(iter_state) is true duplicating the efforts of + * js_MarkEnumeratorState. But this is rare so we optimize for code + * simplicity. + */ + JSObject *iterable = obj->getParent(); + if (!iterable) { + /* for (x in null) creates an iterator object with a null parent. */ + return; + } + jsval iter_state = obj->fslots[JSSLOT_ITER_STATE]; + js_MarkEnumeratorState(trc, iterable, iter_state); +} + +JSClass js_IteratorClass = { + "Iterator", + JSCLASS_HAS_RESERVED_SLOTS(2) | /* slots for state and flags */ + JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator) | + JSCLASS_MARK_IS_TRACE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, JS_CLASS_TRACE(iterator_trace), NULL +}; + +static JSBool +InitNativeIterator(JSContext *cx, JSObject *iterobj, JSObject *obj, uintN flags) +{ + jsval state; + JSBool ok; + + JS_ASSERT(STOBJ_GET_CLASS(iterobj) == &js_IteratorClass); + + /* Initialize iterobj in case of enumerate hook failure. */ + STOBJ_SET_PARENT(iterobj, obj); + STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, JSVAL_NULL); + STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_FLAGS, INT_TO_JSVAL(flags)); + if (!js_RegisterCloseableIterator(cx, iterobj)) + return JS_FALSE; + if (!obj) + return JS_TRUE; + + ok = +#if JS_HAS_XML_SUPPORT + ((flags & JSITER_FOREACH) && OBJECT_IS_XML(cx, obj)) + ? js_EnumerateXMLValues(cx, obj, JSENUMERATE_INIT, &state, NULL, NULL) + : +#endif + obj->enumerate(cx, JSENUMERATE_INIT, &state, NULL); + if (!ok) + return JS_FALSE; + + STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, state); + if (flags & JSITER_ENUMERATE) { + /* + * The enumerating iterator needs the original object to suppress + * enumeration of deleted or shadowed prototype properties. Since the + * enumerator never escapes to scripts, we use the prototype slot to + * store the original object. + */ + JS_ASSERT(obj != iterobj); + STOBJ_SET_PROTO(iterobj, obj); + } + return JS_TRUE; +} + +static JSBool +Iterator(JSContext *cx, JSObject *iterobj, uintN argc, jsval *argv, jsval *rval) +{ + JSBool keyonly; + uintN flags; + JSObject *obj; + + keyonly = js_ValueToBoolean(argv[1]); + flags = keyonly ? 0 : JSITER_FOREACH; + + if (JS_IsConstructing(cx)) { + /* XXX work around old valueOf call hidden beneath js_ValueToObject */ + if (!JSVAL_IS_PRIMITIVE(argv[0])) { + obj = JSVAL_TO_OBJECT(argv[0]); + } else { + obj = js_ValueToNonNullObject(cx, argv[0]); + if (!obj) + return JS_FALSE; + argv[0] = OBJECT_TO_JSVAL(obj); + } + return InitNativeIterator(cx, iterobj, obj, flags); + } + + *rval = argv[0]; + return js_ValueToIterator(cx, flags, rval); +} + +static JSBool +NewKeyValuePair(JSContext *cx, jsid key, jsval val, jsval *rval) +{ + jsval vec[2]; + JSTempValueRooter tvr; + JSObject *aobj; + + vec[0] = ID_TO_VALUE(key); + vec[1] = val; + + JS_PUSH_TEMP_ROOT(cx, 2, vec, &tvr); + aobj = js_NewArrayObject(cx, 2, vec); + *rval = OBJECT_TO_JSVAL(aobj); + JS_POP_TEMP_ROOT(cx, &tvr); + + return aobj != NULL; +} + +static JSBool +IteratorNextImpl(JSContext *cx, JSObject *obj, jsval *rval) +{ + JSObject *iterable; + jsval state; + uintN flags; + JSBool foreach, ok; + jsid id; + + JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_IteratorClass); + + iterable = OBJ_GET_PARENT(cx, obj); + JS_ASSERT(iterable); + state = STOBJ_GET_SLOT(obj, JSSLOT_ITER_STATE); + if (JSVAL_IS_NULL(state)) + goto stop; + + flags = JSVAL_TO_INT(STOBJ_GET_SLOT(obj, JSSLOT_ITER_FLAGS)); + JS_ASSERT(!(flags & JSITER_ENUMERATE)); + foreach = (flags & JSITER_FOREACH) != 0; + ok = +#if JS_HAS_XML_SUPPORT + (foreach && OBJECT_IS_XML(cx, iterable)) + ? js_EnumerateXMLValues(cx, iterable, JSENUMERATE_NEXT, &state, + &id, rval) + : +#endif + iterable->enumerate(cx, JSENUMERATE_NEXT, &state, &id); + if (!ok) + return JS_FALSE; + + STOBJ_SET_SLOT(obj, JSSLOT_ITER_STATE, state); + if (JSVAL_IS_NULL(state)) + goto stop; + + if (foreach) { +#if JS_HAS_XML_SUPPORT + if (!OBJECT_IS_XML(cx, iterable) && + !iterable->getProperty(cx, id, rval)) { + return JS_FALSE; + } +#endif + if (!NewKeyValuePair(cx, id, *rval, rval)) + return JS_FALSE; + } else { + *rval = ID_TO_VALUE(id); + } + return JS_TRUE; + + stop: + JS_ASSERT(STOBJ_GET_SLOT(obj, JSSLOT_ITER_STATE) == JSVAL_NULL); + *rval = JSVAL_HOLE; + return JS_TRUE; +} + +JSBool +js_ThrowStopIteration(JSContext *cx) +{ + jsval v; + + JS_ASSERT(!JS_IsExceptionPending(cx)); + if (js_FindClassObject(cx, NULL, INT_TO_JSID(JSProto_StopIteration), &v)) + JS_SetPendingException(cx, v); + return JS_FALSE; +} + +static JSBool +iterator_next(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *obj; + + obj = JS_THIS_OBJECT(cx, vp); + if (!JS_InstanceOf(cx, obj, &js_IteratorClass, vp + 2)) + return JS_FALSE; + + if (!IteratorNextImpl(cx, obj, vp)) + return JS_FALSE; + + if (*vp == JSVAL_HOLE) { + *vp = JSVAL_NULL; + js_ThrowStopIteration(cx); + return JS_FALSE; + } + return JS_TRUE; +} + +static JSBool +iterator_self(JSContext *cx, uintN argc, jsval *vp) +{ + *vp = JS_THIS(cx, vp); + return !JSVAL_IS_NULL(*vp); +} + +#define JSPROP_ROPERM (JSPROP_READONLY | JSPROP_PERMANENT) + +static JSFunctionSpec iterator_methods[] = { + JS_FN(js_iterator_str, iterator_self, 0,JSPROP_ROPERM), + JS_FN(js_next_str, iterator_next, 0,JSPROP_ROPERM), + JS_FS_END +}; + +uintN +js_GetNativeIteratorFlags(JSContext *cx, JSObject *iterobj) +{ + if (OBJ_GET_CLASS(cx, iterobj) != &js_IteratorClass) + return 0; + return JSVAL_TO_INT(STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_FLAGS)); +} + +/* + * Call ToObject(v).__iterator__(keyonly) if ToObject(v).__iterator__ exists. + * Otherwise construct the default iterator. + */ +JS_FRIEND_API(JSBool) +js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp) +{ + JSObject *obj; + JSTempValueRooter tvr; + JSAtom *atom; + JSClass *clasp; + JSExtendedClass *xclasp; + JSBool ok; + JSObject *iterobj; + jsval arg; + + JS_ASSERT(!(flags & ~(JSITER_ENUMERATE | + JSITER_FOREACH | + JSITER_KEYVALUE))); + + /* JSITER_KEYVALUE must always come with JSITER_FOREACH */ + JS_ASSERT(!(flags & JSITER_KEYVALUE) || (flags & JSITER_FOREACH)); + + /* XXX work around old valueOf call hidden beneath js_ValueToObject */ + if (!JSVAL_IS_PRIMITIVE(*vp)) { + obj = JSVAL_TO_OBJECT(*vp); + } else { + /* + * Enumerating over null and undefined gives an empty enumerator. + * This is contrary to ECMA-262 9.9 ToObject, invoked from step 3 of + * the first production in 12.6.4 and step 4 of the second production, + * but it's "web JS" compatible. + */ + if ((flags & JSITER_ENUMERATE)) { + if (!js_ValueToObject(cx, *vp, &obj)) + return JS_FALSE; + if (!obj) + goto default_iter; + } else { + obj = js_ValueToNonNullObject(cx, *vp); + if (!obj) + return JS_FALSE; + } + } + + JS_ASSERT(obj); + JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr); + + clasp = OBJ_GET_CLASS(cx, obj); + if ((clasp->flags & JSCLASS_IS_EXTENDED) && + (xclasp = (JSExtendedClass *) clasp)->iteratorObject) { + iterobj = xclasp->iteratorObject(cx, obj, !(flags & JSITER_FOREACH)); + if (!iterobj) + goto bad; + *vp = OBJECT_TO_JSVAL(iterobj); + } else { + atom = cx->runtime->atomState.iteratorAtom; + if (!js_GetMethod(cx, obj, ATOM_TO_JSID(atom), JSGET_NO_METHOD_BARRIER, vp)) + goto bad; + if (JSVAL_IS_VOID(*vp)) { + default_iter: + /* + * Fail over to the default enumerating native iterator. + * + * Create iterobj with a NULL parent to ensure that we use the + * correct scope chain to lookup the iterator's constructor. Since + * we use the parent slot to keep track of the iterable, we must + * fix it up after. + */ + iterobj = js_NewObject(cx, &js_IteratorClass, NULL, NULL); + if (!iterobj) + goto bad; + + /* Store in *vp to protect it from GC (callers must root vp). */ + *vp = OBJECT_TO_JSVAL(iterobj); + + if (!InitNativeIterator(cx, iterobj, obj, flags)) + goto bad; + } else { + js_LeaveTrace(cx); + arg = BOOLEAN_TO_JSVAL((flags & JSITER_FOREACH) == 0); + if (!js_InternalInvoke(cx, obj, *vp, JSINVOKE_ITERATOR, 1, &arg, + vp)) { + goto bad; + } + if (JSVAL_IS_PRIMITIVE(*vp)) { + js_ReportValueError(cx, JSMSG_BAD_ITERATOR_RETURN, + JSDVG_SEARCH_STACK, *vp, NULL); + goto bad; + } + } + } + + ok = JS_TRUE; + out: + if (obj) + JS_POP_TEMP_ROOT(cx, &tvr); + return ok; + bad: + ok = JS_FALSE; + goto out; +} + +JS_FRIEND_API(JSBool) JS_FASTCALL +js_CloseIterator(JSContext *cx, jsval v) +{ + JSObject *obj; + JSClass *clasp; + + JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); + obj = JSVAL_TO_OBJECT(v); + clasp = OBJ_GET_CLASS(cx, obj); + + if (clasp == &js_IteratorClass) { + js_CloseNativeIterator(cx, obj); + } +#if JS_HAS_GENERATORS + else if (clasp == &js_GeneratorClass) { + JS_ASSERT_NOT_ON_TRACE(cx); + if (!CloseGenerator(cx, obj)) + return JS_FALSE; + } +#endif + return JS_TRUE; +} +JS_DEFINE_CALLINFO_2(FRIEND, BOOL, js_CloseIterator, CONTEXT, JSVAL, 0, 0) + +static JSBool +CallEnumeratorNext(JSContext *cx, JSObject *iterobj, uintN flags, jsval *rval) +{ + JSObject *obj, *origobj; + jsval state; + JSBool foreach; + jsid id; + JSObject *obj2; + JSBool cond; + JSClass *clasp; + JSExtendedClass *xclasp; + JSProperty *prop; + JSString *str; + + JS_ASSERT(flags & JSITER_ENUMERATE); + JS_ASSERT(STOBJ_GET_CLASS(iterobj) == &js_IteratorClass); + + obj = STOBJ_GET_PARENT(iterobj); + origobj = STOBJ_GET_PROTO(iterobj); + state = STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_STATE); + if (JSVAL_IS_NULL(state)) + goto stop; + + foreach = (flags & JSITER_FOREACH) != 0; +#if JS_HAS_XML_SUPPORT + /* + * Treat an XML object specially only when it starts the prototype chain. + * Otherwise we need to do the usual deleted and shadowed property checks. + */ + if (obj == origobj && OBJECT_IS_XML(cx, obj)) { + if (foreach) { + if (!js_EnumerateXMLValues(cx, obj, JSENUMERATE_NEXT, &state, + &id, rval)) { + return JS_FALSE; + } + } else { + if (!obj->enumerate(cx, JSENUMERATE_NEXT, &state, &id)) + return JS_FALSE; + } + STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, state); + if (JSVAL_IS_NULL(state)) + goto stop; + } else +#endif + { + restart: + if (!obj->enumerate(cx, JSENUMERATE_NEXT, &state, &id)) + return JS_FALSE; + + STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, state); + if (JSVAL_IS_NULL(state)) { +#if JS_HAS_XML_SUPPORT + if (OBJECT_IS_XML(cx, obj)) { + /* + * We just finished enumerating an XML obj that is present on + * the prototype chain of a non-XML origobj. Stop further + * prototype chain searches because XML objects don't + * enumerate prototypes. + */ + JS_ASSERT(origobj != obj); + JS_ASSERT(!OBJECT_IS_XML(cx, origobj)); + } else +#endif + { + obj = OBJ_GET_PROTO(cx, obj); + if (obj) { + STOBJ_SET_PARENT(iterobj, obj); + if (!obj->enumerate(cx, JSENUMERATE_INIT, &state, NULL)) + return JS_FALSE; + STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, state); + if (!JSVAL_IS_NULL(state)) + goto restart; + } + } + goto stop; + } + + /* Skip properties not in obj when looking from origobj. */ + if (!origobj->lookupProperty(cx, id, &obj2, &prop)) + return JS_FALSE; + if (!prop) + goto restart; + obj2->dropProperty(cx, prop); + + /* + * If the id was found in a prototype object or an unrelated object + * (specifically, not in an inner object for obj), skip it. This step + * means that all lookupProperty implementations must return an + * object further along on the prototype chain, or else possibly an + * object returned by the JSExtendedClass.outerObject optional hook. + */ + if (obj != obj2) { + cond = JS_FALSE; + clasp = OBJ_GET_CLASS(cx, obj2); + if (clasp->flags & JSCLASS_IS_EXTENDED) { + xclasp = (JSExtendedClass *) clasp; + cond = xclasp->outerObject && + xclasp->outerObject(cx, obj2) == obj; + } + if (!cond) + goto restart; + } + + if (foreach) { + /* Get property querying the original object. */ + if (!origobj->getProperty(cx, id, rval)) + return JS_FALSE; + } + } + + if (foreach) { + if (flags & JSITER_KEYVALUE) { + if (!NewKeyValuePair(cx, id, *rval, rval)) + return JS_FALSE; + } + } else { + /* Make rval a string for uniformity and compatibility. */ + str = js_ValueToString(cx, ID_TO_VALUE(id)); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + } + return JS_TRUE; + + stop: + JS_ASSERT(STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_STATE) == JSVAL_NULL); + *rval = JSVAL_HOLE; + return JS_TRUE; +} + +JS_FRIEND_API(JSBool) +js_CallIteratorNext(JSContext *cx, JSObject *iterobj, jsval *rval) +{ + uintN flags; + + /* Fast path for native iterators */ + if (OBJ_GET_CLASS(cx, iterobj) == &js_IteratorClass) { + flags = JSVAL_TO_INT(STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_FLAGS)); + if (flags & JSITER_ENUMERATE) + return CallEnumeratorNext(cx, iterobj, flags, rval); + + /* + * Call next directly as all the methods of the native iterator are + * read-only and permanent. + */ + if (!IteratorNextImpl(cx, iterobj, rval)) + return JS_FALSE; + } else { + jsid id = ATOM_TO_JSID(cx->runtime->atomState.nextAtom); + + if (!JS_GetMethodById(cx, iterobj, id, &iterobj, rval)) + return JS_FALSE; + if (!js_InternalCall(cx, iterobj, *rval, 0, NULL, rval)) { + /* Check for StopIteration. */ + if (!cx->throwing || !js_ValueIsStopIteration(cx->exception)) + return JS_FALSE; + + /* Inline JS_ClearPendingException(cx). */ + cx->throwing = JS_FALSE; + cx->exception = JSVAL_VOID; + *rval = JSVAL_HOLE; + return JS_TRUE; + } + } + + return JS_TRUE; +} + +static JSBool +stopiter_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) +{ + *bp = js_ValueIsStopIteration(v); + return JS_TRUE; +} + +JSClass js_StopIterationClass = { + js_StopIteration_str, + JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration), + JS_PropertyStub, JS_PropertyStub, + JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, + JS_ConvertStub, NULL, + NULL, NULL, + NULL, NULL, + NULL, stopiter_hasInstance, + NULL, NULL +}; + +#if JS_HAS_GENERATORS + +static void +generator_finalize(JSContext *cx, JSObject *obj) +{ + JSGenerator *gen = (JSGenerator *) obj->getPrivate(); + if (!gen) + return; + + /* + * gen is open when a script has not called its close method while + * explicitly manipulating it. + */ + JS_ASSERT(gen->state == JSGEN_NEWBORN || + gen->state == JSGEN_CLOSED || + gen->state == JSGEN_OPEN); + cx->free(gen); +} + +static void +generator_trace(JSTracer *trc, JSObject *obj) +{ + JSGenerator *gen = (JSGenerator *) obj->getPrivate(); + if (!gen) + return; + + /* + * js_TraceStackFrame does not recursively trace the down-linked frame + * chain, so we insist that gen->frame has no parent to trace when the + * generator is not running. + */ + JS_ASSERT_IF(gen->state != JSGEN_RUNNING && gen->state != JSGEN_CLOSING, + !gen->frame.down); + + /* + * FIXME be 390950. Generator's frame is a part of the JS stack when the + * generator is running or closing. Thus tracing the frame in this case + * here duplicates the work done in js_TraceContext. + */ + js_TraceStackFrame(trc, &gen->frame); +} + +JS_FRIEND_DATA(JSClass) js_GeneratorClass = { + js_Generator_str, + JSCLASS_HAS_PRIVATE | JSCLASS_IS_ANONYMOUS | + JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Generator), + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, generator_finalize, + NULL, NULL, NULL, NULL, + NULL, NULL, JS_CLASS_TRACE(generator_trace), NULL +}; + +/* + * Called from the JSOP_GENERATOR case in the interpreter, with fp referring + * to the frame by which the generator function was activated. Create a new + * JSGenerator object, which contains its own JSStackFrame that we populate + * from *fp. We know that upon return, the JSOP_GENERATOR opcode will return + * from the activation in fp, so we can steal away fp->callobj and fp->argsobj + * if they are non-null. + */ +JSObject * +js_NewGenerator(JSContext *cx, JSStackFrame *fp) +{ + JSObject *obj; + uintN argc, nargs, nslots; + JSGenerator *gen; + jsval *slots; + + obj = js_NewObject(cx, &js_GeneratorClass, NULL, NULL); + if (!obj) + return NULL; + + /* Load and compute stack slot counts. */ + argc = fp->argc; + nargs = JS_MAX(argc, fp->fun->nargs); + nslots = 2 + nargs + fp->script->nslots; + + /* Allocate obj's private data struct. */ + gen = (JSGenerator *) + cx->malloc(sizeof(JSGenerator) + (nslots - 1) * sizeof(jsval)); + if (!gen) + return NULL; + + gen->obj = obj; + + /* Steal away objects reflecting fp and point them at gen->frame. */ + gen->frame.callobj = fp->callobj; + if (fp->callobj) { + fp->callobj->setPrivate(&gen->frame); + fp->callobj = NULL; + } + gen->frame.argsobj = fp->argsobj; + if (fp->argsobj) { + JSVAL_TO_OBJECT(fp->argsobj)->setPrivate(&gen->frame); + fp->argsobj = NULL; + } + + /* These two references can be shared with fp until it goes away. */ + gen->frame.varobj = fp->varobj; + gen->frame.thisv = fp->thisv; + + /* Copy call-invariant script and function references. */ + gen->frame.script = fp->script; + gen->frame.fun = fp->fun; + + /* Use slots to carve space out of gen->slots. */ + slots = gen->slots; + gen->arena.next = NULL; + gen->arena.base = (jsuword) slots; + gen->arena.limit = gen->arena.avail = (jsuword) (slots + nslots); + + /* Copy rval, argv and vars. */ + gen->frame.rval = fp->rval; + memcpy(slots, fp->argv - 2, (2 + nargs) * sizeof(jsval)); + gen->frame.argc = nargs; + gen->frame.argv = slots + 2; + slots += 2 + nargs; + memcpy(slots, fp->slots, fp->script->nfixed * sizeof(jsval)); + + /* Initialize or copy virtual machine state. */ + gen->frame.down = NULL; + gen->frame.annotation = NULL; + gen->frame.scopeChain = fp->scopeChain; + + gen->frame.imacpc = NULL; + gen->frame.slots = slots; + JS_ASSERT(StackBase(fp) == fp->regs->sp); + gen->savedRegs.sp = slots + fp->script->nfixed; + gen->savedRegs.pc = fp->regs->pc; + gen->frame.regs = &gen->savedRegs; + + gen->frame.flags = (fp->flags & ~JSFRAME_ROOTED_ARGV) | JSFRAME_GENERATOR; + gen->frame.dormantNext = NULL; + + /* JSOP_GENERATOR appears in the prologue, outside all blocks. */ + JS_ASSERT(!fp->blockChain); + gen->frame.blockChain = NULL; + + /* Note that gen is newborn. */ + gen->state = JSGEN_NEWBORN; + + obj->setPrivate(gen); + return obj; +} + +typedef enum JSGeneratorOp { + JSGENOP_NEXT, + JSGENOP_SEND, + JSGENOP_THROW, + JSGENOP_CLOSE +} JSGeneratorOp; + +/* + * Start newborn or restart yielding generator and perform the requested + * operation inside its frame. + */ +static JS_REQUIRES_STACK JSBool +SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj, + JSGenerator *gen, jsval arg) +{ + JSStackFrame *fp; + JSArena *arena; + JSBool ok; + + if (gen->state == JSGEN_RUNNING || gen->state == JSGEN_CLOSING) { + js_ReportValueError(cx, JSMSG_NESTING_GENERATOR, + JSDVG_SEARCH_STACK, OBJECT_TO_JSVAL(obj), + JS_GetFunctionId(gen->frame.fun)); + return JS_FALSE; + } + + JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN); + switch (op) { + case JSGENOP_NEXT: + case JSGENOP_SEND: + if (gen->state == JSGEN_OPEN) { + /* + * Store the argument to send as the result of the yield + * expression. + */ + gen->savedRegs.sp[-1] = arg; + } + gen->state = JSGEN_RUNNING; + break; + + case JSGENOP_THROW: + JS_SetPendingException(cx, arg); + gen->state = JSGEN_RUNNING; + break; + + default: + JS_ASSERT(op == JSGENOP_CLOSE); + JS_SetPendingException(cx, JSVAL_ARETURN); + gen->state = JSGEN_CLOSING; + break; + } + + /* Extend the current stack pool with gen->arena. */ + arena = cx->stackPool.current; + JS_ASSERT(!arena->next); + JS_ASSERT(!gen->arena.next); + JS_ASSERT(cx->stackPool.current != &gen->arena); + cx->stackPool.current = arena->next = &gen->arena; + + /* Push gen->frame around the interpreter activation. */ + fp = js_GetTopStackFrame(cx); + cx->fp = &gen->frame; + gen->frame.down = fp; + ok = js_Interpret(cx); + cx->fp = fp; + gen->frame.down = NULL; + + /* Retract the stack pool and sanitize gen->arena. */ + JS_ASSERT(!gen->arena.next); + JS_ASSERT(arena->next == &gen->arena); + JS_ASSERT(cx->stackPool.current == &gen->arena); + cx->stackPool.current = arena; + arena->next = NULL; + + if (gen->frame.flags & JSFRAME_YIELDING) { + /* Yield cannot fail, throw or be called on closing. */ + JS_ASSERT(ok); + JS_ASSERT(!cx->throwing); + JS_ASSERT(gen->state == JSGEN_RUNNING); + JS_ASSERT(op != JSGENOP_CLOSE); + gen->frame.flags &= ~JSFRAME_YIELDING; + gen->state = JSGEN_OPEN; + return JS_TRUE; + } + + gen->frame.rval = JSVAL_VOID; + gen->state = JSGEN_CLOSED; + if (ok) { + /* Returned, explicitly or by falling off the end. */ + if (op == JSGENOP_CLOSE) + return JS_TRUE; + return js_ThrowStopIteration(cx); + } + + /* + * An error, silent termination by operation callback or an exception. + * Propagate the condition to the caller. + */ + return JS_FALSE; +} + +static JS_REQUIRES_STACK JSBool +CloseGenerator(JSContext *cx, JSObject *obj) +{ + JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_GeneratorClass); + + JSGenerator *gen = (JSGenerator *) obj->getPrivate(); + if (!gen) { + /* Generator prototype object. */ + return JS_TRUE; + } + + if (gen->state == JSGEN_CLOSED) + return JS_TRUE; + + return SendToGenerator(cx, JSGENOP_CLOSE, obj, gen, JSVAL_VOID); +} + +/* + * Common subroutine of generator_(next|send|throw|close) methods. + */ +static JSBool +generator_op(JSContext *cx, JSGeneratorOp op, jsval *vp, uintN argc) +{ + JSObject *obj; + jsval arg; + + js_LeaveTrace(cx); + + obj = JS_THIS_OBJECT(cx, vp); + if (!JS_InstanceOf(cx, obj, &js_GeneratorClass, vp + 2)) + return JS_FALSE; + + JSGenerator *gen = (JSGenerator *) obj->getPrivate(); + if (!gen) { + /* This happens when obj is the generator prototype. See bug 352885. */ + goto closed_generator; + } + + if (gen->state == JSGEN_NEWBORN) { + switch (op) { + case JSGENOP_NEXT: + case JSGENOP_THROW: + break; + + case JSGENOP_SEND: + if (argc >= 1 && !JSVAL_IS_VOID(vp[2])) { + js_ReportValueError(cx, JSMSG_BAD_GENERATOR_SEND, + JSDVG_SEARCH_STACK, vp[2], NULL); + return JS_FALSE; + } + break; + + default: + JS_ASSERT(op == JSGENOP_CLOSE); + gen->state = JSGEN_CLOSED; + return JS_TRUE; + } + } else if (gen->state == JSGEN_CLOSED) { + closed_generator: + switch (op) { + case JSGENOP_NEXT: + case JSGENOP_SEND: + return js_ThrowStopIteration(cx); + case JSGENOP_THROW: + JS_SetPendingException(cx, argc >= 1 ? vp[2] : JSVAL_VOID); + return JS_FALSE; + default: + JS_ASSERT(op == JSGENOP_CLOSE); + return JS_TRUE; + } + } + + arg = ((op == JSGENOP_SEND || op == JSGENOP_THROW) && argc != 0) + ? vp[2] + : JSVAL_VOID; + if (!SendToGenerator(cx, op, obj, gen, arg)) + return JS_FALSE; + *vp = gen->frame.rval; + return JS_TRUE; +} + +static JSBool +generator_send(JSContext *cx, uintN argc, jsval *vp) +{ + return generator_op(cx, JSGENOP_SEND, vp, argc); +} + +static JSBool +generator_next(JSContext *cx, uintN argc, jsval *vp) +{ + return generator_op(cx, JSGENOP_NEXT, vp, argc); +} + +static JSBool +generator_throw(JSContext *cx, uintN argc, jsval *vp) +{ + return generator_op(cx, JSGENOP_THROW, vp, argc); +} + +static JSBool +generator_close(JSContext *cx, uintN argc, jsval *vp) +{ + return generator_op(cx, JSGENOP_CLOSE, vp, argc); +} + +static JSFunctionSpec generator_methods[] = { + JS_FN(js_iterator_str, iterator_self, 0,JSPROP_ROPERM), + JS_FN(js_next_str, generator_next, 0,JSPROP_ROPERM), + JS_FN(js_send_str, generator_send, 1,JSPROP_ROPERM), + JS_FN(js_throw_str, generator_throw, 1,JSPROP_ROPERM), + JS_FN(js_close_str, generator_close, 0,JSPROP_ROPERM), + JS_FS_END +}; + +#endif /* JS_HAS_GENERATORS */ + +JSObject * +js_InitIteratorClasses(JSContext *cx, JSObject *obj) +{ + JSObject *proto, *stop; + + /* Idempotency required: we initialize several things, possibly lazily. */ + if (!js_GetClassObject(cx, obj, JSProto_StopIteration, &stop)) + return NULL; + if (stop) + return stop; + + proto = JS_InitClass(cx, obj, NULL, &js_IteratorClass, Iterator, 2, + NULL, iterator_methods, NULL, NULL); + if (!proto) + return NULL; + STOBJ_SET_SLOT(proto, JSSLOT_ITER_STATE, JSVAL_NULL); + STOBJ_SET_SLOT(proto, JSSLOT_ITER_FLAGS, JSVAL_ZERO); + +#if JS_HAS_GENERATORS + /* Initialize the generator internals if configured. */ + if (!JS_InitClass(cx, obj, NULL, &js_GeneratorClass, NULL, 0, + NULL, generator_methods, NULL, NULL)) { + return NULL; + } +#endif + + return JS_InitClass(cx, obj, NULL, &js_StopIterationClass, NULL, 0, + NULL, NULL, NULL, NULL); +} diff --git a/ape-server/deps/js/src/jsiter.h b/ape-server/deps/js/src/jsiter.h new file mode 100755 index 0000000..caeeb3b --- /dev/null +++ b/ape-server/deps/js/src/jsiter.h @@ -0,0 +1,140 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsiter_h___ +#define jsiter_h___ + +/* + * JavaScript iterators. + */ +#include "jsprvtd.h" +#include "jspubtd.h" + +JS_BEGIN_EXTERN_C + +/* + * NB: these flag bits are encoded into the bytecode stream in the immediate + * operand of JSOP_ITER, so don't change them without advancing jsxdrapi.h's + * JSXDR_BYTECODE_VERSION. + */ +#define JSITER_ENUMERATE 0x1 /* for-in compatible hidden default iterator */ +#define JSITER_FOREACH 0x2 /* return [key, value] pair rather than key */ +#define JSITER_KEYVALUE 0x4 /* destructuring for-in wants [key, value] */ + +/* + * Native iterator object slots, shared between jsiter.cpp and jstracer.cpp. + */ +const uint32 JSSLOT_ITER_STATE = JSSLOT_PRIVATE; +const uint32 JSSLOT_ITER_FLAGS = JSSLOT_PRIVATE + 1; + +/* + * Convert the value stored in *vp to its iteration object. The flags should + * contain JSITER_ENUMERATE if js_ValueToIterator is called when enumerating + * for-in semantics are required, and when the caller can guarantee that the + * iterator will never be exposed to scripts. + */ +extern JS_FRIEND_API(JSBool) +js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp); + +extern JS_FRIEND_API(JSBool) JS_FASTCALL +js_CloseIterator(JSContext *cx, jsval v); + +/* + * Given iterobj, call iterobj.next(). If the iterator stopped, set *rval to + * JSVAL_HOLE. Otherwise set it to the result of the next call. + */ +extern JS_FRIEND_API(JSBool) +js_CallIteratorNext(JSContext *cx, JSObject *iterobj, jsval *rval); + +/* + * Close iterobj, whose class must be js_IteratorClass. + */ +extern void +js_CloseNativeIterator(JSContext *cx, JSObject *iterobj); + +extern JSBool +js_ThrowStopIteration(JSContext *cx); + +#if JS_HAS_GENERATORS + +/* + * Generator state codes. + */ +typedef enum JSGeneratorState { + JSGEN_NEWBORN, /* not yet started */ + JSGEN_OPEN, /* started by a .next() or .send(undefined) call */ + JSGEN_RUNNING, /* currently executing via .next(), etc., call */ + JSGEN_CLOSING, /* close method is doing asynchronous return */ + JSGEN_CLOSED /* closed, cannot be started or closed again */ +} JSGeneratorState; + +struct JSGenerator { + JSObject *obj; + JSGeneratorState state; + JSStackFrame frame; + JSFrameRegs savedRegs; + JSArena arena; + jsval slots[1]; +}; + +#define FRAME_TO_GENERATOR(fp) \ + ((JSGenerator *) ((uint8 *)(fp) - offsetof(JSGenerator, frame))) + +extern JSObject * +js_NewGenerator(JSContext *cx, JSStackFrame *fp); + +#endif + +extern JS_FRIEND_API(JSClass) js_GeneratorClass; +extern JSClass js_IteratorClass; +extern JSClass js_StopIterationClass; + +static inline bool +js_ValueIsStopIteration(jsval v) +{ + return !JSVAL_IS_PRIMITIVE(v) && + STOBJ_GET_CLASS(JSVAL_TO_OBJECT(v)) == &js_StopIterationClass; +} + +extern JSObject * +js_InitIteratorClasses(JSContext *cx, JSObject *obj); + +JS_END_EXTERN_C + +#endif /* jsiter_h___ */ diff --git a/ape-server/deps/js/src/jskeyword.tbl b/ape-server/deps/js/src/jskeyword.tbl new file mode 100755 index 0000000..adcbf0e --- /dev/null +++ b/ape-server/deps/js/src/jskeyword.tbl @@ -0,0 +1,124 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set sw=4 ts=8 et tw=80: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +JS_KEYWORD(break, TOK_BREAK, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(case, TOK_CASE, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(continue, TOK_CONTINUE, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(default, TOK_DEFAULT, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(delete, TOK_DELETE, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(do, TOK_DO, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(else, TOK_ELSE, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(export, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(false, TOK_PRIMARY, JSOP_FALSE, JSVERSION_DEFAULT) +JS_KEYWORD(for, TOK_FOR, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(function, TOK_FUNCTION, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(if, TOK_IF, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(in, TOK_IN, JSOP_IN, JSVERSION_DEFAULT) +JS_KEYWORD(new, TOK_NEW, JSOP_NEW, JSVERSION_DEFAULT) +JS_KEYWORD(null, TOK_PRIMARY, JSOP_NULL, JSVERSION_DEFAULT) +JS_KEYWORD(return, TOK_RETURN, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(switch, TOK_SWITCH, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(this, TOK_PRIMARY, JSOP_THIS, JSVERSION_DEFAULT) +JS_KEYWORD(true, TOK_PRIMARY, JSOP_TRUE, JSVERSION_DEFAULT) +JS_KEYWORD(typeof, TOK_UNARYOP, JSOP_TYPEOF, JSVERSION_DEFAULT) +JS_KEYWORD(var, TOK_VAR, JSOP_DEFVAR, JSVERSION_DEFAULT) +JS_KEYWORD(void, TOK_UNARYOP, JSOP_VOID, JSVERSION_DEFAULT) +JS_KEYWORD(while, TOK_WHILE, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(with, TOK_WITH, JSOP_NOP, JSVERSION_DEFAULT) +#if JS_HAS_CONST +JS_KEYWORD(const, TOK_VAR, JSOP_DEFCONST, JSVERSION_DEFAULT) +#else +JS_KEYWORD(const, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) +#endif + +JS_KEYWORD(try, TOK_TRY, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(catch, TOK_CATCH, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(finally, TOK_FINALLY, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(throw, TOK_THROW, JSOP_NOP, JSVERSION_DEFAULT) + +JS_KEYWORD(instanceof, TOK_INSTANCEOF, JSOP_INSTANCEOF,JSVERSION_DEFAULT) + +#if JS_HAS_RESERVED_JAVA_KEYWORDS +JS_KEYWORD(abstract, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(boolean, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(byte, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(char, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(class, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(double, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(extends, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(final, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(float, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(goto, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(implements, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(import, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(int, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(interface, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(long, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(native, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(package, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(private, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(protected, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(public, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(short, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(static, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(super, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(synchronized,TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(throws, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(transient, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) +JS_KEYWORD(volatile, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) +#endif + +#if JS_HAS_RESERVED_ECMA_KEYWORDS +JS_KEYWORD(enum, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) +#endif + +#if JS_HAS_DEBUGGER_KEYWORD +JS_KEYWORD(debugger, TOK_DEBUGGER, JSOP_NOP, JSVERSION_DEFAULT) +#elif JS_HAS_RESERVED_ECMA_KEYWORDS +JS_KEYWORD(debugger, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) +#endif + +#if JS_HAS_GENERATORS +JS_KEYWORD(yield, TOK_YIELD, JSOP_NOP, JSVERSION_1_7) +#endif + +#if JS_HAS_BLOCK_SCOPE +JS_KEYWORD(let, TOK_LET, JSOP_NOP, JSVERSION_1_7) +#endif diff --git a/ape-server/deps/js/src/jskwgen.cpp b/ape-server/deps/js/src/jskwgen.cpp new file mode 100755 index 0000000..f808b92 --- /dev/null +++ b/ape-server/deps/js/src/jskwgen.cpp @@ -0,0 +1,460 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set sw=4 ts=8 et tw=80: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is String Switch Generator for JavaScript Keywords, + * released 2005-12-09. + * + * The Initial Developer of the Original Code is + * Igor Bukanov. + * Portions created by the Initial Developer are Copyright (C) 2005-2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include +#include +#include +#include +#include +#include +#include + +#include "jsversion.h" + +const char * const keyword_list[] = { +#define JS_KEYWORD(keyword, type, op, version) #keyword, +#include "jskeyword.tbl" +#undef JS_KEYWORD +}; + +struct gen_opt { + FILE *output; /* output file for generated source */ + unsigned use_if_threshold; /* max number of choices to generate + "if" selector instead of "switch" */ + unsigned char_tail_test_threshold; /* max number of unprocessed columns + to use inlined char compare + for remaining chars and not generic + string compare code */ + unsigned indent_level; /* current source identation level */ +}; + +static unsigned column_to_compare; + +static int +length_comparator(const void *a, const void *b) +{ + const char *str1 = keyword_list[*(unsigned *)a]; + const char *str2 = keyword_list[*(unsigned *)b]; + return (int)strlen(str1) - (int)strlen(str2); +} + +static int +column_comparator(const void *a, const void *b) +{ + const char *str1 = keyword_list[*(unsigned *)a]; + const char *str2 = keyword_list[*(unsigned *)b]; + return (int)str1[column_to_compare] - (int)str2[column_to_compare]; +} + +static unsigned +count_different_lengths(unsigned indexes[], unsigned nelem) +{ + unsigned nlength, current_length, i, l; + + current_length = 0; + nlength = 0; + for (i = 0; i != nelem; ++i) { + l = (unsigned)strlen(keyword_list[indexes[i]]); + assert(l != 0); + if (current_length != l) { + ++nlength; + current_length = l; + } + } + return nlength; +} + +static void +find_char_span_and_count(unsigned indexes[], unsigned nelem, unsigned column, + unsigned *span_result, unsigned *count_result) +{ + unsigned i, count; + unsigned char c, prev, minc, maxc; + + assert(nelem != 0); + minc = maxc = prev = (unsigned char)keyword_list[indexes[0]][column]; + count = 1; + for (i = 1; i != nelem; ++i) { + c = (unsigned char)keyword_list[indexes[i]][column]; + if (prev != c) { + prev = c; + ++count; + if (minc > c) { + minc = c; + } else if (maxc < c) { + maxc = c; + } + } + } + + *span_result = maxc - minc + 1; + *count_result = count; +} + +static unsigned +find_optimal_switch_column(struct gen_opt *opt, + unsigned indexes[], unsigned nelem, + unsigned columns[], unsigned unprocessed_columns, + int *use_if_result) +{ + unsigned i; + unsigned span, min_span, min_span_index; + unsigned nchar, min_nchar, min_nchar_index; + + assert(unprocessed_columns != 0); + i = 0; + min_nchar = min_span = (unsigned)-1; + min_nchar_index = min_span_index = 0; + do { + column_to_compare = columns[i]; + qsort(indexes, nelem, sizeof(indexes[0]), column_comparator); + find_char_span_and_count(indexes, nelem, column_to_compare, + &span, &nchar); + assert(span != 0); + if (span == 1) { + assert(nchar == 1); + *use_if_result = 1; + return 1; + } + assert(nchar != 1); + if (min_span > span) { + min_span = span; + min_span_index = i; + } + if (min_nchar > nchar) { + min_nchar = nchar; + min_nchar_index = i; + } + } while (++i != unprocessed_columns); + + if (min_nchar <= opt->use_if_threshold) { + *use_if_result = 1; + i = min_nchar_index; + } else { + *use_if_result = 0; + i = min_span_index; + } + + /* + * Restore order corresponding to i if it was destroyed by + * subsequent sort. + */ + if (i != unprocessed_columns - 1) { + column_to_compare = columns[i]; + qsort(indexes, nelem, sizeof(indexes[0]), column_comparator); + } + + return i; +} + + +static void +p(struct gen_opt *opt, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + vfprintf(opt->output, format, ap); + va_end(ap); +} + +/* Size for '\xxx' where xxx is octal escape */ +#define MIN_QUOTED_CHAR_BUFFER 7 + +static char * +qchar(char c, char *quoted_buffer) +{ + char *s; + + s = quoted_buffer; + *s++ = '\''; + switch (c) { + case '\n': c = 'n'; goto one_char_escape; + case '\r': c = 'r'; goto one_char_escape; + case '\t': c = 't'; goto one_char_escape; + case '\f': c = 't'; goto one_char_escape; + case '\0': c = '0'; goto one_char_escape; + case '\'': goto one_char_escape; + one_char_escape: + *s++ = '\\'; + break; + default: + if (!isprint(c)) { + *s++ = '\\'; + *s++ = (char)('0' + (0x3 & (((unsigned char)c) >> 6))); + *s++ = (char)('0' + (0x7 & (((unsigned char)c) >> 3))); + c = (char)('0' + (0x7 & ((unsigned char)c))); + } + } + *s++ = c; + *s++ = '\''; + *s = '\0'; + assert(s + 1 <= quoted_buffer + MIN_QUOTED_CHAR_BUFFER); + return quoted_buffer; +} + +static void +nl(struct gen_opt *opt) +{ + putc('\n', opt->output); +} + +static void +indent(struct gen_opt *opt) +{ + unsigned n = opt->indent_level; + while (n != 0) { + --n; + fputs(" ", opt->output); + } +} + +static void +line(struct gen_opt *opt, const char *format, ...) +{ + va_list ap; + + indent(opt); + va_start(ap, format); + vfprintf(opt->output, format, ap); + va_end(ap); + nl(opt); +} + +static void +generate_letter_switch_r(struct gen_opt *opt, + unsigned indexes[], unsigned nelem, + unsigned columns[], unsigned unprocessed_columns) +{ + char qbuf[MIN_QUOTED_CHAR_BUFFER]; + + assert(nelem != 0); + if (nelem == 1) { + unsigned kw_index = indexes[0]; + const char *keyword = keyword_list[kw_index]; + + if (unprocessed_columns == 0) { + line(opt, "JSKW_GOT_MATCH(%u) /* %s */", kw_index, keyword); + } else if (unprocessed_columns > opt->char_tail_test_threshold) { + line(opt, "JSKW_TEST_GUESS(%u) /* %s */", kw_index, keyword); + } else { + unsigned i, column; + + indent(opt); p(opt, "if ("); + for (i = 0; i != unprocessed_columns; ++i) { + column = columns[i]; + qchar(keyword[column], qbuf); + p(opt, "%sJSKW_AT(%u)==%s", (i == 0) ? "" : " && ", + column, qbuf); + } + p(opt, ") {"); nl(opt); + ++opt->indent_level; + line(opt, "JSKW_GOT_MATCH(%u) /* %s */", kw_index, keyword); + --opt->indent_level; + line(opt, "}"); + line(opt, "JSKW_NO_MATCH()"); + } + } else { + unsigned optimal_column_index, optimal_column; + unsigned i; + int use_if; + char current; + + assert(unprocessed_columns != 0); + optimal_column_index = find_optimal_switch_column(opt, indexes, nelem, + columns, + unprocessed_columns, + &use_if); + optimal_column = columns[optimal_column_index]; + columns[optimal_column_index] = columns[unprocessed_columns - 1]; + + if (!use_if) + line(opt, "switch (JSKW_AT(%u)) {", optimal_column); + + current = keyword_list[indexes[0]][optimal_column]; + for (i = 0; i != nelem;) { + unsigned same_char_begin = i; + char next = current; + + for (++i; i != nelem; ++i) { + next = keyword_list[indexes[i]][optimal_column]; + if (next != current) + break; + } + qchar(current, qbuf); + if (use_if) { + line(opt, "if (JSKW_AT(%u) == %s) {", optimal_column, qbuf); + } else { + line(opt, " case %s:", qbuf); + } + ++opt->indent_level; + generate_letter_switch_r(opt, indexes + same_char_begin, + i - same_char_begin, + columns, unprocessed_columns - 1); + --opt->indent_level; + if (use_if) { + line(opt, "}"); + } + current = next; + } + + if (!use_if) { + line(opt, "}"); + } + + columns[optimal_column_index] = optimal_column; + + line(opt, "JSKW_NO_MATCH()"); + } +} + +static void +generate_letter_switch(struct gen_opt *opt, + unsigned indexes[], unsigned nelem, + unsigned current_length) +{ + unsigned *columns; + unsigned i; + + columns = (unsigned *) malloc(sizeof(columns[0]) * current_length); + if (!columns) { + perror("malloc"); + exit(EXIT_FAILURE); + } + for (i = 0; i != current_length; ++i) { + columns[i] = i; + } + generate_letter_switch_r(opt, indexes, nelem, columns, current_length); + free(columns); +} + + +static void +generate_switch(struct gen_opt *opt) +{ + unsigned *indexes; + unsigned nlength; + unsigned i, current; + int use_if; + unsigned nelem; + + nelem = sizeof(keyword_list)/sizeof(keyword_list[0]); + + line(opt, "/*"); + line(opt, " * Generating switch for the list of %u entries:", nelem); + for (i = 0; i != nelem; ++i) { + line(opt, " * %s", keyword_list[i]); + } + line(opt, " */"); + + indexes = (unsigned *) malloc(sizeof(indexes[0]) * nelem); + if (!indexes) { + perror("malloc"); + exit(EXIT_FAILURE); + } + for (i = 0; i != nelem; ++i) + indexes[i] = i; + qsort(indexes, nelem, sizeof(indexes[i]), length_comparator); + nlength = count_different_lengths(indexes, nelem); + + use_if = (nlength <= opt->use_if_threshold); + + if (!use_if) + line(opt, "switch (JSKW_LENGTH()) {"); + + current = (unsigned)strlen(keyword_list[indexes[0]]); + for (i = 0; i != nelem;) { + unsigned same_length_begin = i; + unsigned next = current; + + for (++i; i != nelem; ++i) { + next = (unsigned)strlen(keyword_list[indexes[i]]); + if (next != current) + break; + } + if (use_if) { + line(opt, "if (JSKW_LENGTH() == %u) {", current); + } else { + line(opt, " case %u:", current); + } + ++opt->indent_level; + generate_letter_switch(opt, indexes + same_length_begin, + i - same_length_begin, + current); + --opt->indent_level; + if (use_if) { + line(opt, "}"); + } + current = next; + } + if (!use_if) + line(opt, "}"); + line(opt, "JSKW_NO_MATCH()"); + free(indexes); +} + +int main(int argc, char **argv) +{ + struct gen_opt opt; + + if (argc < 2) { + opt.output = stdout; + } else { + opt.output = fopen(argv[1], "w"); + if (!opt.output) { + perror("fopen"); + exit(EXIT_FAILURE); + } + } + opt.indent_level = 1; + opt.use_if_threshold = 3; + opt.char_tail_test_threshold = 4; + + generate_switch(&opt); + + if (opt.output != stdout) { + if (fclose(opt.output)) { + perror("fclose"); + exit(EXIT_FAILURE); + } + } + + return EXIT_SUCCESS; +} diff --git a/ape-server/deps/js/src/jslibmath.h b/ape-server/deps/js/src/jslibmath.h new file mode 100755 index 0000000..13aa01a --- /dev/null +++ b/ape-server/deps/js/src/jslibmath.h @@ -0,0 +1,86 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * IBM Corp. + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _LIBMATH_H +#define _LIBMATH_H + +#include +#include "jsversion.h" + +/* + * Use system provided math routines. + */ + +/* The right copysign function is not always named the same thing. */ +#if __GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +#define js_copysign __builtin_copysign +#elif defined WINCE +#define js_copysign _copysign +#elif defined _WIN32 +#if _MSC_VER < 1400 +/* Try to work around apparent _copysign bustage in VC7.x. */ +#define js_copysign js_copysign +extern double js_copysign(double, double); +#else +#define js_copysign _copysign +#endif +#else +#define js_copysign copysign +#endif + +/* Consistency wrapper for platform deviations in fmod() */ +static inline double +js_fmod(double d, double d2) +{ +#ifdef XP_WIN + /* + * Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. + * Workaround MS fmod bug where -0 % -N => 0, not -0. + */ + if ((JSDOUBLE_IS_FINITE(d) && JSDOUBLE_IS_INFINITE(d2)) || + (d == 0 && JSDOUBLE_IS_FINITE(d2))) { + return d; + } +#endif + return fmod(d, d2); +} + +#endif /* _LIBMATH_H */ + diff --git a/ape-server/deps/js/src/jslock.cpp b/ape-server/deps/js/src/jslock.cpp new file mode 100755 index 0000000..ff216ad --- /dev/null +++ b/ape-server/deps/js/src/jslock.cpp @@ -0,0 +1,1521 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifdef JS_THREADSAFE + +/* + * JS locking stubs. + */ +#include +#include +#include "jspubtd.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jstypes.h" +#include "jsstdint.h" +#include "jsbit.h" +#include "jscntxt.h" +#include "jsdtoa.h" +#include "jsgc.h" +#include "jslock.h" +#include "jsscope.h" +#include "jsstr.h" + +#define ReadWord(W) (W) + +#if !defined(__GNUC__) +# define __asm__ asm +# define __volatile__ volatile +#endif + +/* Implement NativeCompareAndSwap. */ + +#if defined(_WIN32) && defined(_M_IX86) +#pragma warning( disable : 4035 ) +JS_BEGIN_EXTERN_C +extern long __cdecl +_InterlockedCompareExchange(long *volatile dest, long exchange, long comp); +JS_END_EXTERN_C +#pragma intrinsic(_InterlockedCompareExchange) + +JS_STATIC_ASSERT(sizeof(jsword) == sizeof(long)); + +static JS_ALWAYS_INLINE int +NativeCompareAndSwapHelper(jsword *w, jsword ov, jsword nv) +{ + _InterlockedCompareExchange((long*) w, nv, ov); + __asm { + sete al + } +} + +static JS_ALWAYS_INLINE int +NativeCompareAndSwap(jsword *w, jsword ov, jsword nv) +{ + return (NativeCompareAndSwapHelper(w, ov, nv) & 1); +} + +#elif defined(_MSC_VER) && (defined(_M_AMD64) || defined(_M_X64)) +JS_BEGIN_EXTERN_C +extern long long __cdecl +_InterlockedCompareExchange64(long long *volatile dest, long long exchange, long long comp); +JS_END_EXTERN_C +#pragma intrinsic(_InterlockedCompareExchange64) + +static JS_ALWAYS_INLINE int +NativeCompareAndSwap(jsword *w, jsword ov, jsword nv) +{ + return _InterlockedCompareExchange64(w, nv, ov) == ov; +} + +#elif defined(XP_MACOSX) || defined(DARWIN) + +#include + +static JS_ALWAYS_INLINE int +NativeCompareAndSwap(jsword *w, jsword ov, jsword nv) +{ + /* Details on these functions available in the manpage for atomic */ + return OSAtomicCompareAndSwapPtrBarrier(ov, nv, w); +} + +#elif defined(__i386) && (defined(__GNUC__) || defined(__SUNPRO_CC)) + +/* Note: This fails on 386 cpus, cmpxchgl is a >= 486 instruction */ +static JS_ALWAYS_INLINE int +NativeCompareAndSwap(jsword *w, jsword ov, jsword nv) +{ + unsigned int res; + + __asm__ __volatile__ ( + "lock\n" + "cmpxchgl %2, (%1)\n" + "sete %%al\n" + "andl $1, %%eax\n" + : "=a" (res) + : "r" (w), "r" (nv), "a" (ov) + : "cc", "memory"); + return (int)res; +} + +#elif defined(__x86_64) && (defined(__GNUC__) || defined(__SUNPRO_CC)) + +static JS_ALWAYS_INLINE int +NativeCompareAndSwap(jsword *w, jsword ov, jsword nv) +{ + unsigned int res; + + __asm__ __volatile__ ( + "lock\n" + "cmpxchgq %2, (%1)\n" + "sete %%al\n" + "movzbl %%al, %%eax\n" + : "=a" (res) + : "r" (w), "r" (nv), "a" (ov) + : "cc", "memory"); + return (int)res; +} + +#elif defined(__sparc) +#if defined(__GNUC__) + +static JS_ALWAYS_INLINE int +NativeCompareAndSwap(jsword *w, jsword ov, jsword nv) +{ + unsigned int res; + + __asm__ __volatile__ ( + "stbar\n" + "cas [%1],%2,%3\n" + "cmp %2,%3\n" + "be,a 1f\n" + "mov 1,%0\n" + "mov 0,%0\n" + "1:" + : "=r" (res) + : "r" (w), "r" (ov), "r" (nv)); + return (int)res; +} + +#elif defined(__SUNPRO_CC) + +/* Implementation in lock_sparc*.il */ +extern "C" int +NativeCompareAndSwap(jsword *w, jsword ov, jsword nv); + +#endif + +#elif defined(AIX) + +#include + +static JS_ALWAYS_INLINE int +NativeCompareAndSwap(jsword *w, jsword ov, jsword nv) +{ + return !_check_lock((atomic_p)w, ov, nv); +} + +#elif defined(USE_ARM_KUSER) + +/* See https://bugzilla.mozilla.org/show_bug.cgi?id=429387 for a + * description of this ABI; this is a function provided at a fixed + * location by the kernel in the memory space of each process. + */ +typedef int (__kernel_cmpxchg_t)(int oldval, int newval, volatile int *ptr); +#define __kernel_cmpxchg (*(__kernel_cmpxchg_t *)0xffff0fc0) + +JS_STATIC_ASSERT(sizeof(jsword) == sizeof(int)); + +static JS_ALWAYS_INLINE int +NativeCompareAndSwap(jsword *w, jsword ov, jsword nv) +{ + volatile int *vp = (volatile int *) w; + PRInt32 failed = 1; + + /* Loop until a __kernel_cmpxchg succeeds. See bug 446169 */ + do { + failed = __kernel_cmpxchg(ov, nv, vp); + } while (failed && *vp == ov); + return !failed; +} + +#elif JS_HAS_NATIVE_COMPARE_AND_SWAP + +#error "JS_HAS_NATIVE_COMPARE_AND_SWAP should be 0 if your platform lacks a compare-and-swap instruction." + +#endif /* arch-tests */ + +#if JS_HAS_NATIVE_COMPARE_AND_SWAP + +JSBool +js_CompareAndSwap(jsword *w, jsword ov, jsword nv) +{ + return !!NativeCompareAndSwap(w, ov, nv); +} + +#elif defined(NSPR_LOCK) + +# ifdef __GNUC__ +# warning "js_CompareAndSwap is implemented using NSPR lock" +# endif + +JSBool +js_CompareAndSwap(jsword *w, jsword ov, jsword nv) +{ + int result; + static PRLock *CompareAndSwapLock = JS_NEW_LOCK(); + + JS_ACQUIRE_LOCK(CompareAndSwapLock); + result = (*w == ov); + if (result) + *w = nv; + JS_RELEASE_LOCK(CompareAndSwapLock); + return result; +} + +#else /* !defined(NSPR_LOCK) */ + +#error "NSPR_LOCK should be on when the platform lacks native compare-and-swap." + +#endif + +void +js_AtomicSetMask(jsword *w, jsword mask) +{ + jsword ov, nv; + + do { + ov = *w; + nv = ov | mask; + } while (!js_CompareAndSwap(w, ov, nv)); +} + +#ifndef NSPR_LOCK + +struct JSFatLock { + int susp; + PRLock *slock; + PRCondVar *svar; + JSFatLock *next; + JSFatLock **prevp; +}; + +typedef struct JSFatLockTable { + JSFatLock *free; + JSFatLock *taken; +} JSFatLockTable; + +#define GLOBAL_LOCK_INDEX(id) (((uint32)(jsuword)(id)>>2) & global_locks_mask) + +static void +js_Dequeue(JSThinLock *); + +static PRLock **global_locks; +static uint32 global_lock_count = 1; +static uint32 global_locks_log2 = 0; +static uint32 global_locks_mask = 0; + +static void +js_LockGlobal(void *id) +{ + uint32 i = GLOBAL_LOCK_INDEX(id); + PR_Lock(global_locks[i]); +} + +static void +js_UnlockGlobal(void *id) +{ + uint32 i = GLOBAL_LOCK_INDEX(id); + PR_Unlock(global_locks[i]); +} + +#endif /* !NSPR_LOCK */ + +void +js_InitLock(JSThinLock *tl) +{ +#ifdef NSPR_LOCK + tl->owner = 0; + tl->fat = (JSFatLock*)JS_NEW_LOCK(); +#else + memset(tl, 0, sizeof(JSThinLock)); +#endif +} + +void +js_FinishLock(JSThinLock *tl) +{ +#ifdef NSPR_LOCK + tl->owner = 0xdeadbeef; + if (tl->fat) + JS_DESTROY_LOCK(((JSLock*)tl->fat)); +#else + JS_ASSERT(tl->owner == 0); + JS_ASSERT(tl->fat == NULL); +#endif +} + +#ifdef DEBUG_SCOPE_COUNT + +#include +#include "jsdhash.h" + +static FILE *logfp = NULL; +static JSDHashTable logtbl; + +typedef struct logentry { + JSDHashEntryStub stub; + char op; + const char *file; + int line; +} logentry; + +static void +logit(JSTitle *title, char op, const char *file, int line) +{ + logentry *entry; + + if (!logfp) { + logfp = fopen("/tmp/scope.log", "w"); + if (!logfp) + return; + setvbuf(logfp, NULL, _IONBF, 0); + } + fprintf(logfp, "%p %d %c %s %d\n", title, title->u.count, op, file, line); + + if (!logtbl.entryStore && + !JS_DHashTableInit(&logtbl, JS_DHashGetStubOps(), NULL, + sizeof(logentry), 100)) { + return; + } + entry = (logentry *) JS_DHashTableOperate(&logtbl, title, JS_DHASH_ADD); + if (!entry) + return; + entry->stub.key = title; + entry->op = op; + entry->file = file; + entry->line = line; +} + +void +js_unlog_title(JSTitle *title) +{ + if (!logtbl.entryStore) + return; + (void) JS_DHashTableOperate(&logtbl, title, JS_DHASH_REMOVE); +} + +# define LOGIT(title,op) logit(title, op, __FILE__, __LINE__) + +#else + +# define LOGIT(title, op) /* nothing */ + +#endif /* DEBUG_SCOPE_COUNT */ + +/* + * Return true if we would deadlock waiting in ClaimTitle on + * rt->titleSharingDone until ownercx finishes its request and shares a title. + * + * (i) rt->gcLock held + */ +static bool +WillDeadlock(JSContext *ownercx, JSThread *thread) +{ + JS_ASSERT(CURRENT_THREAD_IS_ME(thread)); + JS_ASSERT(ownercx->thread != thread); + + for (;;) { + JS_ASSERT(ownercx->thread); + JS_ASSERT(ownercx->requestDepth > 0); + JSTitle *title = ownercx->thread->titleToShare; + if (!title || !title->ownercx) { + /* + * ownercx->thread doesn't wait or has just been notified that the + * title became shared. + */ + return false; + } + + /* + * ownercx->thread is waiting in ClaimTitle for a context from some + * thread to finish its request. If that thread is the current thread, + * we would deadlock. Otherwise we must recursively check if that + * thread waits for the current thread. + */ + if (title->ownercx->thread == thread) { + JS_RUNTIME_METER(ownercx->runtime, deadlocksAvoided); + return true; + } + ownercx = title->ownercx; + } +} + +static void +FinishSharingTitle(JSContext *cx, JSTitle *title); + +/* + * Make title multi-threaded, i.e. share its ownership among contexts in rt + * using a "thin" or (if necessary due to contention) "fat" lock. Called only + * from ClaimTitle, immediately below, when we detect deadlock were we to wait + * for title's lock, because its ownercx is waiting on a title owned by the + * calling cx. + * + * (i) rt->gcLock held + */ +static void +ShareTitle(JSContext *cx, JSTitle *title) +{ + JSRuntime *rt; + JSTitle **todop; + + rt = cx->runtime; + if (title->u.link) { + for (todop = &rt->titleSharingTodo; *todop != title; + todop = &(*todop)->u.link) { + JS_ASSERT(*todop != NO_TITLE_SHARING_TODO); + } + *todop = title->u.link; + title->u.link = NULL; /* null u.link for sanity ASAP */ + JS_NOTIFY_ALL_CONDVAR(rt->titleSharingDone); + } + FinishSharingTitle(cx, title); +} + +/* + * FinishSharingTitle is the tail part of ShareTitle, split out to become a + * subroutine of js_ShareWaitingTitles too. The bulk of the work here involves + * making mutable strings in the title's object's slots be immutable. We have + * to do this because such strings will soon be available to multiple threads, + * so their buffers can't be realloc'd any longer in js_ConcatStrings, and + * their members can't be modified by js_ConcatStrings, js_UndependString or + * MinimizeDependentStrings. + * + * The last bit of work done by this function nulls title->ownercx and updates + * rt->sharedTitles. + */ +static void +FinishSharingTitle(JSContext *cx, JSTitle *title) +{ + js_InitLock(&title->lock); + title->u.count = 0; /* NULL may not pun as 0 */ + + JSScope *scope = TITLE_TO_SCOPE(title); + JSObject *obj = scope->object; + if (obj) { + uint32 nslots = scope->freeslot; + JS_ASSERT(nslots >= JSSLOT_START(obj->getClass())); + for (uint32 i = JSSLOT_START(obj->getClass()); i != nslots; ++i) { + jsval v = STOBJ_GET_SLOT(obj, i); + if (JSVAL_IS_STRING(v) && + !js_MakeStringImmutable(cx, JSVAL_TO_STRING(v))) { + /* + * FIXME bug 363059: The following error recovery changes + * runtime execution semantics, arbitrarily and silently + * ignoring errors except out-of-memory, which should have been + * reported through JS_ReportOutOfMemory at this point. + */ + STOBJ_SET_SLOT(obj, i, JSVAL_VOID); + } + } + } + + title->ownercx = NULL; /* NB: set last, after lock init */ + JS_RUNTIME_METER(cx->runtime, sharedTitles); +} + +/* + * Notify all contexts that are currently in a request, which will give them a + * chance to yield their current request. + */ +void +js_NudgeOtherContexts(JSContext *cx) +{ + JSRuntime *rt = cx->runtime; + JSContext *acx = NULL; + + while ((acx = js_NextActiveContext(rt, acx)) != NULL) { + if (cx != acx) + JS_TriggerOperationCallback(acx); + } +} + +/* + * Notify all contexts that are currently in a request and execute on this + * specific thread. + */ +static void +NudgeThread(JSThread *thread) +{ + JSCList *link; + JSContext *acx; + + link = &thread->contextList; + while ((link = link->next) != &thread->contextList) { + acx = CX_FROM_THREAD_LINKS(link); + JS_ASSERT(acx->thread == thread); + if (acx->requestDepth) + JS_TriggerOperationCallback(acx); + } +} + +/* + * Given a title with apparently non-null ownercx different from cx, try to + * set ownercx to cx, claiming exclusive (single-threaded) ownership of title. + * If we claim ownership, return true. Otherwise, we wait for ownercx to be + * set to null (indicating that title is multi-threaded); or if waiting would + * deadlock, we set ownercx to null ourselves via ShareTitle. In any case, + * once ownercx is null we return false. + */ +static JSBool +ClaimTitle(JSTitle *title, JSContext *cx) +{ + JSRuntime *rt; + JSContext *ownercx; + uint32 requestDebit; + + rt = cx->runtime; + JS_RUNTIME_METER(rt, claimAttempts); + JS_LOCK_GC(rt); + + /* Reload in case ownercx went away while we blocked on the lock. */ + while ((ownercx = title->ownercx) != NULL) { + /* + * Avoid selflock if ownercx is dead, or is not running a request, or + * has the same thread as cx. Set title->ownercx to cx so that the + * matching JS_UNLOCK_SCOPE or JS_UNLOCK_OBJ macro call will take the + * fast path around the corresponding js_UnlockTitle or js_UnlockObj + * function call. + * + * If title->u.link is non-null, title has already been inserted on + * the rt->titleSharingTodo list, because another thread's context + * already wanted to lock title while ownercx was running a request. + * That context must still be in request and cannot be dead. We can + * claim it if its thread matches ours but only if cx itself is in a + * request. + * + * The latter check covers the case when the embedding triggers a call + * to js_GC on a cx outside a request while having ownercx running a + * request on the same thread, and then js_GC calls a mark hook or a + * finalizer accessing the title. In this case we cannot claim the + * title but must share it now as no title-sharing JS_EndRequest will + * follow. + */ + bool canClaim; + if (title->u.link) { + JS_ASSERT(js_ValidContextPointer(rt, ownercx)); + JS_ASSERT(ownercx->requestDepth > 0); + JS_ASSERT_IF(cx->requestDepth == 0, cx->thread == rt->gcThread); + canClaim = (ownercx->thread == cx->thread && + cx->requestDepth > 0); + } else { + canClaim = (!js_ValidContextPointer(rt, ownercx) || + !ownercx->requestDepth || + ownercx->thread == cx->thread); + } + if (canClaim) { + title->ownercx = cx; + JS_UNLOCK_GC(rt); + JS_RUNTIME_METER(rt, claimedTitles); + return JS_TRUE; + } + + /* + * Avoid deadlock if title's owner thread is waiting on a title that + * the current thread owns, by revoking title's ownership. This + * approach to deadlock avoidance works because the engine never nests + * title locks. + * + * If cx->thread could hold locks on ownercx->thread->titleToShare, or + * if ownercx->thread could hold locks on title, we would need to keep + * reentrancy counts for all such "flyweight" (ownercx != NULL) locks, + * so that control would unwind properly once these locks became + * "thin" or "fat". The engine promotes a title from exclusive to + * shared access only when locking, never when holding or unlocking. + * + * Avoid deadlock before any of this title/context cycle detection if + * cx is on the active GC's thread, because in that case, no requests + * will run until the GC completes. Any title wanted by the GC (from + * a finalizer or a mark hook) that can't be claimed must become + * shared. + */ + if (rt->gcThread == cx->thread || WillDeadlock(ownercx, cx->thread)) { + ShareTitle(cx, title); + break; + } + + /* + * Thanks to the non-zero NO_TITLE_SHARING_TODO link terminator, we + * can decide whether title is on rt->titleSharingTodo with a single + * non-null test, and avoid double-insertion bugs. + */ + if (!title->u.link) { + TITLE_TO_SCOPE(title)->hold(); + title->u.link = rt->titleSharingTodo; + rt->titleSharingTodo = title; + } + + /* + * Discount all the requests running on the current thread so a + * possible GC can proceed on another thread while we wait on + * rt->titleSharingDone. + */ + requestDebit = js_DiscountRequestsForGC(cx); + if (title->ownercx != ownercx) { + /* + * js_DiscountRequestsForGC released and reacquired the GC lock, + * and the title was taken or shared. Start over. + */ + js_RecountRequestsAfterGC(rt, requestDebit); + continue; + } + + /* + * We know that some other thread's context owns title, which is now + * linked onto rt->titleSharingTodo, awaiting the end of that other + * thread's request. So it is safe to wait on rt->titleSharingDone. + * But before waiting, we force the operation callback for that other + * thread so it can quickly suspend. + */ + NudgeThread(ownercx->thread); + + JS_ASSERT(!cx->thread->titleToShare); + cx->thread->titleToShare = title; +#ifdef DEBUG + PRStatus stat = +#endif + PR_WaitCondVar(rt->titleSharingDone, PR_INTERVAL_NO_TIMEOUT); + JS_ASSERT(stat != PR_FAILURE); + + js_RecountRequestsAfterGC(rt, requestDebit); + + /* + * Don't clear titleToShare until after we're through waiting on + * all condition variables protected by rt->gcLock -- that includes + * rt->titleSharingDone *and* rt->gcDone (hidden in the call to + * js_RecountRequestsAfterGC immediately above). + * + * Otherwise, the GC could easily deadlock with another thread that + * owns a title wanted by a finalizer. By keeping cx->titleToShare + * set till here, we ensure that such deadlocks are detected, which + * results in the finalized object's title being shared (it must, of + * course, have other, live objects sharing it). + */ + cx->thread->titleToShare = NULL; + } + + JS_UNLOCK_GC(rt); + return JS_FALSE; +} + +void +js_ShareWaitingTitles(JSContext *cx) +{ + JSTitle *title, **todop; + bool shared; + + /* See whether cx has any single-threaded titles to start sharing. */ + todop = &cx->runtime->titleSharingTodo; + shared = false; + while ((title = *todop) != NO_TITLE_SHARING_TODO) { + if (title->ownercx != cx) { + todop = &title->u.link; + continue; + } + *todop = title->u.link; + title->u.link = NULL; /* null u.link for sanity ASAP */ + + /* + * If JSScope::drop returns false, we held the last ref to scope. The + * waiting thread(s) must have been killed, after which the GC + * collected the object that held this scope. Unlikely, because it + * requires that the GC ran (e.g., from an operation callback) + * during this request, but possible. + */ + if (TITLE_TO_SCOPE(title)->drop(cx, NULL)) { + FinishSharingTitle(cx, title); /* set ownercx = NULL */ + shared = true; + } + } + if (shared) + JS_NOTIFY_ALL_CONDVAR(cx->runtime->titleSharingDone); +} + +/* Exported to js.c, which calls it via OBJ_GET_* and JSVAL_IS_* macros. */ +JS_FRIEND_API(jsval) +js_GetSlotThreadSafe(JSContext *cx, JSObject *obj, uint32 slot) +{ + jsval v; + JSScope *scope; + JSTitle *title; +#ifndef NSPR_LOCK + JSThinLock *tl; + jsword me; +#endif + + OBJ_CHECK_SLOT(obj, slot); + + /* + * Native object locking is inlined here to optimize the single-threaded + * and contention-free multi-threaded cases. + */ + scope = OBJ_SCOPE(obj); + title = &scope->title; + JS_ASSERT(title->ownercx != cx); + JS_ASSERT(slot < scope->freeslot); + + /* + * Avoid locking if called from the GC. Also avoid locking an object + * owning a sealed scope. If neither of those special cases applies, try + * to claim scope's flyweight lock from whatever context may have had it in + * an earlier request. + */ + if (CX_THREAD_IS_RUNNING_GC(cx) || + scope->sealed() || + (title->ownercx && ClaimTitle(title, cx))) { + return STOBJ_GET_SLOT(obj, slot); + } + +#ifndef NSPR_LOCK + tl = &title->lock; + me = CX_THINLOCK_ID(cx); + JS_ASSERT(CURRENT_THREAD_IS_ME(me)); + if (NativeCompareAndSwap(&tl->owner, 0, me)) { + /* + * Got the lock with one compare-and-swap. Even so, someone else may + * have mutated obj so it now has its own scope and lock, which would + * require either a restart from the top of this routine, or a thin + * lock release followed by fat lock acquisition. + */ + if (scope == OBJ_SCOPE(obj)) { + v = STOBJ_GET_SLOT(obj, slot); + if (!NativeCompareAndSwap(&tl->owner, me, 0)) { + /* Assert that scope locks never revert to flyweight. */ + JS_ASSERT(title->ownercx != cx); + LOGIT(title, '1'); + title->u.count = 1; + js_UnlockObj(cx, obj); + } + return v; + } + if (!NativeCompareAndSwap(&tl->owner, me, 0)) + js_Dequeue(tl); + } + else if (Thin_RemoveWait(ReadWord(tl->owner)) == me) { + return STOBJ_GET_SLOT(obj, slot); + } +#endif + + js_LockObj(cx, obj); + v = STOBJ_GET_SLOT(obj, slot); + + /* + * Test whether cx took ownership of obj's scope during js_LockObj. + * + * This does not mean that a given scope reverted to flyweight from "thin" + * or "fat" -- it does mean that obj's map pointer changed due to another + * thread setting a property, requiring obj to cease sharing a prototype + * object's scope (whose lock was not flyweight, else we wouldn't be here + * in the first place!). + */ + title = &OBJ_SCOPE(obj)->title; + if (title->ownercx != cx) + js_UnlockTitle(cx, title); + return v; +} + +void +js_SetSlotThreadSafe(JSContext *cx, JSObject *obj, uint32 slot, jsval v) +{ + JSTitle *title; + JSScope *scope; +#ifndef NSPR_LOCK + JSThinLock *tl; + jsword me; +#endif + + OBJ_CHECK_SLOT(obj, slot); + + /* Any string stored in a thread-safe object must be immutable. */ + if (JSVAL_IS_STRING(v) && + !js_MakeStringImmutable(cx, JSVAL_TO_STRING(v))) { + /* FIXME bug 363059: See comments in js_FinishSharingScope. */ + v = JSVAL_NULL; + } + + /* + * Native object locking is inlined here to optimize the single-threaded + * and contention-free multi-threaded cases. + */ + scope = OBJ_SCOPE(obj); + title = &scope->title; + JS_ASSERT(title->ownercx != cx); + JS_ASSERT(slot < scope->freeslot); + + /* + * Avoid locking if called from the GC. Also avoid locking an object + * owning a sealed scope. If neither of those special cases applies, try + * to claim scope's flyweight lock from whatever context may have had it in + * an earlier request. + */ + if (CX_THREAD_IS_RUNNING_GC(cx) || + scope->sealed() || + (title->ownercx && ClaimTitle(title, cx))) { + LOCKED_OBJ_SET_SLOT(obj, slot, v); + return; + } + +#ifndef NSPR_LOCK + tl = &title->lock; + me = CX_THINLOCK_ID(cx); + JS_ASSERT(CURRENT_THREAD_IS_ME(me)); + if (NativeCompareAndSwap(&tl->owner, 0, me)) { + if (scope == OBJ_SCOPE(obj)) { + LOCKED_OBJ_SET_SLOT(obj, slot, v); + if (!NativeCompareAndSwap(&tl->owner, me, 0)) { + /* Assert that scope locks never revert to flyweight. */ + JS_ASSERT(title->ownercx != cx); + LOGIT(title, '1'); + title->u.count = 1; + js_UnlockObj(cx, obj); + } + return; + } + if (!NativeCompareAndSwap(&tl->owner, me, 0)) + js_Dequeue(tl); + } else if (Thin_RemoveWait(ReadWord(tl->owner)) == me) { + LOCKED_OBJ_SET_SLOT(obj, slot, v); + return; + } +#endif + + js_LockObj(cx, obj); + LOCKED_OBJ_SET_SLOT(obj, slot, v); + + /* + * Same drill as above, in js_GetSlotThreadSafe. + */ + title = &OBJ_SCOPE(obj)->title; + if (title->ownercx != cx) + js_UnlockTitle(cx, title); +} + +#ifndef NSPR_LOCK + +static JSFatLock * +NewFatlock() +{ + JSFatLock *fl = (JSFatLock *)malloc(sizeof(JSFatLock)); /* for now */ + if (!fl) return NULL; + fl->susp = 0; + fl->next = NULL; + fl->prevp = NULL; + fl->slock = PR_NewLock(); + fl->svar = PR_NewCondVar(fl->slock); + return fl; +} + +static void +DestroyFatlock(JSFatLock *fl) +{ + PR_DestroyLock(fl->slock); + PR_DestroyCondVar(fl->svar); + js_free(fl); +} + +static JSFatLock * +ListOfFatlocks(int listc) +{ + JSFatLock *m; + JSFatLock *m0; + int i; + + JS_ASSERT(listc>0); + m0 = m = NewFatlock(); + for (i=1; inext = NewFatlock(); + m = m->next; + } + return m0; +} + +static void +DeleteListOfFatlocks(JSFatLock *m) +{ + JSFatLock *m0; + for (; m; m=m0) { + m0 = m->next; + DestroyFatlock(m); + } +} + +static JSFatLockTable *fl_list_table = NULL; +static uint32 fl_list_table_len = 0; +static uint32 fl_list_chunk_len = 0; + +static JSFatLock * +GetFatlock(void *id) +{ + JSFatLock *m; + + uint32 i = GLOBAL_LOCK_INDEX(id); + if (fl_list_table[i].free == NULL) { +#ifdef DEBUG + if (fl_list_table[i].taken) + printf("Ran out of fat locks!\n"); +#endif + fl_list_table[i].free = ListOfFatlocks(fl_list_chunk_len); + } + m = fl_list_table[i].free; + fl_list_table[i].free = m->next; + m->susp = 0; + m->next = fl_list_table[i].taken; + m->prevp = &fl_list_table[i].taken; + if (fl_list_table[i].taken) + fl_list_table[i].taken->prevp = &m->next; + fl_list_table[i].taken = m; + return m; +} + +static void +PutFatlock(JSFatLock *m, void *id) +{ + uint32 i; + if (m == NULL) + return; + + /* Unlink m from fl_list_table[i].taken. */ + *m->prevp = m->next; + if (m->next) + m->next->prevp = m->prevp; + + /* Insert m in fl_list_table[i].free. */ + i = GLOBAL_LOCK_INDEX(id); + m->next = fl_list_table[i].free; + fl_list_table[i].free = m; +} + +#endif /* !NSPR_LOCK */ + +JSBool +js_SetupLocks(int listc, int globc) +{ +#ifndef NSPR_LOCK + uint32 i; + + if (global_locks) + return JS_TRUE; +#ifdef DEBUG + if (listc > 10000 || listc < 0) /* listc == fat lock list chunk length */ + printf("Bad number %d in js_SetupLocks()!\n", listc); + if (globc > 100 || globc < 0) /* globc == number of global locks */ + printf("Bad number %d in js_SetupLocks()!\n", listc); +#endif + global_locks_log2 = JS_CeilingLog2(globc); + global_locks_mask = JS_BITMASK(global_locks_log2); + global_lock_count = JS_BIT(global_locks_log2); + global_locks = (PRLock **) js_malloc(global_lock_count * sizeof(PRLock*)); + if (!global_locks) + return JS_FALSE; + for (i = 0; i < global_lock_count; i++) { + global_locks[i] = PR_NewLock(); + if (!global_locks[i]) { + global_lock_count = i; + js_CleanupLocks(); + return JS_FALSE; + } + } + fl_list_table = (JSFatLockTable *) js_malloc(i * sizeof(JSFatLockTable)); + if (!fl_list_table) { + js_CleanupLocks(); + return JS_FALSE; + } + fl_list_table_len = global_lock_count; + for (i = 0; i < global_lock_count; i++) + fl_list_table[i].free = fl_list_table[i].taken = NULL; + fl_list_chunk_len = listc; +#endif /* !NSPR_LOCK */ + return JS_TRUE; +} + +void +js_CleanupLocks() +{ +#ifndef NSPR_LOCK + uint32 i; + + if (global_locks) { + for (i = 0; i < global_lock_count; i++) + PR_DestroyLock(global_locks[i]); + js_free(global_locks); + global_locks = NULL; + global_lock_count = 1; + global_locks_log2 = 0; + global_locks_mask = 0; + } + if (fl_list_table) { + for (i = 0; i < fl_list_table_len; i++) { + DeleteListOfFatlocks(fl_list_table[i].free); + fl_list_table[i].free = NULL; + DeleteListOfFatlocks(fl_list_table[i].taken); + fl_list_table[i].taken = NULL; + } + js_free(fl_list_table); + fl_list_table = NULL; + fl_list_table_len = 0; + } +#endif /* !NSPR_LOCK */ +} + +#ifdef NSPR_LOCK + +static JS_ALWAYS_INLINE void +ThinLock(JSThinLock *tl, jsword me) +{ + JS_ACQUIRE_LOCK((JSLock *) tl->fat); + tl->owner = me; +} + +static JS_ALWAYS_INLINE void +ThinUnlock(JSThinLock *tl, jsword /*me*/) +{ + tl->owner = 0; + JS_RELEASE_LOCK((JSLock *) tl->fat); +} + +#else + +/* + * Fast locking and unlocking is implemented by delaying the allocation of a + * system lock (fat lock) until contention. As long as a locking thread A + * runs uncontended, the lock is represented solely by storing A's identity in + * the object being locked. + * + * If another thread B tries to lock the object currently locked by A, B is + * enqueued into a fat lock structure (which might have to be allocated and + * pointed to by the object), and suspended using NSPR conditional variables + * (wait). A wait bit (Bacon bit) is set in the lock word of the object, + * signalling to A that when releasing the lock, B must be dequeued and + * notified. + * + * The basic operation of the locking primitives (js_Lock, js_Unlock, + * js_Enqueue, and js_Dequeue) is compare-and-swap. Hence, when locking into + * the word pointed at by p, compare-and-swap(p, 0, A) success implies that p + * is unlocked. Similarly, when unlocking p, if compare-and-swap(p, A, 0) + * succeeds this implies that p is uncontended (no one is waiting because the + * wait bit is not set). + * + * When dequeueing, the lock is released, and one of the threads suspended on + * the lock is notified. If other threads still are waiting, the wait bit is + * kept (in js_Enqueue), and if not, the fat lock is deallocated. + * + * The functions js_Enqueue, js_Dequeue, js_SuspendThread, and js_ResumeThread + * are serialized using a global lock. For scalability, a hashtable of global + * locks is used, which is indexed modulo the thin lock pointer. + */ + +/* + * Invariants: + * (i) global lock is held + * (ii) fl->susp >= 0 + */ +static int +js_SuspendThread(JSThinLock *tl) +{ + JSFatLock *fl; + PRStatus stat; + + if (tl->fat == NULL) + fl = tl->fat = GetFatlock(tl); + else + fl = tl->fat; + JS_ASSERT(fl->susp >= 0); + fl->susp++; + PR_Lock(fl->slock); + js_UnlockGlobal(tl); + stat = PR_WaitCondVar(fl->svar, PR_INTERVAL_NO_TIMEOUT); + JS_ASSERT(stat != PR_FAILURE); + PR_Unlock(fl->slock); + js_LockGlobal(tl); + fl->susp--; + if (fl->susp == 0) { + PutFatlock(fl, tl); + tl->fat = NULL; + } + return tl->fat == NULL; +} + +/* + * (i) global lock is held + * (ii) fl->susp > 0 + */ +static void +js_ResumeThread(JSThinLock *tl) +{ + JSFatLock *fl = tl->fat; + PRStatus stat; + + JS_ASSERT(fl != NULL); + JS_ASSERT(fl->susp > 0); + PR_Lock(fl->slock); + js_UnlockGlobal(tl); + stat = PR_NotifyCondVar(fl->svar); + JS_ASSERT(stat != PR_FAILURE); + PR_Unlock(fl->slock); +} + +static void +js_Enqueue(JSThinLock *tl, jsword me) +{ + jsword o, n; + + js_LockGlobal(tl); + for (;;) { + o = ReadWord(tl->owner); + n = Thin_SetWait(o); + if (o != 0 && NativeCompareAndSwap(&tl->owner, o, n)) { + if (js_SuspendThread(tl)) + me = Thin_RemoveWait(me); + else + me = Thin_SetWait(me); + } + else if (NativeCompareAndSwap(&tl->owner, 0, me)) { + js_UnlockGlobal(tl); + return; + } + } +} + +static void +js_Dequeue(JSThinLock *tl) +{ + jsword o; + + js_LockGlobal(tl); + o = ReadWord(tl->owner); + JS_ASSERT(Thin_GetWait(o) != 0); + JS_ASSERT(tl->fat != NULL); + if (!NativeCompareAndSwap(&tl->owner, o, 0)) /* release it */ + JS_ASSERT(0); + js_ResumeThread(tl); +} + +static JS_ALWAYS_INLINE void +ThinLock(JSThinLock *tl, jsword me) +{ + JS_ASSERT(CURRENT_THREAD_IS_ME(me)); + if (NativeCompareAndSwap(&tl->owner, 0, me)) + return; + if (Thin_RemoveWait(ReadWord(tl->owner)) != me) + js_Enqueue(tl, me); +#ifdef DEBUG + else + JS_ASSERT(0); +#endif +} + +static JS_ALWAYS_INLINE void +ThinUnlock(JSThinLock *tl, jsword me) +{ + JS_ASSERT(CURRENT_THREAD_IS_ME(me)); + + /* + * Since we can race with the NativeCompareAndSwap in js_Enqueue, we need + * to use a C_A_S here as well -- Arjan van de Ven 30/1/08 + */ + if (NativeCompareAndSwap(&tl->owner, me, 0)) + return; + + JS_ASSERT(Thin_GetWait(tl->owner)); + if (Thin_RemoveWait(ReadWord(tl->owner)) == me) + js_Dequeue(tl); +#ifdef DEBUG + else + JS_ASSERT(0); /* unbalanced unlock */ +#endif +} + +#endif /* !NSPR_LOCK */ + +void +js_Lock(JSContext *cx, JSThinLock *tl) +{ + ThinLock(tl, CX_THINLOCK_ID(cx)); +} + +void +js_Unlock(JSContext *cx, JSThinLock *tl) +{ + ThinUnlock(tl, CX_THINLOCK_ID(cx)); +} + +void +js_LockRuntime(JSRuntime *rt) +{ + PR_Lock(rt->rtLock); +#ifdef DEBUG + rt->rtLockOwner = js_CurrentThreadId(); +#endif +} + +void +js_UnlockRuntime(JSRuntime *rt) +{ +#ifdef DEBUG + rt->rtLockOwner = 0; +#endif + PR_Unlock(rt->rtLock); +} + +void +js_LockTitle(JSContext *cx, JSTitle *title) +{ + jsword me = CX_THINLOCK_ID(cx); + + JS_ASSERT(CURRENT_THREAD_IS_ME(me)); + JS_ASSERT(title->ownercx != cx); + if (CX_THREAD_IS_RUNNING_GC(cx)) + return; + if (title->ownercx && ClaimTitle(title, cx)) + return; + + if (Thin_RemoveWait(ReadWord(title->lock.owner)) == me) { + JS_ASSERT(title->u.count > 0); + LOGIT(scope, '+'); + title->u.count++; + } else { + ThinLock(&title->lock, me); + JS_ASSERT(title->u.count == 0); + LOGIT(scope, '1'); + title->u.count = 1; + } +} + +void +js_UnlockTitle(JSContext *cx, JSTitle *title) +{ + jsword me = CX_THINLOCK_ID(cx); + + /* We hope compilers use me instead of reloading cx->thread in the macro. */ + if (CX_THREAD_IS_RUNNING_GC(cx)) + return; + if (cx->lockedSealedTitle == title) { + cx->lockedSealedTitle = NULL; + return; + } + + /* + * If title->ownercx is not null, it's likely that two contexts not using + * requests nested locks for title. The first context, cx here, claimed + * title; the second, title->ownercx here, re-claimed it because the first + * was not in a request, or was on the same thread. We don't want to keep + * track of such nesting, because it penalizes the common non-nested case. + * Instead of asserting here and silently coping, we simply re-claim title + * for cx and return. + * + * See http://bugzilla.mozilla.org/show_bug.cgi?id=229200 for a real world + * case where an asymmetric thread model (Mozilla's main thread is known + * to be the only thread that runs the GC) combined with multiple contexts + * per thread has led to such request-less nesting. + */ + if (title->ownercx) { + JS_ASSERT(title->u.count == 0); + JS_ASSERT(title->lock.owner == 0); + title->ownercx = cx; + return; + } + + JS_ASSERT(title->u.count > 0); + if (Thin_RemoveWait(ReadWord(title->lock.owner)) != me) { + JS_ASSERT(0); /* unbalanced unlock */ + return; + } + LOGIT(title, '-'); + if (--title->u.count == 0) + ThinUnlock(&title->lock, me); +} + +/* + * NB: oldtitle may be null if our caller is js_GetMutableScope and it just + * dropped the last reference to oldtitle. + */ +void +js_TransferTitle(JSContext *cx, JSTitle *oldtitle, JSTitle *newtitle) +{ + JS_ASSERT(JS_IS_TITLE_LOCKED(cx, newtitle)); + + /* + * If the last reference to oldtitle went away, newtitle needs no lock + * state update. + */ + if (!oldtitle) + return; + JS_ASSERT(JS_IS_TITLE_LOCKED(cx, oldtitle)); + + /* + * Special case in js_LockTitle and js_UnlockTitle for the GC calling + * code that locks, unlocks, or mutates. Nothing to do in these cases, + * because title and newtitle were "locked" by the GC thread, so neither + * was actually locked. + */ + if (CX_THREAD_IS_RUNNING_GC(cx)) + return; + + /* + * Special case in js_LockObj and js_UnlockTitle for locking the sealed + * scope of an object that owns that scope (the prototype or mutated obj + * for which OBJ_SCOPE(obj)->object == obj), and unlocking it. + */ + JS_ASSERT(cx->lockedSealedTitle != newtitle); + if (cx->lockedSealedTitle == oldtitle) { + JS_ASSERT(newtitle->ownercx == cx || + (!newtitle->ownercx && newtitle->u.count == 1)); + cx->lockedSealedTitle = NULL; + return; + } + + /* + * If oldtitle is single-threaded, there's nothing to do. + */ + if (oldtitle->ownercx) { + JS_ASSERT(oldtitle->ownercx == cx); + JS_ASSERT(newtitle->ownercx == cx || + (!newtitle->ownercx && newtitle->u.count == 1)); + return; + } + + /* + * We transfer oldtitle->u.count only if newtitle is not single-threaded. + * Flow unwinds from here through some number of JS_UNLOCK_TITLE and/or + * JS_UNLOCK_OBJ macro calls, which will decrement newtitle->u.count only + * if they find newtitle->ownercx != cx. + */ + if (newtitle->ownercx != cx) { + JS_ASSERT(!newtitle->ownercx); + newtitle->u.count = oldtitle->u.count; + } + + /* + * Reset oldtitle's lock state so that it is completely unlocked. + */ + LOGIT(oldtitle, '0'); + oldtitle->u.count = 0; + ThinUnlock(&oldtitle->lock, CX_THINLOCK_ID(cx)); +} + +void +js_LockObj(JSContext *cx, JSObject *obj) +{ + JSScope *scope; + JSTitle *title; + + JS_ASSERT(OBJ_IS_NATIVE(obj)); + + /* + * We must test whether the GC is calling and return without mutating any + * state, especially cx->lockedSealedScope. Note asymmetry with respect to + * js_UnlockObj, which is a thin-layer on top of js_UnlockTitle. + */ + if (CX_THREAD_IS_RUNNING_GC(cx)) + return; + + for (;;) { + scope = OBJ_SCOPE(obj); + title = &scope->title; + if (scope->sealed() && !cx->lockedSealedTitle) { + cx->lockedSealedTitle = title; + return; + } + + js_LockTitle(cx, title); + + /* If obj still has this scope, we're done. */ + if (scope == OBJ_SCOPE(obj)) + return; + + /* Lost a race with a mutator; retry with obj's new scope. */ + js_UnlockTitle(cx, title); + } +} + +void +js_UnlockObj(JSContext *cx, JSObject *obj) +{ + JS_ASSERT(OBJ_IS_NATIVE(obj)); + js_UnlockTitle(cx, &OBJ_SCOPE(obj)->title); +} + +bool +js_LockObjIfShape(JSContext *cx, JSObject *obj, uint32 shape) +{ + JS_ASSERT(OBJ_SCOPE(obj)->title.ownercx != cx); + js_LockObj(cx, obj); + if (OBJ_SHAPE(obj) == shape) + return true; + js_UnlockObj(cx, obj); + return false; +} + +void +js_InitTitle(JSContext *cx, JSTitle *title) +{ +#ifdef JS_THREADSAFE + title->ownercx = cx; + memset(&title->lock, 0, sizeof title->lock); + + /* + * Set u.link = NULL, not u.count = 0, in case the target architecture's + * null pointer has a non-zero integer representation. + */ + title->u.link = NULL; + +#ifdef JS_DEBUG_TITLE_LOCKS + title->file[0] = title->file[1] = title->file[2] = title->file[3] = NULL; + title->line[0] = title->line[1] = title->line[2] = title->line[3] = 0; +#endif +#endif +} + +void +js_FinishTitle(JSContext *cx, JSTitle *title) +{ +#ifdef DEBUG_SCOPE_COUNT + js_unlog_title(title); +#endif + +#ifdef JS_THREADSAFE + /* Title must be single-threaded at this point, so set ownercx. */ + JS_ASSERT(title->u.count == 0); + title->ownercx = cx; + js_FinishLock(&title->lock); +#endif +} + +#ifdef DEBUG + +JSBool +js_IsRuntimeLocked(JSRuntime *rt) +{ + return js_CurrentThreadId() == rt->rtLockOwner; +} + +JSBool +js_IsObjLocked(JSContext *cx, JSObject *obj) +{ + return js_IsTitleLocked(cx, &OBJ_SCOPE(obj)->title); +} + +JSBool +js_IsTitleLocked(JSContext *cx, JSTitle *title) +{ + /* Special case: the GC locking any object's title, see js_LockTitle. */ + if (CX_THREAD_IS_RUNNING_GC(cx)) + return JS_TRUE; + + /* Special case: locked object owning a sealed scope, see js_LockObj. */ + if (cx->lockedSealedTitle == title) + return JS_TRUE; + + /* + * General case: the title is either exclusively owned (by cx), or it has + * a thin or fat lock to cope with shared (concurrent) ownership. + */ + if (title->ownercx) { + JS_ASSERT(title->ownercx == cx || title->ownercx->thread == cx->thread); + return JS_TRUE; + } + return js_CurrentThreadId() == + ((JSThread *)Thin_RemoveWait(ReadWord(title->lock.owner)))->id; +} + +#ifdef JS_DEBUG_TITLE_LOCKS +void +js_SetScopeInfo(JSScope *scope, const char *file, int line) +{ + JSTitle *title = &scope->title; + if (!title->ownercx) { + jsrefcount count = title->u.count; + JS_ASSERT_IF(!scope->sealed(), count > 0); + JS_ASSERT(count <= 4); + title->file[count - 1] = file; + title->line[count - 1] = line; + } +} +#endif /* JS_DEBUG_TITLE_LOCKS */ +#endif /* DEBUG */ +#endif /* JS_THREADSAFE */ diff --git a/ape-server/deps/js/src/jslock.h b/ape-server/deps/js/src/jslock.h new file mode 100755 index 0000000..8c6a6c4 --- /dev/null +++ b/ape-server/deps/js/src/jslock.h @@ -0,0 +1,324 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +#ifndef jslock_h__ +#define jslock_h__ + +#include "jstypes.h" +#include "jsprvtd.h" /* for JSScope, etc. */ +#include "jspubtd.h" /* for JSRuntime, etc. */ + +#ifdef JS_THREADSAFE +# include "pratom.h" +# include "prlock.h" +# include "prcvar.h" +# include "prthread.h" +#endif + +JS_BEGIN_EXTERN_C + +#ifdef JS_THREADSAFE + +#if (defined(_WIN32) && defined(_M_IX86)) || \ + (defined(_WIN64) && (defined(_M_AMD64) || defined(_M_X64))) || \ + (defined(__i386) && (defined(__GNUC__) || defined(__SUNPRO_CC))) || \ + (defined(__x86_64) && (defined(__GNUC__) || defined(__SUNPRO_CC))) || \ + (defined(__sparc) && (defined(__GNUC__) || defined(__SUNPRO_CC))) || \ + defined(AIX) || \ + defined(USE_ARM_KUSER) +# define JS_HAS_NATIVE_COMPARE_AND_SWAP 1 +#else +# define JS_HAS_NATIVE_COMPARE_AND_SWAP 0 +#endif + +#if defined(JS_USE_ONLY_NSPR_LOCKS) || !JS_HAS_NATIVE_COMPARE_AND_SWAP +# define NSPR_LOCK 1 +#else +# undef NSPR_LOCK +#endif + +#define Thin_GetWait(W) ((jsword)(W) & 0x1) +#define Thin_SetWait(W) ((jsword)(W) | 0x1) +#define Thin_RemoveWait(W) ((jsword)(W) & ~0x1) + +typedef struct JSFatLock JSFatLock; + +typedef struct JSThinLock { + jsword owner; + JSFatLock *fat; +} JSThinLock; + +#define CX_THINLOCK_ID(cx) ((jsword)(cx)->thread) +#define CURRENT_THREAD_IS_ME(me) (((JSThread *)me)->id == js_CurrentThreadId()) + +typedef PRLock JSLock; + +typedef struct JSTitle JSTitle; + +struct JSTitle { + JSContext *ownercx; /* creating context, NULL if shared */ + JSThinLock lock; /* binary semaphore protecting title */ + union { /* union lockful and lock-free state: */ + jsrefcount count; /* lock entry count for reentrancy */ + JSTitle *link; /* next link in rt->titleSharingTodo */ + } u; +#ifdef JS_DEBUG_TITLE_LOCKS + const char *file[4]; /* file where lock was (re-)taken */ + unsigned int line[4]; /* line where lock was (re-)taken */ +#endif +}; + +/* + * Title structure is always allocated as a field of JSScope. + */ +#define TITLE_TO_SCOPE(title) \ + ((JSScope *)((uint8 *) (title) - offsetof(JSScope, title))) + +/* + * Atomic increment and decrement for a reference counter, given jsrefcount *p. + * NB: jsrefcount is int32, aka PRInt32, so that pratom.h functions work. + */ +#define JS_ATOMIC_INCREMENT(p) PR_AtomicIncrement((PRInt32 *)(p)) +#define JS_ATOMIC_DECREMENT(p) PR_AtomicDecrement((PRInt32 *)(p)) +#define JS_ATOMIC_ADD(p,v) PR_AtomicAdd((PRInt32 *)(p), (PRInt32)(v)) +#define JS_ATOMIC_SET(p,v) PR_AtomicSet((PRInt32 *)(p), (PRInt32)(v)) + +#define js_CurrentThreadId() (jsword)PR_GetCurrentThread() +#define JS_NEW_LOCK() PR_NewLock() +#define JS_DESTROY_LOCK(l) PR_DestroyLock(l) +#define JS_ACQUIRE_LOCK(l) PR_Lock(l) +#define JS_RELEASE_LOCK(l) PR_Unlock(l) + +#define JS_NEW_CONDVAR(l) PR_NewCondVar(l) +#define JS_DESTROY_CONDVAR(cv) PR_DestroyCondVar(cv) +#define JS_WAIT_CONDVAR(cv,to) PR_WaitCondVar(cv,to) +#define JS_NO_TIMEOUT PR_INTERVAL_NO_TIMEOUT +#define JS_NOTIFY_CONDVAR(cv) PR_NotifyCondVar(cv) +#define JS_NOTIFY_ALL_CONDVAR(cv) PR_NotifyAllCondVar(cv) + +#ifdef JS_DEBUG_TITLE_LOCKS + +#define JS_SET_OBJ_INFO(obj_, file_, line_) \ + JS_SET_SCOPE_INFO(OBJ_SCOPE(obj_), file_, line_) + +#define JS_SET_SCOPE_INFO(scope_, file_, line_) \ + js_SetScopeInfo(scope_, file_, line_) + +#endif + +#define JS_LOCK(cx, tl) js_Lock(cx, tl) +#define JS_UNLOCK(cx, tl) js_Unlock(cx, tl) + +#define JS_LOCK_RUNTIME(rt) js_LockRuntime(rt) +#define JS_UNLOCK_RUNTIME(rt) js_UnlockRuntime(rt) + +/* + * NB: The JS_LOCK_OBJ and JS_UNLOCK_OBJ macros work *only* on native objects + * (objects for which OBJ_IS_NATIVE returns true). All uses of these macros in + * the engine are predicated on OBJ_IS_NATIVE or equivalent checks. These uses + * are for optimizations above the JSObjectOps layer, under which object locks + * normally hide. + */ +#define JS_LOCK_OBJ(cx,obj) ((OBJ_SCOPE(obj)->title.ownercx == (cx)) \ + ? (void)0 \ + : (js_LockObj(cx, obj), \ + JS_SET_OBJ_INFO(obj,__FILE__,__LINE__))) +#define JS_UNLOCK_OBJ(cx,obj) ((OBJ_SCOPE(obj)->title.ownercx == (cx)) \ + ? (void)0 : js_UnlockObj(cx, obj)) + +/* + * Lock object only if its scope has the given shape. + */ +#define JS_LOCK_OBJ_IF_SHAPE(cx,obj,shape) \ + (OBJ_SHAPE(obj) == (shape) \ + ? (OBJ_SCOPE(obj)->title.ownercx == (cx) \ + ? true \ + : js_LockObjIfShape(cx, obj, shape)) \ + : false) + +#define JS_LOCK_TITLE(cx,title) \ + ((title)->ownercx == (cx) ? (void)0 \ + : (js_LockTitle(cx, (title)), \ + JS_SET_TITLE_INFO(title,__FILE__,__LINE__))) + +#define JS_UNLOCK_TITLE(cx,title) ((title)->ownercx == (cx) ? (void)0 \ + : js_UnlockTitle(cx, title)) + +#define JS_LOCK_SCOPE(cx,scope) JS_LOCK_TITLE(cx,&(scope)->title) +#define JS_UNLOCK_SCOPE(cx,scope) JS_UNLOCK_TITLE(cx,&(scope)->title) + +#define JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope) \ + js_TransferTitle(cx, &scope->title, &newscope->title) + + +extern void js_Lock(JSContext *cx, JSThinLock *tl); +extern void js_Unlock(JSContext *cx, JSThinLock *tl); +extern void js_LockRuntime(JSRuntime *rt); +extern void js_UnlockRuntime(JSRuntime *rt); +extern void js_LockObj(JSContext *cx, JSObject *obj); +extern void js_UnlockObj(JSContext *cx, JSObject *obj); +extern bool js_LockObjIfShape(JSContext *cx, JSObject *obj, uint32 shape); +extern void js_InitTitle(JSContext *cx, JSTitle *title); +extern void js_FinishTitle(JSContext *cx, JSTitle *title); +extern void js_LockTitle(JSContext *cx, JSTitle *title); +extern void js_UnlockTitle(JSContext *cx, JSTitle *title); +extern int js_SetupLocks(int,int); +extern void js_CleanupLocks(); +extern void js_TransferTitle(JSContext *, JSTitle *, JSTitle *); +extern JS_FRIEND_API(jsval) +js_GetSlotThreadSafe(JSContext *, JSObject *, uint32); +extern void js_SetSlotThreadSafe(JSContext *, JSObject *, uint32, jsval); +extern void js_InitLock(JSThinLock *); +extern void js_FinishLock(JSThinLock *); + +/* + * This function must be called with the GC lock held. + */ +extern void +js_ShareWaitingTitles(JSContext *cx); + +extern void +js_NudgeOtherContexts(JSContext *cx); + +#ifdef DEBUG + +#define JS_IS_RUNTIME_LOCKED(rt) js_IsRuntimeLocked(rt) +#define JS_IS_OBJ_LOCKED(cx,obj) js_IsObjLocked(cx,obj) +#define JS_IS_TITLE_LOCKED(cx,title) js_IsTitleLocked(cx,title) + +extern JSBool js_IsRuntimeLocked(JSRuntime *rt); +extern JSBool js_IsObjLocked(JSContext *cx, JSObject *obj); +extern JSBool js_IsTitleLocked(JSContext *cx, JSTitle *title); +#ifdef JS_DEBUG_TITLE_LOCKS +extern void js_SetScopeInfo(JSScope *scope, const char *file, int line); +#endif + +#else + +#define JS_IS_RUNTIME_LOCKED(rt) 0 +#define JS_IS_OBJ_LOCKED(cx,obj) 1 +#define JS_IS_TITLE_LOCKED(cx,title) 1 + +#endif /* DEBUG */ + +#else /* !JS_THREADSAFE */ + +#define JS_ATOMIC_INCREMENT(p) (++*(p)) +#define JS_ATOMIC_DECREMENT(p) (--*(p)) +#define JS_ATOMIC_ADD(p,v) (*(p) += (v)) +#define JS_ATOMIC_SET(p,v) (*(p) = (v)) + +#define JS_CurrentThreadId() 0 +#define JS_NEW_LOCK() NULL +#define JS_DESTROY_LOCK(l) ((void)0) +#define JS_ACQUIRE_LOCK(l) ((void)0) +#define JS_RELEASE_LOCK(l) ((void)0) +#define JS_LOCK(cx, tl) ((void)0) +#define JS_UNLOCK(cx, tl) ((void)0) + +#define JS_NEW_CONDVAR(l) NULL +#define JS_DESTROY_CONDVAR(cv) ((void)0) +#define JS_WAIT_CONDVAR(cv,to) ((void)0) +#define JS_NOTIFY_CONDVAR(cv) ((void)0) +#define JS_NOTIFY_ALL_CONDVAR(cv) ((void)0) + +#define JS_LOCK_RUNTIME(rt) ((void)0) +#define JS_UNLOCK_RUNTIME(rt) ((void)0) +#define JS_LOCK_OBJ(cx,obj) ((void)0) +#define JS_UNLOCK_OBJ(cx,obj) ((void)0) +#define JS_LOCK_OBJ_IF_SHAPE(cx,obj,shape) (OBJ_SHAPE(obj) == (shape)) + +#define JS_LOCK_OBJ_VOID(cx,obj,e) (e) +#define JS_LOCK_SCOPE(cx,scope) ((void)0) +#define JS_UNLOCK_SCOPE(cx,scope) ((void)0) +#define JS_TRANSFER_SCOPE_LOCK(c,o,n) ((void)0) + +#define JS_IS_RUNTIME_LOCKED(rt) 1 +#define JS_IS_OBJ_LOCKED(cx,obj) 1 +#define JS_IS_TITLE_LOCKED(cx,title) 1 + +#endif /* !JS_THREADSAFE */ + +#define JS_LOCK_RUNTIME_VOID(rt,e) \ + JS_BEGIN_MACRO \ + JS_LOCK_RUNTIME(rt); \ + e; \ + JS_UNLOCK_RUNTIME(rt); \ + JS_END_MACRO + +#define JS_LOCK_GC(rt) JS_ACQUIRE_LOCK((rt)->gcLock) +#define JS_UNLOCK_GC(rt) JS_RELEASE_LOCK((rt)->gcLock) +#define JS_AWAIT_GC_DONE(rt) JS_WAIT_CONDVAR((rt)->gcDone, JS_NO_TIMEOUT) +#define JS_NOTIFY_GC_DONE(rt) JS_NOTIFY_ALL_CONDVAR((rt)->gcDone) +#define JS_AWAIT_REQUEST_DONE(rt) JS_WAIT_CONDVAR((rt)->requestDone, \ + JS_NO_TIMEOUT) +#define JS_NOTIFY_REQUEST_DONE(rt) JS_NOTIFY_CONDVAR((rt)->requestDone) + +#ifndef JS_SET_OBJ_INFO +#define JS_SET_OBJ_INFO(obj,f,l) ((void)0) +#endif +#ifndef JS_SET_TITLE_INFO +#define JS_SET_TITLE_INFO(title,f,l) ((void)0) +#endif + +#ifdef JS_THREADSAFE + +extern JSBool +js_CompareAndSwap(jsword *w, jsword ov, jsword nv); + +/* Atomically bitwise-or the mask into the word *w using compare and swap. */ +extern void +js_AtomicSetMask(jsword *w, jsword mask); + +#define JS_ATOMIC_SET_MASK(w, mask) js_AtomicSetMask(w, mask) + +#else + +static inline JSBool +js_CompareAndSwap(jsword *w, jsword ov, jsword nv) +{ + return (*w == ov) ? *w = nv, JS_TRUE : JS_FALSE; +} + +#define JS_ATOMIC_SET_MASK(w, mask) (*(w) |= (mask)) + +#endif /* JS_THREADSAFE */ + +JS_END_EXTERN_C + +#endif /* jslock_h___ */ diff --git a/ape-server/deps/js/src/jslocko.asm b/ape-server/deps/js/src/jslocko.asm new file mode 100755 index 0000000..95353ba --- /dev/null +++ b/ape-server/deps/js/src/jslocko.asm @@ -0,0 +1,60 @@ +; -*- Mode: asm; tab-width: 8; c-basic-offset: 4 -*- + +; ***** BEGIN LICENSE BLOCK ***** +; Version: MPL 1.1/GPL 2.0/LGPL 2.1 +; +; The contents of this file are subject to the Mozilla Public License Version +; 1.1 (the "License"); you may not use this file except in compliance with +; the License. You may obtain a copy of the License at +; http://www.mozilla.org/MPL/ +; +; Software distributed under the License is distributed on an "AS IS" basis, +; WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +; for the specific language governing rights and limitations under the +; License. +; +; The Original Code is an OS/2 implementation of js_CompareAndSwap in assembly. +; +; The Initial Developer of the Original Code is +; IBM Corporation. +; Portions created by the Initial Developer are Copyright (C) 2001 +; the Initial Developer. All Rights Reserved. +; +; Contributor(s): +; +; Alternatively, the contents of this file may be used under the terms of +; either the GNU General Public License Version 2 or later (the "GPL"), or +; the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +; in which case the provisions of the GPL or the LGPL are applicable instead +; of those above. If you wish to allow use of your version of this file only +; under the terms of either the GPL or the LGPL, and not to allow others to +; use your version of this file under the terms of the MPL, indicate your +; decision by deleting the provisions above and replace them with the notice +; and other provisions required by the GPL or the LGPL. If you do not delete +; the provisions above, a recipient may use your version of this file under +; the terms of any one of the MPL, the GPL or the LGPL. +; +; ***** END LICENSE BLOCK ***** + + .486P + .MODEL FLAT, OPTLINK + .STACK + + .CODE + +;;;--------------------------------------------------------------------- +;;; int _Optlink js_CompareAndSwap(jsword *w, jsword ov, jsword nv) +;;;--------------------------------------------------------------------- +js_CompareAndSwap PROC OPTLINK EXPORT + push ebx + mov ebx, eax + mov eax, edx + mov edx, ebx + lock cmpxchg [ebx], ecx + sete al + and eax, 1h + pop ebx + ret +js_CompareAndSwap endp + + END diff --git a/ape-server/deps/js/src/jslog2.cpp b/ape-server/deps/js/src/jslog2.cpp new file mode 100755 index 0000000..81e6511 --- /dev/null +++ b/ape-server/deps/js/src/jslog2.cpp @@ -0,0 +1,111 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "jsstdint.h" +#include "jsbit.h" +#include "jsutil.h" + +/* + * Check that we can use js_bitscan_clz32 to implement JS_FLOOR_LOG2 and + * JS_FLOOR_LOG2W and js_bitscan_clz64 to implement JS_FLOOR_LOG2W on 64-bit + * systems. + */ +#ifdef JS_HAS_BUILTIN_BITSCAN32 +JS_STATIC_ASSERT(sizeof(unsigned int) == sizeof(JSUint32)); +JS_STATIC_ASSERT_IF(JS_BYTES_PER_WORD == 4, + sizeof(unsigned int) == sizeof(JSUword)); +#endif +#ifdef JS_HAS_BUILTIN_BITSCAN64 +JS_STATIC_ASSERT_IF(JS_BYTES_PER_WORD == 8, + sizeof(unsigned long long) == sizeof(JSUword)); +#endif + +/* + * Compute the log of the least power of 2 greater than or equal to n + */ +JS_PUBLIC_API(JSIntn) +JS_CeilingLog2(JSUint32 n) +{ + JSIntn log2; + + JS_CEILING_LOG2(log2, n); + return log2; +} + +/* + * Compute the log of the greatest power of 2 less than or equal to n. + * This really just finds the highest set bit in the word. + */ +JS_PUBLIC_API(JSIntn) +JS_FloorLog2(JSUint32 n) +{ + JSIntn log2; + + JS_FLOOR_LOG2(log2, n); + return log2; +} + +/* + * js_FloorLog2wImpl has to be defined only for 64-bit non-GCC case. + */ +#if !defined(JS_HAS_BUILTIN_BITSCAN64) && JS_BYTES_PER_WORD == 8 + +JSUword +js_FloorLog2wImpl(JSUword n) +{ + JSUword log2, m; + + JS_ASSERT(n != 0); + + log2 = 0; + m = n >> 32; + if (m != 0) { n = m; log2 = 32; } + m = n >> 16; + if (m != 0) { n = m; log2 |= 16; } + m = n >> 8; + if (m != 0) { n = m; log2 |= 8; } + m = n >> 4; + if (m != 0) { n = m; log2 |= 4; } + m = n >> 2; + if (m != 0) { n = m; log2 |= 2; } + log2 |= (n >> 1); + + return log2; +} + +#endif diff --git a/ape-server/deps/js/src/jslong.h b/ape-server/deps/js/src/jslong.h new file mode 100755 index 0000000..4a1fccc --- /dev/null +++ b/ape-server/deps/js/src/jslong.h @@ -0,0 +1,167 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* +** File: jslong.h +** Description: Portable access to 64 bit numerics +** +** Long-long (64-bit signed integer type) support. Some C compilers +** don't support 64 bit integers yet, so we use these macros to +** support both machines that do and don't. +**/ +#ifndef jslong_h___ +#define jslong_h___ + +#include "jstypes.h" + +JS_BEGIN_EXTERN_C + +#define JSLL_INIT(hi, lo) ((((JSInt64)(hi)) << 32) + (JSInt64)(lo)) + +/*********************************************************************** +** MACROS: JSLL_* +** DESCRIPTION: +** The following macros define portable access to the 64 bit +** math facilities. +** +***********************************************************************/ + +/*********************************************************************** +** MACROS: JSLL_ +** +** JSLL_IS_ZERO Test for zero +** JSLL_EQ Test for equality +** JSLL_NE Test for inequality +** JSLL_GE_ZERO Test for zero or positive +** JSLL_CMP Compare two values +***********************************************************************/ +#define JSLL_IS_ZERO(a) ((a) == 0) +#define JSLL_EQ(a, b) ((a) == (b)) +#define JSLL_NE(a, b) ((a) != (b)) +#define JSLL_GE_ZERO(a) ((a) >= 0) +#define JSLL_CMP(a, op, b) ((JSInt64)(a) op (JSInt64)(b)) +#define JSLL_UCMP(a, op, b) ((JSUint64)(a) op (JSUint64)(b)) + +/*********************************************************************** +** MACROS: JSLL_ +** +** JSLL_AND Logical and +** JSLL_OR Logical or +** JSLL_XOR Logical exclusion +** JSLL_OR2 A disgusting deviation +** JSLL_NOT Negation (one's compliment) +***********************************************************************/ +#define JSLL_AND(r, a, b) ((r) = (a) & (b)) +#define JSLL_OR(r, a, b) ((r) = (a) | (b)) +#define JSLL_XOR(r, a, b) ((r) = (a) ^ (b)) +#define JSLL_OR2(r, a) ((r) = (r) | (a)) +#define JSLL_NOT(r, a) ((r) = ~(a)) + +/*********************************************************************** +** MACROS: JSLL_ +** +** JSLL_NEG Negation (two's compliment) +** JSLL_ADD Summation (two's compliment) +** JSLL_SUB Difference (two's compliment) +***********************************************************************/ +#define JSLL_NEG(r, a) ((r) = -(a)) +#define JSLL_ADD(r, a, b) ((r) = (a) + (b)) +#define JSLL_SUB(r, a, b) ((r) = (a) - (b)) + +/*********************************************************************** +** MACROS: JSLL_ +** +** JSLL_MUL Product (two's compliment) +** JSLL_DIV Quotient (two's compliment) +** JSLL_MOD Modulus (two's compliment) +***********************************************************************/ +#define JSLL_MUL(r, a, b) ((r) = (a) * (b)) +#define JSLL_DIV(r, a, b) ((r) = (a) / (b)) +#define JSLL_MOD(r, a, b) ((r) = (a) % (b)) + +/*********************************************************************** +** MACROS: JSLL_ +** +** JSLL_SHL Shift left [0..64] bits +** JSLL_SHR Shift right [0..64] bits with sign extension +** JSLL_USHR Unsigned shift right [0..64] bits +** JSLL_ISHL Signed shift left [0..64] bits +***********************************************************************/ +#define JSLL_SHL(r, a, b) ((r) = (JSInt64)(a) << (b)) +#define JSLL_SHR(r, a, b) ((r) = (JSInt64)(a) >> (b)) +#define JSLL_USHR(r, a, b) ((r) = (JSUint64)(a) >> (b)) +#define JSLL_ISHL(r, a, b) ((r) = (JSInt64)(a) << (b)) + +/*********************************************************************** +** MACROS: JSLL_ +** +** JSLL_L2I Convert to signed 32 bit +** JSLL_L2UI Convert to unsigned 32 bit +** JSLL_L2F Convert to floating point +** JSLL_L2D Convert to floating point +** JSLL_I2L Convert signed to 64 bit +** JSLL_UI2L Convert unsigned to 64 bit +** JSLL_F2L Convert float to 64 bit +** JSLL_D2L Convert float to 64 bit +***********************************************************************/ +#define JSLL_L2I(i, l) ((i) = (JSInt32)(l)) +#define JSLL_L2UI(ui, l) ((ui) = (JSUint32)(l)) +#define JSLL_L2F(f, l) ((f) = (JSFloat64)(l)) +#define JSLL_L2D(d, l) ((d) = (JSFloat64)(l)) + +#define JSLL_I2L(l, i) ((l) = (JSInt64)(i)) +#define JSLL_UI2L(l, ui) ((l) = (JSInt64)(ui)) +#define JSLL_F2L(l, f) ((l) = (JSInt64)(f)) +#define JSLL_D2L(l, d) ((l) = (JSInt64)(d)) + +/*********************************************************************** +** MACROS: JSLL_UDIVMOD +** DESCRIPTION: +** Produce both a quotient and a remainder given an unsigned +** INPUTS: JSUint64 a: The dividend of the operation +** JSUint64 b: The quotient of the operation +** OUTPUTS: JSUint64 *qp: pointer to quotient +** JSUint64 *rp: pointer to remainder +***********************************************************************/ +#define JSLL_UDIVMOD(qp, rp, a, b) \ + (*(qp) = ((JSUint64)(a) / (b)), \ + *(rp) = ((JSUint64)(a) % (b))) + +JS_END_EXTERN_C + +#endif /* jslong_h___ */ diff --git a/ape-server/deps/js/src/jsmath.cpp b/ape-server/deps/js/src/jsmath.cpp new file mode 100755 index 0000000..b26a0ae --- /dev/null +++ b/ape-server/deps/js/src/jsmath.cpp @@ -0,0 +1,762 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * JS math package. + */ +#include +#include "jstypes.h" +#include "jsstdint.h" +#include "jslong.h" +#include "prmjtime.h" +#include "jsapi.h" +#include "jsatom.h" +#include "jsbuiltins.h" +#include "jscntxt.h" +#include "jsversion.h" +#include "jslock.h" +#include "jsmath.h" +#include "jsnum.h" +#include "jslibmath.h" +#include "jsobj.h" + +#ifndef M_E +#define M_E 2.7182818284590452354 +#endif +#ifndef M_LOG2E +#define M_LOG2E 1.4426950408889634074 +#endif +#ifndef M_LOG10E +#define M_LOG10E 0.43429448190325182765 +#endif +#ifndef M_LN2 +#define M_LN2 0.69314718055994530942 +#endif +#ifndef M_LN10 +#define M_LN10 2.30258509299404568402 +#endif +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif +#ifndef M_SQRT2 +#define M_SQRT2 1.41421356237309504880 +#endif +#ifndef M_SQRT1_2 +#define M_SQRT1_2 0.70710678118654752440 +#endif + +static JSConstDoubleSpec math_constants[] = { + {M_E, "E", 0, {0,0,0}}, + {M_LOG2E, "LOG2E", 0, {0,0,0}}, + {M_LOG10E, "LOG10E", 0, {0,0,0}}, + {M_LN2, "LN2", 0, {0,0,0}}, + {M_LN10, "LN10", 0, {0,0,0}}, + {M_PI, "PI", 0, {0,0,0}}, + {M_SQRT2, "SQRT2", 0, {0,0,0}}, + {M_SQRT1_2, "SQRT1_2", 0, {0,0,0}}, + {0,0,0,{0,0,0}} +}; + +JSClass js_MathClass = { + js_Math_str, + JSCLASS_HAS_CACHED_PROTO(JSProto_Math), + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +static JSBool +math_abs(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble x, z; + + if (argc == 0) { + *vp = cx->runtime->NaNValue; + return JS_TRUE; + } + x = js_ValueToNumber(cx, &vp[2]); + if (JSVAL_IS_NULL(vp[2])) + return JS_FALSE; + z = fabs(x); + return js_NewNumberInRootedValue(cx, z, vp); +} + +static JSBool +math_acos(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble x, z; + + if (argc == 0) { + *vp = cx->runtime->NaNValue; + return JS_TRUE; + } + x = js_ValueToNumber(cx, &vp[2]); + if (JSVAL_IS_NULL(vp[2])) + return JS_FALSE; +#if defined(SOLARIS) && defined(__GNUC__) + if (x < -1 || 1 < x) { + *vp = cx->runtime->NaNValue; + return JS_TRUE; + } +#endif + z = acos(x); + return js_NewNumberInRootedValue(cx, z, vp); +} + +static JSBool +math_asin(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble x, z; + + if (argc == 0) { + *vp = cx->runtime->NaNValue; + return JS_TRUE; + } + x = js_ValueToNumber(cx, &vp[2]); + if (JSVAL_IS_NULL(vp[2])) + return JS_FALSE; +#if defined(SOLARIS) && defined(__GNUC__) + if (x < -1 || 1 < x) { + *vp = cx->runtime->NaNValue; + return JS_TRUE; + } +#endif + z = asin(x); + return js_NewNumberInRootedValue(cx, z, vp); +} + +static JSBool +math_atan(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble x, z; + + if (argc == 0) { + *vp = cx->runtime->NaNValue; + return JS_TRUE; + } + x = js_ValueToNumber(cx, &vp[2]); + if (JSVAL_IS_NULL(vp[2])) + return JS_FALSE; + z = atan(x); + return js_NewNumberInRootedValue(cx, z, vp); +} + +static inline jsdouble JS_FASTCALL +math_atan2_kernel(jsdouble x, jsdouble y) +{ +#if defined(_MSC_VER) + /* + * MSVC's atan2 does not yield the result demanded by ECMA when both x + * and y are infinite. + * - The result is a multiple of pi/4. + * - The sign of x determines the sign of the result. + * - The sign of y determines the multiplicator, 1 or 3. + */ + if (JSDOUBLE_IS_INFINITE(x) && JSDOUBLE_IS_INFINITE(y)) { + jsdouble z = js_copysign(M_PI / 4, x); + if (y < 0) + z *= 3; + return z; + } +#endif + +#if defined(SOLARIS) && defined(__GNUC__) + if (x == 0) { + if (JSDOUBLE_IS_NEGZERO(y)) + return js_copysign(M_PI, x); + if (y == 0) + return x; + } +#endif + return atan2(x, y); +} + +static JSBool +math_atan2(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble x, y; + + if (argc <= 1) { + *vp = cx->runtime->NaNValue; + return JS_TRUE; + } + x = js_ValueToNumber(cx, &vp[2]); + if (JSVAL_IS_NULL(vp[2])) + return JS_FALSE; + y = js_ValueToNumber(cx, &vp[3]); + if (JSVAL_IS_NULL(vp[3])) + return JS_FALSE; + return js_NewNumberInRootedValue(cx, math_atan2_kernel (x, y), vp); +} + +static inline jsdouble JS_FASTCALL +math_ceil_kernel(jsdouble x) +{ +#ifdef __APPLE__ + if (x < 0 && x > -1.0) + return js_copysign(0, -1); +#endif + return ceil(x); +} + +JSBool +js_math_ceil(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble x, z; + + if (argc == 0) { + *vp = cx->runtime->NaNValue; + return JS_TRUE; + } + x = js_ValueToNumber(cx, &vp[2]); + if (JSVAL_IS_NULL(vp[2])) + return JS_FALSE; + z = math_ceil_kernel(x); + return js_NewNumberInRootedValue(cx, z, vp); +} + +static JSBool +math_cos(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble x, z; + + if (argc == 0) { + *vp = cx->runtime->NaNValue; + return JS_TRUE; + } + x = js_ValueToNumber(cx, &vp[2]); + if (JSVAL_IS_NULL(vp[2])) + return JS_FALSE; + z = cos(x); + return js_NewNumberInRootedValue(cx, z, vp); +} + +static JSBool +math_exp(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble x, z; + + if (argc == 0) { + *vp = cx->runtime->NaNValue; + return JS_TRUE; + } + x = js_ValueToNumber(cx, &vp[2]); + if (JSVAL_IS_NULL(vp[2])) + return JS_FALSE; +#ifdef _WIN32 + if (!JSDOUBLE_IS_NaN(x)) { + if (x == js_PositiveInfinity) { + *vp = cx->runtime->positiveInfinityValue; + return JS_TRUE; + } + if (x == js_NegativeInfinity) { + *vp = JSVAL_ZERO; + return JS_TRUE; + } + } +#endif + z = exp(x); + return js_NewNumberInRootedValue(cx, z, vp); +} + +JSBool +js_math_floor(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble x, z; + + if (argc == 0) { + *vp = cx->runtime->NaNValue; + return JS_TRUE; + } + x = js_ValueToNumber(cx, &vp[2]); + if (JSVAL_IS_NULL(vp[2])) + return JS_FALSE; + z = floor(x); + return js_NewNumberInRootedValue(cx, z, vp); +} + +static JSBool +math_log(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble x, z; + + if (argc == 0) { + *vp = cx->runtime->NaNValue; + return JS_TRUE; + } + x = js_ValueToNumber(cx, &vp[2]); + if (JSVAL_IS_NULL(vp[2])) + return JS_FALSE; +#if defined(SOLARIS) && defined(__GNUC__) + if (x < 0) { + *vp = cx->runtime->NaNValue; + return JS_TRUE; + } +#endif + z = log(x); + return js_NewNumberInRootedValue(cx, z, vp); +} + +JSBool +js_math_max(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble x, z = js_NegativeInfinity; + jsval *argv; + uintN i; + + if (argc == 0) { + *vp = cx->runtime->negativeInfinityValue; + return JS_TRUE; + } + argv = vp + 2; + for (i = 0; i < argc; i++) { + x = js_ValueToNumber(cx, &argv[i]); + if (JSVAL_IS_NULL(argv[i])) + return JS_FALSE; + if (JSDOUBLE_IS_NaN(x)) { + *vp = cx->runtime->NaNValue; + return JS_TRUE; + } + if (x == 0 && x == z) { + if (js_copysign(1.0, z) == -1) + z = x; + } else { + z = (x > z) ? x : z; + } + } + return js_NewNumberInRootedValue(cx, z, vp); +} + +JSBool +js_math_min(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble x, z = js_PositiveInfinity; + jsval *argv; + uintN i; + + if (argc == 0) { + *vp = cx->runtime->positiveInfinityValue; + return JS_TRUE; + } + argv = vp + 2; + for (i = 0; i < argc; i++) { + x = js_ValueToNumber(cx, &argv[i]); + if (JSVAL_IS_NULL(argv[i])) + return JS_FALSE; + if (JSDOUBLE_IS_NaN(x)) { + *vp = cx->runtime->NaNValue; + return JS_TRUE; + } + if (x == 0 && x == z) { + if (js_copysign(1.0, x) == -1) + z = x; + } else { + z = (x < z) ? x : z; + } + } + return js_NewNumberInRootedValue(cx, z, vp); +} + +static JSBool +math_pow(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble x, y, z; + + if (argc <= 1) { + *vp = cx->runtime->NaNValue; + return JS_TRUE; + } + x = js_ValueToNumber(cx, &vp[2]); + if (JSVAL_IS_NULL(vp[2])) + return JS_FALSE; + y = js_ValueToNumber(cx, &vp[3]); + if (JSVAL_IS_NULL(vp[3])) + return JS_FALSE; + /* + * Because C99 and ECMA specify different behavior for pow(), + * we need to wrap the libm call to make it ECMA compliant. + */ + if (!JSDOUBLE_IS_FINITE(y) && (x == 1.0 || x == -1.0)) { + *vp = cx->runtime->NaNValue; + return JS_TRUE; + } + /* pow(x, +-0) is always 1, even for x = NaN. */ + if (y == 0) { + *vp = JSVAL_ONE; + return JS_TRUE; + } + z = pow(x, y); + return js_NewNumberInRootedValue(cx, z, vp); +} + +static const int64 RNG_MULTIPLIER = 0x5DEECE66DLL; +static const int64 RNG_ADDEND = 0xBLL; +static const int64 RNG_MASK = (1LL << 48) - 1; +static const jsdouble RNG_DSCALE = jsdouble(1LL << 53); + +/* + * Math.random() support, lifted from java.util.Random.java. + */ +static inline void +random_setSeed(JSThreadData *data, int64 seed) +{ + data->rngSeed = (seed ^ RNG_MULTIPLIER) & RNG_MASK; +} + +void +js_InitRandom(JSThreadData *data) +{ + /* Finally, set the seed from current time. */ + random_setSeed(data, PRMJ_Now() / 1000); +} + +static inline uint64 +random_next(JSThreadData *data, int bits) +{ + uint64 nextseed = data->rngSeed * RNG_MULTIPLIER; + nextseed += RNG_ADDEND; + nextseed &= RNG_MASK; + data->rngSeed = nextseed; + return nextseed >> (48 - bits); +} + +static inline jsdouble +random_nextDouble(JSThreadData *data) +{ + return jsdouble((random_next(data, 26) << 27) + random_next(data, 27)) / RNG_DSCALE; +} + +static JSBool +math_random(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble z = random_nextDouble(JS_THREAD_DATA(cx)); + return js_NewNumberInRootedValue(cx, z, vp); +} + +#if defined _WIN32 && !defined WINCE && _MSC_VER < 1400 +/* Try to work around apparent _copysign bustage in VC7.x. */ +double +js_copysign(double x, double y) +{ + jsdpun xu, yu; + + xu.d = x; + yu.d = y; + xu.s.hi &= ~JSDOUBLE_HI32_SIGNBIT; + xu.s.hi |= yu.s.hi & JSDOUBLE_HI32_SIGNBIT; + return xu.d; +} +#endif + +JSBool +js_math_round(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble x, z; + + if (argc == 0) { + *vp = cx->runtime->NaNValue; + return JS_TRUE; + } + x = js_ValueToNumber(cx, &vp[2]); + if (JSVAL_IS_NULL(vp[2])) + return JS_FALSE; + z = js_copysign(floor(x + 0.5), x); + return js_NewNumberInRootedValue(cx, z, vp); +} + +static JSBool +math_sin(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble x, z; + + if (argc == 0) { + *vp = cx->runtime->NaNValue; + return JS_TRUE; + } + x = js_ValueToNumber(cx, &vp[2]); + if (JSVAL_IS_NULL(vp[2])) + return JS_FALSE; + z = sin(x); + return js_NewNumberInRootedValue(cx, z, vp); +} + +static JSBool +math_sqrt(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble x, z; + + if (argc == 0) { + *vp = cx->runtime->NaNValue; + return JS_TRUE; + } + x = js_ValueToNumber(cx, &vp[2]); + if (JSVAL_IS_NULL(vp[2])) + return JS_FALSE; + z = sqrt(x); + return js_NewNumberInRootedValue(cx, z, vp); +} + +static JSBool +math_tan(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble x, z; + + if (argc == 0) { + *vp = cx->runtime->NaNValue; + return JS_TRUE; + } + x = js_ValueToNumber(cx, &vp[2]); + if (JSVAL_IS_NULL(vp[2])) + return JS_FALSE; + z = tan(x); + return js_NewNumberInRootedValue(cx, z, vp); +} + +#if JS_HAS_TOSOURCE +static JSBool +math_toSource(JSContext *cx, uintN argc, jsval *vp) +{ + *vp = ATOM_KEY(CLASS_ATOM(cx, Math)); + return JS_TRUE; +} +#endif + +#ifdef JS_TRACER + +#define MATH_BUILTIN_1(name) MATH_BUILTIN_CFUN_1(name, name) +#define MATH_BUILTIN_CFUN_1(name, cfun) \ + static jsdouble FASTCALL math_##name##_tn(jsdouble d) { return cfun(d); } \ + JS_DEFINE_TRCINFO_1(math_##name, \ + (1, (static, DOUBLE, math_##name##_tn, DOUBLE, 1, 1))) + +MATH_BUILTIN_CFUN_1(abs, fabs) +MATH_BUILTIN_1(atan) +MATH_BUILTIN_1(sin) +MATH_BUILTIN_1(cos) +MATH_BUILTIN_1(sqrt) +MATH_BUILTIN_1(tan) + +static jsdouble FASTCALL +math_acos_tn(jsdouble d) +{ +#if defined(SOLARIS) && defined(__GNUC__) + if (d < -1 || 1 < d) { + return js_NaN; + } +#endif + return acos(d); +} + +static jsdouble FASTCALL +math_asin_tn(jsdouble d) +{ +#if defined(SOLARIS) && defined(__GNUC__) + if (d < -1 || 1 < d) { + return js_NaN; + } +#endif + return asin(d); +} + +#ifdef _WIN32 + +static jsdouble FASTCALL +math_exp_tn(JSContext *cx, jsdouble d) +{ + if (!JSDOUBLE_IS_NaN(d)) { + if (d == js_PositiveInfinity) + return js_PositiveInfinity; + if (d == js_NegativeInfinity) + return 0.0; + } + return exp(d); +} + +JS_DEFINE_TRCINFO_1(math_exp, + (2, (static, DOUBLE, math_exp_tn, CONTEXT, DOUBLE, 1, 1))) + +#else + +MATH_BUILTIN_1(exp) + +#endif + +static jsdouble FASTCALL +math_log_tn(jsdouble d) +{ +#if defined(SOLARIS) && defined(__GNUC__) + if (d < 0) + return js_NaN; +#endif + return log(d); +} + +static jsdouble FASTCALL +math_max_tn(jsdouble d, jsdouble p) +{ + if (JSDOUBLE_IS_NaN(d) || JSDOUBLE_IS_NaN(p)) + return js_NaN; + + if (p == 0 && p == d) { + // Max prefers 0.0 to -0.0. + if (js_copysign(1.0, d) == -1) + return p; + return d; + } + return (p > d) ? p : d; +} + +static jsdouble FASTCALL +math_min_tn(jsdouble d, jsdouble p) +{ + if (JSDOUBLE_IS_NaN(d) || JSDOUBLE_IS_NaN(p)) + return js_NaN; + + if (p == 0 && p == d) { + // Min prefers -0.0 to 0.0. + if (js_copysign (1.0, p) == -1) + return p; + return d; + } + return (p < d) ? p : d; +} + +static jsdouble FASTCALL +math_pow_tn(jsdouble d, jsdouble p) +{ + if (!JSDOUBLE_IS_FINITE(p) && (d == 1.0 || d == -1.0)) + return js_NaN; + if (p == 0) + return 1.0; + return pow(d, p); +} + +static jsdouble FASTCALL +math_random_tn(JSContext *cx) +{ + return random_nextDouble(JS_THREAD_DATA(cx)); +} + +static jsdouble FASTCALL +math_round_tn(jsdouble x) +{ + return js_copysign(floor(x + 0.5), x); +} + +static jsdouble FASTCALL +math_ceil_tn(jsdouble x) +{ + return math_ceil_kernel(x); +} + +static jsdouble FASTCALL +math_floor_tn(jsdouble x) +{ + return floor(x); +} + +JS_DEFINE_TRCINFO_1(math_acos, + (1, (static, DOUBLE, math_acos_tn, DOUBLE, 1, 1))) +JS_DEFINE_TRCINFO_1(math_asin, + (1, (static, DOUBLE, math_asin_tn, DOUBLE, 1, 1))) +JS_DEFINE_TRCINFO_1(math_atan2, + (2, (static, DOUBLE, math_atan2_kernel, DOUBLE, DOUBLE, 1, 1))) +JS_DEFINE_TRCINFO_1(js_math_floor, + (1, (static, DOUBLE, math_floor_tn, DOUBLE, 1, 1))) +JS_DEFINE_TRCINFO_1(math_log, + (1, (static, DOUBLE, math_log_tn, DOUBLE, 1, 1))) +JS_DEFINE_TRCINFO_1(js_math_max, + (2, (static, DOUBLE, math_max_tn, DOUBLE, DOUBLE, 1, 1))) +JS_DEFINE_TRCINFO_1(js_math_min, + (2, (static, DOUBLE, math_min_tn, DOUBLE, DOUBLE, 1, 1))) +JS_DEFINE_TRCINFO_1(math_pow, + (2, (static, DOUBLE, math_pow_tn, DOUBLE, DOUBLE, 1, 1))) +JS_DEFINE_TRCINFO_1(math_random, + (1, (static, DOUBLE, math_random_tn, CONTEXT, 0, 0))) +JS_DEFINE_TRCINFO_1(js_math_round, + (1, (static, DOUBLE, math_round_tn, DOUBLE, 1, 1))) +JS_DEFINE_TRCINFO_1(js_math_ceil, + (1, (static, DOUBLE, math_ceil_tn, DOUBLE, 1, 1))) + +#endif /* JS_TRACER */ + +static JSFunctionSpec math_static_methods[] = { +#if JS_HAS_TOSOURCE + JS_FN(js_toSource_str, math_toSource, 0, 0), +#endif + JS_TN("abs", math_abs, 1, 0, &math_abs_trcinfo), + JS_TN("acos", math_acos, 1, 0, &math_acos_trcinfo), + JS_TN("asin", math_asin, 1, 0, &math_asin_trcinfo), + JS_TN("atan", math_atan, 1, 0, &math_atan_trcinfo), + JS_TN("atan2", math_atan2, 2, 0, &math_atan2_trcinfo), + JS_TN("ceil", js_math_ceil, 1, 0, &js_math_ceil_trcinfo), + JS_TN("cos", math_cos, 1, 0, &math_cos_trcinfo), + JS_TN("exp", math_exp, 1, 0, &math_exp_trcinfo), + JS_TN("floor", js_math_floor, 1, 0, &js_math_floor_trcinfo), + JS_TN("log", math_log, 1, 0, &math_log_trcinfo), + JS_TN("max", js_math_max, 2, 0, &js_math_max_trcinfo), + JS_TN("min", js_math_min, 2, 0, &js_math_min_trcinfo), + JS_TN("pow", math_pow, 2, 0, &math_pow_trcinfo), + JS_TN("random", math_random, 0, 0, &math_random_trcinfo), + JS_TN("round", js_math_round, 1, 0, &js_math_round_trcinfo), + JS_TN("sin", math_sin, 1, 0, &math_sin_trcinfo), + JS_TN("sqrt", math_sqrt, 1, 0, &math_sqrt_trcinfo), + JS_TN("tan", math_tan, 1, 0, &math_tan_trcinfo), + JS_FS_END +}; + +JSObject * +js_InitMathClass(JSContext *cx, JSObject *obj) +{ + JSObject *Math; + + Math = JS_NewObject(cx, &js_MathClass, NULL, obj); + if (!Math) + return NULL; + if (!JS_DefineProperty(cx, obj, js_Math_str, OBJECT_TO_JSVAL(Math), + JS_PropertyStub, JS_PropertyStub, 0)) { + return NULL; + } + + if (!JS_DefineFunctions(cx, Math, math_static_methods)) + return NULL; + if (!JS_DefineConstDoubles(cx, Math, math_constants)) + return NULL; + return Math; +} diff --git a/ape-server/deps/js/src/jsmath.h b/ape-server/deps/js/src/jsmath.h new file mode 100755 index 0000000..98a2bf1 --- /dev/null +++ b/ape-server/deps/js/src/jsmath.h @@ -0,0 +1,73 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsmath_h___ +#define jsmath_h___ +/* + * JS math functions. + */ + +JS_BEGIN_EXTERN_C + +extern JSClass js_MathClass; + +extern JSObject * +js_InitMathClass(JSContext *cx, JSObject *obj); + +extern void +js_InitRandom(JSThreadData *data); + +extern JSBool +js_math_ceil(JSContext *cx, uintN argc, jsval *vp); + +extern JSBool +js_math_floor(JSContext *cx, uintN argc, jsval *vp); + +extern JSBool +js_math_max(JSContext *cx, uintN argc, jsval *vp); + +extern JSBool +js_math_min(JSContext *cx, uintN argc, jsval *vp); + +extern JSBool +js_math_round(JSContext *cx, uintN argc, jsval *vp); + +JS_END_EXTERN_C + +#endif /* jsmath_h___ */ diff --git a/ape-server/deps/js/src/jsnum.cpp b/ape-server/deps/js/src/jsnum.cpp new file mode 100755 index 0000000..43dc687 --- /dev/null +++ b/ape-server/deps/js/src/jsnum.cpp @@ -0,0 +1,1404 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * IBM Corp. + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#define __STDC_LIMIT_MACROS + +/* + * JS number type and wrapper class. + */ +#ifdef XP_OS2 +#define _PC_53 PC_53 +#define _MCW_EM MCW_EM +#define _MCW_PC MCW_PC +#endif +#include +#include +#include +#include +#include +#include "jstypes.h" +#include "jsstdint.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jsapi.h" +#include "jsatom.h" +#include "jsbuiltins.h" +#include "jscntxt.h" +#include "jsversion.h" +#include "jsdtoa.h" +#include "jsgc.h" +#include "jsinterp.h" +#include "jsnum.h" +#include "jsobj.h" +#include "jsopcode.h" +#include "jsprf.h" +#include "jsscope.h" +#include "jsstr.h" +#include "jsstrinlines.h" +#include "jsvector.h" + + +#ifndef JS_HAVE_STDINT_H /* Native support is innocent until proven guilty. */ + +JS_STATIC_ASSERT(uint8_t(-1) == UINT8_MAX); +JS_STATIC_ASSERT(uint16_t(-1) == UINT16_MAX); +JS_STATIC_ASSERT(uint32_t(-1) == UINT32_MAX); +JS_STATIC_ASSERT(uint64_t(-1) == UINT64_MAX); + +JS_STATIC_ASSERT(INT8_MAX > INT8_MIN); +JS_STATIC_ASSERT(uint8_t(INT8_MAX) + uint8_t(1) == uint8_t(INT8_MIN)); +JS_STATIC_ASSERT(INT16_MAX > INT16_MIN); +JS_STATIC_ASSERT(uint16_t(INT16_MAX) + uint16_t(1) == uint16_t(INT16_MIN)); +JS_STATIC_ASSERT(INT32_MAX > INT32_MIN); +JS_STATIC_ASSERT(uint32_t(INT32_MAX) + uint32_t(1) == uint32_t(INT32_MIN)); +JS_STATIC_ASSERT(INT64_MAX > INT64_MIN); +JS_STATIC_ASSERT(uint64_t(INT64_MAX) + uint64_t(1) == uint64_t(INT64_MIN)); + +JS_STATIC_ASSERT(INTPTR_MAX > INTPTR_MIN); +JS_STATIC_ASSERT(uintptr_t(INTPTR_MAX) + uintptr_t(1) == uintptr_t(INTPTR_MIN)); +JS_STATIC_ASSERT(uintptr_t(-1) == UINTPTR_MAX); +JS_STATIC_ASSERT(size_t(-1) == SIZE_MAX); +JS_STATIC_ASSERT(PTRDIFF_MAX > PTRDIFF_MIN); +JS_STATIC_ASSERT(ptrdiff_t(PTRDIFF_MAX) == PTRDIFF_MAX); +JS_STATIC_ASSERT(ptrdiff_t(PTRDIFF_MIN) == PTRDIFF_MIN); +JS_STATIC_ASSERT(uintptr_t(PTRDIFF_MAX) + uintptr_t(1) == uintptr_t(PTRDIFF_MIN)); + +#endif /* JS_HAVE_STDINT_H */ + +static JSBool +num_isNaN(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble x; + + if (argc == 0) { + *vp = JSVAL_TRUE; + return JS_TRUE; + } + x = js_ValueToNumber(cx, &vp[2]); + if (JSVAL_IS_NULL(vp[2])) + return JS_FALSE; + *vp = BOOLEAN_TO_JSVAL(JSDOUBLE_IS_NaN(x)); + return JS_TRUE; +} + +static JSBool +num_isFinite(JSContext *cx, uintN argc, jsval *vp) +{ + jsdouble x; + + if (argc == 0) { + *vp = JSVAL_FALSE; + return JS_TRUE; + } + x = js_ValueToNumber(cx, &vp[2]); + if (JSVAL_IS_NULL(vp[2])) + return JS_FALSE; + *vp = BOOLEAN_TO_JSVAL(JSDOUBLE_IS_FINITE(x)); + return JS_TRUE; +} + +static JSBool +num_parseFloat(JSContext *cx, uintN argc, jsval *vp) +{ + JSString *str; + jsdouble d; + const jschar *bp, *end, *ep; + + if (argc == 0) { + *vp = cx->runtime->NaNValue; + return JS_TRUE; + } + str = js_ValueToString(cx, vp[2]); + if (!str) + return JS_FALSE; + str->getCharsAndEnd(bp, end); + if (!js_strtod(cx, bp, end, &ep, &d)) + return JS_FALSE; + if (ep == bp) { + *vp = cx->runtime->NaNValue; + return JS_TRUE; + } + return js_NewNumberInRootedValue(cx, d, vp); +} + +#ifdef JS_TRACER +static jsdouble FASTCALL +ParseFloat(JSContext* cx, JSString* str) +{ + const jschar* bp; + const jschar* end; + const jschar* ep; + jsdouble d; + + str->getCharsAndEnd(bp, end); + if (!js_strtod(cx, bp, end, &ep, &d) || ep == bp) + return js_NaN; + return d; +} +#endif + +/* See ECMA 15.1.2.2. */ +static JSBool +num_parseInt(JSContext *cx, uintN argc, jsval *vp) +{ + jsint radix; + JSString *str; + jsdouble d; + const jschar *bp, *end, *ep; + + if (argc == 0) { + *vp = cx->runtime->NaNValue; + return JS_TRUE; + } + if (argc > 1) { + radix = js_ValueToECMAInt32(cx, &vp[3]); + if (JSVAL_IS_NULL(vp[3])) + return JS_FALSE; + } else { + radix = 0; + } + if (radix != 0 && (radix < 2 || radix > 36)) { + *vp = cx->runtime->NaNValue; + return JS_TRUE; + } + + if (JSVAL_IS_INT(vp[2]) && (radix == 0 || radix == 10)) { + *vp = vp[2]; + return JS_TRUE; + } + + str = js_ValueToString(cx, vp[2]); + if (!str) + return JS_FALSE; + str->getCharsAndEnd(bp, end); + if (!js_strtointeger(cx, bp, end, &ep, radix, &d)) + return JS_FALSE; + if (ep == bp) { + *vp = cx->runtime->NaNValue; + return JS_TRUE; + } + return js_NewNumberInRootedValue(cx, d, vp); +} + +#ifdef JS_TRACER +static jsdouble FASTCALL +ParseInt(JSContext* cx, JSString* str) +{ + const jschar* bp; + const jschar* end; + const jschar* ep; + jsdouble d; + + str->getCharsAndEnd(bp, end); + if (!js_strtointeger(cx, bp, end, &ep, 0, &d) || ep == bp) + return js_NaN; + return d; +} + +static jsdouble FASTCALL +ParseIntDouble(jsdouble d) +{ + if (!JSDOUBLE_IS_FINITE(d)) + return js_NaN; + if (d > 0) + return floor(d); + if (d < 0) + return -floor(-d); + return 0; +} +#endif + +const char js_Infinity_str[] = "Infinity"; +const char js_NaN_str[] = "NaN"; +const char js_isNaN_str[] = "isNaN"; +const char js_isFinite_str[] = "isFinite"; +const char js_parseFloat_str[] = "parseFloat"; +const char js_parseInt_str[] = "parseInt"; + +#ifdef JS_TRACER + +JS_DEFINE_TRCINFO_2(num_parseInt, + (2, (static, DOUBLE, ParseInt, CONTEXT, STRING, 1, 1)), + (1, (static, DOUBLE, ParseIntDouble, DOUBLE, 1, 1))) + +JS_DEFINE_TRCINFO_1(num_parseFloat, + (2, (static, DOUBLE, ParseFloat, CONTEXT, STRING, 1, 1))) + +#endif /* JS_TRACER */ + +static JSFunctionSpec number_functions[] = { + JS_FN(js_isNaN_str, num_isNaN, 1,0), + JS_FN(js_isFinite_str, num_isFinite, 1,0), + JS_TN(js_parseFloat_str, num_parseFloat, 1,0, &num_parseFloat_trcinfo), + JS_TN(js_parseInt_str, num_parseInt, 2,0, &num_parseInt_trcinfo), + JS_FS_END +}; + +JSClass js_NumberClass = { + js_Number_str, + JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_HAS_CACHED_PROTO(JSProto_Number), + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +static JSBool +Number(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval v; + jsdouble d; + + if (argc != 0) { + d = js_ValueToNumber(cx, &argv[0]); + v = argv[0]; + if (JSVAL_IS_NULL(v)) + return JS_FALSE; + if (v != JSVAL_TRUE) { + JS_ASSERT(JSVAL_IS_INT(v) || JSVAL_IS_DOUBLE(v)); + } else { + if (!js_NewNumberInRootedValue(cx, d, &argv[0])) + return JS_FALSE; + v = argv[0]; + } + } else { + v = JSVAL_ZERO; + } + if (!JS_IsConstructing(cx)) + *rval = v; + else + obj->fslots[JSSLOT_PRIMITIVE_THIS] = v; + return true; +} + +#if JS_HAS_TOSOURCE +static JSBool +num_toSource(JSContext *cx, uintN argc, jsval *vp) +{ + jsval v; + jsdouble d; + char numBuf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr; + char buf[64]; + JSString *str; + + if (!js_GetPrimitiveThis(cx, vp, &js_NumberClass, &v)) + return JS_FALSE; + JS_ASSERT(JSVAL_IS_NUMBER(v)); + d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v); + numStr = JS_dtostr(numBuf, sizeof numBuf, DTOSTR_STANDARD, 0, d); + if (!numStr) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + JS_snprintf(buf, sizeof buf, "(new %s(%s))", js_NumberClass.name, numStr); + str = JS_NewStringCopyZ(cx, buf); + if (!str) + return JS_FALSE; + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; +} +#endif + +/* The buf must be big enough for MIN_INT to fit including '-' and '\0'. */ +static char * +IntToCString(jsint i, jsint base, char *buf, size_t bufSize) +{ + char *cp; + jsuint u; + + u = (i < 0) ? -i : i; + + cp = buf + bufSize; /* one past last buffer cell */ + *--cp = '\0'; /* null terminate the string to be */ + + /* + * Build the string from behind. We use multiply and subtraction + * instead of modulus because that's much faster. + */ + switch (base) { + case 10: + do { + jsuint newu = u / 10; + *--cp = (char)(u - newu * 10) + '0'; + u = newu; + } while (u != 0); + break; + case 16: + do { + jsuint newu = u / 16; + *--cp = "0123456789abcdef"[u - newu * 16]; + u = newu; + } while (u != 0); + break; + default: + JS_ASSERT(base >= 2 && base <= 36); + do { + jsuint newu = u / base; + *--cp = "0123456789abcdefghijklmnopqrstuvwxyz"[u - newu * base]; + u = newu; + } while (u != 0); + break; + } + if (i < 0) + *--cp = '-'; + + JS_ASSERT(cp >= buf); + return cp; +} + +static JSString * JS_FASTCALL +js_NumberToStringWithBase(JSContext *cx, jsdouble d, jsint base); + +static JSBool +num_toString(JSContext *cx, uintN argc, jsval *vp) +{ + jsval v; + jsdouble d; + jsint base; + JSString *str; + + if (!js_GetPrimitiveThis(cx, vp, &js_NumberClass, &v)) + return JS_FALSE; + JS_ASSERT(JSVAL_IS_NUMBER(v)); + d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v); + base = 10; + if (argc != 0 && !JSVAL_IS_VOID(vp[2])) { + base = js_ValueToECMAInt32(cx, &vp[2]); + if (JSVAL_IS_NULL(vp[2])) + return JS_FALSE; + if (base < 2 || base > 36) { + char numBuf[12]; + char *numStr = IntToCString(base, 10, numBuf, sizeof numBuf); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_RADIX, + numStr); + return JS_FALSE; + } + } + str = js_NumberToStringWithBase(cx, d, base); + if (!str) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +num_toLocaleString(JSContext *cx, uintN argc, jsval *vp) +{ + char thousandsLength, decimalLength; + const char *numGrouping, *tmpGroup; + JSRuntime *rt; + JSString *numStr, *str; + const char *num, *end, *tmpSrc; + char *buf, *tmpDest; + const char *nint; + int digits, size, remainder, nrepeat; + + /* + * Create the string, move back to bytes to make string twiddling + * a bit easier and so we can insert platform charset seperators. + */ + if (!num_toString(cx, 0, vp)) + return JS_FALSE; + JS_ASSERT(JSVAL_IS_STRING(*vp)); + numStr = JSVAL_TO_STRING(*vp); + num = js_GetStringBytes(cx, numStr); + if (!num) + return JS_FALSE; + + /* + * Find the first non-integer value, whether it be a letter as in + * 'Infinity', a decimal point, or an 'e' from exponential notation. + */ + nint = num; + if (*nint == '-') + nint++; + while (*nint >= '0' && *nint <= '9') + nint++; + digits = nint - num; + end = num + digits; + if (!digits) + return JS_TRUE; + + rt = cx->runtime; + thousandsLength = strlen(rt->thousandsSeparator); + decimalLength = strlen(rt->decimalSeparator); + + /* Figure out how long resulting string will be. */ + size = digits + (*nint ? strlen(nint + 1) + 1 : 0); + if (*nint == '.') + size += decimalLength; + + numGrouping = tmpGroup = rt->numGrouping; + remainder = digits; + if (*num == '-') + remainder--; + + while (*tmpGroup != CHAR_MAX && *tmpGroup != '\0') { + if (*tmpGroup >= remainder) + break; + size += thousandsLength; + remainder -= *tmpGroup; + tmpGroup++; + } + if (*tmpGroup == '\0' && *numGrouping != '\0') { + nrepeat = (remainder - 1) / tmpGroup[-1]; + size += thousandsLength * nrepeat; + remainder -= nrepeat * tmpGroup[-1]; + } else { + nrepeat = 0; + } + tmpGroup--; + + buf = (char *)cx->malloc(size + 1); + if (!buf) + return JS_FALSE; + + tmpDest = buf; + tmpSrc = num; + + while (*tmpSrc == '-' || remainder--) + *tmpDest++ = *tmpSrc++; + while (tmpSrc < end) { + strcpy(tmpDest, rt->thousandsSeparator); + tmpDest += thousandsLength; + memcpy(tmpDest, tmpSrc, *tmpGroup); + tmpDest += *tmpGroup; + tmpSrc += *tmpGroup; + if (--nrepeat < 0) + tmpGroup--; + } + + if (*nint == '.') { + strcpy(tmpDest, rt->decimalSeparator); + tmpDest += decimalLength; + strcpy(tmpDest, nint + 1); + } else { + strcpy(tmpDest, nint); + } + + if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode) + return cx->localeCallbacks->localeToUnicode(cx, buf, vp); + + str = JS_NewString(cx, buf, size); + if (!str) { + cx->free(buf); + return JS_FALSE; + } + + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +num_valueOf(JSContext *cx, uintN argc, jsval *vp) +{ + jsval v; + JSObject *obj; + + v = vp[1]; + if (JSVAL_IS_NUMBER(v)) { + *vp = v; + return JS_TRUE; + } + obj = JS_THIS_OBJECT(cx, vp); + if (!JS_InstanceOf(cx, obj, &js_NumberClass, vp + 2)) + return JS_FALSE; + *vp = obj->fslots[JSSLOT_PRIMITIVE_THIS]; + return JS_TRUE; +} + + +#define MAX_PRECISION 100 + +static JSBool +num_to(JSContext *cx, JSDToStrMode zeroArgMode, JSDToStrMode oneArgMode, + jsint precisionMin, jsint precisionMax, jsint precisionOffset, + uintN argc, jsval *vp) +{ + jsval v; + jsdouble d, precision; + JSString *str; + + /* Use MAX_PRECISION+1 because precisionOffset can be 1. */ + char buf[DTOSTR_VARIABLE_BUFFER_SIZE(MAX_PRECISION+1)]; + char *numStr; + + if (!js_GetPrimitiveThis(cx, vp, &js_NumberClass, &v)) + return JS_FALSE; + JS_ASSERT(JSVAL_IS_NUMBER(v)); + d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v); + + if (argc == 0) { + precision = 0.0; + oneArgMode = zeroArgMode; + } else { + precision = js_ValueToNumber(cx, &vp[2]); + if (JSVAL_IS_NULL(vp[2])) + return JS_FALSE; + precision = js_DoubleToInteger(precision); + if (precision < precisionMin || precision > precisionMax) { + numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, precision); + if (!numStr) + JS_ReportOutOfMemory(cx); + else + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PRECISION_RANGE, numStr); + return JS_FALSE; + } + } + + numStr = JS_dtostr(buf, sizeof buf, oneArgMode, (jsint)precision + precisionOffset, d); + if (!numStr) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + str = JS_NewStringCopyZ(cx, numStr); + if (!str) + return JS_FALSE; + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +/* + * In the following three implementations, we allow a larger range of precision + * than ECMA requires; this is permitted by ECMA-262. + */ +static JSBool +num_toFixed(JSContext *cx, uintN argc, jsval *vp) +{ + return num_to(cx, DTOSTR_FIXED, DTOSTR_FIXED, -20, MAX_PRECISION, 0, + argc, vp); +} + +static JSBool +num_toExponential(JSContext *cx, uintN argc, jsval *vp) +{ + return num_to(cx, DTOSTR_STANDARD_EXPONENTIAL, DTOSTR_EXPONENTIAL, 0, + MAX_PRECISION, 1, argc, vp); +} + +static JSBool +num_toPrecision(JSContext *cx, uintN argc, jsval *vp) +{ + if (argc == 0 || JSVAL_IS_VOID(vp[2])) + return num_toString(cx, 0, vp); + return num_to(cx, DTOSTR_STANDARD, DTOSTR_PRECISION, 1, MAX_PRECISION, 0, + argc, vp); +} + +#ifdef JS_TRACER + +JS_DEFINE_TRCINFO_2(num_toString, + (2, (extern, STRING_RETRY, js_NumberToString, CONTEXT, THIS_DOUBLE, 1, 1)), + (3, (static, STRING_RETRY, js_NumberToStringWithBase, CONTEXT, THIS_DOUBLE, INT32, 1, 1))) + +#endif /* JS_TRACER */ + +static JSFunctionSpec number_methods[] = { +#if JS_HAS_TOSOURCE + JS_FN(js_toSource_str, num_toSource, 0,JSFUN_THISP_NUMBER), +#endif + JS_TN(js_toString_str, num_toString, 1,JSFUN_THISP_NUMBER, &num_toString_trcinfo), + JS_FN(js_toLocaleString_str, num_toLocaleString, 0,JSFUN_THISP_NUMBER), + JS_FN(js_valueOf_str, num_valueOf, 0,JSFUN_THISP_NUMBER), + JS_FN(js_toJSON_str, num_valueOf, 0,JSFUN_THISP_NUMBER), + JS_FN("toFixed", num_toFixed, 1,JSFUN_THISP_NUMBER), + JS_FN("toExponential", num_toExponential, 1,JSFUN_THISP_NUMBER), + JS_FN("toPrecision", num_toPrecision, 1,JSFUN_THISP_NUMBER), + JS_FS_END +}; + +/* NB: Keep this in synch with number_constants[]. */ +enum nc_slot { + NC_NaN, + NC_POSITIVE_INFINITY, + NC_NEGATIVE_INFINITY, + NC_MAX_VALUE, + NC_MIN_VALUE, + NC_LIMIT +}; + +/* + * Some to most C compilers forbid spelling these at compile time, or barf + * if you try, so all but MAX_VALUE are set up by js_InitRuntimeNumberState + * using union jsdpun. + */ +static JSConstDoubleSpec number_constants[] = { + {0, js_NaN_str, 0,{0,0,0}}, + {0, "POSITIVE_INFINITY", 0,{0,0,0}}, + {0, "NEGATIVE_INFINITY", 0,{0,0,0}}, + {1.7976931348623157E+308, "MAX_VALUE", 0,{0,0,0}}, + {0, "MIN_VALUE", 0,{0,0,0}}, + {0,0,0,{0,0,0}} +}; + +jsdouble js_NaN; +jsdouble js_PositiveInfinity; +jsdouble js_NegativeInfinity; + +#if (defined __GNUC__ && defined __i386__) + +/* + * Set the exception mask to mask all exceptions and set the FPU precision + * to 53 bit mantissa (64 bit doubles). + */ +inline void FIX_FPU() { + short control; + asm("fstcw %0" : "=m" (control) : ); + control &= ~0x300; // Lower bits 8 and 9 (precision control). + control |= 0x2f3; // Raise bits 0-5 (exception masks) and 9 (64-bit precision). + asm("fldcw %0" : : "m" (control) ); +} + +#else + +#define FIX_FPU() ((void)0) + +#endif + +JSBool +js_InitRuntimeNumberState(JSContext *cx) +{ + JS_STATIC_ASSERT(JSVAL_NULL == jsval(0)); + + JSRuntime *rt = cx->runtime; + JS_ASSERT(JSVAL_IS_NULL(rt->NaNValue)); + + FIX_FPU(); + + jsdpun u; + u.s.hi = JSDOUBLE_HI32_EXPMASK | JSDOUBLE_HI32_MANTMASK; + u.s.lo = 0xffffffff; + number_constants[NC_NaN].dval = js_NaN = u.d; + if (!js_NewDoubleInRootedValue(cx, u.d, &rt->NaNValue)) + return false; + + u.s.hi = JSDOUBLE_HI32_EXPMASK; + u.s.lo = 0x00000000; + number_constants[NC_POSITIVE_INFINITY].dval = js_PositiveInfinity = u.d; + if (!js_NewDoubleInRootedValue(cx, u.d, &rt->positiveInfinityValue)) + return false; + + u.s.hi = JSDOUBLE_HI32_SIGNBIT | JSDOUBLE_HI32_EXPMASK; + u.s.lo = 0x00000000; + number_constants[NC_NEGATIVE_INFINITY].dval = js_NegativeInfinity = u.d; + if (!js_NewDoubleInRootedValue(cx, u.d, &rt->negativeInfinityValue)) + return false; + + u.s.hi = 0; + u.s.lo = 1; + number_constants[NC_MIN_VALUE].dval = u.d; + + struct lconv *locale = localeconv(); + rt->thousandsSeparator = + JS_strdup(cx, locale->thousands_sep ? locale->thousands_sep : "'"); + rt->decimalSeparator = + JS_strdup(cx, locale->decimal_point ? locale->decimal_point : "."); + rt->numGrouping = + JS_strdup(cx, locale->grouping ? locale->grouping : "\3\0"); + + return rt->thousandsSeparator && rt->decimalSeparator && rt->numGrouping; +} + +void +js_TraceRuntimeNumberState(JSTracer *trc) +{ + JSRuntime *rt = trc->context->runtime; + + if (!JSVAL_IS_NULL(rt->NaNValue)) + JS_CALL_DOUBLE_TRACER(trc, JSVAL_TO_DOUBLE(rt->NaNValue), "NaN"); + if (!JSVAL_IS_NULL(rt->positiveInfinityValue)) { + JS_CALL_DOUBLE_TRACER(trc, JSVAL_TO_DOUBLE(rt->positiveInfinityValue), + "+Infinity"); + } + if (!JSVAL_IS_NULL(rt->negativeInfinityValue)) { + JS_CALL_DOUBLE_TRACER(trc, JSVAL_TO_DOUBLE(rt->negativeInfinityValue), + "-Infinity"); + } +} + +void +js_FinishRuntimeNumberState(JSContext *cx) +{ + JSRuntime *rt = cx->runtime; + + rt->NaNValue = JSVAL_NULL; + rt->negativeInfinityValue = JSVAL_NULL; + rt->positiveInfinityValue = JSVAL_NULL; + + cx->free((void *) rt->thousandsSeparator); + cx->free((void *) rt->decimalSeparator); + cx->free((void *) rt->numGrouping); + rt->thousandsSeparator = rt->decimalSeparator = rt->numGrouping = NULL; +} + +JSObject * +js_InitNumberClass(JSContext *cx, JSObject *obj) +{ + JSObject *proto, *ctor; + JSRuntime *rt; + + /* XXX must do at least once per new thread, so do it per JSContext... */ + FIX_FPU(); + + if (!JS_DefineFunctions(cx, obj, number_functions)) + return NULL; + + proto = JS_InitClass(cx, obj, NULL, &js_NumberClass, Number, 1, + NULL, number_methods, NULL, NULL); + if (!proto || !(ctor = JS_GetConstructor(cx, proto))) + return NULL; + proto->fslots[JSSLOT_PRIMITIVE_THIS] = JSVAL_ZERO; + if (!JS_DefineConstDoubles(cx, ctor, number_constants)) + return NULL; + + /* ECMA 15.1.1.1 */ + rt = cx->runtime; + if (!JS_DefineProperty(cx, obj, js_NaN_str, rt->NaNValue, + NULL, NULL, JSPROP_PERMANENT)) { + return NULL; + } + + /* ECMA 15.1.1.2 */ + if (!JS_DefineProperty(cx, obj, js_Infinity_str, rt->positiveInfinityValue, + NULL, NULL, JSPROP_PERMANENT)) { + return NULL; + } + return proto; +} + +JSBool +js_NewNumberInRootedValue(JSContext *cx, jsdouble d, jsval *vp) +{ + jsint i; + + if (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i)) { + *vp = INT_TO_JSVAL(i); + return JS_TRUE; + } + return js_NewDoubleInRootedValue(cx, d, vp); +} + +JSBool +js_NewWeaklyRootedNumber(JSContext *cx, jsdouble d, jsval *rval) +{ + jsint i; + if (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i)) { + *rval = INT_TO_JSVAL(i); + return JS_TRUE; + } + return JS_NewDoubleValue(cx, d, rval); +} + +/* + * Convert a number to C string. The buf must be large enough to accommodate + * the result, including '-' and '\0', if base == 10 or d is an integer that + * fits in 32 bits. The caller must free the resulting pointer if it does not + * point into buf. + */ +static char * +NumberToCString(JSContext *cx, jsdouble d, jsint base, char *buf, size_t bufSize) +{ + jsint i; + char *numStr; + + JS_ASSERT(bufSize >= DTOSTR_STANDARD_BUFFER_SIZE); + if (JSDOUBLE_IS_INT(d, i)) { + numStr = IntToCString(i, base, buf, bufSize); + } else { + if (base == 10) + numStr = JS_dtostr(buf, bufSize, DTOSTR_STANDARD, 0, d); + else + numStr = JS_dtobasestr(base, d); + if (!numStr) { + JS_ReportOutOfMemory(cx); + return NULL; + } + } + return numStr; +} + +static JSString * JS_FASTCALL +js_NumberToStringWithBase(JSContext *cx, jsdouble d, jsint base) +{ + /* + * The longest possible result here that would need to fit in buf is + * (-0x80000000).toString(2), which has length 33. (This can produce + * longer results, but in those cases buf is not used; see comment at + * NumberToCString.) + */ + char buf[34]; + char *numStr; + JSString *s; + + /* + * Caller is responsible for error reporting. When called from trace, + * returning NULL here will cause us to fall of trace and then retry + * from the interpreter (which will report the error). + */ + if (base < 2 || base > 36) + return NULL; + + jsint i; + if (JSDOUBLE_IS_INT(d, i)) { + if (base == 10 && jsuint(i) < INT_STRING_LIMIT) + return JSString::intString(i); + if (jsuint(i) < jsuint(base)) { + if (i < 10) + return JSString::intString(i); + return JSString::unitString('a' + i - 10); + } + } + numStr = NumberToCString(cx, d, base, buf, sizeof buf); + if (!numStr) + return NULL; + s = JS_NewStringCopyZ(cx, numStr); + if (!(numStr >= buf && numStr < buf + sizeof buf)) + js_free(numStr); + return s; +} + +JSString * JS_FASTCALL +js_NumberToString(JSContext *cx, jsdouble d) +{ + return js_NumberToStringWithBase(cx, d, 10); +} + +JSBool JS_FASTCALL +js_NumberValueToCharBuffer(JSContext *cx, jsval v, JSCharBuffer &cb) +{ + /* Convert to C-string. */ + static const size_t arrSize = DTOSTR_STANDARD_BUFFER_SIZE; + char arr[arrSize]; + const char *cstr; + if (JSVAL_IS_INT(v)) { + cstr = IntToCString(JSVAL_TO_INT(v), 10, arr, arrSize); + } else { + JS_ASSERT(JSVAL_IS_DOUBLE(v)); + cstr = JS_dtostr(arr, arrSize, DTOSTR_STANDARD, 0, *JSVAL_TO_DOUBLE(v)); + } + if (!cstr) + return JS_FALSE; + + /* + * Inflate to jschar string. The input C-string characters are < 127, so + * even if jschars are UTF-8, all chars should map to one jschar. + */ + size_t cstrlen = strlen(cstr); + JS_ASSERT(cstrlen < arrSize); + size_t sizeBefore = cb.length(); + if (!cb.growBy(cstrlen)) + return JS_FALSE; + jschar *appendBegin = cb.begin() + sizeBefore; +#ifdef DEBUG + size_t oldcstrlen = cstrlen; + JSBool ok = +#endif + js_InflateStringToBuffer(cx, cstr, cstrlen, appendBegin, &cstrlen); + JS_ASSERT(ok && cstrlen == oldcstrlen); + return JS_TRUE; +} + +jsdouble +js_ValueToNumber(JSContext *cx, jsval *vp) +{ + jsval v; + JSString *str; + const jschar *bp, *end, *ep; + jsdouble d; + JSObject *obj; + + v = *vp; + for (;;) { + if (JSVAL_IS_INT(v)) + return (jsdouble) JSVAL_TO_INT(v); + if (JSVAL_IS_DOUBLE(v)) + return *JSVAL_TO_DOUBLE(v); + if (JSVAL_IS_STRING(v)) { + str = JSVAL_TO_STRING(v); + + /* + * Note that ECMA doesn't treat a string beginning with a '0' as + * an octal number here. This works because all such numbers will + * be interpreted as decimal by js_strtod and will never get + * passed to js_strtointeger (which would interpret them as + * octal). + */ + str->getCharsAndEnd(bp, end); + + /* ECMA doesn't allow signed hex numbers (bug 273467). */ + bp = js_SkipWhiteSpace(bp, end); + if (bp + 2 < end && (*bp == '-' || *bp == '+') && + bp[1] == '0' && (bp[2] == 'X' || bp[2] == 'x')) { + break; + } + + if ((!js_strtod(cx, bp, end, &ep, &d) || + js_SkipWhiteSpace(ep, end) != end) && + (!js_strtointeger(cx, bp, end, &ep, 0, &d) || + js_SkipWhiteSpace(ep, end) != end)) { + break; + } + + /* + * JSVAL_TRUE indicates that double jsval was never constructed + * for the result. + */ + *vp = JSVAL_TRUE; + return d; + } + if (JSVAL_IS_BOOLEAN(v)) { + if (JSVAL_TO_BOOLEAN(v)) { + *vp = JSVAL_ONE; + return 1.0; + } else { + *vp = JSVAL_ZERO; + return 0.0; + } + } + if (JSVAL_IS_NULL(v)) { + *vp = JSVAL_ZERO; + return 0.0; + } + if (JSVAL_IS_VOID(v)) + break; + + JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); + obj = JSVAL_TO_OBJECT(v); + + /* + * vp roots obj so we cannot use it as an extra root for + * obj->defaultValue result when calling the hook. + */ + JSAutoTempValueRooter tvr(cx, v); + if (!obj->defaultValue(cx, JSTYPE_NUMBER, tvr.addr())) + obj = NULL; + else + v = *vp = tvr.value(); + if (!obj) { + *vp = JSVAL_NULL; + return 0.0; + } + if (!JSVAL_IS_PRIMITIVE(v)) + break; + } + + *vp = cx->runtime->NaNValue; + return js_NaN; +} + +int32 +js_ValueToECMAInt32(JSContext *cx, jsval *vp) +{ + jsval v; + jsdouble d; + + v = *vp; + if (JSVAL_IS_INT(v)) + return JSVAL_TO_INT(v); + if (JSVAL_IS_DOUBLE(v)) { + d = *JSVAL_TO_DOUBLE(v); + *vp = JSVAL_TRUE; + } else { + d = js_ValueToNumber(cx, vp); + if (JSVAL_IS_NULL(*vp)) + return 0; + *vp = JSVAL_TRUE; + } + return js_DoubleToECMAInt32(d); +} + +uint32 +js_ValueToECMAUint32(JSContext *cx, jsval *vp) +{ + jsval v; + jsint i; + jsdouble d; + + v = *vp; + if (JSVAL_IS_INT(v)) { + i = JSVAL_TO_INT(v); + if (i < 0) + *vp = JSVAL_TRUE; + return (uint32) i; + } + if (JSVAL_IS_DOUBLE(v)) { + d = *JSVAL_TO_DOUBLE(v); + *vp = JSVAL_TRUE; + } else { + d = js_ValueToNumber(cx, vp); + if (JSVAL_IS_NULL(*vp)) + return 0; + *vp = JSVAL_TRUE; + } + return js_DoubleToECMAUint32(d); +} + +uint32 +js_DoubleToECMAUint32(jsdouble d) +{ + int32 i; + JSBool neg; + jsdouble two32; + + if (!JSDOUBLE_IS_FINITE(d)) + return 0; + + /* + * We check whether d fits int32, not uint32, as all but the ">>>" bit + * manipulation bytecode stores the result as int, not uint. When the + * result does not fit int jsval, it will be stored as a negative double. + */ + i = (int32) d; + if ((jsdouble) i == d) + return (int32)i; + + neg = (d < 0); + d = floor(neg ? -d : d); + d = neg ? -d : d; + + two32 = 4294967296.0; + d = fmod(d, two32); + + return (uint32) (d >= 0 ? d : d + two32); +} + +int32 +js_ValueToInt32(JSContext *cx, jsval *vp) +{ + jsval v; + jsdouble d; + + v = *vp; + if (JSVAL_IS_INT(v)) + return JSVAL_TO_INT(v); + d = js_ValueToNumber(cx, vp); + if (JSVAL_IS_NULL(*vp)) + return 0; + if (JSVAL_IS_INT(*vp)) + return JSVAL_TO_INT(*vp); + + *vp = JSVAL_TRUE; + if (JSDOUBLE_IS_NaN(d) || d <= -2147483649.0 || 2147483648.0 <= d) { + js_ReportValueError(cx, JSMSG_CANT_CONVERT, + JSDVG_SEARCH_STACK, v, NULL); + *vp = JSVAL_NULL; + return 0; + } + return (int32) floor(d + 0.5); /* Round to nearest */ +} + +uint16 +js_ValueToUint16(JSContext *cx, jsval *vp) +{ + jsdouble d; + uint16 u; + jsuint m; + JSBool neg; + + d = js_ValueToNumber(cx, vp); + if (JSVAL_IS_NULL(*vp)) + return 0; + + if (JSVAL_IS_INT(*vp)) { + u = (uint16) JSVAL_TO_INT(*vp); + } else if (d == 0 || !JSDOUBLE_IS_FINITE(d)) { + u = (uint16) 0; + } else { + u = (uint16) d; + if ((jsdouble) u != d) { + neg = (d < 0); + d = floor(neg ? -d : d); + d = neg ? -d : d; + m = JS_BIT(16); + d = fmod(d, (double) m); + if (d < 0) + d += m; + u = (uint16) d; + } + } + *vp = INT_TO_JSVAL(u); + return u; +} + +JSBool +js_strtod(JSContext *cx, const jschar *s, const jschar *send, + const jschar **ep, jsdouble *dp) +{ + const jschar *s1; + size_t length, i; + char cbuf[32]; + char *cstr, *istr, *estr; + JSBool negative; + jsdouble d; + + s1 = js_SkipWhiteSpace(s, send); + length = send - s1; + + /* Use cbuf to avoid malloc */ + if (length >= sizeof cbuf) { + cstr = (char *) cx->malloc(length + 1); + if (!cstr) + return JS_FALSE; + } else { + cstr = cbuf; + } + + for (i = 0; i != length; i++) { + if (s1[i] >> 8) + break; + cstr[i] = (char)s1[i]; + } + cstr[i] = 0; + + istr = cstr; + if ((negative = (*istr == '-')) != 0 || *istr == '+') + istr++; + if (*istr == 'I' && !strncmp(istr, js_Infinity_str, sizeof js_Infinity_str - 1)) { + d = negative ? js_NegativeInfinity : js_PositiveInfinity; + estr = istr + 8; + } else { + int err; + d = JS_strtod(cstr, &estr, &err); + if (d == HUGE_VAL) + d = js_PositiveInfinity; + else if (d == -HUGE_VAL) + d = js_NegativeInfinity; + } + + i = estr - cstr; + if (cstr != cbuf) + cx->free(cstr); + *ep = i ? s1 + i : s; + *dp = d; + return JS_TRUE; +} + +struct BinaryDigitReader +{ + uintN base; /* Base of number; must be a power of 2 */ + uintN digit; /* Current digit value in radix given by base */ + uintN digitMask; /* Mask to extract the next bit from digit */ + const jschar *digits; /* Pointer to the remaining digits */ + const jschar *end; /* Pointer to first non-digit */ +}; + +/* Return the next binary digit from the number or -1 if done */ +static intN GetNextBinaryDigit(struct BinaryDigitReader *bdr) +{ + intN bit; + + if (bdr->digitMask == 0) { + uintN c; + + if (bdr->digits == bdr->end) + return -1; + + c = *bdr->digits++; + if ('0' <= c && c <= '9') + bdr->digit = c - '0'; + else if ('a' <= c && c <= 'z') + bdr->digit = c - 'a' + 10; + else + bdr->digit = c - 'A' + 10; + bdr->digitMask = bdr->base >> 1; + } + bit = (bdr->digit & bdr->digitMask) != 0; + bdr->digitMask >>= 1; + return bit; +} + +JSBool +js_strtointeger(JSContext *cx, const jschar *s, const jschar *send, + const jschar **ep, jsint base, jsdouble *dp) +{ + const jschar *s1, *start; + JSBool negative; + jsdouble value; + + s1 = js_SkipWhiteSpace(s, send); + if (s1 == send) + goto no_digits; + if ((negative = (*s1 == '-')) != 0 || *s1 == '+') { + s1++; + if (s1 == send) + goto no_digits; + } + + if (base == 0) { + /* No base supplied, or some base that evaluated to 0. */ + if (*s1 == '0') { + /* It's either hex or octal; only increment char if str isn't '0' */ + if (s1 + 1 != send && (s1[1] == 'X' || s1[1] == 'x')) { + base = 16; + s1 += 2; + if (s1 == send) + goto no_digits; + } else { + base = 8; + } + } else { + base = 10; /* Default to decimal. */ + } + } else if (base == 16) { + /* If base is 16, ignore hex prefix. */ + if (*s1 == '0' && s1 + 1 != send && (s1[1] == 'X' || s1[1] == 'x')) { + s1 += 2; + if (s1 == send) + goto no_digits; + } + } + + /* + * Done with the preliminaries; find some prefix of the string that's + * a number in the given base. + */ + JS_ASSERT(s1 < send); + start = s1; + value = 0.0; + do { + uintN digit; + jschar c = *s1; + if ('0' <= c && c <= '9') + digit = c - '0'; + else if ('a' <= c && c <= 'z') + digit = c - 'a' + 10; + else if ('A' <= c && c <= 'Z') + digit = c - 'A' + 10; + else + break; + if (digit >= (uintN)base) + break; + value = value * base + digit; + } while (++s1 != send); + + if (value >= 9007199254740992.0) { + if (base == 10) { + /* + * If we're accumulating a decimal number and the number is >= + * 2^53, then the result from the repeated multiply-add above may + * be inaccurate. Call JS_strtod to get the correct answer. + */ + size_t i; + size_t length = s1 - start; + char *cstr = (char *) cx->malloc(length + 1); + char *estr; + int err=0; + + if (!cstr) + return JS_FALSE; + for (i = 0; i != length; i++) + cstr[i] = (char)start[i]; + cstr[length] = 0; + + value = JS_strtod(cstr, &estr, &err); + if (err == JS_DTOA_ENOMEM) { + JS_ReportOutOfMemory(cx); + cx->free(cstr); + return JS_FALSE; + } + if (err == JS_DTOA_ERANGE && value == HUGE_VAL) + value = js_PositiveInfinity; + cx->free(cstr); + } else if ((base & (base - 1)) == 0) { + /* + * The number may also be inaccurate for power-of-two bases. This + * happens if the addition in value * base + digit causes a round- + * down to an even least significant mantissa bit when the first + * dropped bit is a one. If any of the following digits in the + * number (which haven't been added in yet) are nonzero, then the + * correct action would have been to round up instead of down. An + * example occurs when reading the number 0x1000000000000081, which + * rounds to 0x1000000000000000 instead of 0x1000000000000100. + */ + struct BinaryDigitReader bdr; + intN bit, bit2; + intN j; + + bdr.base = base; + bdr.digit = 0; // shut GCC up + bdr.digitMask = 0; + bdr.digits = start; + bdr.end = s1; + value = 0.0; + + /* Skip leading zeros. */ + do { + bit = GetNextBinaryDigit(&bdr); + } while (bit == 0); + + if (bit == 1) { + /* Gather the 53 significant bits (including the leading 1) */ + value = 1.0; + for (j = 52; j; j--) { + bit = GetNextBinaryDigit(&bdr); + if (bit < 0) + goto done; + value = value*2 + bit; + } + /* bit2 is the 54th bit (the first dropped from the mantissa) */ + bit2 = GetNextBinaryDigit(&bdr); + if (bit2 >= 0) { + jsdouble factor = 2.0; + intN sticky = 0; /* sticky is 1 if any bit beyond the 54th is 1 */ + intN bit3; + + while ((bit3 = GetNextBinaryDigit(&bdr)) >= 0) { + sticky |= bit3; + factor *= 2; + } + value += bit2 & (bit | sticky); + value *= factor; + } + done:; + } + } + } + /* We don't worry about inaccurate numbers for any other base. */ + + if (s1 == start) { + no_digits: + *dp = 0.0; + *ep = s; + } else { + *dp = negative ? -value : value; + *ep = s1; + } + return JS_TRUE; +} diff --git a/ape-server/deps/js/src/jsnum.h b/ape-server/deps/js/src/jsnum.h new file mode 100755 index 0000000..f3e7a25 --- /dev/null +++ b/ape-server/deps/js/src/jsnum.h @@ -0,0 +1,429 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsnum_h___ +#define jsnum_h___ + +#include +#if defined(XP_WIN) || defined(XP_OS2) +#include +#endif +#ifdef SOLARIS +#include +#endif + +/* + * JS number (IEEE double) interface. + * + * JS numbers are optimistically stored in the top 31 bits of 32-bit integers, + * but floating point literals, results that overflow 31 bits, and division and + * modulus operands and results require a 64-bit IEEE double. These are GC'ed + * and pointed to by 32-bit jsvals on the stack and in object properties. + */ + +JS_BEGIN_EXTERN_C + +/* + * The ARM architecture supports two floating point models: VFP and FPA. When + * targetting FPA, doubles are mixed-endian on little endian ARMs (meaning that + * the high and low words are in big endian order). + */ +#if defined(__arm) || defined(__arm32__) || defined(__arm26__) || defined(__arm__) +#if !defined(__VFP_FP__) +#define FPU_IS_ARM_FPA +#endif +#endif + +typedef union jsdpun { + struct { +#if defined(IS_LITTLE_ENDIAN) && !defined(FPU_IS_ARM_FPA) + uint32 lo, hi; +#else + uint32 hi, lo; +#endif + } s; + uint64 u64; + jsdouble d; +} jsdpun; + +static inline int +JSDOUBLE_IS_NaN(jsdouble d) +{ +#ifdef WIN32 + return _isnan(d); +#else + return isnan(d); +#endif +} + +static inline int +JSDOUBLE_IS_FINITE(jsdouble d) +{ +#ifdef WIN32 + return _finite(d); +#else + return finite(d); +#endif +} + +static inline int +JSDOUBLE_IS_INFINITE(jsdouble d) +{ +#ifdef WIN32 + int c = _fpclass(d); + return c == _FPCLASS_NINF || c == _FPCLASS_PINF; +#elif defined(SOLARIS) + return !finite(d) && !isnan(d); +#else + return isinf(d); +#endif +} + +static inline int +JSDOUBLE_IS_NEGZERO(jsdouble d) +{ +#ifdef WIN32 + return (d == 0 && (_fpclass(d) & _FPCLASS_NZ)); +#elif defined(SOLARIS) + return (d == 0 && copysign(1, d) < 0); +#else + return (d == 0 && signbit(d)); +#endif +} + +#define JSDOUBLE_HI32_SIGNBIT 0x80000000 +#define JSDOUBLE_HI32_EXPMASK 0x7ff00000 +#define JSDOUBLE_HI32_MANTMASK 0x000fffff + +static inline int +JSDOUBLE_IS_INT(jsdouble d, jsint& i) +{ + if (JSDOUBLE_IS_NEGZERO(d)) + return false; + return d == (i = jsint(d)); +} + +static inline int +JSDOUBLE_IS_NEG(jsdouble d) +{ +#ifdef WIN32 + return JSDOUBLE_IS_NEGZERO(d) || d < 0; +#elif defined(SOLARIS) + return copysign(1, d) < 0; +#else + return signbit(d); +#endif +} + +static inline uint32 +JS_HASH_DOUBLE(jsdouble d) +{ + jsdpun u; + u.d = d; + return u.s.lo ^ u.s.hi; +} + +#if defined(XP_WIN) +#define JSDOUBLE_COMPARE(LVAL, OP, RVAL, IFNAN) \ + ((JSDOUBLE_IS_NaN(LVAL) || JSDOUBLE_IS_NaN(RVAL)) \ + ? (IFNAN) \ + : (LVAL) OP (RVAL)) +#else +#define JSDOUBLE_COMPARE(LVAL, OP, RVAL, IFNAN) ((LVAL) OP (RVAL)) +#endif + +extern jsdouble js_NaN; +extern jsdouble js_PositiveInfinity; +extern jsdouble js_NegativeInfinity; + +/* Initialize number constants and runtime state for the first context. */ +extern JSBool +js_InitRuntimeNumberState(JSContext *cx); + +extern void +js_TraceRuntimeNumberState(JSTracer *trc); + +extern void +js_FinishRuntimeNumberState(JSContext *cx); + +/* Initialize the Number class, returning its prototype object. */ +extern JSClass js_NumberClass; + +extern JSObject * +js_InitNumberClass(JSContext *cx, JSObject *obj); + +/* + * String constants for global function names, used in jsapi.c and jsnum.c. + */ +extern const char js_Infinity_str[]; +extern const char js_NaN_str[]; +extern const char js_isNaN_str[]; +extern const char js_isFinite_str[]; +extern const char js_parseFloat_str[]; +extern const char js_parseInt_str[]; + +/* + * vp must be a root. + */ +extern JSBool +js_NewNumberInRootedValue(JSContext *cx, jsdouble d, jsval *vp); + +/* + * Create a weakly rooted integer or double jsval as appropriate for the given + * jsdouble. + */ +extern JSBool +js_NewWeaklyRootedNumber(JSContext *cx, jsdouble d, jsval *vp); + +/* Convert a number to a GC'ed string. */ +extern JSString * JS_FASTCALL +js_NumberToString(JSContext *cx, jsdouble d); + +/* + * Convert an integer or double (contained in the given jsval) to a string and + * append to the given buffer. + */ +extern JSBool JS_FASTCALL +js_NumberValueToCharBuffer(JSContext *cx, jsval v, JSCharBuffer &cb); + +/* + * Convert a value to a number. On exit JSVAL_IS_NULL(*vp) iff there was an + * error. If on exit JSVAL_IS_NUMBER(*vp), then *vp holds the jsval that + * matches the result. Otherwise *vp is JSVAL_TRUE indicating that the jsval + * for result has to be created explicitly using, for example, the + * js_NewNumberInRootedValue function. + */ +extern jsdouble +js_ValueToNumber(JSContext *cx, jsval* vp); + +/* + * Convert a value to an int32 or uint32, according to the ECMA rules for + * ToInt32 and ToUint32. On exit JSVAL_IS_NULL(*vp) iff there was an error. If + * on exit JSVAL_IS_INT(*vp), then *vp holds the jsval matching the result. + * Otherwise *vp is JSVAL_TRUE indicating that the jsval for result has to be + * created explicitly using, for example, the js_NewNumberInRootedValue + * function. + */ +extern int32 +js_ValueToECMAInt32(JSContext *cx, jsval *vp); + +extern uint32 +js_ValueToECMAUint32(JSContext *cx, jsval *vp); + +/* + * Specialized ToInt32 and ToUint32 converters for doubles. + */ +/* + * From the ES3 spec, 9.5 + * 2. If Result(1) is NaN, +0, -0, +Inf, or -Inf, return +0. + * 3. Compute sign(Result(1)) * floor(abs(Result(1))). + * 4. Compute Result(3) modulo 2^32; that is, a finite integer value k of Number + * type with positive sign and less than 2^32 in magnitude such the mathematical + * difference of Result(3) and k is mathematically an integer multiple of 2^32. + * 5. If Result(4) is greater than or equal to 2^31, return Result(4)- 2^32, + * otherwise return Result(4). + */ +static inline int32 +js_DoubleToECMAInt32(jsdouble d) +{ +#ifdef __i386__ + jsdpun du, duh, two32; + uint32 di_h, u_tmp, expon, shift_amount; + int32 mask32; + + /* + * Algorithm Outline + * Step 1. If d is NaN, +/-Inf or |d|>=2^84 or |d|<1, then return 0 + * All of this is implemented based on an exponent comparison. + * Step 2. If |d|<2^31, then return (int)d + * The cast to integer (conversion in RZ mode) returns the correct result. + * Step 3. If |d|>=2^32, d:=fmod(d, 2^32) is taken -- but without a call + * Step 4. If |d|>=2^31, then the fractional bits are cleared before + * applying the correction by 2^32: d - sign(d)*2^32 + * Step 5. Return (int)d + */ + + du.d = d; + di_h = du.s.hi; + + u_tmp = (di_h & 0x7ff00000) - 0x3ff00000; + if (u_tmp >= (0x45300000-0x3ff00000)) { + // d is Nan, +/-Inf or +/-0, or |d|>=2^(32+52) or |d|<1, in which case result=0 + return 0; + } + + if (u_tmp < 0x01f00000) { + // |d|<2^31 + return int32_t(d); + } + + if (u_tmp > 0x01f00000) { + // |d|>=2^32 + expon = u_tmp >> 20; + shift_amount = expon - 21; + duh.u64 = du.u64; + mask32 = 0x80000000; + if (shift_amount < 32) { + mask32 >>= shift_amount; + duh.s.hi = du.s.hi & mask32; + duh.s.lo = 0; + } else { + mask32 >>= (shift_amount-32); + duh.s.hi = du.s.hi; + duh.s.lo = du.s.lo & mask32; + } + du.d -= duh.d; + } + + di_h = du.s.hi; + + // eliminate fractional bits + u_tmp = (di_h & 0x7ff00000); + if (u_tmp >= 0x41e00000) { + // |d|>=2^31 + expon = u_tmp >> 20; + shift_amount = expon - (0x3ff - 11); + mask32 = 0x80000000; + if (shift_amount < 32) { + mask32 >>= shift_amount; + du.s.hi &= mask32; + du.s.lo = 0; + } else { + mask32 >>= (shift_amount-32); + du.s.lo &= mask32; + } + two32.s.hi = 0x41f00000 ^ (du.s.hi & 0x80000000); + two32.s.lo = 0; + du.d -= two32.d; + } + + return int32(du.d); +#else + int32 i; + jsdouble two32, two31; + + if (!JSDOUBLE_IS_FINITE(d)) + return 0; + + i = (int32) d; + if ((jsdouble) i == d) + return i; + + two32 = 4294967296.0; + two31 = 2147483648.0; + d = fmod(d, two32); + d = (d >= 0) ? floor(d) : ceil(d) + two32; + return (int32) (d >= two31 ? d - two32 : d); +#endif +} + +extern uint32 +js_DoubleToECMAUint32(jsdouble d); + +/* + * Convert a value to a number, then to an int32 if it fits by rounding to + * nearest; but failing with an error report if the double is out of range + * or unordered. On exit JSVAL_IS_NULL(*vp) iff there was an error. If on exit + * JSVAL_IS_INT(*vp), then *vp holds the jsval matching the result. Otherwise + * *vp is JSVAL_TRUE indicating that the jsval for result has to be created + * explicitly using, for example, the js_NewNumberInRootedValue function. + */ +extern int32 +js_ValueToInt32(JSContext *cx, jsval *vp); + +/* + * Convert a value to a number, then to a uint16 according to the ECMA rules + * for ToUint16. On exit JSVAL_IS_NULL(*vp) iff there was an error, otherwise + * vp is jsval matching the result. + */ +extern uint16 +js_ValueToUint16(JSContext *cx, jsval *vp); + +/* + * Convert a jsdouble to an integral number, stored in a jsdouble. + * If d is NaN, return 0. If d is an infinity, return it without conversion. + */ +static inline jsdouble +js_DoubleToInteger(jsdouble d) +{ + if (d == 0) + return d; + + if (!JSDOUBLE_IS_FINITE(d)) { + if (JSDOUBLE_IS_NaN(d)) + return 0; + return d; + } + + JSBool neg = (d < 0); + d = floor(neg ? -d : d); + + return neg ? -d : d; +} + +/* + * Similar to strtod except that it replaces overflows with infinities of the + * correct sign, and underflows with zeros of the correct sign. Guaranteed to + * return the closest double number to the given input in dp. + * + * Also allows inputs of the form [+|-]Infinity, which produce an infinity of + * the appropriate sign. The case of the "Infinity" string must match exactly. + * If the string does not contain a number, set *ep to s and return 0.0 in dp. + * Return false if out of memory. + */ +extern JSBool +js_strtod(JSContext *cx, const jschar *s, const jschar *send, + const jschar **ep, jsdouble *dp); + +/* + * Similar to strtol except that it handles integers of arbitrary size. + * Guaranteed to return the closest double number to the given input when radix + * is 10 or a power of 2. Callers may see round-off errors for very large + * numbers of a different radix than 10 or a power of 2. + * + * If the string does not contain a number, set *ep to s and return 0.0 in dp. + * Return false if out of memory. + */ +extern JSBool +js_strtointeger(JSContext *cx, const jschar *s, const jschar *send, + const jschar **ep, jsint radix, jsdouble *dp); + +JS_END_EXTERN_C + +#endif /* jsnum_h___ */ diff --git a/ape-server/deps/js/src/jsobj.cpp b/ape-server/deps/js/src/jsobj.cpp new file mode 100755 index 0000000..c8ac745 --- /dev/null +++ b/ape-server/deps/js/src/jsobj.cpp @@ -0,0 +1,6453 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=79: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * JS object implementation. + */ +#define __STDC_LIMIT_MACROS + +#include +#include +#include "jstypes.h" +#include "jsstdint.h" +#include "jsarena.h" /* Added by JSIFY */ +#include "jsbit.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jshash.h" /* Added by JSIFY */ +#include "jsdhash.h" +#include "jsprf.h" +#include "jsapi.h" +#include "jsarray.h" +#include "jsatom.h" +#include "jsbool.h" +#include "jsbuiltins.h" +#include "jscntxt.h" +#include "jsversion.h" +#include "jsemit.h" +#include "jsfun.h" +#include "jsgc.h" +#include "jsinterp.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsobj.h" +#include "jsopcode.h" +#include "jsparse.h" +#include "jsscope.h" +#include "jsscript.h" +#include "jsstaticcheck.h" +#include "jsstdint.h" +#include "jsstr.h" +#include "jstracer.h" +#include "jsdbgapi.h" + +#include "jsscopeinlines.h" +#include "jsscriptinlines.h" + +#if JS_HAS_GENERATORS +#include "jsiter.h" +#endif + +#if JS_HAS_XML_SUPPORT +#include "jsxml.h" +#endif + +#if JS_HAS_XDR +#include "jsxdrapi.h" +#endif + +#ifdef INCLUDE_MOZILLA_DTRACE +#include "jsdtracef.h" +#endif + +#include "jsatominlines.h" +#include "jsobjinlines.h" +#include "jsscriptinlines.h" + +#include "jsautooplen.h" + +#ifdef JS_THREADSAFE +#define NATIVE_DROP_PROPERTY js_DropProperty + +extern void +js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop); +#else +#define NATIVE_DROP_PROPERTY NULL +#endif + +JS_FRIEND_DATA(JSObjectOps) js_ObjectOps = { + NULL, + js_LookupProperty, js_DefineProperty, + js_GetProperty, js_SetProperty, + js_GetAttributes, js_SetAttributes, + js_DeleteProperty, js_DefaultValue, + js_Enumerate, js_CheckAccess, + NULL, NATIVE_DROP_PROPERTY, + js_Call, js_Construct, + js_HasInstance, js_TraceObject, + js_Clear +}; + +JSClass js_ObjectClass = { + js_Object_str, + JSCLASS_HAS_CACHED_PROTO(JSProto_Object), + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +#if JS_HAS_OBJ_PROTO_PROP + +static JSBool +obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + +static JSBool +obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + +static JSBool +obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + +static JSPropertySpec object_props[] = { + /* These two must come first; see object_props[slot].name usage below. */ + {js_proto_str, JSSLOT_PROTO, JSPROP_PERMANENT|JSPROP_SHARED, + obj_getSlot, obj_setSlot}, + {js_parent_str,JSSLOT_PARENT,JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED, + obj_getSlot, obj_setSlot}, + {js_count_str, 0, JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED, + obj_getCount, NULL}, + {0,0,0,0,0} +}; + +/* NB: JSSLOT_PROTO and JSSLOT_PARENT are already indexes into object_props. */ +#define JSSLOT_COUNT 2 + +static JSBool +ReportStrictSlot(JSContext *cx, uint32 slot) +{ + if (slot == JSSLOT_PROTO) + return JS_TRUE; + return JS_ReportErrorFlagsAndNumber(cx, + JSREPORT_WARNING | JSREPORT_STRICT, + js_GetErrorMessage, NULL, + JSMSG_DEPRECATED_USAGE, + object_props[slot].name); +} + +static JSBool +obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + uint32 slot; + jsid propid; + JSAccessMode mode; + uintN attrs; + JSObject *pobj; + JSClass *clasp; + + slot = (uint32) JSVAL_TO_INT(id); + if (id == INT_TO_JSVAL(JSSLOT_PROTO)) { + propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom); + mode = JSACC_PROTO; + } else { + propid = ATOM_TO_JSID(cx->runtime->atomState.parentAtom); + mode = JSACC_PARENT; + } + + /* Let obj->checkAccess get the slot's value, based on the access mode. */ + if (!obj->checkAccess(cx, propid, mode, vp, &attrs)) + return JS_FALSE; + + pobj = JSVAL_TO_OBJECT(*vp); + if (pobj) { + clasp = OBJ_GET_CLASS(cx, pobj); + if (clasp == &js_CallClass || clasp == &js_BlockClass) { + /* Censor activations and lexical scopes per ECMA-262. */ + *vp = JSVAL_NULL; + } else { + /* + * DeclEnv only exists as a parent for a Call object which we + * censor. So it cannot escape to scripts. + */ + JS_ASSERT(clasp != &js_DeclEnvClass); + if (pobj->map->ops->thisObject) { + pobj = pobj->map->ops->thisObject(cx, pobj); + if (!pobj) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(pobj); + } + } + } + return JS_TRUE; +} + +static JSBool +obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSObject *pobj; + uint32 slot; + jsid propid; + uintN attrs; + + if (!JSVAL_IS_OBJECT(*vp)) + return JS_TRUE; + pobj = JSVAL_TO_OBJECT(*vp); + + if (pobj) { + /* + * Innerize pobj here to avoid sticking unwanted properties on the + * outer object. This ensures that any with statements only grant + * access to the inner object. + */ + OBJ_TO_INNER_OBJECT(cx, pobj); + if (!pobj) + return JS_FALSE; + } + slot = (uint32) JSVAL_TO_INT(id); + if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, slot)) + return JS_FALSE; + + /* __parent__ is readonly and permanent, only __proto__ may be set. */ + propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom); + if (!obj->checkAccess(cx, propid, (JSAccessMode)(JSACC_PROTO|JSACC_WRITE), vp, &attrs)) + return JS_FALSE; + + return js_SetProtoOrParent(cx, obj, slot, pobj, JS_TRUE); +} + +static JSBool +obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + jsval iter_state; + jsid num_properties; + JSBool ok; + + if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, JSSLOT_COUNT)) + return JS_FALSE; + + iter_state = JSVAL_NULL; + JSAutoEnumStateRooter tvr(cx, obj, &iter_state); + + /* Get the number of properties to enumerate. */ + ok = obj->enumerate(cx, JSENUMERATE_INIT, &iter_state, &num_properties); + if (!ok) + goto out; + + if (!JSVAL_IS_INT(num_properties)) { + JS_ASSERT(0); + *vp = JSVAL_ZERO; + goto out; + } + *vp = num_properties; + +out: + if (!JSVAL_IS_NULL(iter_state)) + ok &= obj->enumerate(cx, JSENUMERATE_DESTROY, &iter_state, 0); + return ok; +} + +#else /* !JS_HAS_OBJ_PROTO_PROP */ + +#define object_props NULL + +#endif /* !JS_HAS_OBJ_PROTO_PROP */ + +JSBool +js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj, + JSBool checkForCycles) +{ + JS_ASSERT(slot == JSSLOT_PARENT || slot == JSSLOT_PROTO); + JS_ASSERT_IF(!checkForCycles, obj != pobj); + + if (slot == JSSLOT_PROTO) { + if (OBJ_IS_NATIVE(obj)) { + JS_LOCK_OBJ(cx, obj); + bool ok = !!js_GetMutableScope(cx, obj); + JS_UNLOCK_OBJ(cx, obj); + if (!ok) + return JS_FALSE; + } + + /* + * Regenerate property cache shape ids for all of the scopes along the + * old prototype chain to invalidate their property cache entries, in + * case any entries were filled by looking up through obj. + */ + JSObject *oldproto = obj; + while (oldproto && OBJ_IS_NATIVE(oldproto)) { + JS_LOCK_OBJ(cx, oldproto); + JSScope *scope = OBJ_SCOPE(oldproto); + scope->protoShapeChange(cx); + JSObject *tmp = STOBJ_GET_PROTO(oldproto); + JS_UNLOCK_OBJ(cx, oldproto); + oldproto = tmp; + } + } + + if (!pobj || !checkForCycles) { + if (slot == JSSLOT_PROTO) + obj->setProto(pobj); + else + obj->setParent(pobj); + } else { + /* + * Use the GC machinery to serialize access to all objects on the + * prototype or parent chain. + */ + JSSetSlotRequest ssr; + ssr.obj = obj; + ssr.pobj = pobj; + ssr.slot = (uint16) slot; + ssr.cycle = false; + + JSRuntime *rt = cx->runtime; + JS_LOCK_GC(rt); + ssr.next = rt->setSlotRequests; + rt->setSlotRequests = &ssr; + for (;;) { + js_GC(cx, GC_SET_SLOT_REQUEST); + JS_UNLOCK_GC(rt); + if (!rt->setSlotRequests) + break; + JS_LOCK_GC(rt); + } + + if (ssr.cycle) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_CYCLIC_VALUE, +#if JS_HAS_OBJ_PROTO_PROP + object_props[slot].name +#else + (slot == JSSLOT_PROTO) ? js_proto_str + : js_parent_str +#endif + ); + return JS_FALSE; + } + } + return JS_TRUE; +} + +static JSHashNumber +js_hash_object(const void *key) +{ + return (JSHashNumber)JS_PTR_TO_UINT32(key) >> JSVAL_TAGBITS; +} + +static JSHashEntry * +MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap) +{ + JSSharpObjectMap *map; + JSHashTable *table; + JSHashNumber hash; + JSHashEntry **hep, *he; + jsatomid sharpid; + JSIdArray *ida; + JSBool ok; + jsint i, length; + jsid id; +#if JS_HAS_GETTER_SETTER + JSObject *obj2; + JSProperty *prop; + uintN attrs; +#endif + jsval val; + + JS_CHECK_RECURSION(cx, return NULL); + + map = &cx->sharpObjectMap; + JS_ASSERT(map->depth >= 1); + table = map->table; + hash = js_hash_object(obj); + hep = JS_HashTableRawLookup(table, hash, obj); + he = *hep; + if (!he) { + sharpid = 0; + he = JS_HashTableRawAdd(table, hep, hash, obj, + JS_UINT32_TO_PTR(sharpid)); + if (!he) { + JS_ReportOutOfMemory(cx); + return NULL; + } + + ida = JS_Enumerate(cx, obj); + if (!ida) + return NULL; + + ok = JS_TRUE; + for (i = 0, length = ida->length; i < length; i++) { + id = ida->vector[i]; +#if JS_HAS_GETTER_SETTER + ok = obj->lookupProperty(cx, id, &obj2, &prop); + if (!ok) + break; + if (!prop) + continue; + ok = obj2->getAttributes(cx, id, prop, &attrs); + if (ok) { + if (OBJ_IS_NATIVE(obj2) && + (attrs & (JSPROP_GETTER | JSPROP_SETTER))) { + JSScopeProperty *sprop = (JSScopeProperty *) prop; + val = JSVAL_NULL; + if (attrs & JSPROP_GETTER) + val = sprop->getterValue(); + if (attrs & JSPROP_SETTER) { + if (val != JSVAL_NULL) { + /* Mark the getter, then set val to setter. */ + ok = (MarkSharpObjects(cx, JSVAL_TO_OBJECT(val), + NULL) + != NULL); + } + val = sprop->setterValue(); + } + } else { + ok = obj->getProperty(cx, id, &val); + } + } + obj2->dropProperty(cx, prop); +#else + ok = obj->getProperty(cx, id, &val); +#endif + if (!ok) + break; + if (!JSVAL_IS_PRIMITIVE(val) && + !MarkSharpObjects(cx, JSVAL_TO_OBJECT(val), NULL)) { + ok = JS_FALSE; + break; + } + } + if (!ok || !idap) + JS_DestroyIdArray(cx, ida); + if (!ok) + return NULL; + } else { + sharpid = JS_PTR_TO_UINT32(he->value); + if (sharpid == 0) { + sharpid = ++map->sharpgen << SHARP_ID_SHIFT; + he->value = JS_UINT32_TO_PTR(sharpid); + } + ida = NULL; + } + if (idap) + *idap = ida; + return he; +} + +JSHashEntry * +js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, + jschar **sp) +{ + JSSharpObjectMap *map; + JSHashTable *table; + JSIdArray *ida; + JSHashNumber hash; + JSHashEntry *he, **hep; + jsatomid sharpid; + char buf[20]; + size_t len; + + if (!JS_CHECK_OPERATION_LIMIT(cx)) + return NULL; + + /* Set to null in case we return an early error. */ + *sp = NULL; + map = &cx->sharpObjectMap; + table = map->table; + if (!table) { + table = JS_NewHashTable(8, js_hash_object, JS_CompareValues, + JS_CompareValues, NULL, NULL); + if (!table) { + JS_ReportOutOfMemory(cx); + return NULL; + } + map->table = table; + JS_KEEP_ATOMS(cx->runtime); + } + + /* From this point the control must flow either through out: or bad:. */ + ida = NULL; + if (map->depth == 0) { + /* + * Although MarkSharpObjects tries to avoid invoking getters, + * it ends up doing so anyway under some circumstances; for + * example, if a wrapped object has getters, the wrapper will + * prevent MarkSharpObjects from recognizing them as such. + * This could lead to js_LeaveSharpObject being called while + * MarkSharpObjects is still working. + * + * Increment map->depth while we call MarkSharpObjects, to + * ensure that such a call doesn't free the hash table we're + * still using. + */ + ++map->depth; + he = MarkSharpObjects(cx, obj, &ida); + --map->depth; + if (!he) + goto bad; + JS_ASSERT((JS_PTR_TO_UINT32(he->value) & SHARP_BIT) == 0); + if (!idap) { + JS_DestroyIdArray(cx, ida); + ida = NULL; + } + } else { + hash = js_hash_object(obj); + hep = JS_HashTableRawLookup(table, hash, obj); + he = *hep; + + /* + * It's possible that the value of a property has changed from the + * first time the object's properties are traversed (when the property + * ids are entered into the hash table) to the second (when they are + * converted to strings), i.e., the JSObject::getProperty() call is not + * idempotent. + */ + if (!he) { + he = JS_HashTableRawAdd(table, hep, hash, obj, NULL); + if (!he) { + JS_ReportOutOfMemory(cx); + goto bad; + } + sharpid = 0; + goto out; + } + } + + sharpid = JS_PTR_TO_UINT32(he->value); + if (sharpid != 0) { + len = JS_snprintf(buf, sizeof buf, "#%u%c", + sharpid >> SHARP_ID_SHIFT, + (sharpid & SHARP_BIT) ? '#' : '='); + *sp = js_InflateString(cx, buf, &len); + if (!*sp) { + if (ida) + JS_DestroyIdArray(cx, ida); + goto bad; + } + } + +out: + JS_ASSERT(he); + if ((sharpid & SHARP_BIT) == 0) { + if (idap && !ida) { + ida = JS_Enumerate(cx, obj); + if (!ida) { + if (*sp) { + cx->free(*sp); + *sp = NULL; + } + goto bad; + } + } + map->depth++; + } + + if (idap) + *idap = ida; + return he; + +bad: + /* Clean up the sharpObjectMap table on outermost error. */ + if (map->depth == 0) { + JS_UNKEEP_ATOMS(cx->runtime); + map->sharpgen = 0; + JS_HashTableDestroy(map->table); + map->table = NULL; + } + return NULL; +} + +void +js_LeaveSharpObject(JSContext *cx, JSIdArray **idap) +{ + JSSharpObjectMap *map; + JSIdArray *ida; + + map = &cx->sharpObjectMap; + JS_ASSERT(map->depth > 0); + if (--map->depth == 0) { + JS_UNKEEP_ATOMS(cx->runtime); + map->sharpgen = 0; + JS_HashTableDestroy(map->table); + map->table = NULL; + } + if (idap) { + ida = *idap; + if (ida) { + JS_DestroyIdArray(cx, ida); + *idap = NULL; + } + } +} + +static intN +gc_sharp_table_entry_marker(JSHashEntry *he, intN i, void *arg) +{ + JS_CALL_OBJECT_TRACER((JSTracer *)arg, (JSObject *)he->key, + "sharp table entry"); + return JS_DHASH_NEXT; +} + +void +js_TraceSharpMap(JSTracer *trc, JSSharpObjectMap *map) +{ + JS_ASSERT(map->depth > 0); + JS_ASSERT(map->table); + + /* + * During recursive calls to MarkSharpObjects a non-native object or + * object with a custom getProperty method can potentially return an + * unrooted value or even cut from the object graph an argument of one of + * MarkSharpObjects recursive invocations. So we must protect map->table + * entries against GC. + * + * We can not simply use JSTempValueRooter to mark the obj argument of + * MarkSharpObjects during recursion as we have to protect *all* entries + * in JSSharpObjectMap including those that contains otherwise unreachable + * objects just allocated through custom getProperty. Otherwise newer + * allocations can re-use the address of an object stored in the hashtable + * confusing js_EnterSharpObject. So to address the problem we simply + * mark all objects from map->table. + * + * An alternative "proper" solution is to use JSTempValueRooter in + * MarkSharpObjects with code to remove during finalization entries + * with otherwise unreachable objects. But this is way too complex + * to justify spending efforts. + */ + JS_HashTableEnumerateEntries(map->table, gc_sharp_table_entry_marker, trc); +} + +#if JS_HAS_TOSOURCE +static JSBool +obj_toSource(JSContext *cx, uintN argc, jsval *vp) +{ + JSBool ok, outermost; + JSObject *obj; + JSHashEntry *he; + JSIdArray *ida; + jschar *chars, *ochars, *vsharp; + const jschar *idstrchars, *vchars; + size_t nchars, idstrlength, gsoplength, vlength, vsharplength, curlen; + const char *comma; + jsint i, j, length, valcnt; + jsid id; +#if JS_HAS_GETTER_SETTER + JSObject *obj2; + JSProperty *prop; + uintN attrs; +#endif + jsval *val; + jsval localroot[4] = {JSVAL_NULL, JSVAL_NULL, JSVAL_NULL, JSVAL_NULL}; + JSTempValueRooter tvr; + JSString *gsopold[2]; + JSString *gsop[2]; + JSString *idstr, *valstr, *str; + + JS_CHECK_RECURSION(cx, return JS_FALSE); + + MUST_FLOW_THROUGH("out"); + JS_PUSH_TEMP_ROOT(cx, 4, localroot, &tvr); + + /* If outermost, we need parentheses to be an expression, not a block. */ + outermost = (cx->sharpObjectMap.depth == 0); + obj = JS_THIS_OBJECT(cx, vp); + if (!obj || !(he = js_EnterSharpObject(cx, obj, &ida, &chars))) { + ok = JS_FALSE; + goto out; + } + if (IS_SHARP(he)) { + /* + * We didn't enter -- obj is already "sharp", meaning we've visited it + * already in our depth first search, and therefore chars contains a + * string of the form "#n#". + */ + JS_ASSERT(!ida); +#if JS_HAS_SHARP_VARS + nchars = js_strlen(chars); +#else + chars[0] = '{'; + chars[1] = '}'; + chars[2] = 0; + nchars = 2; +#endif + goto make_string; + } + JS_ASSERT(ida); + ok = JS_TRUE; + + if (!chars) { + /* If outermost, allocate 4 + 1 for "({})" and the terminator. */ + chars = (jschar *) js_malloc(((outermost ? 4 : 2) + 1) * sizeof(jschar)); + nchars = 0; + if (!chars) + goto error; + if (outermost) + chars[nchars++] = '('; + } else { + /* js_EnterSharpObject returned a string of the form "#n=" in chars. */ + MAKE_SHARP(he); + nchars = js_strlen(chars); + chars = (jschar *) + js_realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar)); + if (!chars) { + js_free(ochars); + goto error; + } + if (outermost) { + /* + * No need for parentheses around the whole shebang, because #n= + * unambiguously begins an object initializer, and never a block + * statement. + */ + outermost = JS_FALSE; + } + } + + chars[nchars++] = '{'; + + comma = NULL; + + /* + * We have four local roots for cooked and raw value GC safety. Hoist the + * "localroot + 2" out of the loop using the val local, which refers to + * the raw (unconverted, "uncooked") values. + */ + val = localroot + 2; + + for (i = 0, length = ida->length; i < length; i++) { + JSBool idIsLexicalIdentifier, needOldStyleGetterSetter; + + /* Get strings for id and value and GC-root them via vp. */ + id = ida->vector[i]; + +#if JS_HAS_GETTER_SETTER + ok = obj->lookupProperty(cx, id, &obj2, &prop); + if (!ok) + goto error; +#endif + + /* + * Convert id to a jsval and then to a string. Decide early whether we + * prefer get/set or old getter/setter syntax. + */ + idstr = js_ValueToString(cx, ID_TO_VALUE(id)); + if (!idstr) { + ok = JS_FALSE; + obj2->dropProperty(cx, prop); + goto error; + } + *vp = STRING_TO_JSVAL(idstr); /* local root */ + idIsLexicalIdentifier = js_IsIdentifier(idstr); + needOldStyleGetterSetter = + !idIsLexicalIdentifier || + js_CheckKeyword(idstr->chars(), idstr->length()) != TOK_EOF; + +#if JS_HAS_GETTER_SETTER + + valcnt = 0; + if (prop) { + ok = obj2->getAttributes(cx, id, prop, &attrs); + if (!ok) { + obj2->dropProperty(cx, prop); + goto error; + } + if (OBJ_IS_NATIVE(obj2) && + (attrs & (JSPROP_GETTER | JSPROP_SETTER))) { + JSScopeProperty *sprop = (JSScopeProperty *) prop; + if (attrs & JSPROP_GETTER) { + val[valcnt] = sprop->getterValue(); + gsopold[valcnt] = + ATOM_TO_STRING(cx->runtime->atomState.getterAtom); + gsop[valcnt] = + ATOM_TO_STRING(cx->runtime->atomState.getAtom); + + valcnt++; + } + if (attrs & JSPROP_SETTER) { + val[valcnt] = sprop->setterValue(); + gsopold[valcnt] = + ATOM_TO_STRING(cx->runtime->atomState.setterAtom); + gsop[valcnt] = + ATOM_TO_STRING(cx->runtime->atomState.setAtom); + + valcnt++; + } + } else { + valcnt = 1; + gsop[0] = NULL; + gsopold[0] = NULL; + ok = obj->getProperty(cx, id, &val[0]); + } + obj2->dropProperty(cx, prop); + } + +#else /* !JS_HAS_GETTER_SETTER */ + + /* + * We simplify the source code at the price of minor dead code bloat in + * the ECMA version (for testing only, see jsversion.h). The null + * default values in gsop[j] suffice to disable non-ECMA getter and + * setter code. + */ + valcnt = 1; + gsop[0] = NULL; + gsopold[0] = NULL; + ok = obj->getProperty(cx, id, &val[0]); + +#endif /* !JS_HAS_GETTER_SETTER */ + + if (!ok) + goto error; + + /* + * If id is a string that's not an identifier, then it needs to be + * quoted. Also, negative integer ids must be quoted. + */ + if (JSID_IS_ATOM(id) + ? !idIsLexicalIdentifier + : (!JSID_IS_INT(id) || JSID_TO_INT(id) < 0)) { + idstr = js_QuoteString(cx, idstr, (jschar)'\''); + if (!idstr) { + ok = JS_FALSE; + goto error; + } + *vp = STRING_TO_JSVAL(idstr); /* local root */ + } + idstr->getCharsAndLength(idstrchars, idstrlength); + + for (j = 0; j < valcnt; j++) { + /* Convert val[j] to its canonical source form. */ + valstr = js_ValueToSource(cx, val[j]); + if (!valstr) { + ok = JS_FALSE; + goto error; + } + localroot[j] = STRING_TO_JSVAL(valstr); /* local root */ + valstr->getCharsAndLength(vchars, vlength); + + if (vchars[0] == '#') + needOldStyleGetterSetter = JS_TRUE; + + if (needOldStyleGetterSetter) + gsop[j] = gsopold[j]; + + /* If val[j] is a non-sharp object, consider sharpening it. */ + vsharp = NULL; + vsharplength = 0; +#if JS_HAS_SHARP_VARS + if (!JSVAL_IS_PRIMITIVE(val[j]) && vchars[0] != '#') { + he = js_EnterSharpObject(cx, JSVAL_TO_OBJECT(val[j]), NULL, + &vsharp); + if (!he) { + ok = JS_FALSE; + goto error; + } + if (IS_SHARP(he)) { + vchars = vsharp; + vlength = js_strlen(vchars); + needOldStyleGetterSetter = JS_TRUE; + gsop[j] = gsopold[j]; + } else { + if (vsharp) { + vsharplength = js_strlen(vsharp); + MAKE_SHARP(he); + needOldStyleGetterSetter = JS_TRUE; + gsop[j] = gsopold[j]; + } + js_LeaveSharpObject(cx, NULL); + } + } +#endif + +#ifndef OLD_GETTER_SETTER + /* + * Remove '(function ' from the beginning of valstr and ')' from the + * end so that we can put "get" in front of the function definition. + */ + if (gsop[j] && VALUE_IS_FUNCTION(cx, val[j]) && + !needOldStyleGetterSetter) { + JSFunction *fun = JS_ValueToFunction(cx, val[j]); + const jschar *start = vchars; + const jschar *end = vchars + vlength; + + uint8 parenChomp = 0; + if (vchars[0] == '(') { + vchars++; + parenChomp = 1; + } + + /* + * Try to jump "getter" or "setter" keywords, if we suspect + * they might appear here. This code can be confused by people + * defining Function.prototype.toString, so let's be cautious. + */ + if (JSFUN_GETTER_TEST(fun->flags) || + JSFUN_SETTER_TEST(fun->flags)) { /* skip "getter/setter" */ + const jschar *tmp = js_strchr_limit(vchars, ' ', end); + if (tmp) + vchars = tmp + 1; + } + + /* Try to jump "function" keyword. */ + if (vchars) + vchars = js_strchr_limit(vchars, ' ', end); + + if (vchars) { + if (*vchars == ' ') + vchars++; + vlength = end - vchars - parenChomp; + } else { + gsop[j] = NULL; + vchars = start; + } + } +#else + needOldStyleGetterSetter = JS_TRUE; + gsop[j] = gsopold[j]; +#endif + +#define SAFE_ADD(n) \ + JS_BEGIN_MACRO \ + size_t n_ = (n); \ + curlen += n_; \ + if (curlen < n_) \ + goto overflow; \ + JS_END_MACRO + + curlen = nchars; + if (comma) + SAFE_ADD(2); + SAFE_ADD(idstrlength + 1); + if (gsop[j]) + SAFE_ADD(gsop[j]->length() + 1); + SAFE_ADD(vsharplength); + SAFE_ADD(vlength); + /* Account for the trailing null. */ + SAFE_ADD((outermost ? 2 : 1) + 1); +#undef SAFE_ADD + + if (curlen > (size_t)-1 / sizeof(jschar)) + goto overflow; + + /* Allocate 1 + 1 at end for closing brace and terminating 0. */ + chars = (jschar *) + js_realloc((ochars = chars), curlen * sizeof(jschar)); + if (!chars) { + /* Save code space on error: let JS_free ignore null vsharp. */ + cx->free(vsharp); + js_free(ochars); + goto error; + } + + if (comma) { + chars[nchars++] = comma[0]; + chars[nchars++] = comma[1]; + } + comma = ", "; + + if (needOldStyleGetterSetter) { + js_strncpy(&chars[nchars], idstrchars, idstrlength); + nchars += idstrlength; + if (gsop[j]) { + chars[nchars++] = ' '; + gsoplength = gsop[j]->length(); + js_strncpy(&chars[nchars], gsop[j]->chars(), + gsoplength); + nchars += gsoplength; + } + chars[nchars++] = ':'; + } else { /* New style "decompilation" */ + if (gsop[j]) { + gsoplength = gsop[j]->length(); + js_strncpy(&chars[nchars], gsop[j]->chars(), + gsoplength); + nchars += gsoplength; + chars[nchars++] = ' '; + } + js_strncpy(&chars[nchars], idstrchars, idstrlength); + nchars += idstrlength; + /* Extraneous space after id here will be extracted later */ + chars[nchars++] = gsop[j] ? ' ' : ':'; + } + + if (vsharplength) { + js_strncpy(&chars[nchars], vsharp, vsharplength); + nchars += vsharplength; + } + js_strncpy(&chars[nchars], vchars, vlength); + nchars += vlength; + + if (vsharp) + cx->free(vsharp); + } + } + + chars[nchars++] = '}'; + if (outermost) + chars[nchars++] = ')'; + chars[nchars] = 0; + + error: + js_LeaveSharpObject(cx, &ida); + + if (!ok) { + if (chars) + js_free(chars); + goto out; + } + + if (!chars) { + JS_ReportOutOfMemory(cx); + ok = JS_FALSE; + goto out; + } + make_string: + str = js_NewString(cx, chars, nchars); + if (!str) { + js_free(chars); + ok = JS_FALSE; + goto out; + } + *vp = STRING_TO_JSVAL(str); + ok = JS_TRUE; + out: + JS_POP_TEMP_ROOT(cx, &tvr); + return ok; + + overflow: + cx->free(vsharp); + js_free(chars); + chars = NULL; + goto error; +} +#endif /* JS_HAS_TOSOURCE */ + +static JSBool +obj_toString(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *obj; + jschar *chars; + size_t nchars; + const char *clazz, *prefix; + JSString *str; + + obj = JS_THIS_OBJECT(cx, vp); + if (!obj) + return JS_FALSE; + obj = js_GetWrappedObject(cx, obj); + clazz = OBJ_GET_CLASS(cx, obj)->name; + nchars = 9 + strlen(clazz); /* 9 for "[object ]" */ + chars = (jschar *) cx->malloc((nchars + 1) * sizeof(jschar)); + if (!chars) + return JS_FALSE; + + prefix = "[object "; + nchars = 0; + while ((chars[nchars] = (jschar)*prefix) != 0) + nchars++, prefix++; + while ((chars[nchars] = (jschar)*clazz) != 0) + nchars++, clazz++; + chars[nchars++] = ']'; + chars[nchars] = 0; + + str = js_NewString(cx, chars, nchars); + if (!str) { + cx->free(chars); + return JS_FALSE; + } + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +obj_toLocaleString(JSContext *cx, uintN argc, jsval *vp) +{ + jsval thisv; + JSString *str; + + thisv = JS_THIS(cx, vp); + if (JSVAL_IS_NULL(thisv)) + return JS_FALSE; + + str = js_ValueToString(cx, thisv); + if (!str) + return JS_FALSE; + + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +obj_valueOf(JSContext *cx, uintN argc, jsval *vp) +{ + *vp = JS_THIS(cx, vp); + return !JSVAL_IS_NULL(*vp); +} + +#ifdef JS_TRACER +static jsval FASTCALL +Object_p_valueOf(JSContext* cx, JSObject* obj, JSString *hint) +{ + return OBJECT_TO_JSVAL(obj); +} +#endif + +/* + * Check whether principals subsumes scopeobj's principals, and return true + * if so (or if scopeobj has no principals, for backward compatibility with + * the JS API, which does not require principals), and false otherwise. + */ +JSBool +js_CheckPrincipalsAccess(JSContext *cx, JSObject *scopeobj, + JSPrincipals *principals, JSAtom *caller) +{ + JSSecurityCallbacks *callbacks; + JSPrincipals *scopePrincipals; + const char *callerstr; + + callbacks = JS_GetSecurityCallbacks(cx); + if (callbacks && callbacks->findObjectPrincipals) { + scopePrincipals = callbacks->findObjectPrincipals(cx, scopeobj); + if (!principals || !scopePrincipals || + !principals->subsume(principals, scopePrincipals)) { + callerstr = js_AtomToPrintableString(cx, caller); + if (!callerstr) + return JS_FALSE; + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_INDIRECT_CALL, callerstr); + return JS_FALSE; + } + } + return JS_TRUE; +} + +JSObject * +js_CheckScopeChainValidity(JSContext *cx, JSObject *scopeobj, const char *caller) +{ + JSClass *clasp; + JSExtendedClass *xclasp; + JSObject *inner; + + if (!scopeobj) + goto bad; + + OBJ_TO_INNER_OBJECT(cx, scopeobj); + if (!scopeobj) + return NULL; + + inner = scopeobj; + + /* XXX This is an awful gross hack. */ + while (scopeobj) { + clasp = OBJ_GET_CLASS(cx, scopeobj); + if (clasp->flags & JSCLASS_IS_EXTENDED) { + xclasp = (JSExtendedClass*)clasp; + if (xclasp->innerObject && + xclasp->innerObject(cx, scopeobj) != scopeobj) { + goto bad; + } + } + + scopeobj = OBJ_GET_PARENT(cx, scopeobj); + } + + return inner; + +bad: + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_INDIRECT_CALL, caller); + return NULL; +} + +const char * +js_ComputeFilename(JSContext *cx, JSStackFrame *caller, + JSPrincipals *principals, uintN *linenop) +{ + uint32 flags; +#ifdef DEBUG + JSSecurityCallbacks *callbacks = JS_GetSecurityCallbacks(cx); +#endif + + JS_ASSERT(principals || !(callbacks && callbacks->findObjectPrincipals)); + flags = JS_GetScriptFilenameFlags(caller->script); + if ((flags & JSFILENAME_PROTECTED) && + principals && + strcmp(principals->codebase, "[System Principal]")) { + *linenop = 0; + return principals->codebase; + } + + if (caller->regs && js_GetOpcode(cx, caller->script, caller->regs->pc) == JSOP_EVAL) { + JS_ASSERT(js_GetOpcode(cx, caller->script, caller->regs->pc + JSOP_EVAL_LENGTH) == JSOP_LINENO); + *linenop = GET_UINT16(caller->regs->pc + JSOP_EVAL_LENGTH); + } else { + *linenop = js_FramePCToLineNumber(cx, caller); + } + return caller->script->filename; +} + +#ifndef EVAL_CACHE_CHAIN_LIMIT +# define EVAL_CACHE_CHAIN_LIMIT 4 +#endif + +static inline JSScript ** +EvalCacheHash(JSContext *cx, JSString *str) +{ + const jschar *s; + size_t n; + uint32 h; + + str->getCharsAndLength(s, n); + if (n > 100) + n = 100; + for (h = 0; n; s++, n--) + h = JS_ROTATE_LEFT32(h, 4) ^ *s; + + h *= JS_GOLDEN_RATIO; + h >>= 32 - JS_EVAL_CACHE_SHIFT; + return &JS_SCRIPTS_TO_GC(cx)[h]; +} + +static JSBool +obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSStackFrame *fp, *caller, *callerFrame; + JSBool indirectCall; + JSPrincipals *principals; + const char *file; + uintN line; + JSString *str; + JSScript *script; + JSBool ok; + JSScript **bucket = NULL; /* avoid GCC warning with early decl&init */ +#if JS_HAS_EVAL_THIS_SCOPE + JSObject *callerScopeChain = NULL, *callerVarObj = NULL; + JSObject *withObject = NULL; + JSBool setCallerScopeChain = JS_FALSE, setCallerVarObj = JS_FALSE; + JSTempValueRooter scopetvr, varobjtvr; +#endif + + fp = js_GetTopStackFrame(cx); + caller = js_GetScriptedCaller(cx, fp); + if (!caller) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_INDIRECT_CALL, js_eval_str); + return JS_FALSE; + } + + indirectCall = (caller->regs && *caller->regs->pc != JSOP_EVAL); + + /* + * This call to js_GetWrappedObject is safe because of the security checks + * we do below. However, the control flow below is confusing, so we double + * check. There are two cases: + * - Direct call: This object is never used. So unwrapping can't hurt. + * - Indirect call: If this object isn't already the scope chain (which + * we're guaranteed to be allowed to access) then we do a security + * check. + */ + obj = js_GetWrappedObject(cx, obj); + + /* + * Ban all indirect uses of eval (global.foo = eval; global.foo(...)) and + * calls that attempt to use a non-global object as the "with" object in + * the former indirect case. + */ + { + JSObject *parent = OBJ_GET_PARENT(cx, obj); + if (indirectCall || parent) { + uintN flags = parent + ? JSREPORT_ERROR + : JSREPORT_STRICT | JSREPORT_WARNING; + if (!JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage, NULL, + JSMSG_BAD_INDIRECT_CALL, + js_eval_str)) { + return JS_FALSE; + } + } + } + + if (!JSVAL_IS_STRING(argv[0])) { + *rval = argv[0]; + return JS_TRUE; + } + + /* + * If the caller is a lightweight function and doesn't have a variables + * object, then we need to provide one for the compiler to stick any + * declared (var) variables into. + */ + if (!caller->varobj && !js_GetCallObject(cx, caller)) + return JS_FALSE; + + /* Accept an optional trailing argument that overrides the scope object. */ + JSObject *scopeobj = NULL; + if (argc >= 2) { + if (!js_ValueToObject(cx, argv[1], &scopeobj)) + return JS_FALSE; + argv[1] = OBJECT_TO_JSVAL(scopeobj); + } + + /* From here on, control must exit through label out with ok set. */ + MUST_FLOW_THROUGH("out"); + uintN staticLevel = caller->script->staticLevel + 1; + if (!scopeobj) { + /* + * Bring fp->scopeChain up to date. We're either going to use + * it (direct call) or save it and restore it (indirect call). + */ + callerScopeChain = js_GetScopeChain(cx, caller); + if (!callerScopeChain) { + ok = JS_FALSE; + goto out; + } + +#if JS_HAS_EVAL_THIS_SCOPE + /* + * If we see an indirect call, then run eval in the global scope. We do + * this so the compiler can make assumptions about what bindings may or + * may not exist in the current frame if it doesn't see 'eval'. + */ + if (indirectCall) { + /* Pretend that we're top level. */ + staticLevel = 0; + + OBJ_TO_INNER_OBJECT(cx, obj); + if (!obj) { + ok = JS_FALSE; + goto out; + } + + ok = js_CheckPrincipalsAccess(cx, obj, + JS_StackFramePrincipals(cx, caller), + cx->runtime->atomState.evalAtom); + if (!ok) + goto out; + + /* NB: We know obj is a global object here. */ + JS_ASSERT(!OBJ_GET_PARENT(cx, obj)); + scopeobj = obj; + + /* Set fp->scopeChain too, for the compiler. */ + caller->scopeChain = fp->scopeChain = scopeobj; + + /* Remember scopeobj so we can null its private when done. */ + setCallerScopeChain = JS_TRUE; + JS_PUSH_TEMP_ROOT_OBJECT(cx, callerScopeChain, &scopetvr); + + callerVarObj = caller->varobj; + if (obj != callerVarObj) { + /* Set fp->varobj too, for the compiler. */ + caller->varobj = fp->varobj = obj; + setCallerVarObj = JS_TRUE; + JS_PUSH_TEMP_ROOT_OBJECT(cx, callerVarObj, &varobjtvr); + } + } else { + /* + * Compile using the caller's current scope object. + * + * NB: This means that native callers (who reach this point through + * the C API) must use the two parameter form. + */ + scopeobj = callerScopeChain; + } +#endif + } else { + scopeobj = js_GetWrappedObject(cx, scopeobj); + OBJ_TO_INNER_OBJECT(cx, scopeobj); + if (!scopeobj) { + ok = JS_FALSE; + goto out; + } + + ok = js_CheckPrincipalsAccess(cx, scopeobj, + JS_StackFramePrincipals(cx, caller), + cx->runtime->atomState.evalAtom); + if (!ok) + goto out; + + /* + * If scopeobj is not a global object, then we need to wrap it in a + * with object to maintain invariants in the engine (see bug 520164). + */ + if (scopeobj->getParent()) { + withObject = js_NewWithObject(cx, scopeobj, scopeobj->getParent(), 0); + if (!withObject) { + ok = JS_FALSE; + goto out; + } + scopeobj = withObject; + + JS_ASSERT(argc >= 2); + argv[1] = OBJECT_TO_JSVAL(scopeobj); + } + + /* We're pretending that we're in global code. */ + staticLevel = 0; + } + + /* Ensure we compile this eval with the right object in the scope chain. */ + scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_eval_str); + if (!scopeobj) { + ok = JS_FALSE; + goto out; + } + + principals = JS_EvalFramePrincipals(cx, fp, caller); + file = js_ComputeFilename(cx, caller, principals, &line); + + str = JSVAL_TO_STRING(argv[0]); + script = NULL; + + /* + * Cache local eval scripts indexed by source qualified by scope. + * + * An eval cache entry should never be considered a hit unless its + * strictness matches that of the new eval code. The existing code takes + * care of this, because hits are qualified by the function from which + * eval was called, whose strictness doesn't change. Scripts produced by + * calls to eval from global code are not cached. + */ + bucket = EvalCacheHash(cx, str); + if (!indirectCall && caller->fun) { + uintN count = 0; + JSScript **scriptp = bucket; + + EVAL_CACHE_METER(probe); + while ((script = *scriptp) != NULL) { + if (script->savedCallerFun && + script->version == cx->version && + (script->principals == principals || + (principals->subsume(principals, script->principals) && + script->principals->subsume(script->principals, principals)))) { + /* + * Get the prior (cache-filling) eval's saved caller function. + * See JSCompiler::compileScript in jsparse.cpp. + */ + JSFunction *fun = script->getFunction(0); + + if (fun == caller->fun) { + /* + * Get the source string passed for safekeeping in the + * atom map by the prior eval to JSCompiler::compileScript. + */ + JSString *src = ATOM_TO_STRING(script->atomMap.vector[0]); + + if (src == str || js_EqualStrings(src, str)) { + /* + * Source matches, qualify by comparing scopeobj to the + * COMPILE_N_GO-memoized parent of the first literal + * function or regexp object if any. If none, then this + * script has no compiled-in dependencies on the prior + * eval's scopeobj. + */ + JSObjectArray *objarray = script->objects(); + int i = 1; + + if (objarray->length == 1) { + if (script->regexpsOffset != 0) { + objarray = script->regexps(); + i = 0; + } else { + EVAL_CACHE_METER(noscope); + i = -1; + } + } + if (i < 0 || + STOBJ_GET_PARENT(objarray->vector[i]) == scopeobj) { + EVAL_CACHE_METER(hit); + *scriptp = script->u.nextToGC; + script->u.nextToGC = NULL; + break; + } + } + } + } + + if (++count == EVAL_CACHE_CHAIN_LIMIT) { + script = NULL; + break; + } + EVAL_CACHE_METER(step); + scriptp = &script->u.nextToGC; + } + } + + /* + * We can't have a callerFrame (down in js_Execute's terms) if we're in + * global code. This includes indirect eval and direct eval called with a + * scope object parameter. + */ + callerFrame = (staticLevel != 0) ? caller : NULL; + if (!script) { + script = JSCompiler::compileScript(cx, scopeobj, callerFrame, + principals, + TCF_COMPILE_N_GO | TCF_NEED_MUTABLE_SCRIPT, + str->chars(), str->length(), + NULL, file, line, str, staticLevel); + if (!script) { + ok = JS_FALSE; + goto out; + } + } + + if (argc < 2) { + /* Execute using caller's new scope object (might be a Call object). */ + scopeobj = caller->scopeChain; + } + + /* + * Belt-and-braces: check that the lesser of eval's principals and the + * caller's principals has access to scopeobj. + */ + ok = js_CheckPrincipalsAccess(cx, scopeobj, principals, + cx->runtime->atomState.evalAtom); + if (ok) + ok = js_Execute(cx, scopeobj, script, callerFrame, JSFRAME_EVAL, rval); + + script->u.nextToGC = *bucket; + *bucket = script; +#ifdef CHECK_SCRIPT_OWNER + script->owner = NULL; +#endif + +out: +#if JS_HAS_EVAL_THIS_SCOPE + /* Restore OBJ_GET_PARENT(scopeobj) not callerScopeChain in case of Call. */ + if (setCallerVarObj) { + caller->varobj = callerVarObj; + JS_POP_TEMP_ROOT(cx, &varobjtvr); + } + if (setCallerScopeChain) { + caller->scopeChain = callerScopeChain; + JS_POP_TEMP_ROOT(cx, &scopetvr); + } + if (withObject) + withObject->setPrivate(NULL); +#endif + return ok; +} + +#if JS_HAS_OBJ_WATCHPOINT + +static JSBool +obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp, + void *closure) +{ + JSObject *callable; + JSSecurityCallbacks *callbacks; + JSStackFrame *caller; + JSPrincipals *subject, *watcher; + JSResolvingKey key; + JSResolvingEntry *entry; + uint32 generation; + jsval argv[3]; + JSBool ok; + + callable = (JSObject *) closure; + + callbacks = JS_GetSecurityCallbacks(cx); + if (callbacks && callbacks->findObjectPrincipals) { + /* Skip over any obj_watch_* frames between us and the real subject. */ + caller = js_GetScriptedCaller(cx, NULL); + if (caller) { + /* + * Only call the watch handler if the watcher is allowed to watch + * the currently executing script. + */ + watcher = callbacks->findObjectPrincipals(cx, callable); + subject = JS_StackFramePrincipals(cx, caller); + + if (watcher && subject && !watcher->subsume(watcher, subject)) { + /* Silently don't call the watch handler. */ + return JS_TRUE; + } + } + } + + /* Avoid recursion on (obj, id) already being watched on cx. */ + key.obj = obj; + key.id = id; + if (!js_StartResolving(cx, &key, JSRESFLAG_WATCH, &entry)) + return JS_FALSE; + if (!entry) + return JS_TRUE; + generation = cx->resolvingTable->generation; + + argv[0] = id; + argv[1] = old; + argv[2] = *nvp; + ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(callable), 3, argv, nvp); + js_StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation); + return ok; +} + +static JSBool +obj_watch(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *callable; + jsval userid, value; + jsid propid; + JSObject *obj; + uintN attrs; + + if (argc <= 1) { + js_ReportMissingArg(cx, vp, 1); + return JS_FALSE; + } + + callable = js_ValueToCallableObject(cx, &vp[3], 0); + if (!callable) + return JS_FALSE; + + /* Compute the unique int/atom symbol id needed by js_LookupProperty. */ + userid = vp[2]; + if (!JS_ValueToId(cx, userid, &propid)) + return JS_FALSE; + + obj = JS_THIS_OBJECT(cx, vp); + if (!obj || !obj->checkAccess(cx, propid, JSACC_WATCH, &value, &attrs)) + return JS_FALSE; + if (attrs & JSPROP_READONLY) + return JS_TRUE; + *vp = JSVAL_VOID; + + if (OBJ_IS_DENSE_ARRAY(cx, obj) && !js_MakeArraySlow(cx, obj)) + return JS_FALSE; + return JS_SetWatchPoint(cx, obj, userid, obj_watch_handler, callable); +} + +static JSBool +obj_unwatch(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *obj; + + obj = JS_THIS_OBJECT(cx, vp); + if (!obj) + return JS_FALSE; + *vp = JSVAL_VOID; + return JS_ClearWatchPoint(cx, obj, argc != 0 ? vp[2] : JSVAL_VOID, + NULL, NULL); +} + +#endif /* JS_HAS_OBJ_WATCHPOINT */ + +/* + * Prototype and property query methods, to complement the 'in' and + * 'instanceof' operators. + */ + +/* Proposed ECMA 15.2.4.5. */ +static JSBool +obj_hasOwnProperty(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *obj; + + obj = JS_THIS_OBJECT(cx, vp); + return obj && + js_HasOwnPropertyHelper(cx, obj->map->ops->lookupProperty, argc, vp); +} + +JSBool +js_HasOwnPropertyHelper(JSContext *cx, JSLookupPropOp lookup, uintN argc, + jsval *vp) +{ + jsid id; + if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id)) + return JS_FALSE; + + JSBool found; + JSObject *obj = JS_THIS_OBJECT(cx, vp); + if (!obj || !js_HasOwnProperty(cx, lookup, obj, id, &found)) + return JS_FALSE; + *vp = BOOLEAN_TO_JSVAL(found); + return JS_TRUE; +} + +JSBool +js_HasOwnProperty(JSContext *cx, JSLookupPropOp lookup, JSObject *obj, jsid id, + JSBool *foundp) +{ + JSObject *obj2; + JSProperty *prop; + JSScopeProperty *sprop; + + if (!lookup(cx, obj, id, &obj2, &prop)) + return JS_FALSE; + if (!prop) { + *foundp = JS_FALSE; + } else if (obj2 == obj) { + *foundp = JS_TRUE; + } else { + JSClass *clasp; + JSExtendedClass *xclasp; + JSObject *outer; + + clasp = OBJ_GET_CLASS(cx, obj2); + if (!(clasp->flags & JSCLASS_IS_EXTENDED) || + !(xclasp = (JSExtendedClass *) clasp)->outerObject) { + outer = NULL; + } else { + outer = xclasp->outerObject(cx, obj2); + if (!outer) + return JS_FALSE; + } + if (outer == obj) { + *foundp = JS_TRUE; + } else if (OBJ_IS_NATIVE(obj2) && OBJ_GET_CLASS(cx, obj) == clasp) { + /* + * The combination of JSPROP_SHARED and JSPROP_PERMANENT in a + * delegated property makes that property appear to be direct in + * all delegating instances of the same native class. This hack + * avoids bloating every function instance with its own 'length' + * (AKA 'arity') property. But it must not extend across class + * boundaries, to avoid making hasOwnProperty lie (bug 320854). + * + * It's not really a hack, of course: a permanent property can't + * be deleted, and JSPROP_SHARED means "don't allocate a slot in + * any instance, prototype or delegating". Without a slot, and + * without the ability to remove and recreate (with differences) + * the property, there is no way to tell whether it is directly + * owned, or indirectly delegated. + */ + sprop = (JSScopeProperty *)prop; + *foundp = SPROP_IS_SHARED_PERMANENT(sprop); + } else { + *foundp = JS_FALSE; + } + } + if (prop) + obj2->dropProperty(cx, prop); + return JS_TRUE; +} + +#ifdef JS_TRACER +static JSBool FASTCALL +Object_p_hasOwnProperty(JSContext* cx, JSObject* obj, JSString *str) +{ + jsid id; + JSBool found; + + if (!js_ValueToStringId(cx, STRING_TO_JSVAL(str), &id) || + !js_HasOwnProperty(cx, obj->map->ops->lookupProperty, obj, id, &found)) { + js_SetBuiltinError(cx); + return JSVAL_TO_BOOLEAN(JSVAL_VOID); + } + + return found; +} +#endif + +/* Proposed ECMA 15.2.4.6. */ +static JSBool +obj_isPrototypeOf(JSContext *cx, uintN argc, jsval *vp) +{ + JSBool b; + + if (!js_IsDelegate(cx, JS_THIS_OBJECT(cx, vp), + argc != 0 ? vp[2] : JSVAL_VOID, &b)) { + return JS_FALSE; + } + *vp = BOOLEAN_TO_JSVAL(b); + return JS_TRUE; +} + +/* Proposed ECMA 15.2.4.7. */ +static JSBool +obj_propertyIsEnumerable(JSContext *cx, uintN argc, jsval *vp) +{ + jsid id; + JSObject *obj; + + if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id)) + return JS_FALSE; + + obj = JS_THIS_OBJECT(cx, vp); + return obj && js_PropertyIsEnumerable(cx, obj, id, vp); +} + +#ifdef JS_TRACER +static JSBool FASTCALL +Object_p_propertyIsEnumerable(JSContext* cx, JSObject* obj, JSString *str) +{ + jsid id = ATOM_TO_JSID(STRING_TO_JSVAL(str)); + jsval v; + + if (!js_PropertyIsEnumerable(cx, obj, id, &v)) { + js_SetBuiltinError(cx); + return JSVAL_TO_BOOLEAN(JSVAL_VOID); + } + + JS_ASSERT(JSVAL_IS_BOOLEAN(v)); + return JSVAL_TO_BOOLEAN(v); +} +#endif + +JSBool +js_PropertyIsEnumerable(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + JSObject *pobj; + uintN attrs; + JSProperty *prop; + JSBool ok; + + if (!obj->lookupProperty(cx, id, &pobj, &prop)) + return JS_FALSE; + + if (!prop) { + *vp = JSVAL_FALSE; + return JS_TRUE; + } + + /* + * XXX ECMA spec error compatible: return false unless hasOwnProperty. + * The ECMA spec really should be fixed so propertyIsEnumerable and the + * for..in loop agree on whether prototype properties are enumerable, + * obviously by fixing this method (not by breaking the for..in loop!). + * + * We check here for shared permanent prototype properties, which should + * be treated as if they are local to obj. They are an implementation + * technique used to satisfy ECMA requirements; users should not be able + * to distinguish a shared permanent proto-property from a local one. + */ + if (pobj != obj && + !(OBJ_IS_NATIVE(pobj) && + SPROP_IS_SHARED_PERMANENT((JSScopeProperty *)prop))) { + pobj->dropProperty(cx, prop); + *vp = JSVAL_FALSE; + return JS_TRUE; + } + + ok = pobj->getAttributes(cx, id, prop, &attrs); + pobj->dropProperty(cx, prop); + if (ok) + *vp = BOOLEAN_TO_JSVAL((attrs & JSPROP_ENUMERATE) != 0); + return ok; +} + +#if JS_HAS_GETTER_SETTER +JS_FRIEND_API(JSBool) +js_obj_defineGetter(JSContext *cx, uintN argc, jsval *vp) +{ + jsval fval, junk; + jsid id; + JSObject *obj; + uintN attrs; + + if (argc <= 1 || JS_TypeOfValue(cx, vp[3]) != JSTYPE_FUNCTION) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_GETTER_OR_SETTER, + js_getter_str); + return JS_FALSE; + } + fval = vp[3]; + + if (!JS_ValueToId(cx, vp[2], &id)) + return JS_FALSE; + obj = JS_THIS_OBJECT(cx, vp); + if (!obj || !js_CheckRedeclaration(cx, obj, id, JSPROP_GETTER, NULL, NULL)) + return JS_FALSE; + /* + * Getters and setters are just like watchpoints from an access + * control point of view. + */ + if (!obj->checkAccess(cx, id, JSACC_WATCH, &junk, &attrs)) + return JS_FALSE; + *vp = JSVAL_VOID; + return obj->defineProperty(cx, id, JSVAL_VOID, + js_CastAsPropertyOp(JSVAL_TO_OBJECT(fval)), JS_PropertyStub, + JSPROP_ENUMERATE | JSPROP_GETTER | JSPROP_SHARED); +} + +JS_FRIEND_API(JSBool) +js_obj_defineSetter(JSContext *cx, uintN argc, jsval *vp) +{ + jsval fval, junk; + jsid id; + JSObject *obj; + uintN attrs; + + if (argc <= 1 || JS_TypeOfValue(cx, vp[3]) != JSTYPE_FUNCTION) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_GETTER_OR_SETTER, + js_setter_str); + return JS_FALSE; + } + fval = vp[3]; + + if (!JS_ValueToId(cx, vp[2], &id)) + return JS_FALSE; + obj = JS_THIS_OBJECT(cx, vp); + if (!obj || !js_CheckRedeclaration(cx, obj, id, JSPROP_SETTER, NULL, NULL)) + return JS_FALSE; + /* + * Getters and setters are just like watchpoints from an access + * control point of view. + */ + if (!obj->checkAccess(cx, id, JSACC_WATCH, &junk, &attrs)) + return JS_FALSE; + *vp = JSVAL_VOID; + return obj->defineProperty(cx, id, JSVAL_VOID, + JS_PropertyStub, js_CastAsPropertyOp(JSVAL_TO_OBJECT(fval)), + JSPROP_ENUMERATE | JSPROP_SETTER | JSPROP_SHARED); +} + +static JSBool +obj_lookupGetter(JSContext *cx, uintN argc, jsval *vp) +{ + jsid id; + JSObject *obj, *pobj; + JSProperty *prop; + JSScopeProperty *sprop; + + if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id)) + return JS_FALSE; + obj = JS_THIS_OBJECT(cx, vp); + if (!obj || !obj->lookupProperty(cx, id, &pobj, &prop)) + return JS_FALSE; + *vp = JSVAL_VOID; + if (prop) { + if (OBJ_IS_NATIVE(pobj)) { + sprop = (JSScopeProperty *) prop; + if (sprop->attrs & JSPROP_GETTER) + *vp = sprop->getterValue(); + } + pobj->dropProperty(cx, prop); + } + return JS_TRUE; +} + +static JSBool +obj_lookupSetter(JSContext *cx, uintN argc, jsval *vp) +{ + jsid id; + JSObject *obj, *pobj; + JSProperty *prop; + JSScopeProperty *sprop; + + if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id)) + return JS_FALSE; + obj = JS_THIS_OBJECT(cx, vp); + if (!obj || !obj->lookupProperty(cx, id, &pobj, &prop)) + return JS_FALSE; + *vp = JSVAL_VOID; + if (prop) { + if (OBJ_IS_NATIVE(pobj)) { + sprop = (JSScopeProperty *) prop; + if (sprop->attrs & JSPROP_SETTER) + *vp = sprop->setterValue(); + } + pobj->dropProperty(cx, prop); + } + return JS_TRUE; +} +#endif /* JS_HAS_GETTER_SETTER */ + +JSBool +obj_getPrototypeOf(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *obj; + uintN attrs; + + if (argc == 0) { + js_ReportMissingArg(cx, vp, 0); + return JS_FALSE; + } + + if (JSVAL_IS_PRIMITIVE(vp[2])) { + char *bytes = js_DecompileValueGenerator(cx, 0 - argc, vp[2], NULL); + if (!bytes) + return JS_FALSE; + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_UNEXPECTED_TYPE, bytes, "not an object"); + JS_free(cx, bytes); + return JS_FALSE; + } + + obj = JSVAL_TO_OBJECT(vp[2]); + return obj->checkAccess(cx, ATOM_TO_JSID(cx->runtime->atomState.protoAtom), + JSACC_PROTO, vp, &attrs); +} + +static JSBool +obj_getOwnPropertyDescriptor(JSContext *cx, uintN argc, jsval *vp) +{ + if (argc == 0 || JSVAL_IS_PRIMITIVE(vp[2])) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT); + return JS_FALSE; + } + + JSObject *obj = JSVAL_TO_OBJECT(vp[2]); + + JSAutoTempIdRooter nameidr(cx); + if (!JS_ValueToId(cx, argc >= 2 ? vp[3] : JSVAL_VOID, nameidr.addr())) + return JS_FALSE; + + JSBool found; + if (!js_HasOwnProperty(cx, obj->map->ops->lookupProperty, obj, nameidr.id(), &found)) + return JS_FALSE; + if (!found) { + *vp = JSVAL_VOID; + return JS_TRUE; + } + + JSObject *pobj; + JSProperty *prop; + if (!obj->lookupProperty(cx, nameidr.id(), &pobj, &prop)) + return JS_FALSE; + JS_ASSERT(prop); + + uintN attrs; + if (!pobj->getAttributes(cx, nameidr.id(), prop, &attrs)) { + pobj->dropProperty(cx, prop); + return JS_FALSE; + } + + jsval roots[] = { JSVAL_VOID, JSVAL_VOID }; + JSAutoTempValueRooter tvr(cx, JS_ARRAY_LENGTH(roots), roots); + if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) { + if (OBJ_IS_NATIVE(obj)) { + JSScopeProperty *sprop = reinterpret_cast(prop); + if (attrs & JSPROP_GETTER) + roots[0] = js_CastAsObjectJSVal(sprop->getter); + if (attrs & JSPROP_SETTER) + roots[1] = js_CastAsObjectJSVal(sprop->setter); + } + + pobj->dropProperty(cx, prop); + } else { + pobj->dropProperty(cx, prop); + + if (!obj->getProperty(cx, nameidr.id(), &roots[0])) + return JS_FALSE; + } + + + /* We have our own property, so start creating the descriptor. */ + JSObject *desc = js_NewObject(cx, &js_ObjectClass, NULL, NULL); + if (!desc) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(desc); /* Root and return. */ + + const JSAtomState &atomState = cx->runtime->atomState; + if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) { + if (!desc->defineProperty(cx, ATOM_TO_JSID(atomState.getAtom), roots[0], + JS_PropertyStub, JS_PropertyStub, JSPROP_ENUMERATE) || + !desc->defineProperty(cx, ATOM_TO_JSID(atomState.setAtom), roots[1], + JS_PropertyStub, JS_PropertyStub, JSPROP_ENUMERATE)) { + return JS_FALSE; + } + } else { + if (!desc->defineProperty(cx, ATOM_TO_JSID(atomState.valueAtom), roots[0], + JS_PropertyStub, JS_PropertyStub, JSPROP_ENUMERATE) || + !desc->defineProperty(cx, ATOM_TO_JSID(atomState.writableAtom), + BOOLEAN_TO_JSVAL((attrs & JSPROP_READONLY) == 0), + JS_PropertyStub, JS_PropertyStub, JSPROP_ENUMERATE)) { + return JS_FALSE; + } + } + + return desc->defineProperty(cx, ATOM_TO_JSID(atomState.enumerableAtom), + BOOLEAN_TO_JSVAL((attrs & JSPROP_ENUMERATE) != 0), + JS_PropertyStub, JS_PropertyStub, JSPROP_ENUMERATE) && + desc->defineProperty(cx, ATOM_TO_JSID(atomState.configurableAtom), + BOOLEAN_TO_JSVAL((attrs & JSPROP_PERMANENT) == 0), + JS_PropertyStub, JS_PropertyStub, JSPROP_ENUMERATE); +} + +static JSBool +obj_keys(JSContext *cx, uintN argc, jsval *vp) +{ + jsval v = argc == 0 ? JSVAL_VOID : vp[2]; + if (JSVAL_IS_PRIMITIVE(v)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT); + return JS_FALSE; + } + + JSObject *obj = JSVAL_TO_OBJECT(v); + JSAutoIdArray ida(cx, JS_Enumerate(cx, obj)); + if (!ida) + return JS_FALSE; + + JSObject *proto; + if (!js_GetClassPrototype(cx, NULL, INT_TO_JSID(JSProto_Array), &proto)) + return JS_FALSE; + vp[1] = OBJECT_TO_JSVAL(proto); + + JS_ASSERT(ida.length() <= UINT32_MAX); + JSObject *aobj = js_NewArrayWithSlots(cx, proto, uint32(ida.length())); + if (!aobj) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(aobj); + + jsval *slots = aobj->dslots; + size_t len = ida.length(); + JS_ASSERT(js_DenseArrayCapacity(aobj) >= len); + for (size_t i = 0; i < len; i++) { + jsid id = ida[i]; + if (JSID_IS_INT(id)) { + if (!js_ValueToStringId(cx, INT_JSID_TO_JSVAL(id), &slots[i])) + return JS_FALSE; + } else { + /* + * Object-valued ids are a possibility admitted by SpiderMonkey for + * the purposes of E4X. It's unclear whether they could ever be + * detected here -- the "obvious" possibility, a property referred + * to by a QName, actually appears as a string jsid -- but in the + * interests of fidelity we pass object jsids through unchanged. + */ + slots[i] = ID_TO_VALUE(id); + } + } + + JS_ASSERT(len <= UINT32_MAX); + aobj->fslots[JSSLOT_ARRAY_COUNT] = len; + + return JS_TRUE; +} + + +#if JS_HAS_OBJ_WATCHPOINT +const char js_watch_str[] = "watch"; +const char js_unwatch_str[] = "unwatch"; +#endif +const char js_hasOwnProperty_str[] = "hasOwnProperty"; +const char js_isPrototypeOf_str[] = "isPrototypeOf"; +const char js_propertyIsEnumerable_str[] = "propertyIsEnumerable"; +#if JS_HAS_GETTER_SETTER +const char js_defineGetter_str[] = "__defineGetter__"; +const char js_defineSetter_str[] = "__defineSetter__"; +const char js_lookupGetter_str[] = "__lookupGetter__"; +const char js_lookupSetter_str[] = "__lookupSetter__"; +#endif + +JS_DEFINE_TRCINFO_1(obj_valueOf, + (3, (static, JSVAL, Object_p_valueOf, CONTEXT, THIS, STRING, 0, 0))) +JS_DEFINE_TRCINFO_1(obj_hasOwnProperty, + (3, (static, BOOL_FAIL, Object_p_hasOwnProperty, CONTEXT, THIS, STRING, 0, 0))) +JS_DEFINE_TRCINFO_1(obj_propertyIsEnumerable, + (3, (static, BOOL_FAIL, Object_p_propertyIsEnumerable, CONTEXT, THIS, STRING, 0, 0))) + +static JSFunctionSpec object_methods[] = { +#if JS_HAS_TOSOURCE + JS_FN(js_toSource_str, obj_toSource, 0,0), +#endif + JS_FN(js_toString_str, obj_toString, 0,0), + JS_FN(js_toLocaleString_str, obj_toLocaleString, 0,0), + JS_TN(js_valueOf_str, obj_valueOf, 0,0, &obj_valueOf_trcinfo), +#if JS_HAS_OBJ_WATCHPOINT + JS_FN(js_watch_str, obj_watch, 2,0), + JS_FN(js_unwatch_str, obj_unwatch, 1,0), +#endif + JS_TN(js_hasOwnProperty_str, obj_hasOwnProperty, 1,0, &obj_hasOwnProperty_trcinfo), + JS_FN(js_isPrototypeOf_str, obj_isPrototypeOf, 1,0), + JS_TN(js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0, &obj_propertyIsEnumerable_trcinfo), +#if JS_HAS_GETTER_SETTER + JS_FN(js_defineGetter_str, js_obj_defineGetter, 2,0), + JS_FN(js_defineSetter_str, js_obj_defineSetter, 2,0), + JS_FN(js_lookupGetter_str, obj_lookupGetter, 1,0), + JS_FN(js_lookupSetter_str, obj_lookupSetter, 1,0), +#endif + JS_FS_END +}; + +static JSFunctionSpec object_static_methods[] = { + JS_FN("getPrototypeOf", obj_getPrototypeOf, 1,0), + JS_FN("getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor,2,0), + JS_FN("keys", obj_keys, 1,0), + JS_FS_END +}; + +static bool +AllocSlots(JSContext *cx, JSObject *obj, size_t nslots); + +static inline bool +InitScopeForObject(JSContext* cx, JSObject* obj, JSObject* proto, JSObjectOps* ops) +{ + JS_ASSERT(OPS_IS_NATIVE(ops)); + JS_ASSERT(proto == OBJ_GET_PROTO(cx, obj)); + + /* Share proto's emptyScope only if obj is similar to proto. */ + JSClass *clasp = OBJ_GET_CLASS(cx, obj); + JSScope *scope; + if (proto && OBJ_IS_NATIVE(proto) && + (scope = OBJ_SCOPE(proto))->canProvideEmptyScope(ops, clasp)) { + scope = scope->getEmptyScope(cx, clasp); + if (!scope) + goto bad; + } else { + scope = JSScope::create(cx, ops, clasp, obj, js_GenerateShape(cx, false)); + if (!scope) + goto bad; + + /* Let JSScope::create set freeslot so as to reserve slots. */ + JS_ASSERT(scope->freeslot >= JSSLOT_PRIVATE); + if (scope->freeslot > JS_INITIAL_NSLOTS && + !AllocSlots(cx, obj, scope->freeslot)) { + JSScope::destroy(cx, scope); + goto bad; + } + } + obj->map = scope; + return true; + + bad: + /* The GC nulls map initially. It should still be null on error. */ + JS_ASSERT(!obj->map); + return false; +} + +JSObject * +js_NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto, + JSObject *parent, size_t objectSize) +{ +#ifdef INCLUDE_MOZILLA_DTRACE + if (JAVASCRIPT_OBJECT_CREATE_START_ENABLED()) + jsdtrace_object_create_start(cx->fp, clasp); +#endif + + /* Assert that the class is a proper class. */ + JS_ASSERT_IF(clasp->flags & JSCLASS_IS_EXTENDED, + ((JSExtendedClass *)clasp)->equality); + + /* Always call the class's getObjectOps hook if it has one. */ + JSObjectOps *ops = clasp->getObjectOps + ? clasp->getObjectOps(cx, clasp) + : &js_ObjectOps; + + /* + * Allocate an object from the GC heap and initialize all its fields before + * doing any operation that can potentially trigger GC. Functions have a + * larger non-standard allocation size. + */ + JSObject* obj; + if (clasp == &js_FunctionClass && !objectSize) { + obj = (JSObject*) js_NewGCFunction(cx); +#ifdef DEBUG + memset((uint8 *) obj + sizeof(JSObject), JS_FREE_PATTERN, + sizeof(JSFunction) - sizeof(JSObject)); +#endif + } else { + JS_ASSERT(!objectSize || objectSize == sizeof(JSObject)); + obj = js_NewGCObject(cx); + } + if (!obj) + goto out; + + /* + * Default parent to the parent of the prototype, which was set from + * the parent of the prototype's constructor. + */ + obj->init(clasp, + proto, + (!parent && proto) ? proto->getParent() : parent, + JSObject::defaultPrivate(clasp)); + + if (OPS_IS_NATIVE(ops)) { + if (!InitScopeForObject(cx, obj, proto, ops)) { + obj = NULL; + goto out; + } + } else { + JS_ASSERT(ops->objectMap->ops == ops); + obj->map = const_cast(ops->objectMap); + } + + /* + * Do not call debug hooks on trace, because we might be in a non-_FAIL + * builtin. See bug 481444. + */ + if (cx->debugHooks->objectHook && !JS_ON_TRACE(cx)) { + JSAutoTempValueRooter tvr(cx, obj); + JS_KEEP_ATOMS(cx->runtime); + cx->debugHooks->objectHook(cx, obj, JS_TRUE, + cx->debugHooks->objectHookData); + JS_UNKEEP_ATOMS(cx->runtime); + cx->weakRoots.finalizableNewborns[FINALIZE_OBJECT] = obj; + } + +out: +#ifdef INCLUDE_MOZILLA_DTRACE + if (JAVASCRIPT_OBJECT_CREATE_ENABLED()) + jsdtrace_object_create(cx, clasp, obj); + if (JAVASCRIPT_OBJECT_CREATE_DONE_ENABLED()) + jsdtrace_object_create_done(cx->fp, clasp); +#endif + return obj; +} + +JSObject * +js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, + JSObject *parent, size_t objectSize) +{ + jsid id; + + /* Bootstrap the ur-object, and make it the default prototype object. */ + if (!proto) { + if (!js_GetClassId(cx, clasp, &id)) + return NULL; + if (!js_GetClassPrototype(cx, parent, id, &proto)) + return NULL; + if (!proto && + !js_GetClassPrototype(cx, parent, INT_TO_JSID(JSProto_Object), + &proto)) { + return NULL; + } + } + + return js_NewObjectWithGivenProto(cx, clasp, proto, parent, objectSize); +} + +JSBool +js_Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + if (argc == 0) { + /* Trigger logic below to construct a blank object. */ + obj = NULL; + } else { + /* If argv[0] is null or undefined, obj comes back null. */ + if (!js_ValueToObject(cx, argv[0], &obj)) + return JS_FALSE; + } + if (!obj) { + JS_ASSERT(!argc || JSVAL_IS_NULL(argv[0]) || JSVAL_IS_VOID(argv[0])); + if (JS_IsConstructing(cx)) + return JS_TRUE; + obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL); + if (!obj) + return JS_FALSE; + } + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +#ifdef JS_TRACER + +JSObject* +js_NewObjectWithClassProto(JSContext *cx, JSClass *clasp, JSObject *proto, + jsval privateSlotValue) +{ + JS_ASSERT(!clasp->getObjectOps); + JS_ASSERT(proto->map->ops == &js_ObjectOps); + + JSObject* obj = js_NewGCObject(cx); + if (!obj) + return NULL; + + obj->initSharingEmptyScope(clasp, proto, proto->getParent(), privateSlotValue); + return obj; +} + +JSObject* FASTCALL +js_Object_tn(JSContext* cx, JSObject* proto) +{ + JS_ASSERT(!(js_ObjectClass.flags & JSCLASS_HAS_PRIVATE)); + return js_NewObjectWithClassProto(cx, &js_ObjectClass, proto, JSVAL_VOID); +} + +JS_DEFINE_TRCINFO_1(js_Object, + (2, (extern, CONSTRUCTOR_RETRY, js_Object_tn, CONTEXT, CALLEE_PROTOTYPE, 0, 0))) + +static inline JSObject* +NewNativeObject(JSContext* cx, JSClass* clasp, JSObject* proto, + JSObject *parent, jsval privateSlotValue) +{ + JS_ASSERT(JS_ON_TRACE(cx)); + JSObject* obj = js_NewGCObject(cx); + if (!obj) + return NULL; + + obj->init(clasp, proto, parent, privateSlotValue); + return InitScopeForObject(cx, obj, proto, &js_ObjectOps) ? obj : NULL; +} + +JSObject* FASTCALL +js_NewInstance(JSContext *cx, JSClass *clasp, JSObject *ctor) +{ + JS_ASSERT(HAS_FUNCTION_CLASS(ctor)); + + JSAtom *atom = cx->runtime->atomState.classPrototypeAtom; + + JSScope *scope = OBJ_SCOPE(ctor); +#ifdef JS_THREADSAFE + if (scope->title.ownercx != cx) + return NULL; +#endif + if (!scope->owned()) { + scope = js_GetMutableScope(cx, ctor); + if (!scope) + return NULL; + } + + JSScopeProperty *sprop = scope->lookup(ATOM_TO_JSID(atom)); + jsval pval = sprop ? STOBJ_GET_SLOT(ctor, sprop->slot) : JSVAL_HOLE; + + JSObject *proto; + if (!JSVAL_IS_PRIMITIVE(pval)) { + /* An object in ctor.prototype, let's use it as the new instance's proto. */ + proto = JSVAL_TO_OBJECT(pval); + } else if (pval == JSVAL_HOLE) { + /* No ctor.prototype yet, inline and optimize fun_resolve's prototype code. */ + proto = js_NewObject(cx, clasp, NULL, OBJ_GET_PARENT(cx, ctor)); + if (!proto) + return NULL; + if (!js_SetClassPrototype(cx, ctor, proto, JSPROP_ENUMERATE | JSPROP_PERMANENT)) + return NULL; + } else { + /* Primitive value in .prototype means we use Object.prototype for proto. */ + if (!js_GetClassPrototype(cx, JSVAL_TO_OBJECT(ctor->fslots[JSSLOT_PARENT]), + INT_TO_JSID(JSProto_Object), &proto)) { + return NULL; + } + } + + return NewNativeObject(cx, clasp, proto, ctor->getParent(), + JSObject::defaultPrivate(clasp)); +} + +JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY, js_NewInstance, CONTEXT, CLASS, OBJECT, 0, 0) + +#else /* !JS_TRACER */ + +# define js_Object_trcinfo NULL + +#endif /* !JS_TRACER */ + +/* + * Given pc pointing after a property accessing bytecode, return true if the + * access is "object-detecting" in the sense used by web scripts, e.g., when + * checking whether document.all is defined. + */ +JS_REQUIRES_STACK JSBool +Detecting(JSContext *cx, jsbytecode *pc) +{ + JSScript *script; + jsbytecode *endpc; + JSOp op; + JSAtom *atom; + + script = cx->fp->script; + endpc = script->code + script->length; + for (;; pc += js_CodeSpec[op].length) { + JS_ASSERT_IF(!cx->fp->imacpc, script->code <= pc && pc < endpc); + + /* General case: a branch or equality op follows the access. */ + op = js_GetOpcode(cx, script, pc); + if (js_CodeSpec[op].format & JOF_DETECTING) + return JS_TRUE; + + switch (op) { + case JSOP_NULL: + /* + * Special case #1: handle (document.all == null). Don't sweat + * about JS1.2's revision of the equality operators here. + */ + if (++pc < endpc) { + op = js_GetOpcode(cx, script, pc); + return *pc == JSOP_EQ || *pc == JSOP_NE; + } + return JS_FALSE; + + case JSOP_NAME: + /* + * Special case #2: handle (document.all == undefined). Don't + * worry about someone redefining undefined, which was added by + * Edition 3, so is read/write for backward compatibility. + */ + GET_ATOM_FROM_BYTECODE(script, pc, 0, atom); + if (atom == cx->runtime->atomState.typeAtoms[JSTYPE_VOID] && + (pc += js_CodeSpec[op].length) < endpc) { + op = js_GetOpcode(cx, script, pc); + return op == JSOP_EQ || op == JSOP_NE || + op == JSOP_STRICTEQ || op == JSOP_STRICTNE; + } + return JS_FALSE; + + default: + /* + * At this point, anything but an extended atom index prefix means + * we're not detecting. + */ + if (!(js_CodeSpec[op].format & JOF_INDEXBASE)) + return JS_FALSE; + break; + } + } +} + +/* + * Infer lookup flags from the currently executing bytecode. This does + * not attempt to infer JSRESOLVE_WITH, because the current bytecode + * does not indicate whether we are in a with statement. Return defaultFlags + * if a currently executing bytecode cannot be determined. + */ +uintN +js_InferFlags(JSContext *cx, uintN defaultFlags) +{ +#ifdef JS_TRACER + if (JS_ON_TRACE(cx)) + return cx->bailExit->lookupFlags; +#endif + + JS_ASSERT_NOT_ON_TRACE(cx); + + JSStackFrame *fp; + jsbytecode *pc; + const JSCodeSpec *cs; + uint32 format; + uintN flags = 0; + + fp = js_GetTopStackFrame(cx); + if (!fp || !fp->regs) + return defaultFlags; + pc = fp->regs->pc; + cs = &js_CodeSpec[js_GetOpcode(cx, fp->script, pc)]; + format = cs->format; + if (JOF_MODE(format) != JOF_NAME) + flags |= JSRESOLVE_QUALIFIED; + if ((format & (JOF_SET | JOF_FOR)) || + (fp->flags & JSFRAME_ASSIGNING)) { + flags |= JSRESOLVE_ASSIGNING; + } else if (cs->length >= 0) { + pc += cs->length; + if (pc < cx->fp->script->code + cx->fp->script->length && Detecting(cx, pc)) + flags |= JSRESOLVE_DETECTING; + } + if (format & JOF_DECLARING) + flags |= JSRESOLVE_DECLARING; + return flags; +} + +/* + * ObjectOps and Class for with-statement stack objects. + */ +static JSBool +with_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, + JSProperty **propp) +{ + /* Fixes bug 463997 */ + uintN flags = cx->resolveFlags; + if (flags == JSRESOLVE_INFER) + flags = js_InferFlags(cx, flags); + flags |= JSRESOLVE_WITH; + JSAutoResolveFlags rf(cx, flags); + JSObject *proto = OBJ_GET_PROTO(cx, obj); + if (!proto) + return js_LookupProperty(cx, obj, id, objp, propp); + return proto->lookupProperty(cx, id, objp, propp); +} + +static JSBool +with_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + JSObject *proto = OBJ_GET_PROTO(cx, obj); + if (!proto) + return js_GetProperty(cx, obj, id, vp); + return proto->getProperty(cx, id, vp); +} + +static JSBool +with_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + JSObject *proto = OBJ_GET_PROTO(cx, obj); + if (!proto) + return js_SetProperty(cx, obj, id, vp); + return proto->setProperty(cx, id, vp); +} + +static JSBool +with_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, + uintN *attrsp) +{ + JSObject *proto = OBJ_GET_PROTO(cx, obj); + if (!proto) + return js_GetAttributes(cx, obj, id, prop, attrsp); + return proto->getAttributes(cx, id, prop, attrsp); +} + +static JSBool +with_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, + uintN *attrsp) +{ + JSObject *proto = OBJ_GET_PROTO(cx, obj); + if (!proto) + return js_SetAttributes(cx, obj, id, prop, attrsp); + return proto->setAttributes(cx, id, prop, attrsp); +} + +static JSBool +with_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval) +{ + JSObject *proto = OBJ_GET_PROTO(cx, obj); + if (!proto) + return js_DeleteProperty(cx, obj, id, rval); + return proto->deleteProperty(cx, id, rval); +} + +static JSBool +with_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp) +{ + JSObject *proto = OBJ_GET_PROTO(cx, obj); + if (!proto) + return js_DefaultValue(cx, obj, hint, vp); + return proto->defaultValue(cx, hint, vp); +} + +static JSBool +with_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, + jsval *statep, jsid *idp) +{ + JSObject *proto = OBJ_GET_PROTO(cx, obj); + if (!proto) + return js_Enumerate(cx, obj, enum_op, statep, idp); + return proto->enumerate(cx, enum_op, statep, idp); +} + +static JSBool +with_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, + jsval *vp, uintN *attrsp) +{ + JSObject *proto = OBJ_GET_PROTO(cx, obj); + if (!proto) + return js_CheckAccess(cx, obj, id, mode, vp, attrsp); + return proto->checkAccess(cx, id, mode, vp, attrsp); +} + +static JSObject * +with_ThisObject(JSContext *cx, JSObject *obj) +{ + JSObject *proto = OBJ_GET_PROTO(cx, obj); + if (!proto) + return obj; + return proto->thisObject(cx); +} + +JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps = { + NULL, + with_LookupProperty, js_DefineProperty, + with_GetProperty, with_SetProperty, + with_GetAttributes, with_SetAttributes, + with_DeleteProperty, with_DefaultValue, + with_Enumerate, with_CheckAccess, + with_ThisObject, NATIVE_DROP_PROPERTY, + NULL, NULL, + NULL, js_TraceObject, + js_Clear +}; + +static JSObjectOps * +with_getObjectOps(JSContext *cx, JSClass *clasp) +{ + return &js_WithObjectOps; +} + +JSClass js_WithClass = { + "With", + JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL, + with_getObjectOps, + 0,0,0,0,0,0,0 +}; + +JS_REQUIRES_STACK JSObject * +js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth) +{ + JSObject *obj; + + obj = js_NewObject(cx, &js_WithClass, proto, parent); + if (!obj) + return NULL; + obj->setPrivate(cx->fp); + OBJ_SET_BLOCK_DEPTH(cx, obj, depth); + return obj; +} + +JSObject * +js_NewBlockObject(JSContext *cx) +{ + /* + * Null obj's proto slot so that Object.prototype.* does not pollute block + * scopes and to give the block object its own scope. + */ + JSObject *blockObj = js_NewObjectWithGivenProto(cx, &js_BlockClass, NULL, NULL); + JS_ASSERT_IF(blockObj, !OBJ_IS_CLONED_BLOCK(blockObj)); + return blockObj; +} + +JSObject * +js_CloneBlockObject(JSContext *cx, JSObject *proto, JSStackFrame *fp) +{ + JS_ASSERT(!OBJ_IS_CLONED_BLOCK(proto)); + JS_ASSERT(STOBJ_GET_CLASS(proto) == &js_BlockClass); + + JSObject *clone = js_NewGCObject(cx); + if (!clone) + return NULL; + + JSScope *scope = OBJ_SCOPE(proto); + scope->hold(); + JS_ASSERT(!scope->owned()); + clone->map = scope; + + clone->classword = jsuword(&js_BlockClass); + clone->setProto(proto); + clone->setParent(NULL); // caller's responsibility + clone->setPrivate(fp); + clone->fslots[JSSLOT_BLOCK_DEPTH] = proto->fslots[JSSLOT_BLOCK_DEPTH]; + JS_ASSERT(scope->freeslot == JSSLOT_BLOCK_DEPTH + 1); + for (uint32 i = JSSLOT_BLOCK_DEPTH + 1; i < JS_INITIAL_NSLOTS; ++i) + clone->fslots[i] = JSVAL_VOID; + clone->dslots = NULL; + JS_ASSERT(OBJ_IS_CLONED_BLOCK(clone)); + return clone; +} + +JS_REQUIRES_STACK JSBool +js_PutBlockObject(JSContext *cx, JSBool normalUnwind) +{ + JSStackFrame *fp; + JSObject *obj; + uintN depth, count; + + /* Blocks have one fixed slot available for the first local.*/ + JS_STATIC_ASSERT(JS_INITIAL_NSLOTS == JSSLOT_BLOCK_DEPTH + 2); + + fp = cx->fp; + obj = fp->scopeChain; + JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass); + JS_ASSERT(obj->getPrivate() == cx->fp); + JS_ASSERT(OBJ_IS_CLONED_BLOCK(obj)); + + /* + * Block objects should never be exposed to scripts. Thus the clone should + * not own the property map and rather always share it with the prototype + * object. This allows us to skip updating OBJ_SCOPE(obj)->freeslot after + * we copy the stack slots into reserved slots. + */ + JS_ASSERT(OBJ_SCOPE(obj)->object != obj); + + /* Block objects should not have reserved slots before they are put. */ + JS_ASSERT(STOBJ_NSLOTS(obj) == JS_INITIAL_NSLOTS); + + /* The block and its locals must be on the current stack for GC safety. */ + depth = OBJ_BLOCK_DEPTH(cx, obj); + count = OBJ_BLOCK_COUNT(cx, obj); + JS_ASSERT(depth <= (size_t) (fp->regs->sp - StackBase(fp))); + JS_ASSERT(count <= (size_t) (fp->regs->sp - StackBase(fp) - depth)); + + /* See comments in CheckDestructuring from jsparse.cpp. */ + JS_ASSERT(count >= 1); + + depth += fp->script->nfixed; + obj->fslots[JSSLOT_BLOCK_DEPTH + 1] = fp->slots[depth]; + if (normalUnwind && count > 1) { + --count; + JS_LOCK_OBJ(cx, obj); + if (!AllocSlots(cx, obj, JS_INITIAL_NSLOTS + count)) + normalUnwind = JS_FALSE; + else + memcpy(obj->dslots, fp->slots + depth + 1, count * sizeof(jsval)); + JS_UNLOCK_OBJ(cx, obj); + } + + /* We must clear the private slot even with errors. */ + obj->setPrivate(NULL); + fp->scopeChain = OBJ_GET_PARENT(cx, obj); + return normalUnwind; +} + +static JSBool +block_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + /* + * Block objects are never exposed to script, and the engine handles them + * with care. So unlike other getters, this one can assert (rather than + * check) certain invariants about obj. + */ + JS_ASSERT(obj->getClass() == &js_BlockClass); + JS_ASSERT(OBJ_IS_CLONED_BLOCK(obj)); + uintN index = (uintN) JSVAL_TO_INT(id); + JS_ASSERT(index < OBJ_BLOCK_COUNT(cx, obj)); + + JSStackFrame *fp = (JSStackFrame *) obj->getPrivate(); + if (fp) { + index += fp->script->nfixed + OBJ_BLOCK_DEPTH(cx, obj); + JS_ASSERT(index < fp->script->nslots); + *vp = fp->slots[index]; + return true; + } + + /* Values are in reserved slots immediately following DEPTH. */ + uint32 slot = JSSLOT_BLOCK_DEPTH + 1 + index; + JS_LOCK_OBJ(cx, obj); + JS_ASSERT(slot < STOBJ_NSLOTS(obj)); + *vp = STOBJ_GET_SLOT(obj, slot); + JS_UNLOCK_OBJ(cx, obj); + return true; +} + +static JSBool +block_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JS_ASSERT(obj->getClass() == &js_BlockClass); + JS_ASSERT(OBJ_IS_CLONED_BLOCK(obj)); + uintN index = (uintN) JSVAL_TO_INT(id); + JS_ASSERT(index < OBJ_BLOCK_COUNT(cx, obj)); + + JSStackFrame *fp = (JSStackFrame *) obj->getPrivate(); + if (fp) { + index += fp->script->nfixed + OBJ_BLOCK_DEPTH(cx, obj); + JS_ASSERT(index < fp->script->nslots); + fp->slots[index] = *vp; + return true; + } + + /* Values are in reserved slots immediately following DEPTH. */ + uint32 slot = JSSLOT_BLOCK_DEPTH + 1 + index; + JS_LOCK_OBJ(cx, obj); + JS_ASSERT(slot < STOBJ_NSLOTS(obj)); + STOBJ_SET_SLOT(obj, slot, *vp); + JS_UNLOCK_OBJ(cx, obj); + return true; +} + +JSBool +js_DefineBlockVariable(JSContext *cx, JSObject *obj, jsid id, intN index) +{ + JS_ASSERT(obj->getClass() == &js_BlockClass); + JS_ASSERT(!OBJ_IS_CLONED_BLOCK(obj)); + + /* Use JSPROP_ENUMERATE to aid the disassembler. */ + return js_DefineNativeProperty(cx, obj, id, JSVAL_VOID, + block_getProperty, block_setProperty, + JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, + SPROP_HAS_SHORTID, index, NULL); +} + +#if JS_HAS_XDR + +#define NO_PARENT_INDEX ((uint32)-1) + +uint32 +FindObjectIndex(JSObjectArray *array, JSObject *obj) +{ + size_t i; + + if (array) { + i = array->length; + do { + + if (array->vector[--i] == obj) + return i; + } while (i != 0); + } + + return NO_PARENT_INDEX; +} + +JSBool +js_XDRBlockObject(JSXDRState *xdr, JSObject **objp) +{ + JSContext *cx; + uint32 parentId; + JSObject *obj, *parent; + uint16 depth, count, i; + uint32 tmp; + JSScopeProperty *sprop; + jsid propid; + JSAtom *atom; + int16 shortid; + JSBool ok; + + cx = xdr->cx; +#ifdef __GNUC__ + obj = NULL; /* quell GCC overwarning */ +#endif + + if (xdr->mode == JSXDR_ENCODE) { + obj = *objp; + parent = OBJ_GET_PARENT(cx, obj); + parentId = (xdr->script->objectsOffset == 0) + ? NO_PARENT_INDEX + : FindObjectIndex(xdr->script->objects(), parent); + depth = (uint16)OBJ_BLOCK_DEPTH(cx, obj); + count = (uint16)OBJ_BLOCK_COUNT(cx, obj); + tmp = (uint32)(depth << 16) | count; + } +#ifdef __GNUC__ /* suppress bogus gcc warnings */ + else count = 0; +#endif + + /* First, XDR the parent atomid. */ + if (!JS_XDRUint32(xdr, &parentId)) + return JS_FALSE; + + if (xdr->mode == JSXDR_DECODE) { + obj = js_NewBlockObject(cx); + if (!obj) + return JS_FALSE; + *objp = obj; + + /* + * If there's a parent id, then get the parent out of our script's + * object array. We know that we XDR block object in outer-to-inner + * order, which means that getting the parent now will work. + */ + if (parentId == NO_PARENT_INDEX) + parent = NULL; + else + parent = xdr->script->getObject(parentId); + STOBJ_SET_PARENT(obj, parent); + } + + JSAutoTempValueRooter tvr(cx, obj); + + if (!JS_XDRUint32(xdr, &tmp)) + return false; + + if (xdr->mode == JSXDR_DECODE) { + depth = (uint16)(tmp >> 16); + count = (uint16)tmp; + STOBJ_SET_SLOT(obj, JSSLOT_BLOCK_DEPTH, INT_TO_JSVAL(depth)); + } + + /* + * XDR the block object's properties. We know that there are 'count' + * properties to XDR, stored as id/shortid pairs. We do not XDR any + * non-native properties, only those that the compiler created. + */ + sprop = NULL; + ok = JS_TRUE; + for (i = 0; i < count; i++) { + if (xdr->mode == JSXDR_ENCODE) { + /* Find a property to XDR. */ + do { + /* If sprop is NULL, this is the first property. */ + sprop = sprop ? sprop->parent : OBJ_SCOPE(obj)->lastProperty(); + } while (!(sprop->flags & SPROP_HAS_SHORTID)); + + JS_ASSERT(sprop->getter == block_getProperty); + propid = sprop->id; + JS_ASSERT(JSID_IS_ATOM(propid)); + atom = JSID_TO_ATOM(propid); + shortid = sprop->shortid; + JS_ASSERT(shortid >= 0); + } + + /* XDR the real id, then the shortid. */ + if (!js_XDRStringAtom(xdr, &atom) || + !JS_XDRUint16(xdr, (uint16 *)&shortid)) { + return false; + } + + if (xdr->mode == JSXDR_DECODE) { + if (!js_DefineBlockVariable(cx, obj, ATOM_TO_JSID(atom), shortid)) + return false; + } + } + + if (xdr->mode == JSXDR_DECODE) { + /* Do as the parser does and make this block scope shareable. */ + OBJ_SCOPE(obj)->object = NULL; + } + return true; +} + +#endif + +static uint32 +block_reserveSlots(JSContext *cx, JSObject *obj) +{ + return OBJ_IS_CLONED_BLOCK(obj) ? OBJ_BLOCK_COUNT(cx, obj) : 0; +} + +JSClass js_BlockClass = { + "Block", + JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, block_reserveSlots +}; + +JSObject * +js_InitEval(JSContext *cx, JSObject *obj) +{ + /* ECMA (15.1.2.1) says 'eval' is a property of the global object. */ + if (!js_DefineFunction(cx, obj, cx->runtime->atomState.evalAtom, + obj_eval, 1, 0)) { + return NULL; + } + + return obj; +} + +JSObject * +js_InitObjectClass(JSContext *cx, JSObject *obj) +{ + return js_InitClass(cx, obj, NULL, &js_ObjectClass, js_Object, 1, + object_props, object_methods, NULL, object_static_methods); +} + +JSObject * +js_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, + JSClass *clasp, JSNative constructor, uintN nargs, + JSPropertySpec *ps, JSFunctionSpec *fs, + JSPropertySpec *static_ps, JSFunctionSpec *static_fs) +{ + JSAtom *atom; + JSProtoKey key; + JSObject *proto, *ctor; + JSTempValueRooter tvr; + jsval cval, rval; + JSBool named; + JSFunction *fun; + + atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0); + if (!atom) + return NULL; + + /* + * When initializing a standard class, if no parent_proto (grand-proto of + * instances of the class, parent-proto of the class's prototype object) + * is given, we must use Object.prototype if it is available. Otherwise, + * we could look up the wrong binding for a class name in obj. Example: + * + * String = Array; + * print("hi there".join); + * + * should print undefined, not Array.prototype.join. This is required by + * ECMA-262, alas. It might have been better to make String readonly and + * permanent in the global object, instead -- but that's too big a change + * to swallow at this point. + */ + key = JSCLASS_CACHED_PROTO_KEY(clasp); + if (key != JSProto_Null && + !parent_proto && + !js_GetClassPrototype(cx, obj, INT_TO_JSID(JSProto_Object), + &parent_proto)) { + return NULL; + } + + /* Create a prototype object for this class. */ + proto = js_NewObject(cx, clasp, parent_proto, obj); + if (!proto) + return NULL; + + /* After this point, control must exit via label bad or out. */ + JS_PUSH_TEMP_ROOT_OBJECT(cx, proto, &tvr); + + if (!constructor) { + /* + * Lacking a constructor, name the prototype (e.g., Math) unless this + * class (a) is anonymous, i.e. for internal use only; (b) the class + * of obj (the global object) is has a reserved slot indexed by key; + * and (c) key is not the null key. + */ + if ((clasp->flags & JSCLASS_IS_ANONYMOUS) && + (OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL) && + key != JSProto_Null) { + named = JS_FALSE; + } else { + named = obj->defineProperty(cx, ATOM_TO_JSID(atom), + OBJECT_TO_JSVAL(proto), + JS_PropertyStub, JS_PropertyStub, + (clasp->flags & JSCLASS_IS_ANONYMOUS) + ? JSPROP_READONLY | JSPROP_PERMANENT + : 0); + if (!named) + goto bad; + } + + ctor = proto; + } else { + /* Define the constructor function in obj's scope. */ + fun = js_DefineFunction(cx, obj, atom, constructor, nargs, + JSFUN_STUB_GSOPS); + named = (fun != NULL); + if (!fun) + goto bad; + + /* + * Remember the class this function is a constructor for so that + * we know to create an object of this class when we call the + * constructor. + */ + FUN_CLASP(fun) = clasp; + + /* + * Optionally construct the prototype object, before the class has + * been fully initialized. Allow the ctor to replace proto with a + * different object, as is done for operator new -- and as at least + * XML support requires. + */ + ctor = FUN_OBJECT(fun); + if (clasp->flags & JSCLASS_CONSTRUCT_PROTOTYPE) { + cval = OBJECT_TO_JSVAL(ctor); + if (!js_InternalConstruct(cx, proto, cval, 0, NULL, &rval)) + goto bad; + if (!JSVAL_IS_PRIMITIVE(rval) && JSVAL_TO_OBJECT(rval) != proto) + proto = JSVAL_TO_OBJECT(rval); + } + + /* Connect constructor and prototype by named properties. */ + if (!js_SetClassPrototype(cx, ctor, proto, + JSPROP_READONLY | JSPROP_PERMANENT)) { + goto bad; + } + + /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */ + if (OBJ_GET_CLASS(cx, ctor) == clasp) + OBJ_SET_PROTO(cx, ctor, proto); + } + + /* Add properties and methods to the prototype and the constructor. */ + if ((ps && !JS_DefineProperties(cx, proto, ps)) || + (fs && !JS_DefineFunctions(cx, proto, fs)) || + (static_ps && !JS_DefineProperties(cx, ctor, static_ps)) || + (static_fs && !JS_DefineFunctions(cx, ctor, static_fs))) { + goto bad; + } + + /* + * Make sure proto's scope's emptyScope is available to be shared by + * objects of this class. JSScope::emptyScope is a one-slot cache. If we + * omit this, some other class could snap it up. (The risk is particularly + * great for Object.prototype.) + * + * All callers of JSObject::initSharingEmptyScope depend on this. + */ + { + JSScope *scope = OBJ_SCOPE(proto)->getEmptyScope(cx, clasp); + if (!scope) + goto bad; + scope->drop(cx, NULL); + } + + /* If this is a standard class, cache its prototype. */ + if (key != JSProto_Null && !js_SetClassObject(cx, obj, key, ctor)) + goto bad; + +out: + JS_POP_TEMP_ROOT(cx, &tvr); + return proto; + +bad: + if (named) + (void) obj->deleteProperty(cx, ATOM_TO_JSID(atom), &rval); + proto = NULL; + goto out; +} + +#define SLOTS_TO_DYNAMIC_WORDS(nslots) \ + (JS_ASSERT((nslots) > JS_INITIAL_NSLOTS), (nslots) + 1 - JS_INITIAL_NSLOTS) + +#define DYNAMIC_WORDS_TO_SLOTS(words) \ + (JS_ASSERT((words) > 1), (words) - 1 + JS_INITIAL_NSLOTS) + + +static bool +AllocSlots(JSContext *cx, JSObject *obj, size_t nslots) +{ + JS_ASSERT(!obj->dslots); + JS_ASSERT(nslots > JS_INITIAL_NSLOTS); + + jsval* slots; + slots = (jsval*) cx->malloc(SLOTS_TO_DYNAMIC_WORDS(nslots) * sizeof(jsval)); + if (!slots) + return true; + + *slots++ = nslots; + /* clear the newly allocated cells. */ + for (jsuint n = JS_INITIAL_NSLOTS; n < nslots; ++n) + slots[n - JS_INITIAL_NSLOTS] = JSVAL_VOID; + obj->dslots = slots; + + return true; +} + +bool +js_GrowSlots(JSContext *cx, JSObject *obj, size_t nslots) +{ + /* + * Minimal number of dynamic slots to allocate. + */ + const size_t MIN_DYNAMIC_WORDS = 4; + + /* + * The limit to switch to linear allocation strategy from the power of 2 + * growth no to waste too much memory. + */ + const size_t LINEAR_GROWTH_STEP = JS_BIT(16); + + /* If we are allocating fslots, there is nothing to do. */ + if (nslots <= JS_INITIAL_NSLOTS) + return JS_TRUE; + + size_t nwords = SLOTS_TO_DYNAMIC_WORDS(nslots); + + /* + * Round up nslots so the number of bytes in dslots array is power + * of 2 to ensure exponential grouth. + */ + uintN log; + if (nwords <= MIN_DYNAMIC_WORDS) { + nwords = MIN_DYNAMIC_WORDS; + } else if (nwords < LINEAR_GROWTH_STEP) { + JS_CEILING_LOG2(log, nwords); + nwords = JS_BIT(log); + } else { + nwords = JS_ROUNDUP(nwords, LINEAR_GROWTH_STEP); + } + nslots = DYNAMIC_WORDS_TO_SLOTS(nwords); + + /* + * If nothing was allocated yet, treat it as initial allocation (but with + * the exponential growth algorithm applied). + */ + jsval* slots = obj->dslots; + if (!slots) + return AllocSlots(cx, obj, nslots); + + size_t oslots = size_t(slots[-1]); + + slots = (jsval*) cx->realloc(slots - 1, nwords * sizeof(jsval)); + *slots++ = nslots; + obj->dslots = slots; + + /* Initialize the additional slots we added. */ + JS_ASSERT(nslots > oslots); + for (size_t i = oslots; i < nslots; i++) + slots[i - JS_INITIAL_NSLOTS] = JSVAL_VOID; + + return true; +} + +void +js_ShrinkSlots(JSContext *cx, JSObject *obj, size_t nslots) +{ + jsval* slots = obj->dslots; + + /* Nothing to shrink? */ + if (!slots) + return; + + JS_ASSERT(size_t(slots[-1]) > JS_INITIAL_NSLOTS); + JS_ASSERT(nslots <= size_t(slots[-1])); + + if (nslots <= JS_INITIAL_NSLOTS) { + cx->free(slots - 1); + obj->dslots = NULL; + } else { + size_t nwords = SLOTS_TO_DYNAMIC_WORDS(nslots); + slots = (jsval*) cx->realloc(slots - 1, nwords * sizeof(jsval)); + *slots++ = nslots; + obj->dslots = slots; + } +} + +bool +js_EnsureReservedSlots(JSContext *cx, JSObject *obj, size_t nreserved) +{ + JS_ASSERT(OBJ_IS_NATIVE(obj)); + JS_ASSERT(!obj->dslots); + + uintN nslots = JSSLOT_FREE(STOBJ_GET_CLASS(obj)) + nreserved; + if (nslots > STOBJ_NSLOTS(obj) && !AllocSlots(cx, obj, nslots)) + return false; + + JSScope *scope = OBJ_SCOPE(obj); + if (scope->owned()) { +#ifdef JS_THREADSAFE + JS_ASSERT(scope->title.ownercx->thread == cx->thread); +#endif + JS_ASSERT(scope->freeslot == JSSLOT_FREE(STOBJ_GET_CLASS(obj))); + if (scope->freeslot < nslots) + scope->freeslot = nslots; + } + return true; +} + +extern JSBool +js_GetClassId(JSContext *cx, JSClass *clasp, jsid *idp) +{ + JSProtoKey key; + JSAtom *atom; + + key = JSCLASS_CACHED_PROTO_KEY(clasp); + if (key != JSProto_Null) { + *idp = INT_TO_JSID(key); + } else if (clasp->flags & JSCLASS_IS_ANONYMOUS) { + *idp = INT_TO_JSID(JSProto_Object); + } else { + atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0); + if (!atom) + return JS_FALSE; + *idp = ATOM_TO_JSID(atom); + } + return JS_TRUE; +} + +JS_BEGIN_EXTERN_C + +static JSObject * +js_InitNullClass(JSContext *cx, JSObject *obj) +{ + JS_ASSERT(0); + return NULL; +} + +#define JS_PROTO(name,code,init) extern JSObject *init(JSContext *, JSObject *); +#include "jsproto.tbl" +#undef JS_PROTO + +static JSObjectOp lazy_prototype_init[JSProto_LIMIT] = { +#define JS_PROTO(name,code,init) init, +#include "jsproto.tbl" +#undef JS_PROTO +}; + +JS_END_EXTERN_C + +JSBool +js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, + JSObject **objp) +{ + JSBool ok; + JSObject *tmp, *cobj; + JSResolvingKey rkey; + JSResolvingEntry *rentry; + uint32 generation; + JSObjectOp init; + jsval v; + + while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL) + obj = tmp; + if (!(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL)) { + *objp = NULL; + return JS_TRUE; + } + + ok = JS_GetReservedSlot(cx, obj, key, &v); + if (!ok) + return JS_FALSE; + if (!JSVAL_IS_PRIMITIVE(v)) { + *objp = JSVAL_TO_OBJECT(v); + return JS_TRUE; + } + + rkey.obj = obj; + rkey.id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]); + if (!js_StartResolving(cx, &rkey, JSRESFLAG_LOOKUP, &rentry)) + return JS_FALSE; + if (!rentry) { + /* Already caching key in obj -- suppress recursion. */ + *objp = NULL; + return JS_TRUE; + } + generation = cx->resolvingTable->generation; + + cobj = NULL; + init = lazy_prototype_init[key]; + if (init) { + if (!init(cx, obj)) { + ok = JS_FALSE; + } else { + ok = JS_GetReservedSlot(cx, obj, key, &v); + if (ok && !JSVAL_IS_PRIMITIVE(v)) + cobj = JSVAL_TO_OBJECT(v); + } + } + + js_StopResolving(cx, &rkey, JSRESFLAG_LOOKUP, rentry, generation); + *objp = cobj; + return ok; +} + +JSBool +js_SetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject *cobj) +{ + JS_ASSERT(!OBJ_GET_PARENT(cx, obj)); + if (!(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL)) + return JS_TRUE; + + return JS_SetReservedSlot(cx, obj, key, OBJECT_TO_JSVAL(cobj)); +} + +JSBool +js_FindClassObject(JSContext *cx, JSObject *start, jsid id, jsval *vp) +{ + JSStackFrame *fp; + JSObject *obj, *cobj, *pobj; + JSProtoKey key; + JSProperty *prop; + jsval v; + JSScopeProperty *sprop; + + /* + * Find the global object. Use cx->fp directly to avoid falling off + * trace; all JIT-elided stack frames have the same global object as + * cx->fp. + */ + VOUCH_DOES_NOT_REQUIRE_STACK(); + if (!start && (fp = cx->fp) != NULL) + start = fp->scopeChain; + + if (start) { + /* Find the topmost object in the scope chain. */ + do { + obj = start; + start = OBJ_GET_PARENT(cx, obj); + } while (start); + } else { + obj = cx->globalObject; + if (!obj) { + *vp = JSVAL_VOID; + return JS_TRUE; + } + } + + OBJ_TO_INNER_OBJECT(cx, obj); + if (!obj) + return JS_FALSE; + + if (JSID_IS_INT(id)) { + key = (JSProtoKey) JSID_TO_INT(id); + JS_ASSERT(key != JSProto_Null); + if (!js_GetClassObject(cx, obj, key, &cobj)) + return JS_FALSE; + if (cobj) { + *vp = OBJECT_TO_JSVAL(cobj); + return JS_TRUE; + } + id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]); + } + + JS_ASSERT(OBJ_IS_NATIVE(obj)); + if (js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_CLASSNAME, + &pobj, &prop) < 0) { + return JS_FALSE; + } + v = JSVAL_VOID; + if (prop) { + if (OBJ_IS_NATIVE(pobj)) { + sprop = (JSScopeProperty *) prop; + if (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))) { + v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot); + if (JSVAL_IS_PRIMITIVE(v)) + v = JSVAL_VOID; + } + } + pobj->dropProperty(cx, prop); + } + *vp = v; + return JS_TRUE; +} + +JSObject * +js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, + JSObject *parent, uintN argc, jsval *argv) +{ + jsid id; + jsval cval, rval; + JSObject *obj, *ctor; + + JSAutoTempValueRooter argtvr(cx, argc, argv); + + if (!js_GetClassId(cx, clasp, &id) || + !js_FindClassObject(cx, parent, id, &cval)) { + return NULL; + } + + if (JSVAL_IS_PRIMITIVE(cval)) { + js_ReportIsNotFunction(cx, &cval, JSV2F_CONSTRUCT | JSV2F_SEARCH_STACK); + return NULL; + } + + /* Protect cval in case a crazy getter for .prototype uproots it. */ + JSAutoTempValueRooter tvr(cx, cval); + + /* + * If proto or parent are NULL, set them to Constructor.prototype and/or + * Constructor.__parent__, just like JSOP_NEW does. + */ + ctor = JSVAL_TO_OBJECT(cval); + if (!parent) + parent = OBJ_GET_PARENT(cx, ctor); + if (!proto) { + if (!ctor->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), + &rval)) { + return NULL; + } + if (JSVAL_IS_OBJECT(rval)) + proto = JSVAL_TO_OBJECT(rval); + } + + obj = js_NewObject(cx, clasp, proto, parent); + if (!obj) + return NULL; + + if (!js_InternalConstruct(cx, obj, cval, argc, argv, &rval)) + return NULL; + + if (JSVAL_IS_PRIMITIVE(rval)) + return obj; + + /* + * If the instance's class differs from what was requested, throw a type + * error. If the given class has both the JSCLASS_HAS_PRIVATE and the + * JSCLASS_CONSTRUCT_PROTOTYPE flags, and the instance does not have its + * private data set at this point, then the constructor was replaced and + * we should throw a type error. + */ + obj = JSVAL_TO_OBJECT(rval); + if (OBJ_GET_CLASS(cx, obj) != clasp || + (!(~clasp->flags & (JSCLASS_HAS_PRIVATE | + JSCLASS_CONSTRUCT_PROTOTYPE)) && + !obj->getPrivate())) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_WRONG_CONSTRUCTOR, clasp->name); + return NULL; + } + return obj; +} + +/* XXXbe if one adds props, deletes earlier props, adds more, the last added + won't recycle the deleted props' slots. */ +JSBool +js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp) +{ + JS_ASSERT(OBJ_IS_NATIVE(obj)); + + JSScope *scope = OBJ_SCOPE(obj); + JSClass *clasp = obj->getClass(); + if (scope->freeslot == JSSLOT_FREE(clasp) && clasp->reserveSlots) { + /* Adjust scope->freeslot to include computed reserved slots, if any. */ + scope->freeslot += clasp->reserveSlots(cx, obj); + } + + if (scope->freeslot >= STOBJ_NSLOTS(obj) && + !js_GrowSlots(cx, obj, scope->freeslot + 1)) { + return JS_FALSE; + } + + /* js_ReallocSlots or js_FreeSlot should set the free slots to void. */ + JS_ASSERT(JSVAL_IS_VOID(STOBJ_GET_SLOT(obj, scope->freeslot))); + *slotp = scope->freeslot++; + return JS_TRUE; +} + +void +js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot) +{ + JS_ASSERT(OBJ_IS_NATIVE(obj)); + + JSScope *scope = OBJ_SCOPE(obj); + LOCKED_OBJ_SET_SLOT(obj, slot, JSVAL_VOID); + if (scope->freeslot == slot + 1) + scope->freeslot = slot; +} + + +/* JSVAL_INT_MAX as a string */ +#define JSVAL_INT_MAX_STRING "1073741823" + +/* + * Convert string indexes that convert to int jsvals as ints to save memory. + * Care must be taken to use this macro every time a property name is used, or + * else double-sets, incorrect property cache misses, or other mistakes could + * occur. + */ +jsid +js_CheckForStringIndex(jsid id) +{ + if (!JSID_IS_ATOM(id)) + return id; + + JSAtom *atom = JSID_TO_ATOM(id); + JSString *str = ATOM_TO_STRING(atom); + const jschar *s = str->flatChars(); + jschar ch = *s; + + JSBool negative = (ch == '-'); + if (negative) + ch = *++s; + + if (!JS7_ISDEC(ch)) + return id; + + size_t n = str->flatLength() - negative; + if (n > sizeof(JSVAL_INT_MAX_STRING) - 1) + return id; + + const jschar *cp = s; + const jschar *end = s + n; + + jsuint index = JS7_UNDEC(*cp++); + jsuint oldIndex = 0; + jsuint c = 0; + + if (index != 0) { + while (JS7_ISDEC(*cp)) { + oldIndex = index; + c = JS7_UNDEC(*cp); + index = 10 * index + c; + cp++; + } + } + + /* + * Non-integer indexes can't be represented as integers. Also, distinguish + * index "-0" from "0", because JSVAL_INT cannot. + */ + if (cp != end || (negative && index == 0)) + return id; + + if (oldIndex < JSVAL_INT_MAX / 10 || + (oldIndex == JSVAL_INT_MAX / 10 && c <= (JSVAL_INT_MAX % 10))) { + if (negative) + index = 0 - index; + id = INT_TO_JSID((jsint)index); + } + + return id; +} + +static JSBool +PurgeProtoChain(JSContext *cx, JSObject *obj, jsid id) +{ + JSScope *scope; + JSScopeProperty *sprop; + + while (obj) { + if (!OBJ_IS_NATIVE(obj)) { + obj = OBJ_GET_PROTO(cx, obj); + continue; + } + JS_LOCK_OBJ(cx, obj); + scope = OBJ_SCOPE(obj); + sprop = scope->lookup(id); + if (sprop) { + PCMETER(JS_PROPERTY_CACHE(cx).pcpurges++); + scope->shadowingShapeChange(cx, sprop); + JS_UNLOCK_SCOPE(cx, scope); + + if (!STOBJ_GET_PARENT(obj)) { + /* + * All scope chains end in a global object, so this will change + * the global shape. jstracer.cpp assumes that the global shape + * never changes on trace, so we must deep-bail here. + */ + js_LeaveTrace(cx); + } + return JS_TRUE; + } + obj = obj->getProto(); + JS_UNLOCK_SCOPE(cx, scope); + } + return JS_FALSE; +} + +void +js_PurgeScopeChainHelper(JSContext *cx, JSObject *obj, jsid id) +{ + JS_ASSERT(obj->isDelegate()); + PurgeProtoChain(cx, obj->getProto(), id); + + /* + * We must purge the scope chain only for Call objects as they are the only + * kind of cacheable non-global object that can gain properties after outer + * properties with the same names have been cached or traced. Call objects + * may gain such properties via eval introducing new vars; see bug 490364. + */ + if (STOBJ_GET_CLASS(obj) == &js_CallClass) { + while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL) { + if (PurgeProtoChain(cx, obj, id)) + break; + } + } +} + +JSScopeProperty * +js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id, + JSPropertyOp getter, JSPropertyOp setter, uint32 slot, + uintN attrs, uintN flags, intN shortid) +{ + JSScope *scope; + JSScopeProperty *sprop; + + JS_ASSERT(!(flags & SPROP_IS_METHOD)); + + /* + * Purge the property cache of now-shadowed id in obj's scope chain. Do + * this optimistically (assuming no failure below) before locking obj, so + * we can lock the shadowed scope. + */ + js_PurgeScopeChain(cx, obj, id); + + JS_LOCK_OBJ(cx, obj); + scope = js_GetMutableScope(cx, obj); + if (!scope) { + sprop = NULL; + } else { + /* Convert string indices to integers if appropriate. */ + id = js_CheckForStringIndex(id); + sprop = scope->putProperty(cx, id, getter, setter, slot, attrs, flags, shortid); + } + JS_UNLOCK_OBJ(cx, obj); + return sprop; +} + +JSScopeProperty * +js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj, + JSScopeProperty *sprop, uintN attrs, uintN mask, + JSPropertyOp getter, JSPropertyOp setter) +{ + JSScope *scope; + + JS_LOCK_OBJ(cx, obj); + scope = js_GetMutableScope(cx, obj); + if (!scope) { + sprop = NULL; + } else { + sprop = scope->changeProperty(cx, sprop, attrs, mask, getter, setter); + } + JS_UNLOCK_OBJ(cx, obj); + return sprop; +} + +JSBool +js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs) +{ + return js_DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, + 0, 0, NULL); +} + +/* + * Backward compatibility requires allowing addProperty hooks to mutate the + * nominal initial value of a slot-full property, while GC safety wants that + * value to be stored before the call-out through the hook. Optimize to do + * both while saving cycles for classes that stub their addProperty hook. + */ +static inline bool +AddPropertyHelper(JSContext *cx, JSClass *clasp, JSObject *obj, JSScope *scope, + JSScopeProperty *sprop, jsval *vp) +{ + if (clasp->addProperty != JS_PropertyStub) { + jsval nominal = *vp; + + if (!clasp->addProperty(cx, obj, SPROP_USERID(sprop), vp)) + return false; + if (*vp != nominal) { + if (SPROP_HAS_VALID_SLOT(sprop, scope)) + LOCKED_OBJ_SET_SLOT(obj, sprop->slot, *vp); + } + } + return true; +} + +JSBool +js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs, + uintN flags, intN shortid, JSProperty **propp, + uintN defineHow /* = 0 */) +{ + JSClass *clasp; + JSScope *scope; + JSScopeProperty *sprop; + JSBool added; + + JS_ASSERT((defineHow & ~(JSDNP_CACHE_RESULT | JSDNP_DONT_PURGE | JSDNP_SET_METHOD)) == 0); + js_LeaveTraceIfGlobalObject(cx, obj); + + /* Convert string indices to integers if appropriate. */ + id = js_CheckForStringIndex(id); + +#if JS_HAS_GETTER_SETTER + /* + * If defining a getter or setter, we must check for its counterpart and + * update the attributes and property ops. A getter or setter is really + * only half of a property. + */ + sprop = NULL; + if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) { + JSObject *pobj; + JSProperty *prop; + + /* + * If JS_THREADSAFE and id is found, js_LookupProperty returns with + * sprop non-null and pobj locked. If pobj == obj, the property is + * already in obj and obj has its own (mutable) scope. So if we are + * defining a getter whose setter was already defined, or vice versa, + * finish the job via js_ChangeScopePropertyAttributes, and refresh + * the property cache line for (obj, id) to map sprop. + */ + if (!js_LookupProperty(cx, obj, id, &pobj, &prop)) + return JS_FALSE; + sprop = (JSScopeProperty *) prop; + if (sprop && + pobj == obj && + (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER))) { + sprop = OBJ_SCOPE(obj)->changeProperty(cx, sprop, attrs, + JSPROP_GETTER | JSPROP_SETTER, + (attrs & JSPROP_GETTER) + ? getter + : sprop->getter, + (attrs & JSPROP_SETTER) + ? setter + : sprop->setter); + + /* NB: obj == pobj, so we can share unlock code at the bottom. */ + if (!sprop) + goto error; + } else if (prop) { + /* NB: call JSObject::dropProperty, as pobj might not be native. */ + pobj->dropProperty(cx, prop); + prop = NULL; + sprop = NULL; + } + } +#endif /* JS_HAS_GETTER_SETTER */ + + /* + * Purge the property cache of any properties named by id that are about + * to be shadowed in obj's scope chain unless it is known a priori that it + * is not possible. We do this before locking obj to avoid nesting locks. + */ + if (!(defineHow & JSDNP_DONT_PURGE)) + js_PurgeScopeChain(cx, obj, id); + + /* + * Check whether a readonly property or setter is being defined on a known + * prototype object. See the comment in jscntxt.h before protoHazardShape's + * member declaration. + */ + if (obj->isDelegate() && (attrs & (JSPROP_READONLY | JSPROP_SETTER))) + cx->runtime->protoHazardShape = js_GenerateShape(cx, false); + + /* Lock if object locking is required by this implementation. */ + JS_LOCK_OBJ(cx, obj); + + /* Use the object's class getter and setter by default. */ + clasp = obj->getClass(); + if (!(defineHow & JSDNP_SET_METHOD)) { + if (!getter) + getter = clasp->getProperty; + if (!setter) + setter = clasp->setProperty; + } + + /* Get obj's own scope if it has one, or create a new one for obj. */ + scope = js_GetMutableScope(cx, obj); + if (!scope) + goto error; + + added = false; + if (!sprop) { + /* Add a new property, or replace an existing one of the same id. */ + if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES) + attrs |= JSPROP_SHARED; + + if (defineHow & JSDNP_SET_METHOD) { + JS_ASSERT(clasp == &js_ObjectClass); + JS_ASSERT(VALUE_IS_FUNCTION(cx, value)); + JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER))); + JS_ASSERT(!getter && !setter); + + JSObject *funobj = JSVAL_TO_OBJECT(value); + if (FUN_OBJECT(GET_FUNCTION_PRIVATE(cx, funobj)) == funobj) { + flags |= SPROP_IS_METHOD; + getter = js_CastAsPropertyOp(funobj); + } + } + + added = !scope->hasProperty(id); + sprop = scope->putProperty(cx, id, getter, setter, SPROP_INVALID_SLOT, + attrs, flags, shortid); + if (!sprop) + goto error; + } + + /* Store value before calling addProperty, in case the latter GC's. */ + if (SPROP_HAS_VALID_SLOT(sprop, scope)) + LOCKED_OBJ_SET_SLOT(obj, sprop->slot, value); + + /* XXXbe called with lock held */ + if (!AddPropertyHelper(cx, clasp, obj, scope, sprop, &value)) { + scope->removeProperty(cx, id); + goto error; + } + + if (defineHow & JSDNP_CACHE_RESULT) { + JS_ASSERT_NOT_ON_TRACE(cx); + JSPropCacheEntry *entry; + entry = js_FillPropertyCache(cx, obj, 0, 0, obj, sprop, added); + TRACE_2(SetPropHit, entry, sprop); + } + if (propp) + *propp = (JSProperty *) sprop; + else + JS_UNLOCK_OBJ(cx, obj); + return JS_TRUE; + +error: // TRACE_2 jumps here on error, as well. + JS_UNLOCK_OBJ(cx, obj); + return JS_FALSE; +} + +JS_FRIEND_API(JSBool) +js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, + JSProperty **propp) +{ + return js_LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags, + objp, propp) >= 0; +} + +#define SCOPE_DEPTH_ACCUM(bs,val) \ + JS_SCOPE_DEPTH_METERING(JS_BASIC_STATS_ACCUM(bs, val)) + +int +js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, + JSObject **objp, JSProperty **propp) +{ + JSObject *start, *obj2, *proto; + int protoIndex; + JSScope *scope; + JSScopeProperty *sprop; + JSClass *clasp; + JSResolveOp resolve; + JSResolvingKey key; + JSResolvingEntry *entry; + uint32 generation; + JSNewResolveOp newresolve; + JSBool ok; + + /* Convert string indices to integers if appropriate. */ + id = js_CheckForStringIndex(id); + + /* Search scopes starting with obj and following the prototype link. */ + start = obj; + for (protoIndex = 0; ; protoIndex++) { + JS_LOCK_OBJ(cx, obj); + scope = OBJ_SCOPE(obj); + sprop = scope->lookup(id); + + /* Try obj's class resolve hook if id was not found in obj's scope. */ + if (!sprop) { + clasp = obj->getClass(); + resolve = clasp->resolve; + if (resolve != JS_ResolveStub) { + /* Avoid recursion on (obj, id) already being resolved on cx. */ + key.obj = obj; + key.id = id; + + /* + * Once we have successfully added an entry for (obj, key) to + * cx->resolvingTable, control must go through cleanup: before + * returning. But note that JS_DHASH_ADD may find an existing + * entry, in which case we bail to suppress runaway recursion. + */ + if (!js_StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry)) { + JS_UNLOCK_OBJ(cx, obj); + return -1; + } + if (!entry) { + /* Already resolving id in obj -- suppress recursion. */ + JS_UNLOCK_OBJ(cx, obj); + goto out; + } + generation = cx->resolvingTable->generation; + + /* Null *propp here so we can test it at cleanup: safely. */ + *propp = NULL; + + if (clasp->flags & JSCLASS_NEW_RESOLVE) { + newresolve = (JSNewResolveOp)resolve; + if (flags == JSRESOLVE_INFER) + flags = js_InferFlags(cx, flags); + obj2 = (clasp->flags & JSCLASS_NEW_RESOLVE_GETS_START) + ? start + : NULL; + JS_UNLOCK_OBJ(cx, obj); + + /* Protect id and all atoms from a GC nested in resolve. */ + JS_KEEP_ATOMS(cx->runtime); + ok = newresolve(cx, obj, ID_TO_VALUE(id), flags, &obj2); + JS_UNKEEP_ATOMS(cx->runtime); + if (!ok) + goto cleanup; + + JS_LOCK_OBJ(cx, obj); + if (obj2) { + /* Resolved: juggle locks and lookup id again. */ + if (obj2 != obj) { + JS_UNLOCK_OBJ(cx, obj); + if (OBJ_IS_NATIVE(obj2)) + JS_LOCK_OBJ(cx, obj2); + } + protoIndex = 0; + for (proto = start; proto && proto != obj2; + proto = OBJ_GET_PROTO(cx, proto)) { + protoIndex++; + } + if (!OBJ_IS_NATIVE(obj2)) { + /* Whoops, newresolve handed back a foreign obj2. */ + JS_ASSERT(obj2 != obj); + ok = obj2->lookupProperty(cx, id, objp, propp); + if (!ok || *propp) + goto cleanup; + JS_LOCK_OBJ(cx, obj2); + } else { + /* + * Require that obj2 have its own scope now, as we + * do for old-style resolve. If it doesn't, then + * id was not truly resolved, and we'll find it in + * the proto chain, or miss it if obj2's proto is + * not on obj's proto chain. That last case is a + * "too bad!" case. + */ + scope = OBJ_SCOPE(obj2); + if (scope->owned()) + sprop = scope->lookup(id); + } + if (sprop) { + JS_ASSERT(scope == OBJ_SCOPE(obj2)); + JS_ASSERT(scope->owned()); + obj = obj2; + } else if (obj2 != obj) { + if (OBJ_IS_NATIVE(obj2)) + JS_UNLOCK_OBJ(cx, obj2); + JS_LOCK_OBJ(cx, obj); + } + } + } else { + /* + * Old resolve always requires id re-lookup if obj owns + * its scope after resolve returns. + */ + JS_UNLOCK_OBJ(cx, obj); + ok = resolve(cx, obj, ID_TO_VALUE(id)); + if (!ok) + goto cleanup; + JS_LOCK_OBJ(cx, obj); + JS_ASSERT(OBJ_IS_NATIVE(obj)); + scope = OBJ_SCOPE(obj); + if (scope->owned()) + sprop = scope->lookup(id); + } + + cleanup: + js_StopResolving(cx, &key, JSRESFLAG_LOOKUP, entry, generation); + if (!ok) + return -1; + if (*propp) + return protoIndex; + } + } + + if (sprop) { + SCOPE_DEPTH_ACCUM(&cx->runtime->protoLookupDepthStats, protoIndex); + JS_ASSERT(OBJ_SCOPE(obj) == scope); + *objp = obj; + + *propp = (JSProperty *) sprop; + return protoIndex; + } + + proto = obj->getProto(); + JS_UNLOCK_OBJ(cx, obj); + if (!proto) + break; + if (!OBJ_IS_NATIVE(proto)) { + if (!proto->lookupProperty(cx, id, objp, propp)) + return -1; + return protoIndex + 1; + } + + /* + * Correctness elsewhere (the property cache and JIT), not here in + * particular, depends on all the objects on the prototype chain having + * different scopes. This is just a convenient place to check. + * + * Cloned Block objects do in fact share their prototype's scope -- but + * that is really just a memory-saving hack, safe because Blocks cannot + * be on the prototype chain of other objects. + */ + JS_ASSERT_IF(OBJ_GET_CLASS(cx, obj) != &js_BlockClass, + OBJ_SCOPE(obj) != OBJ_SCOPE(proto)); + + obj = proto; + } + +out: + *objp = NULL; + *propp = NULL; + return protoIndex; +} + +JSPropCacheEntry * +js_FindPropertyHelper(JSContext *cx, jsid id, JSBool cacheResult, + JSObject **objp, JSObject **pobjp, JSProperty **propp) +{ + JSObject *scopeChain, *obj, *parent, *pobj; + JSPropCacheEntry *entry; + int scopeIndex, protoIndex; + JSProperty *prop; + + JS_ASSERT_IF(cacheResult, !JS_ON_TRACE(cx)); + scopeChain = js_GetTopStackFrame(cx)->scopeChain; + + /* Scan entries on the scope chain that we can cache across. */ + entry = JS_NO_PROP_CACHE_FILL; + obj = scopeChain; + parent = OBJ_GET_PARENT(cx, obj); + for (scopeIndex = 0; + parent + ? js_IsCacheableNonGlobalScope(obj) + : obj->map->ops->lookupProperty == js_LookupProperty; + ++scopeIndex) { + protoIndex = + js_LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags, + &pobj, &prop); + if (protoIndex < 0) + return NULL; + + if (prop) { +#ifdef DEBUG + if (parent) { + JSClass *clasp = OBJ_GET_CLASS(cx, obj); + JS_ASSERT(OBJ_IS_NATIVE(pobj)); + JS_ASSERT(OBJ_GET_CLASS(cx, pobj) == clasp); + if (clasp == &js_BlockClass) { + /* + * Block instances on the scope chain are immutable and + * always share their scope with compile-time prototypes. + */ + JS_ASSERT(pobj == obj); + JS_ASSERT(protoIndex == 0); + } else { + /* Call and DeclEnvClass objects have no prototypes. */ + JS_ASSERT(!OBJ_GET_PROTO(cx, obj)); + JS_ASSERT(protoIndex == 0); + } + } +#endif + if (cacheResult) { + entry = js_FillPropertyCache(cx, scopeChain, + scopeIndex, protoIndex, pobj, + (JSScopeProperty *) prop, false); + } + SCOPE_DEPTH_ACCUM(&rt->scopeSearchDepthStats, scopeIndex); + goto out; + } + + if (!parent) { + pobj = NULL; + goto out; + } + obj = parent; + parent = OBJ_GET_PARENT(cx, obj); + } + + for (;;) { + if (!obj->lookupProperty(cx, id, &pobj, &prop)) + return NULL; + if (prop) { + PCMETER(JS_PROPERTY_CACHE(cx).nofills++); + goto out; + } + + /* + * We conservatively assume that a resolve hook could mutate the scope + * chain during JSObject::lookupProperty. So we read parent here again. + */ + parent = OBJ_GET_PARENT(cx, obj); + if (!parent) { + pobj = NULL; + break; + } + obj = parent; + } + + out: + JS_ASSERT(!!pobj == !!prop); + *objp = obj; + *pobjp = pobj; + *propp = prop; + return entry; +} + +JS_FRIEND_API(JSBool) +js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp, + JSProperty **propp) +{ + return !!js_FindPropertyHelper(cx, id, false, objp, pobjp, propp); +} + +JSObject * +js_FindIdentifierBase(JSContext *cx, JSObject *scopeChain, jsid id) +{ + /* + * This function should not be called for a global object or from the + * trace and should have a valid cache entry for native scopeChain. + */ + JS_ASSERT(OBJ_GET_PARENT(cx, scopeChain)); + JS_ASSERT(!JS_ON_TRACE(cx)); + + JSObject *obj = scopeChain; + + /* + * Loop over cacheable objects on the scope chain until we find a + * property. We also stop when we reach the global object skipping any + * farther checks or lookups. For details see the JSOP_BINDNAME case of + * js_Interpret. + */ + for (int scopeIndex = 0; js_IsCacheableNonGlobalScope(obj); scopeIndex++) { + JSObject *pobj; + JSProperty *prop; + int protoIndex = js_LookupPropertyWithFlags(cx, obj, id, + cx->resolveFlags, + &pobj, &prop); + if (protoIndex < 0) + return NULL; + if (prop) { + JS_ASSERT(OBJ_IS_NATIVE(pobj)); + JS_ASSERT(OBJ_GET_CLASS(cx, pobj) == OBJ_GET_CLASS(cx, obj)); +#ifdef DEBUG + JSPropCacheEntry *entry = +#endif + js_FillPropertyCache(cx, scopeChain, + scopeIndex, protoIndex, pobj, + (JSScopeProperty *) prop, false); + JS_ASSERT(entry); + JS_UNLOCK_OBJ(cx, pobj); + return obj; + } + + /* Call and other cacheable objects always have a parent. */ + obj = OBJ_GET_PARENT(cx, obj); + if (!OBJ_GET_PARENT(cx, obj)) + return obj; + } + + /* Loop until we find a property or reach the global object. */ + do { + JSObject *pobj; + JSProperty *prop; + if (!obj->lookupProperty(cx, id, &pobj, &prop)) + return NULL; + if (prop) { + pobj->dropProperty(cx, prop); + break; + } + + /* + * We conservatively assume that a resolve hook could mutate the scope + * chain during JSObject::lookupProperty. So we must check if parent is + * not null here even if it wasn't before the lookup. + */ + JSObject *parent = OBJ_GET_PARENT(cx, obj); + if (!parent) + break; + obj = parent; + } while (OBJ_GET_PARENT(cx, obj)); + return obj; +} + +JSBool +js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj, + JSScopeProperty *sprop, uintN getHow, jsval *vp) +{ + js_LeaveTraceIfGlobalObject(cx, pobj); + + JSScope *scope; + uint32 slot; + int32 sample; + JSTempValueRooter tvr, tvr2; + JSBool ok; + + JS_ASSERT(OBJ_IS_NATIVE(pobj)); + JS_ASSERT(JS_IS_OBJ_LOCKED(cx, pobj)); + scope = OBJ_SCOPE(pobj); + + slot = sprop->slot; + *vp = (slot != SPROP_INVALID_SLOT) + ? LOCKED_OBJ_GET_SLOT(pobj, slot) + : JSVAL_VOID; + if (SPROP_HAS_STUB_GETTER(sprop)) + return true; + + if (JS_UNLIKELY(sprop->isMethod()) && (getHow & JSGET_NO_METHOD_BARRIER)) { + JS_ASSERT(sprop->methodValue() == *vp); + return true; + } + + sample = cx->runtime->propertyRemovals; + JS_UNLOCK_SCOPE(cx, scope); + JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr); + JS_PUSH_TEMP_ROOT_OBJECT(cx, pobj, &tvr2); + ok = sprop->get(cx, obj, pobj, vp); + JS_POP_TEMP_ROOT(cx, &tvr2); + JS_POP_TEMP_ROOT(cx, &tvr); + if (!ok) + return false; + + JS_LOCK_SCOPE(cx, scope); + if (SLOT_IN_SCOPE(slot, scope) && + (JS_LIKELY(cx->runtime->propertyRemovals == sample) || + scope->hasProperty(sprop))) { + jsval v = *vp; + if (!scope->methodWriteBarrier(cx, sprop, v)) { + JS_UNLOCK_SCOPE(cx, scope); + return false; + } + LOCKED_OBJ_SET_SLOT(pobj, slot, v); + } + + return true; +} + +JSBool +js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, bool added, + jsval *vp) +{ + js_LeaveTraceIfGlobalObject(cx, obj); + + JSScope *scope; + uint32 slot; + int32 sample; + JSTempValueRooter tvr; + JSBool ok; + + JS_ASSERT(OBJ_IS_NATIVE(obj)); + JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj)); + scope = OBJ_SCOPE(obj); + + slot = sprop->slot; + if (slot != SPROP_INVALID_SLOT) { + OBJ_CHECK_SLOT(obj, slot); + + /* If sprop has a stub setter, keep scope locked and just store *vp. */ + if (SPROP_HAS_STUB_SETTER(sprop)) { + if (!added && !scope->methodWriteBarrier(cx, sprop, *vp)) { + JS_UNLOCK_SCOPE(cx, scope); + return false; + } + LOCKED_OBJ_SET_SLOT(obj, slot, *vp); + return true; + } + } else { + /* + * Allow API consumers to create shared properties with stub setters. + * Such properties lack value storage, so setting them is like writing + * to /dev/null. + */ + if (SPROP_HAS_STUB_SETTER(sprop)) + return true; + } + + sample = cx->runtime->propertyRemovals; + JS_UNLOCK_SCOPE(cx, scope); + JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr); + ok = sprop->set(cx, obj, vp); + JS_POP_TEMP_ROOT(cx, &tvr); + if (!ok) + return false; + + JS_LOCK_SCOPE(cx, scope); + if (SLOT_IN_SCOPE(slot, scope) && + (JS_LIKELY(cx->runtime->propertyRemovals == sample) || + scope->hasProperty(sprop))) { + jsval v = *vp; + if (!added && !scope->methodWriteBarrier(cx, sprop, v)) { + JS_UNLOCK_SCOPE(cx, scope); + return false; + } + LOCKED_OBJ_SET_SLOT(obj, slot, v); + } + + return true; +} + +JSBool +js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN getHow, + jsval *vp) +{ + JSObject *aobj, *obj2; + int protoIndex; + JSProperty *prop; + JSScopeProperty *sprop; + + JS_ASSERT_IF(getHow & JSGET_CACHE_RESULT, !JS_ON_TRACE(cx)); + + /* Convert string indices to integers if appropriate. */ + id = js_CheckForStringIndex(id); + + aobj = js_GetProtoIfDenseArray(cx, obj); + protoIndex = js_LookupPropertyWithFlags(cx, aobj, id, cx->resolveFlags, + &obj2, &prop); + if (protoIndex < 0) + return JS_FALSE; + if (!prop) { + *vp = JSVAL_VOID; + + if (!OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, ID_TO_VALUE(id), vp)) + return JS_FALSE; + + PCMETER(getHow & JSGET_CACHE_RESULT && JS_PROPERTY_CACHE(cx).nofills++); + + /* + * Give a strict warning if foo.bar is evaluated by a script for an + * object foo with no property named 'bar'. + */ + jsbytecode *pc; + if (JSVAL_IS_VOID(*vp) && ((pc = js_GetCurrentBytecodePC(cx)) != NULL)) { + JSOp op; + uintN flags; + + op = (JSOp) *pc; + if (op == JSOP_TRAP) { + JS_ASSERT_NOT_ON_TRACE(cx); + op = JS_GetTrapOpcode(cx, cx->fp->script, pc); + } + if (op == JSOP_GETXPROP) { + flags = JSREPORT_ERROR; + } else { + if (!JS_HAS_STRICT_OPTION(cx) || + (op != JSOP_GETPROP && op != JSOP_GETELEM) || + js_CurrentPCIsInImacro(cx)) { + return JS_TRUE; + } + + /* + * XXX do not warn about missing __iterator__ as the function + * may be called from JS_GetMethodById. See bug 355145. + */ + if (id == ATOM_TO_JSID(cx->runtime->atomState.iteratorAtom)) + return JS_TRUE; + + /* Do not warn about tests like (obj[prop] == undefined). */ + if (cx->resolveFlags == JSRESOLVE_INFER) { + js_LeaveTrace(cx); + pc += js_CodeSpec[op].length; + if (Detecting(cx, pc)) + return JS_TRUE; + } else if (cx->resolveFlags & JSRESOLVE_DETECTING) { + return JS_TRUE; + } + + flags = JSREPORT_WARNING | JSREPORT_STRICT; + } + + /* Ok, bad undefined property reference: whine about it. */ + if (!js_ReportValueErrorFlags(cx, flags, JSMSG_UNDEFINED_PROP, + JSDVG_IGNORE_STACK, ID_TO_VALUE(id), + NULL, NULL, NULL)) { + return JS_FALSE; + } + } + return JS_TRUE; + } + + if (!OBJ_IS_NATIVE(obj2)) { + obj2->dropProperty(cx, prop); + return obj2->getProperty(cx, id, vp); + } + + sprop = (JSScopeProperty *) prop; + + if (getHow & JSGET_CACHE_RESULT) { + JS_ASSERT_NOT_ON_TRACE(cx); + js_FillPropertyCache(cx, aobj, 0, protoIndex, obj2, sprop, false); + } + + if (!js_NativeGet(cx, obj, obj2, sprop, getHow, vp)) + return JS_FALSE; + + JS_UNLOCK_OBJ(cx, obj2); + return JS_TRUE; +} + +JSBool +js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + return js_GetPropertyHelper(cx, obj, id, JSGET_METHOD_BARRIER, vp); +} + +JSBool +js_GetMethod(JSContext *cx, JSObject *obj, jsid id, uintN getHow, jsval *vp) +{ + JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED); + + if (obj->map->ops == &js_ObjectOps || + obj->map->ops->getProperty == js_GetProperty) { + return js_GetPropertyHelper(cx, obj, id, getHow, vp); + } + JS_ASSERT_IF(getHow & JSGET_CACHE_RESULT, OBJ_IS_DENSE_ARRAY(cx, obj)); +#if JS_HAS_XML_SUPPORT + if (OBJECT_IS_XML(cx, obj)) + return js_GetXMLMethod(cx, obj, id, vp); +#endif + return obj->getProperty(cx, id, vp); +} + +JS_FRIEND_API(bool) +js_CheckUndeclaredVarAssignment(JSContext *cx) +{ + JSStackFrame *fp = js_GetTopStackFrame(cx); + if (!fp) + return true; + + /* If neither cx nor the code is strict, then no check is needed. */ + if (!(fp->script && fp->script->strictModeCode) && + !JS_HAS_STRICT_OPTION(cx)) { + return true; + } + + /* This check is only appropriate when executing JSOP_SETNAME. */ + if (!fp->regs || + js_GetOpcode(cx, fp->script, fp->regs->pc) != JSOP_SETNAME) { + return true; + } + + JSAtom *atom; + GET_ATOM_FROM_BYTECODE(fp->script, fp->regs->pc, 0, atom); + + const char *bytes = js_AtomToPrintableString(cx, atom); + return bytes && + JS_ReportErrorFlagsAndNumber(cx, + (JSREPORT_WARNING | JSREPORT_STRICT + | JSREPORT_STRICT_MODE_ERROR), + js_GetErrorMessage, NULL, + JSMSG_UNDECLARED_VAR, bytes); +} + +/* + * Note: all non-error exits in this function must notify the tracer using + * SetPropHit when called from the interpreter, which is detected by testing + * (defineHow & JSDNP_CACHE_RESULT). + */ +JSBool +js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow, + jsval *vp) +{ + int protoIndex; + JSObject *pobj; + JSProperty *prop; + JSScopeProperty *sprop; + JSScope *scope; + uintN attrs, flags; + intN shortid; + JSClass *clasp; + JSPropertyOp getter, setter; + bool added; + + JS_ASSERT((defineHow & ~(JSDNP_CACHE_RESULT | JSDNP_SET_METHOD)) == 0); + if (defineHow & JSDNP_CACHE_RESULT) + JS_ASSERT_NOT_ON_TRACE(cx); + + /* Convert string indices to integers if appropriate. */ + id = js_CheckForStringIndex(id); + + /* + * We peek at OBJ_SCOPE(obj) without locking obj. Any race means a failure + * to seal before sharing, which is inherently ambiguous. + */ + if (OBJ_SCOPE(obj)->sealed() && OBJ_SCOPE(obj)->object == obj) { + flags = JSREPORT_ERROR; + goto read_only_error; + } + + protoIndex = js_LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags, + &pobj, &prop); + if (protoIndex < 0) + return JS_FALSE; + if (prop) { + if (!OBJ_IS_NATIVE(pobj)) { + pobj->dropProperty(cx, prop); + prop = NULL; + } + } else { + /* We should never add properties to lexical blocks. */ + JS_ASSERT(OBJ_GET_CLASS(cx, obj) != &js_BlockClass); + + if (!OBJ_GET_PARENT(cx, obj) && !js_CheckUndeclaredVarAssignment(cx)) + return JS_FALSE; + } + sprop = (JSScopeProperty *) prop; + + /* + * Now either sprop is null, meaning id was not found in obj or one of its + * prototypes; or sprop is non-null, meaning id was found in pobj's scope. + * If JS_THREADSAFE and sprop is non-null, then scope is locked, and sprop + * is held: we must JSObject::dropProperty or JS_UNLOCK_SCOPE before we + * return (the two are equivalent for native objects, but we use + * JS_UNLOCK_SCOPE because it is cheaper). + */ + attrs = JSPROP_ENUMERATE; + flags = 0; + shortid = 0; + clasp = OBJ_GET_CLASS(cx, obj); + getter = clasp->getProperty; + setter = clasp->setProperty; + + if (sprop) { + /* + * Set scope for use below. It was locked by js_LookupProperty, and + * we know pobj owns it (i.e., scope->object == pobj). Therefore we + * optimize JS_UNLOCK_OBJ(cx, pobj) into JS_UNLOCK_SCOPE(cx, scope). + */ + scope = OBJ_SCOPE(pobj); + + attrs = sprop->attrs; + if ((attrs & JSPROP_READONLY) || + (scope->sealed() && (attrs & JSPROP_SHARED))) { + JS_UNLOCK_SCOPE(cx, scope); + + /* + * Here, we'll either return true or goto read_only_error, which + * reports a strict warning or throws an error. So we redefine + * the |flags| local variable to be JSREPORT_* flags to pass to + * JS_ReportErrorFlagsAndNumberUC at label read_only_error. We + * must likewise re-task flags further below for the other 'goto + * read_only_error;' case. + */ + flags = JSREPORT_ERROR; + if (attrs & JSPROP_READONLY) { + if (!JS_HAS_STRICT_OPTION(cx)) { + /* Just return true per ECMA if not in strict mode. */ + PCMETER((defineHow & JSDNP_CACHE_RESULT) && JS_PROPERTY_CACHE(cx).rofills++); + if (defineHow & JSDNP_CACHE_RESULT) { + JS_ASSERT_NOT_ON_TRACE(cx); + TRACE_2(SetPropHit, JS_NO_PROP_CACHE_FILL, sprop); + } + return JS_TRUE; +#ifdef JS_TRACER + error: // TRACE_2 jumps here in case of error. + return JS_FALSE; +#endif + } + + /* Strict mode: report a read-only strict warning. */ + flags = JSREPORT_STRICT | JSREPORT_WARNING; + } + goto read_only_error; + } + + if (pobj != obj) { + /* + * We found id in a prototype object: prepare to share or shadow. + * + * NB: Thanks to the immutable, garbage-collected property tree + * maintained by jsscope.c in cx->runtime, we needn't worry about + * sprop going away behind our back after we've unlocked scope. + */ + JS_UNLOCK_SCOPE(cx, scope); + + /* Don't clone a shared prototype property. */ + if (attrs & JSPROP_SHARED) { + if (defineHow & JSDNP_CACHE_RESULT) { + JS_ASSERT_NOT_ON_TRACE(cx); + JSPropCacheEntry *entry; + entry = js_FillPropertyCache(cx, obj, 0, protoIndex, pobj, sprop, false); + TRACE_2(SetPropHit, entry, sprop); + } + + if (SPROP_HAS_STUB_SETTER(sprop) && + !(sprop->attrs & JSPROP_GETTER)) { + return JS_TRUE; + } + + return sprop->set(cx, obj, vp); + } + + /* Restore attrs to the ECMA default for new properties. */ + attrs = JSPROP_ENUMERATE; + + /* + * Preserve the shortid, getter, and setter when shadowing any + * property that has a shortid. An old API convention requires + * that the property's getter and setter functions receive the + * shortid, not id, when they are called on the shadow we are + * about to create in obj's scope. + */ + if (sprop->flags & SPROP_HAS_SHORTID) { + flags = SPROP_HAS_SHORTID; + shortid = sprop->shortid; + getter = sprop->getter; + setter = sprop->setter; + } + + /* + * Forget we found the proto-property now that we've copied any + * needed member values. + */ + sprop = NULL; + } +#ifdef __GNUC__ /* suppress bogus gcc warnings */ + } else { + scope = NULL; +#endif + } + + added = false; + if (!sprop) { + /* + * Purge the property cache of now-shadowed id in obj's scope chain. + * Do this early, before locking obj to avoid nesting locks. + */ + js_PurgeScopeChain(cx, obj, id); + + /* Find or make a property descriptor with the right heritage. */ + JS_LOCK_OBJ(cx, obj); + scope = js_GetMutableScope(cx, obj); + if (!scope) { + JS_UNLOCK_OBJ(cx, obj); + return JS_FALSE; + } + + if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES) + attrs |= JSPROP_SHARED; + + /* + * Check for Object class here to avoid defining a method on a class + * with magic resolve, addProperty, getProperty, etc. hooks. + */ + if ((defineHow & JSDNP_SET_METHOD) && + obj->getClass() == &js_ObjectClass) { + JS_ASSERT(VALUE_IS_FUNCTION(cx, *vp)); + JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER))); + + JSObject *funobj = JSVAL_TO_OBJECT(*vp); + if (FUN_OBJECT(GET_FUNCTION_PRIVATE(cx, funobj)) == funobj) { + flags |= SPROP_IS_METHOD; + getter = js_CastAsPropertyOp(funobj); + } + } + + sprop = scope->addProperty(cx, id, getter, setter, SPROP_INVALID_SLOT, + attrs, flags, shortid); + if (!sprop) { + JS_UNLOCK_SCOPE(cx, scope); + return JS_FALSE; + } + + /* + * Initialize the new property value (passed to setter) to undefined. + * Note that we store before calling addProperty, to match the order + * in js_DefineNativeProperty. + */ + if (SPROP_HAS_VALID_SLOT(sprop, scope)) + LOCKED_OBJ_SET_SLOT(obj, sprop->slot, JSVAL_VOID); + + /* XXXbe called with obj locked */ + if (!AddPropertyHelper(cx, clasp, obj, scope, sprop, vp)) { + scope->removeProperty(cx, id); + JS_UNLOCK_SCOPE(cx, scope); + return JS_FALSE; + } + added = true; + } + + if (defineHow & JSDNP_CACHE_RESULT) { + JS_ASSERT_NOT_ON_TRACE(cx); + JSPropCacheEntry *entry; + entry = js_FillPropertyCache(cx, obj, 0, 0, obj, sprop, added); + TRACE_2(SetPropHit, entry, sprop); + } + + if (!js_NativeSet(cx, obj, sprop, added, vp)) + return NULL; + + JS_UNLOCK_SCOPE(cx, scope); + return JS_TRUE; + + read_only_error: + return js_ReportValueErrorFlags(cx, flags, JSMSG_READ_ONLY, + JSDVG_IGNORE_STACK, ID_TO_VALUE(id), NULL, + NULL, NULL); +} + +JSBool +js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + return js_SetPropertyHelper(cx, obj, id, false, vp); +} + +JSBool +js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, + uintN *attrsp) +{ + JSBool noprop, ok; + JSScopeProperty *sprop; + + noprop = !prop; + if (noprop) { + if (!js_LookupProperty(cx, obj, id, &obj, &prop)) + return JS_FALSE; + if (!prop) { + *attrsp = 0; + return JS_TRUE; + } + if (!OBJ_IS_NATIVE(obj)) { + ok = obj->getAttributes(cx, id, prop, attrsp); + obj->dropProperty(cx, prop); + return ok; + } + } + sprop = (JSScopeProperty *)prop; + *attrsp = sprop->attrs; + if (noprop) + obj->dropProperty(cx, prop); + return JS_TRUE; +} + +JSBool +js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, + uintN *attrsp) +{ + JSBool noprop, ok; + JSScopeProperty *sprop; + + noprop = !prop; + if (noprop) { + if (!js_LookupProperty(cx, obj, id, &obj, &prop)) + return JS_FALSE; + if (!prop) + return JS_TRUE; + if (!OBJ_IS_NATIVE(obj)) { + ok = obj->setAttributes(cx, id, prop, attrsp); + obj->dropProperty(cx, prop); + return ok; + } + } + sprop = (JSScopeProperty *)prop; + sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, *attrsp, 0, + sprop->getter, sprop->setter); + if (noprop) + obj->dropProperty(cx, prop); + return (sprop != NULL); +} + +JSBool +js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval) +{ + JSObject *proto; + JSProperty *prop; + JSScopeProperty *sprop; + JSScope *scope; + JSBool ok; + + *rval = JSVAL_TRUE; + + /* Convert string indices to integers if appropriate. */ + id = js_CheckForStringIndex(id); + + if (!js_LookupProperty(cx, obj, id, &proto, &prop)) + return JS_FALSE; + if (!prop || proto != obj) { + /* + * If the property was found in a native prototype, check whether it's + * shared and permanent. Such a property stands for direct properties + * in all delegating objects, matching ECMA semantics without bloating + * each delegating object. + */ + if (prop) { + if (OBJ_IS_NATIVE(proto)) { + sprop = (JSScopeProperty *)prop; + if (SPROP_IS_SHARED_PERMANENT(sprop)) + *rval = JSVAL_FALSE; + } + proto->dropProperty(cx, prop); + if (*rval == JSVAL_FALSE) + return JS_TRUE; + } + + /* + * If no property, or the property comes unshared or impermanent from + * a prototype, call the class's delProperty hook, passing rval as the + * result parameter. + */ + return OBJ_GET_CLASS(cx, obj)->delProperty(cx, obj, ID_TO_VALUE(id), + rval); + } + + sprop = (JSScopeProperty *)prop; + if (sprop->attrs & JSPROP_PERMANENT) { + obj->dropProperty(cx, prop); + *rval = JSVAL_FALSE; + return JS_TRUE; + } + + /* XXXbe called with obj locked */ + if (!obj->getClass()->delProperty(cx, obj, SPROP_USERID(sprop), rval)) { + obj->dropProperty(cx, prop); + return JS_FALSE; + } + + scope = OBJ_SCOPE(obj); + if (SPROP_HAS_VALID_SLOT(sprop, scope)) + GC_POKE(cx, LOCKED_OBJ_GET_SLOT(obj, sprop->slot)); + + ok = scope->removeProperty(cx, id); + obj->dropProperty(cx, prop); + return ok; +} + +JSBool +js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp) +{ + jsval v, save; + JSString *str; + + v = save = OBJECT_TO_JSVAL(obj); + switch (hint) { + case JSTYPE_STRING: + /* + * Optimize for String objects with standard toString methods. Support + * new String(...) instances whether mutated to have their own scope or + * not, as well as direct String.prototype references. + */ + if (OBJ_GET_CLASS(cx, obj) == &js_StringClass) { + jsid toStringId = ATOM_TO_JSID(cx->runtime->atomState.toStringAtom); + + JS_LOCK_OBJ(cx, obj); + JSScope *scope = OBJ_SCOPE(obj); + JSScopeProperty *sprop = scope->lookup(toStringId); + JSObject *pobj = obj; + + if (!sprop) { + pobj = obj->getProto(); + + if (pobj && OBJ_GET_CLASS(cx, pobj) == &js_StringClass) { + JS_UNLOCK_SCOPE(cx, scope); + JS_LOCK_OBJ(cx, pobj); + scope = OBJ_SCOPE(pobj); + sprop = scope->lookup(toStringId); + } + } + + if (sprop && + SPROP_HAS_STUB_GETTER(sprop) && + SPROP_HAS_VALID_SLOT(sprop, scope)) { + jsval fval = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot); + + if (VALUE_IS_FUNCTION(cx, fval)) { + JSObject *funobj = JSVAL_TO_OBJECT(fval); + JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj); + + if (FUN_FAST_NATIVE(fun) == js_str_toString) { + JS_UNLOCK_SCOPE(cx, scope); + *vp = obj->fslots[JSSLOT_PRIMITIVE_THIS]; + return JS_TRUE; + } + } + } + JS_UNLOCK_SCOPE(cx, scope); + } + + /* + * Propagate the exception if js_TryMethod finds an appropriate + * method, and calling that method returned failure. + */ + if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL, + &v)) { + return JS_FALSE; + } + + if (!JSVAL_IS_PRIMITIVE(v)) { + if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v)) + return JS_FALSE; + } + break; + + default: + if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v)) + return JS_FALSE; + if (!JSVAL_IS_PRIMITIVE(v)) { + JSType type = JS_TypeOfValue(cx, v); + if (type == hint || + (type == JSTYPE_FUNCTION && hint == JSTYPE_OBJECT)) { + goto out; + } + if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, + NULL, &v)) { + return JS_FALSE; + } + } + break; + } + if (!JSVAL_IS_PRIMITIVE(v)) { + /* Avoid recursive death when decompiling in js_ReportValueError. */ + if (hint == JSTYPE_STRING) { + str = JS_InternString(cx, OBJ_GET_CLASS(cx, obj)->name); + if (!str) + return JS_FALSE; + } else { + str = NULL; + } + *vp = OBJECT_TO_JSVAL(obj); + js_ReportValueError2(cx, JSMSG_CANT_CONVERT_TO, + JSDVG_SEARCH_STACK, save, str, + (hint == JSTYPE_VOID) + ? "primitive type" + : JS_TYPE_STR(hint)); + return JS_FALSE; + } +out: + *vp = v; + return JS_TRUE; +} + +/* + * Private type used to enumerate properties of a native JS object. It is + * allocated as necessary from JSENUMERATE_INIT and is freed when running the + * GC. The structure is not allocated when there are no enumerable properties + * in the object. Instead for the empty enumerator the code uses JSVAL_ZERO as + * the enumeration state. + * + * JSThreadData.nativeEnumCache caches the enumerators using scope's shape to + * avoid repeated scanning of scopes for enumerable properties. The cache + * entry is either JSNativeEnumerator* or, for the empty enumerator, the shape + * value itself. The latter is stored as (shape << 1) | 1 to ensure that it is + * always different from JSNativeEnumerator* values. + * + * We cache the enumerators in the JSENUMERATE_INIT case of js_Enumerate, not + * during JSENUMERATE_DESTROY. The GC can invoke the latter case during the + * finalization when JSNativeEnumerator contains finalized ids and the + * enumerator must be freed. + */ +struct JSNativeEnumerator { + /* + * The index into the ids array. It runs from the length down to 1 when + * the enumerator is running. It is 0 when the enumerator is finished and + * can be reused on a cache hit. + */ + uint32 cursor; + uint32 length; /* length of ids array */ + uint32 shape; /* "shape" number -- see jsscope.h */ + jsid ids[1]; /* enumeration id array */ + + static inline size_t size(uint32 length) { + JS_ASSERT(length != 0); + return offsetof(JSNativeEnumerator, ids) + + (size_t) length * sizeof(jsid); + } + + bool isFinished() const { + return cursor == 0; + } + + void mark(JSTracer *trc) { + JS_ASSERT(length >= 1); + jsid *cursor = ids; + jsid *end = ids + length; + do { + js_TraceId(trc, *cursor); + } while (++cursor != end); + } +}; + +/* The tagging of shape values requires one bit. */ +JS_STATIC_ASSERT((jsuword) SHAPE_OVERFLOW_BIT <= + ((jsuword) 1 << (JS_BITS_PER_WORD - 1))); + +static void +SetEnumeratorCache(JSContext *cx, jsuword *cachep, jsuword newcache) +{ + jsuword old = *cachep; + *cachep = newcache; + if (!(old & jsuword(1)) && old) { + /* Free the cached enumerator unless it is running. */ + JSNativeEnumerator *ne = reinterpret_cast(old); + if (ne->isFinished()) + cx->free(ne); + } +} + +JSBool +js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, + jsval *statep, jsid *idp) +{ + /* Here cx is JSTracer when enum_op is JSENUMERATE_MARK. */ + JSClass *clasp = obj->getClass(); + JSEnumerateOp enumerate = clasp->enumerate; + if (clasp->flags & JSCLASS_NEW_ENUMERATE) { + JS_ASSERT(enumerate != JS_EnumerateStub); + return ((JSNewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp); + } + + switch (enum_op) { + case JSENUMERATE_INIT: { + if (!enumerate(cx, obj)) + return false; + + /* + * The set of all property ids is pre-computed when the iterator is + * initialized to avoid problems caused by properties being deleted + * during the iteration. + * + * Use a do-while(0) loop to avoid too many nested ifs. If ne is null + * after the loop, it indicates an empty enumerator. + */ + JSNativeEnumerator *ne; + uint32 length; + do { + uint32 shape = OBJ_SHAPE(obj); + + ENUM_CACHE_METER(nativeEnumProbes); + jsuword *cachep = &JS_THREAD_DATA(cx)-> + nativeEnumCache[NATIVE_ENUM_CACHE_HASH(shape)]; + jsuword oldcache = *cachep; + if (oldcache & (jsuword) 1) { + if (uint32(oldcache >> 1) == shape) { + /* scope has a shape with no enumerable properties. */ + ne = NULL; + length = 0; + break; + } + } else if (oldcache != jsuword(0)) { + ne = reinterpret_cast(oldcache); + JS_ASSERT(ne->length >= 1); + if (ne->shape == shape && ne->isFinished()) { + /* Mark ne as active. */ + ne->cursor = ne->length; + length = ne->length; + JS_ASSERT(!ne->isFinished()); + break; + } + } + ENUM_CACHE_METER(nativeEnumMisses); + + JS_LOCK_OBJ(cx, obj); + + /* Count all enumerable properties in object's scope. */ + JSScope *scope = OBJ_SCOPE(obj); + length = 0; + for (JSScopeProperty *sprop = scope->lastProperty(); sprop; sprop = sprop->parent) { + if ((sprop->attrs & JSPROP_ENUMERATE) && + !(sprop->flags & SPROP_IS_ALIAS)) { + length++; + } + } + if (length == 0) { + /* + * Cache the scope without enumerable properties unless its + * shape overflows, see bug 440834. + */ + JS_UNLOCK_SCOPE(cx, scope); + if (shape < SHAPE_OVERFLOW_BIT) { + SetEnumeratorCache(cx, cachep, + (jsuword(shape) << 1) | jsuword(1)); + } + ne = NULL; + break; + } + + ne = (JSNativeEnumerator *) + cx->mallocNoReport(JSNativeEnumerator::size(length)); + if (!ne) { + /* Report the OOM error outside the lock. */ + JS_UNLOCK_SCOPE(cx, scope); + JS_ReportOutOfMemory(cx); + return false; + } + ne->cursor = length; + ne->length = length; + ne->shape = shape; + + jsid *ids = ne->ids; + for (JSScopeProperty *sprop = scope->lastProperty(); sprop; sprop = sprop->parent) { + if ((sprop->attrs & JSPROP_ENUMERATE) && + !(sprop->flags & SPROP_IS_ALIAS)) { + JS_ASSERT(ids < ne->ids + length); + *ids++ = sprop->id; + } + } + JS_ASSERT(ids == ne->ids + length); + JS_UNLOCK_SCOPE(cx, scope); + + /* + * Do not cache enumerators for objects with with a shape + * that had overflowed, see bug 440834. + */ + if (shape < SHAPE_OVERFLOW_BIT) + SetEnumeratorCache(cx, cachep, reinterpret_cast(ne)); + } while (0); + + if (!ne) { + JS_ASSERT(length == 0); + *statep = JSVAL_ZERO; + } else { + JS_ASSERT(length != 0); + JS_ASSERT(ne->cursor == length); + JS_ASSERT(!(reinterpret_cast(ne) & jsuword(1))); + *statep = PRIVATE_TO_JSVAL(ne); + } + if (idp) + *idp = INT_TO_JSVAL(length); + break; + } + + case JSENUMERATE_NEXT: + case JSENUMERATE_DESTROY: { + if (*statep == JSVAL_ZERO) { + *statep = JSVAL_NULL; + break; + } + JSNativeEnumerator *ne = (JSNativeEnumerator *) + JSVAL_TO_PRIVATE(*statep); + JS_ASSERT(ne->length >= 1); + JS_ASSERT(ne->cursor >= 1); + if (enum_op == JSENUMERATE_NEXT) { + uint32 newcursor = ne->cursor - 1; + *idp = ne->ids[newcursor]; + if (newcursor != 0) { + ne->cursor = newcursor; + break; + } + } else { + /* The enumerator has not iterated over all ids. */ + JS_ASSERT(enum_op == JSENUMERATE_DESTROY); + } + *statep = JSVAL_ZERO; + + jsuword *cachep = &JS_THREAD_DATA(cx)-> + nativeEnumCache[NATIVE_ENUM_CACHE_HASH(ne->shape)]; + if (reinterpret_cast(ne) == *cachep) { + /* Mark the cached iterator as available. */ + ne->cursor = 0; + } else { + cx->free(ne); + } + break; + } + } + return true; +} + +void +js_MarkEnumeratorState(JSTracer *trc, JSObject *obj, jsval state) +{ + if (JSVAL_IS_TRACEABLE(state)) { + JS_CALL_TRACER(trc, JSVAL_TO_TRACEABLE(state), + JSVAL_TRACE_KIND(state), "enumerator_value"); + } else if (obj->map->ops->enumerate == js_Enumerate && + !(obj->getClass()->flags & JSCLASS_NEW_ENUMERATE)) { + /* Check if state stores JSNativeEnumerator. */ + JS_ASSERT(JSVAL_IS_INT(state) || + JSVAL_IS_NULL(state) || + JSVAL_IS_VOID(state)); + if (JSVAL_IS_INT(state) && state != JSVAL_ZERO) + ((JSNativeEnumerator *) JSVAL_TO_PRIVATE(state))->mark(trc); + } +} + +void +js_PurgeCachedNativeEnumerators(JSContext *cx, JSThreadData *data) +{ + jsuword *cachep = &data->nativeEnumCache[0]; + jsuword *end = cachep + JS_ARRAY_LENGTH(data->nativeEnumCache); + for (; cachep != end; ++cachep) + SetEnumeratorCache(cx, cachep, jsuword(0)); + +#ifdef JS_DUMP_ENUM_CACHE_STATS + printf("nativeEnumCache hit rate %g%%\n", + 100.0 * (cx->runtime->nativeEnumProbes - + cx->runtime->nativeEnumMisses) / + cx->runtime->nativeEnumProbes); +#endif +} + +JSBool +js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, + jsval *vp, uintN *attrsp) +{ + JSBool writing; + JSObject *pobj; + JSProperty *prop; + JSClass *clasp; + JSScopeProperty *sprop; + JSSecurityCallbacks *callbacks; + JSCheckAccessOp check; + + writing = (mode & JSACC_WRITE) != 0; + switch (mode & JSACC_TYPEMASK) { + case JSACC_PROTO: + pobj = obj; + if (!writing) + *vp = OBJECT_TO_JSVAL(OBJ_GET_PROTO(cx, obj)); + *attrsp = JSPROP_PERMANENT; + break; + + case JSACC_PARENT: + JS_ASSERT(!writing); + pobj = obj; + *vp = OBJECT_TO_JSVAL(OBJ_GET_PARENT(cx, obj)); + *attrsp = JSPROP_READONLY | JSPROP_PERMANENT; + break; + + default: + if (!obj->lookupProperty(cx, id, &pobj, &prop)) + return JS_FALSE; + if (!prop) { + if (!writing) + *vp = JSVAL_VOID; + *attrsp = 0; + pobj = obj; + break; + } + + if (!OBJ_IS_NATIVE(pobj)) { + pobj->dropProperty(cx, prop); + + /* Avoid diverging for non-natives that reuse js_CheckAccess. */ + if (pobj->map->ops->checkAccess == js_CheckAccess) { + if (!writing) { + *vp = JSVAL_VOID; + *attrsp = 0; + } + break; + } + return pobj->checkAccess(cx, id, mode, vp, attrsp); + } + + sprop = (JSScopeProperty *)prop; + *attrsp = sprop->attrs; + if (!writing) { + *vp = (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))) + ? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot) + : JSVAL_VOID; + } + pobj->dropProperty(cx, prop); + } + + /* + * If obj's class has a stub (null) checkAccess hook, use the per-runtime + * checkObjectAccess callback, if configured. + * + * We don't want to require all classes to supply a checkAccess hook; we + * need that hook only for certain classes used when precompiling scripts + * and functions ("brutal sharing"). But for general safety of built-in + * magic properties such as __proto__ and __parent__, we route all access + * checks, even for classes that stub out checkAccess, through the global + * checkObjectAccess hook. This covers precompilation-based sharing and + * (possibly unintended) runtime sharing across trust boundaries. + */ + clasp = OBJ_GET_CLASS(cx, pobj); + check = clasp->checkAccess; + if (!check) { + callbacks = JS_GetSecurityCallbacks(cx); + check = callbacks ? callbacks->checkObjectAccess : NULL; + } + return !check || check(cx, pobj, ID_TO_VALUE(id), mode, vp); +} + +#ifdef JS_THREADSAFE +void +js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop) +{ + JS_UNLOCK_OBJ(cx, obj); +} +#endif + +#ifdef NARCISSUS +static JSBool +GetCurrentExecutionContext(JSContext *cx, JSObject *obj, jsval *rval) +{ + JSObject *tmp; + jsval xcval; + + while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL) + obj = tmp; + if (!obj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.ExecutionContextAtom), &xcval)) + return JS_FALSE; + if (JSVAL_IS_PRIMITIVE(xcval)) { + JS_ReportError(cx, "invalid ExecutionContext in global object"); + return JS_FALSE; + } + if (!JSVAL_TO_OBJECT(xcval)->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.currentAtom), + rval)) { + return JS_FALSE; + } + return JS_TRUE; +} +#endif + +JSBool +js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSClass *clasp; + + clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2])); + if (!clasp->call) { +#ifdef NARCISSUS + JSObject *callee, *args; + jsval fval, nargv[3]; + JSBool ok; + + callee = JSVAL_TO_OBJECT(argv[-2]); + if (!callee->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.__call__Atom), &fval)) + return JS_FALSE; + if (VALUE_IS_FUNCTION(cx, fval)) { + if (!GetCurrentExecutionContext(cx, obj, &nargv[2])) + return JS_FALSE; + args = js_GetArgsObject(cx, js_GetTopStackFrame(cx)); + if (!args) + return JS_FALSE; + nargv[0] = OBJECT_TO_JSVAL(obj); + nargv[1] = OBJECT_TO_JSVAL(args); + return js_InternalCall(cx, callee, fval, 3, nargv, rval); + } + if (JSVAL_IS_OBJECT(fval) && JSVAL_TO_OBJECT(fval) != callee) { + argv[-2] = fval; + ok = js_Call(cx, obj, argc, argv, rval); + argv[-2] = OBJECT_TO_JSVAL(callee); + return ok; + } +#endif + js_ReportIsNotFunction(cx, &argv[-2], js_GetTopStackFrame(cx)->flags & JSFRAME_ITERATOR); + return JS_FALSE; + } + return clasp->call(cx, obj, argc, argv, rval); +} + +JSBool +js_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSClass *clasp; + + clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2])); + if (!clasp->construct) { +#ifdef NARCISSUS + JSObject *callee, *args; + jsval cval, nargv[2]; + JSBool ok; + + callee = JSVAL_TO_OBJECT(argv[-2]); + if (!callee->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.__construct__Atom), + &cval)) { + return JS_FALSE; + } + if (VALUE_IS_FUNCTION(cx, cval)) { + if (!GetCurrentExecutionContext(cx, obj, &nargv[1])) + return JS_FALSE; + args = js_GetArgsObject(cx, js_GetTopStackFrame(cx)); + if (!args) + return JS_FALSE; + nargv[0] = OBJECT_TO_JSVAL(args); + return js_InternalCall(cx, callee, cval, 2, nargv, rval); + } + if (JSVAL_IS_OBJECT(cval) && JSVAL_TO_OBJECT(cval) != callee) { + argv[-2] = cval; + ok = js_Call(cx, obj, argc, argv, rval); + argv[-2] = OBJECT_TO_JSVAL(callee); + return ok; + } +#endif + js_ReportIsNotFunction(cx, &argv[-2], JSV2F_CONSTRUCT); + return JS_FALSE; + } + return clasp->construct(cx, obj, argc, argv, rval); +} + +JSBool +js_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) +{ + JSClass *clasp; + + clasp = OBJ_GET_CLASS(cx, obj); + if (clasp->hasInstance) + return clasp->hasInstance(cx, obj, v, bp); +#ifdef NARCISSUS + { + jsval fval, rval; + + if (!obj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.__hasInstance__Atom), &fval)) + return JS_FALSE; + if (VALUE_IS_FUNCTION(cx, fval)) { + if (!js_InternalCall(cx, obj, fval, 1, &v, &rval)) + return JS_FALSE; + *bp = js_ValueToBoolean(rval); + return JS_TRUE; + } + } +#endif + js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS, + JSDVG_SEARCH_STACK, OBJECT_TO_JSVAL(obj), NULL); + return JS_FALSE; +} + +JSBool +js_IsDelegate(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) +{ + JSObject *obj2; + + *bp = JS_FALSE; + if (JSVAL_IS_PRIMITIVE(v)) + return JS_TRUE; + obj2 = JSVAL_TO_OBJECT(v); + while ((obj2 = OBJ_GET_PROTO(cx, obj2)) != NULL) { + if (obj2 == obj) { + *bp = JS_TRUE; + break; + } + } + return JS_TRUE; +} + +JSBool +js_GetClassPrototype(JSContext *cx, JSObject *scope, jsid id, + JSObject **protop) +{ + jsval v; + JSObject *ctor; + + if (!js_FindClassObject(cx, scope, id, &v)) + return JS_FALSE; + if (VALUE_IS_FUNCTION(cx, v)) { + ctor = JSVAL_TO_OBJECT(v); + if (!ctor->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), &v)) + return JS_FALSE; + if (!JSVAL_IS_PRIMITIVE(v)) { + /* + * Set the newborn root in case v is otherwise unreferenced. + * It's ok to overwrite newborn roots here, since the getter + * called just above could have. Unlike the common GC rooting + * model, our callers do not have to protect protop thanks to + * this newborn root, since they all immediately create a new + * instance that delegates to this object, or just query the + * prototype for its class. + */ + cx->weakRoots.finalizableNewborns[FINALIZE_OBJECT] = + JSVAL_TO_OBJECT(v); + } + } + *protop = JSVAL_IS_OBJECT(v) ? JSVAL_TO_OBJECT(v) : NULL; + return JS_TRUE; +} + +/* + * For shared precompilation of function objects, we support cloning on entry + * to an execution context in which the function declaration or expression + * should be processed as if it were not precompiled, where the precompiled + * function's scope chain does not match the execution context's. The cloned + * function object carries its execution-context scope in its parent slot; it + * links to the precompiled function (the "clone-parent") via its proto slot. + * + * Note that this prototype-based delegation leaves an unchecked access path + * from the clone to the clone-parent's 'constructor' property. If the clone + * lives in a less privileged or shared scope than the clone-parent, this is + * a security hole, a sharing hazard, or both. Therefore we check all such + * accesses with the following getter/setter pair, which we use when defining + * 'constructor' in f.prototype for all function objects f. + */ +static JSBool +CheckCtorGetAccess(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSAtom *atom; + uintN attrs; + + atom = cx->runtime->atomState.constructorAtom; + JS_ASSERT(id == ATOM_TO_JSID(atom)); + return obj->checkAccess(cx, ATOM_TO_JSID(atom), JSACC_READ, vp, &attrs); +} + +static JSBool +CheckCtorSetAccess(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSAtom *atom; + uintN attrs; + + atom = cx->runtime->atomState.constructorAtom; + JS_ASSERT(id == ATOM_TO_JSID(atom)); + return obj->checkAccess(cx, ATOM_TO_JSID(atom), JSACC_WRITE, vp, &attrs); +} + +JSBool +js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto, + uintN attrs) +{ + /* + * Use the given attributes for the prototype property of the constructor, + * as user-defined constructors have a DontDelete prototype (which may be + * reset), while native or "system" constructors have DontEnum | ReadOnly | + * DontDelete. + */ + if (!ctor->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), + OBJECT_TO_JSVAL(proto), JS_PropertyStub, JS_PropertyStub, + attrs)) { + return JS_FALSE; + } + + /* + * ECMA says that Object.prototype.constructor, or f.prototype.constructor + * for a user-defined function f, is DontEnum. + */ + return proto->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.constructorAtom), + OBJECT_TO_JSVAL(ctor), CheckCtorGetAccess, CheckCtorSetAccess, 0); +} + +JSBool +js_PrimitiveToObject(JSContext *cx, jsval *vp) +{ + JSClass *clasp; + JSObject *obj; + + /* Table to map primitive value's tag into the corresponding class. */ + JS_STATIC_ASSERT(JSVAL_INT == 1); + JS_STATIC_ASSERT(JSVAL_DOUBLE == 2); + JS_STATIC_ASSERT(JSVAL_STRING == 4); + JS_STATIC_ASSERT(JSVAL_SPECIAL == 6); + static JSClass *const PrimitiveClasses[] = { + &js_NumberClass, /* INT */ + &js_NumberClass, /* DOUBLE */ + &js_NumberClass, /* INT */ + &js_StringClass, /* STRING */ + &js_NumberClass, /* INT */ + &js_BooleanClass, /* BOOLEAN */ + &js_NumberClass /* INT */ + }; + + JS_ASSERT(!JSVAL_IS_OBJECT(*vp)); + JS_ASSERT(!JSVAL_IS_VOID(*vp)); + clasp = PrimitiveClasses[JSVAL_TAG(*vp) - 1]; + obj = js_NewObject(cx, clasp, NULL, NULL); + if (!obj) + return JS_FALSE; + obj->fslots[JSSLOT_PRIMITIVE_THIS] = *vp; + *vp = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +JSBool +js_ValueToObject(JSContext *cx, jsval v, JSObject **objp) +{ + JSObject *obj; + + if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) { + obj = NULL; + } else if (JSVAL_IS_OBJECT(v)) { + obj = JSVAL_TO_OBJECT(v); + if (!obj->defaultValue(cx, JSTYPE_OBJECT, &v)) + return JS_FALSE; + if (!JSVAL_IS_PRIMITIVE(v)) + obj = JSVAL_TO_OBJECT(v); + } else { + if (!js_PrimitiveToObject(cx, &v)) + return JS_FALSE; + obj = JSVAL_TO_OBJECT(v); + } + *objp = obj; + return JS_TRUE; +} + +JSObject * +js_ValueToNonNullObject(JSContext *cx, jsval v) +{ + JSObject *obj; + + if (!js_ValueToObject(cx, v, &obj)) + return NULL; + if (!obj) + js_ReportIsNullOrUndefined(cx, JSDVG_SEARCH_STACK, v, NULL); + return obj; +} + +JSBool +js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, jsval *rval) +{ + jsval argv[1]; + + argv[0] = ATOM_KEY(cx->runtime->atomState.typeAtoms[type]); + return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom, 1, argv, + rval); +} + +JSBool +js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom, + uintN argc, jsval *argv, jsval *rval) +{ + JSErrorReporter older; + jsid id; + jsval fval; + JSBool ok; + + JS_CHECK_RECURSION(cx, return JS_FALSE); + + /* + * Report failure only if an appropriate method was found, and calling it + * returned failure. We propagate failure in this case to make exceptions + * behave properly. + */ + older = JS_SetErrorReporter(cx, NULL); + id = ATOM_TO_JSID(atom); + fval = JSVAL_VOID; + ok = js_GetMethod(cx, obj, id, JSGET_NO_METHOD_BARRIER, &fval); + if (!ok) + JS_ClearPendingException(cx); + JS_SetErrorReporter(cx, older); + + if (JSVAL_IS_PRIMITIVE(fval)) + return JS_TRUE; + return js_InternalCall(cx, obj, fval, argc, argv, rval); +} + +#if JS_HAS_XDR + +JSBool +js_XDRObject(JSXDRState *xdr, JSObject **objp) +{ + JSContext *cx; + JSAtom *atom; + JSClass *clasp; + uint32 classId, classDef; + JSProtoKey protoKey; + jsid classKey; + JSObject *proto; + + cx = xdr->cx; + atom = NULL; + if (xdr->mode == JSXDR_ENCODE) { + clasp = OBJ_GET_CLASS(cx, *objp); + classId = JS_XDRFindClassIdByName(xdr, clasp->name); + classDef = !classId; + if (classDef) { + if (!JS_XDRRegisterClass(xdr, clasp, &classId)) + return JS_FALSE; + protoKey = JSCLASS_CACHED_PROTO_KEY(clasp); + if (protoKey != JSProto_Null) { + classDef |= (protoKey << 1); + } else { + atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0); + if (!atom) + return JS_FALSE; + } + } + } else { + clasp = NULL; /* quell GCC overwarning */ + classDef = 0; + } + + /* + * XDR a flag word, which could be 0 for a class use, in which case no + * name follows, only the id in xdr's class registry; 1 for a class def, + * in which case the flag word is followed by the class name transferred + * from or to atom; or a value greater than 1, an odd number that when + * divided by two yields the JSProtoKey for class. In the last case, as + * in the 0 classDef case, no name is transferred via atom. + */ + if (!JS_XDRUint32(xdr, &classDef)) + return JS_FALSE; + if (classDef == 1 && !js_XDRStringAtom(xdr, &atom)) + return JS_FALSE; + + if (!JS_XDRUint32(xdr, &classId)) + return JS_FALSE; + + if (xdr->mode == JSXDR_DECODE) { + if (classDef) { + /* NB: we know that JSProto_Null is 0 here, for backward compat. */ + protoKey = (JSProtoKey) (classDef >> 1); + classKey = (protoKey != JSProto_Null) + ? INT_TO_JSID(protoKey) + : ATOM_TO_JSID(atom); + if (!js_GetClassPrototype(cx, NULL, classKey, &proto)) + return JS_FALSE; + clasp = OBJ_GET_CLASS(cx, proto); + if (!JS_XDRRegisterClass(xdr, clasp, &classId)) + return JS_FALSE; + } else { + clasp = JS_XDRFindClassById(xdr, classId); + if (!clasp) { + char numBuf[12]; + JS_snprintf(numBuf, sizeof numBuf, "%ld", (long)classId); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_CANT_FIND_CLASS, numBuf); + return JS_FALSE; + } + } + } + + if (!clasp->xdrObject) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_CANT_XDR_CLASS, clasp->name); + return JS_FALSE; + } + return clasp->xdrObject(xdr, objp); +} + +#endif /* JS_HAS_XDR */ + +#ifdef JS_DUMP_SCOPE_METERS + +#include + +JSBasicStats js_entry_count_bs = JS_INIT_STATIC_BASIC_STATS; + +static void +MeterEntryCount(uintN count) +{ + JS_BASIC_STATS_ACCUM(&js_entry_count_bs, count); +} + +void +js_DumpScopeMeters(JSRuntime *rt) +{ + static FILE *logfp; + if (!logfp) + logfp = fopen("/tmp/scope.stats", "a"); + + { + double mean, sigma; + + mean = JS_MeanAndStdDevBS(&js_entry_count_bs, &sigma); + + fprintf(logfp, "scopes %u entries %g mean %g sigma %g max %u", + js_entry_count_bs.num, js_entry_count_bs.sum, mean, sigma, + js_entry_count_bs.max); + } + + JS_DumpHistogram(&js_entry_count_bs, logfp); + JS_BASIC_STATS_INIT(&js_entry_count_bs); + fflush(logfp); +} +#endif + +#ifdef DEBUG +void +js_PrintObjectSlotName(JSTracer *trc, char *buf, size_t bufsize) +{ + JS_ASSERT(trc->debugPrinter == js_PrintObjectSlotName); + + JSObject *obj = (JSObject *)trc->debugPrintArg; + uint32 slot = (uint32)trc->debugPrintIndex; + JS_ASSERT(slot >= JSSLOT_START(obj->getClass())); + + JSScopeProperty *sprop; + if (OBJ_IS_NATIVE(obj)) { + JSScope *scope = OBJ_SCOPE(obj); + sprop = scope->lastProperty(); + while (sprop && sprop->slot != slot) + sprop = sprop->parent; + } else { + sprop = NULL; + } + + if (!sprop) { + const char *slotname = NULL; + JSClass *clasp = obj->getClass(); + if (clasp->flags & JSCLASS_IS_GLOBAL) { + uint32 key = slot - JSSLOT_START(clasp); +#define JS_PROTO(name,code,init) \ + if ((code) == key) { slotname = js_##name##_str; goto found; } +#include "jsproto.tbl" +#undef JS_PROTO + } + found: + if (slotname) + JS_snprintf(buf, bufsize, "CLASS_OBJECT(%s)", slotname); + else + JS_snprintf(buf, bufsize, "**UNKNOWN SLOT %ld**", (long)slot); + } else { + jsval nval = ID_TO_VALUE(sprop->id); + if (JSVAL_IS_INT(nval)) { + JS_snprintf(buf, bufsize, "%ld", (long)JSVAL_TO_INT(nval)); + } else if (JSVAL_IS_STRING(nval)) { + js_PutEscapedString(buf, bufsize, JSVAL_TO_STRING(nval), 0); + } else { + JS_snprintf(buf, bufsize, "**FINALIZED ATOM KEY**"); + } + } +} +#endif + +void +js_TraceObject(JSTracer *trc, JSObject *obj) +{ + JS_ASSERT(OBJ_IS_NATIVE(obj)); + + JSContext *cx = trc->context; + JSScope *scope = OBJ_SCOPE(obj); + if (scope->owned() && IS_GC_MARKING_TRACER(trc)) { + /* + * Check whether we should shrink the object's slots. Skip this check + * if the scope is shared, since for Block objects and flat closures + * that share their scope, scope->freeslot can be an underestimate. + */ + size_t slots = scope->freeslot; + if (STOBJ_NSLOTS(obj) != slots) + js_ShrinkSlots(cx, obj, slots); + } + +#ifdef JS_DUMP_SCOPE_METERS + MeterEntryCount(scope->entryCount); +#endif + + scope->trace(trc); + + if (!JS_CLIST_IS_EMPTY(&cx->runtime->watchPointList)) + js_TraceWatchPoints(trc, obj); + + /* No one runs while the GC is running, so we can use LOCKED_... here. */ + JSClass *clasp = obj->getClass(); + if (clasp->mark) { + if (clasp->flags & JSCLASS_MARK_IS_TRACE) + ((JSTraceOp) clasp->mark)(trc, obj); + else if (IS_GC_MARKING_TRACER(trc)) + (void) clasp->mark(cx, obj, trc); + } + + obj->traceProtoAndParent(trc); + + /* + * An unmutated object that shares a prototype object's scope. We can't + * tell how many slots are in use in obj by looking at its scope, so we + * use STOBJ_NSLOTS(obj). + * + * NB: In case clasp->mark mutates something, leave this code here -- + * don't move it up and unify it with the |if (!traceScope)| section + * above. + */ + uint32 nslots = STOBJ_NSLOTS(obj); + if (scope->owned() && scope->freeslot < nslots) + nslots = scope->freeslot; + JS_ASSERT(nslots >= JSSLOT_START(clasp)); + + for (uint32 i = JSSLOT_START(clasp); i != nslots; ++i) { + jsval v = STOBJ_GET_SLOT(obj, i); + if (JSVAL_IS_TRACEABLE(v)) { + JS_SET_TRACING_DETAILS(trc, js_PrintObjectSlotName, obj, i); + JS_CallTracer(trc, JSVAL_TO_TRACEABLE(v), JSVAL_TRACE_KIND(v)); + } + } +} + +void +js_Clear(JSContext *cx, JSObject *obj) +{ + JSScope *scope; + uint32 i, n; + + /* + * Clear our scope and the property cache of all obj's properties only if + * obj owns the scope (i.e., not if obj is sharing another object's scope). + * NB: we do not clear any reserved slots lying below JSSLOT_FREE(clasp). + */ + JS_LOCK_OBJ(cx, obj); + scope = OBJ_SCOPE(obj); + if (scope->owned()) { + /* Now that we're done using scope->lastProp/table, clear scope. */ + scope->clear(cx); + + /* Clear slot values and reset freeslot so we're consistent. */ + i = STOBJ_NSLOTS(obj); + n = JSSLOT_FREE(obj->getClass()); + while (--i >= n) + STOBJ_SET_SLOT(obj, i, JSVAL_VOID); + scope->freeslot = n; + } + JS_UNLOCK_OBJ(cx, obj); +} + +/* On failure the function unlocks the object. */ +static bool +ReservedSlotIndexOK(JSContext *cx, JSObject *obj, JSClass *clasp, + uint32 index, uint32 limit) +{ + JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj)); + + /* Check the computed, possibly per-instance, upper bound. */ + if (clasp->reserveSlots) + limit += clasp->reserveSlots(cx, obj); + if (index >= limit) { + JS_UNLOCK_OBJ(cx, obj); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_RESERVED_SLOT_RANGE); + return false; + } + return true; +} + +bool +js_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval *vp) +{ + if (!OBJ_IS_NATIVE(obj)) { + *vp = JSVAL_VOID; + return true; + } + + JSClass *clasp = obj->getClass(); + uint32 limit = JSCLASS_RESERVED_SLOTS(clasp); + + JS_LOCK_OBJ(cx, obj); + if (index >= limit && !ReservedSlotIndexOK(cx, obj, clasp, index, limit)) + return false; + + uint32 slot = JSSLOT_START(clasp) + index; + *vp = (slot < STOBJ_NSLOTS(obj)) ? STOBJ_GET_SLOT(obj, slot) : JSVAL_VOID; + JS_UNLOCK_OBJ(cx, obj); + return true; +} + +bool +js_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval v) +{ + if (!OBJ_IS_NATIVE(obj)) + return true; + + JSClass *clasp = OBJ_GET_CLASS(cx, obj); + uint32 limit = JSCLASS_RESERVED_SLOTS(clasp); + + JS_LOCK_OBJ(cx, obj); + if (index >= limit && !ReservedSlotIndexOK(cx, obj, clasp, index, limit)) + return false; + + uint32 slot = JSSLOT_START(clasp) + index; + if (slot >= JS_INITIAL_NSLOTS && !obj->dslots) { + /* + * At this point, obj may or may not own scope, and we may or may not + * need to allocate dslots. If scope is shared, scope->freeslot may not + * be accurate for obj (see comment below). + */ + uint32 nslots = JSSLOT_FREE(clasp); + if (clasp->reserveSlots) + nslots += clasp->reserveSlots(cx, obj); + JS_ASSERT(slot < nslots); + if (!AllocSlots(cx, obj, nslots)) { + JS_UNLOCK_OBJ(cx, obj); + return false; + } + } + + /* + * Whether or not we grew nslots, we may need to advance freeslot. + * + * If scope is shared, do not modify scope->freeslot. It is OK for freeslot + * to be an underestimate in objects with shared scopes, as they will get + * their own scopes before mutating, and elsewhere (e.g. js_TraceObject) we + * use STOBJ_NSLOTS(obj) rather than rely on freeslot. + */ + JSScope *scope = OBJ_SCOPE(obj); + if (scope->owned() && slot >= scope->freeslot) + scope->freeslot = slot + 1; + + STOBJ_SET_SLOT(obj, slot, v); + GC_POKE(cx, JS_NULL); + JS_UNLOCK_SCOPE(cx, scope); + return true; +} + +JSObject * +js_GetWrappedObject(JSContext *cx, JSObject *obj) +{ + JSClass *clasp; + + clasp = OBJ_GET_CLASS(cx, obj); + if (clasp->flags & JSCLASS_IS_EXTENDED) { + JSExtendedClass *xclasp; + JSObject *obj2; + + xclasp = (JSExtendedClass *)clasp; + if (xclasp->wrappedObject && (obj2 = xclasp->wrappedObject(cx, obj))) + return obj2; + } + return obj; +} + +JSBool +js_IsCallable(JSObject *obj, JSContext *cx) +{ + if (!OBJ_IS_NATIVE(obj)) + return obj->map->ops->call != NULL; + + JS_LOCK_OBJ(cx, obj); + JSBool callable = (obj->map->ops == &js_ObjectOps) + ? HAS_FUNCTION_CLASS(obj) || STOBJ_GET_CLASS(obj)->call + : obj->map->ops->call != NULL; + JS_UNLOCK_OBJ(cx, obj); + return callable; +} + +void +js_ReportGetterOnlyAssignment(JSContext *cx) +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_GETTER_ONLY, NULL); +} + +JS_FRIEND_API(JSBool) +js_GetterOnlyPropertyStub(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + js_ReportGetterOnlyAssignment(cx); + return JS_FALSE; +} + +#ifdef DEBUG + +/* + * Routines to print out values during debugging. These are FRIEND_API to help + * the debugger find them and to support temporarily hacking js_Dump* calls + * into other code. + */ + +void +dumpChars(const jschar *s, size_t n) +{ + size_t i; + + if (n == (size_t) -1) { + while (s[++n]) ; + } + + fputc('"', stderr); + for (i = 0; i < n; i++) { + if (s[i] == '\n') + fprintf(stderr, "\\n"); + else if (s[i] == '\t') + fprintf(stderr, "\\t"); + else if (s[i] >= 32 && s[i] < 127) + fputc(s[i], stderr); + else if (s[i] <= 255) + fprintf(stderr, "\\x%02x", (unsigned int) s[i]); + else + fprintf(stderr, "\\u%04x", (unsigned int) s[i]); + } + fputc('"', stderr); +} + +JS_FRIEND_API(void) +js_DumpChars(const jschar *s, size_t n) +{ + fprintf(stderr, "jschar * (%p) = ", (void *) s); + dumpChars(s, n); + fputc('\n', stderr); +} + +void +dumpString(JSString *str) +{ + dumpChars(str->chars(), str->length()); +} + +JS_FRIEND_API(void) +js_DumpString(JSString *str) +{ + fprintf(stderr, "JSString* (%p) = jschar * (%p) = ", + (void *) str, (void *) str->chars()); + dumpString(str); + fputc('\n', stderr); +} + +JS_FRIEND_API(void) +js_DumpAtom(JSAtom *atom) +{ + fprintf(stderr, "JSAtom* (%p) = ", (void *) atom); + js_DumpValue(ATOM_KEY(atom)); +} + +void +dumpValue(jsval val) +{ + if (JSVAL_IS_NULL(val)) { + fprintf(stderr, "null"); + } else if (JSVAL_IS_VOID(val)) { + fprintf(stderr, "undefined"); + } else if (JSVAL_IS_OBJECT(val) && + HAS_FUNCTION_CLASS(JSVAL_TO_OBJECT(val))) { + JSObject *funobj = JSVAL_TO_OBJECT(val); + JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj); + fprintf(stderr, "<%s %s at %p (JSFunction at %p)>", + fun->atom ? "function" : "unnamed", + fun->atom ? JS_GetStringBytes(ATOM_TO_STRING(fun->atom)) : "function", + (void *) funobj, + (void *) fun); + } else if (JSVAL_IS_OBJECT(val)) { + JSObject *obj = JSVAL_TO_OBJECT(val); + JSClass *cls = STOBJ_GET_CLASS(obj); + fprintf(stderr, "<%s%s at %p>", + cls->name, + cls == &js_ObjectClass ? "" : " object", + (void *) obj); + } else if (JSVAL_IS_INT(val)) { + fprintf(stderr, "%d", JSVAL_TO_INT(val)); + } else if (JSVAL_IS_STRING(val)) { + dumpString(JSVAL_TO_STRING(val)); + } else if (JSVAL_IS_DOUBLE(val)) { + fprintf(stderr, "%g", *JSVAL_TO_DOUBLE(val)); + } else if (val == JSVAL_TRUE) { + fprintf(stderr, "true"); + } else if (val == JSVAL_FALSE) { + fprintf(stderr, "false"); + } else if (val == JSVAL_HOLE) { + fprintf(stderr, "hole"); + } else { + /* jsvals are pointer-sized, and %p is portable */ + fprintf(stderr, "unrecognized jsval %p", (void *) val); + } +} + +JS_FRIEND_API(void) +js_DumpValue(jsval val) +{ + fprintf(stderr, "jsval %p = ", (void *) val); + dumpValue(val); + fputc('\n', stderr); +} + +JS_FRIEND_API(void) +js_DumpId(jsid id) +{ + fprintf(stderr, "jsid %p = ", (void *) id); + dumpValue(ID_TO_VALUE(id)); + fputc('\n', stderr); +} + +static void +dumpScopeProp(JSScopeProperty *sprop) +{ + jsid id = sprop->id; + uint8 attrs = sprop->attrs; + + fprintf(stderr, " "); + if (attrs & JSPROP_ENUMERATE) fprintf(stderr, "enumerate "); + if (attrs & JSPROP_READONLY) fprintf(stderr, "readonly "); + if (attrs & JSPROP_PERMANENT) fprintf(stderr, "permanent "); + if (attrs & JSPROP_GETTER) fprintf(stderr, "getter "); + if (attrs & JSPROP_SETTER) fprintf(stderr, "setter "); + if (attrs & JSPROP_SHARED) fprintf(stderr, "shared "); + if (sprop->flags & SPROP_IS_ALIAS) fprintf(stderr, "alias "); + if (JSID_IS_ATOM(id)) + dumpString(JSVAL_TO_STRING(ID_TO_VALUE(id))); + else if (JSID_IS_INT(id)) + fprintf(stderr, "%d", (int) JSID_TO_INT(id)); + else + fprintf(stderr, "unknown jsid %p", (void *) id); + fprintf(stderr, ": slot %d", sprop->slot); + fprintf(stderr, "\n"); +} + +JS_FRIEND_API(void) +js_DumpObject(JSObject *obj) +{ + uint32 i, slots; + JSClass *clasp; + jsuint reservedEnd; + + fprintf(stderr, "object %p\n", (void *) obj); + clasp = STOBJ_GET_CLASS(obj); + fprintf(stderr, "class %p %s\n", (void *)clasp, clasp->name); + + /* OBJ_IS_DENSE_ARRAY ignores the cx argument. */ + if (OBJ_IS_DENSE_ARRAY(BOGUS_CX, obj)) { + slots = JS_MIN((jsuint) obj->fslots[JSSLOT_ARRAY_LENGTH], + js_DenseArrayCapacity(obj)); + fprintf(stderr, "elements\n"); + for (i = 0; i < slots; i++) { + fprintf(stderr, " %3d: ", i); + dumpValue(obj->dslots[i]); + fprintf(stderr, "\n"); + fflush(stderr); + } + return; + } + + if (OBJ_IS_NATIVE(obj)) { + JSScope *scope = OBJ_SCOPE(obj); + if (scope->sealed()) + fprintf(stderr, "sealed\n"); + + fprintf(stderr, "properties:\n"); + for (JSScopeProperty *sprop = scope->lastProperty(); sprop; + sprop = sprop->parent) { + dumpScopeProp(sprop); + } + } else { + if (!OBJ_IS_NATIVE(obj)) + fprintf(stderr, "not native\n"); + } + + fprintf(stderr, "proto "); + dumpValue(OBJECT_TO_JSVAL(STOBJ_GET_PROTO(obj))); + fputc('\n', stderr); + + fprintf(stderr, "parent "); + dumpValue(OBJECT_TO_JSVAL(STOBJ_GET_PARENT(obj))); + fputc('\n', stderr); + + i = JSSLOT_PRIVATE; + if (clasp->flags & JSCLASS_HAS_PRIVATE) { + i = JSSLOT_PRIVATE + 1; + fprintf(stderr, "private %p\n", obj->getPrivate()); + } + + fprintf(stderr, "slots:\n"); + reservedEnd = i + JSCLASS_RESERVED_SLOTS(clasp); + slots = (OBJ_IS_NATIVE(obj) && OBJ_SCOPE(obj)->owned()) + ? OBJ_SCOPE(obj)->freeslot + : STOBJ_NSLOTS(obj); + for (; i < slots; i++) { + fprintf(stderr, " %3d ", i); + if (i < reservedEnd) + fprintf(stderr, "(reserved) "); + fprintf(stderr, "= "); + dumpValue(STOBJ_GET_SLOT(obj, i)); + fputc('\n', stderr); + } + fputc('\n', stderr); +} + +static void +MaybeDumpObject(const char *name, JSObject *obj) +{ + if (obj) { + fprintf(stderr, " %s: ", name); + dumpValue(OBJECT_TO_JSVAL(obj)); + fputc('\n', stderr); + } +} + +static void +MaybeDumpValue(const char *name, jsval v) +{ + if (!JSVAL_IS_NULL(v)) { + fprintf(stderr, " %s: ", name); + dumpValue(v); + fputc('\n', stderr); + } +} + +JS_FRIEND_API(void) +js_DumpStackFrame(JSStackFrame *fp) +{ + jsval *sp = NULL; + + for (; fp; fp = fp->down) { + fprintf(stderr, "JSStackFrame at %p\n", (void *) fp); + if (fp->argv) + dumpValue(fp->argv[-2]); + else + fprintf(stderr, "global frame, no callee"); + fputc('\n', stderr); + + if (fp->script) + fprintf(stderr, "file %s line %u\n", fp->script->filename, (unsigned) fp->script->lineno); + + if (fp->regs) { + if (!fp->regs->pc) { + fprintf(stderr, "*** regs && !regs->pc, skipping frame\n\n"); + continue; + } + if (!fp->script) { + fprintf(stderr, "*** regs && !script, skipping frame\n\n"); + continue; + } + jsbytecode *pc = fp->regs->pc; + sp = fp->regs->sp; + if (fp->imacpc) { + fprintf(stderr, " pc in imacro at %p\n called from ", pc); + pc = fp->imacpc; + } else { + fprintf(stderr, " "); + } + fprintf(stderr, "pc = %p\n", pc); + fprintf(stderr, " current op: %s\n", js_CodeName[*pc]); + } + if (sp && fp->slots) { + fprintf(stderr, " slots: %p\n", (void *) fp->slots); + fprintf(stderr, " sp: %p = slots + %u\n", (void *) sp, (unsigned) (sp - fp->slots)); + if (sp - fp->slots < 10000) { // sanity + for (jsval *p = fp->slots; p < sp; p++) { + fprintf(stderr, " %p: ", (void *) p); + dumpValue(*p); + fputc('\n', stderr); + } + } + } else { + fprintf(stderr, " sp: %p\n", (void *) sp); + fprintf(stderr, " slots: %p\n", (void *) fp->slots); + } + fprintf(stderr, " argv: %p (argc: %u)\n", (void *) fp->argv, (unsigned) fp->argc); + MaybeDumpObject("callobj", fp->callobj); + MaybeDumpObject("argsobj", JSVAL_TO_OBJECT(fp->argsobj)); + MaybeDumpObject("varobj", fp->varobj); + MaybeDumpValue("this", fp->thisv); + fprintf(stderr, " rval: "); + dumpValue(fp->rval); + fputc('\n', stderr); + + fprintf(stderr, " flags:"); + if (fp->flags == 0) + fprintf(stderr, " none"); + if (fp->flags & JSFRAME_CONSTRUCTING) + fprintf(stderr, " constructing"); + if (fp->flags & JSFRAME_COMPUTED_THIS) + fprintf(stderr, " computed_this"); + if (fp->flags & JSFRAME_ASSIGNING) + fprintf(stderr, " assigning"); + if (fp->flags & JSFRAME_DEBUGGER) + fprintf(stderr, " debugger"); + if (fp->flags & JSFRAME_EVAL) + fprintf(stderr, " eval"); + if (fp->flags & JSFRAME_ROOTED_ARGV) + fprintf(stderr, " rooted_argv"); + if (fp->flags & JSFRAME_YIELDING) + fprintf(stderr, " yielding"); + if (fp->flags & JSFRAME_ITERATOR) + fprintf(stderr, " iterator"); + if (fp->flags & JSFRAME_GENERATOR) + fprintf(stderr, " generator"); + if (fp->flags & JSFRAME_OVERRIDE_ARGS) + fprintf(stderr, " overridden_args"); + fputc('\n', stderr); + + if (fp->scopeChain) + fprintf(stderr, " scopeChain: (JSObject *) %p\n", (void *) fp->scopeChain); + if (fp->blockChain) + fprintf(stderr, " blockChain: (JSObject *) %p\n", (void *) fp->blockChain); + + if (fp->dormantNext) + fprintf(stderr, " dormantNext: (JSStackFrame *) %p\n", (void *) fp->dormantNext); + if (fp->displaySave) + fprintf(stderr, " displaySave: (JSStackFrame *) %p\n", (void *) fp->displaySave); + + fputc('\n', stderr); + } +} + +#endif diff --git a/ape-server/deps/js/src/jsobj.h b/ape-server/deps/js/src/jsobj.h new file mode 100755 index 0000000..1ee8394 --- /dev/null +++ b/ape-server/deps/js/src/jsobj.h @@ -0,0 +1,1018 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsobj_h___ +#define jsobj_h___ +/* + * JS object definitions. + * + * A JS object consists of a possibly-shared object descriptor containing + * ordered property names, called the map; and a dense vector of property + * values, called slots. The map/slot pointer pair is GC'ed, while the map + * is reference counted and the slot vector is malloc'ed. + */ +#include "jshash.h" /* Added by JSIFY */ +#include "jsprvtd.h" +#include "jspubtd.h" + +JS_BEGIN_EXTERN_C + +/* For detailed comments on these function pointer types, see jsprvtd.h. */ +struct JSObjectOps { + /* + * Custom shared object map for non-native objects. For native objects + * this should be null indicating, that JSObject.map is an instance of + * JSScope. + */ + const JSObjectMap *objectMap; + + /* Mandatory non-null function pointer members. */ + JSLookupPropOp lookupProperty; + JSDefinePropOp defineProperty; + JSPropertyIdOp getProperty; + JSPropertyIdOp setProperty; + JSAttributesOp getAttributes; + JSAttributesOp setAttributes; + JSPropertyIdOp deleteProperty; + JSConvertOp defaultValue; + JSNewEnumerateOp enumerate; + JSCheckAccessIdOp checkAccess; + + /* Optionally non-null members start here. */ + JSObjectOp thisObject; + JSPropertyRefOp dropProperty; + JSNative call; + JSNative construct; + JSHasInstanceOp hasInstance; + JSTraceOp trace; + JSFinalizeOp clear; +}; + +struct JSObjectMap { + const JSObjectOps * const ops; /* high level object operation vtable */ + uint32 shape; /* shape identifier */ + + explicit JSObjectMap(const JSObjectOps *ops, uint32 shape) : ops(ops), shape(shape) {} + + enum { SHAPELESS = 0xffffffff }; +}; + +const uint32 JS_INITIAL_NSLOTS = 5; + +const uint32 JSSLOT_PROTO = 0; +const uint32 JSSLOT_PARENT = 1; + +/* + * The first available slot to store generic value. For JSCLASS_HAS_PRIVATE + * classes the slot stores a pointer to private data reinterpreted as jsval. + * Such pointer is stored as is without an overhead of PRIVATE_TO_JSVAL + * tagging and should be accessed using the (get|set)Private methods of + * JSObject. + */ +const uint32 JSSLOT_PRIVATE = 2; + +const uint32 JSSLOT_PRIMITIVE_THIS = JSSLOT_PRIVATE; + +const uintptr_t JSSLOT_CLASS_MASK_BITS = 3; + +/* + * JSObject struct, with members sized to fit in 32 bytes on 32-bit targets, + * 64 bytes on 64-bit systems. The JSFunction struct is an extension of this + * struct allocated from a larger GC size-class. + * + * The classword member stores the JSClass pointer for this object, with the + * least two bits encoding whether this object is a "delegate" or a "system" + * object. We do *not* synchronize updates of classword -- API clients must + * take care. + * + * An object is a delegate if it is on another object's prototype (linked by + * JSSLOT_PROTO) or scope (JSSLOT_PARENT) chain, and therefore the delegate + * might be asked implicitly to get or set a property on behalf of another + * object. Delegates may be accessed directly too, as may any object, but only + * those objects linked after the head of any prototype or scope chain are + * flagged as delegates. This definition helps to optimize shape-based property + * cache invalidation (see Purge{Scope,Proto}Chain in jsobj.cpp). + * + * The meaning of the system object bit is defined by the API client. It is + * set in JS_NewSystemObject and is queried by JS_IsSystemObject (jsdbgapi.h), + * but it has no intrinsic meaning to SpiderMonkey. Further, JSFILENAME_SYSTEM + * and JS_FlagScriptFilenamePrefix (also exported via jsdbgapi.h) are intended + * to be complementary to this bit, but it is up to the API client to implement + * any such association. + * + * Both these classword tag bits are initially zero; they may be set or queried + * using the (is|set)(Delegate|System) inline methods. + * + * The dslots member is null or a pointer into a dynamically allocated vector + * of jsvals for reserved and dynamic slots. If dslots is not null, dslots[-1] + * records the number of available slots. + */ +struct JSObject { + JSObjectMap *map; /* property map, see jsscope.h */ + jsuword classword; /* JSClass ptr | bits, see above */ + jsval fslots[JS_INITIAL_NSLOTS]; /* small number of fixed slots */ + jsval *dslots; /* dynamically allocated slots */ + + JSClass *getClass() const { + return (JSClass *) (classword & ~JSSLOT_CLASS_MASK_BITS); + } + + bool isDelegate() const { + return (classword & jsuword(1)) != jsuword(0); + } + + void setDelegate() { + classword |= jsuword(1); + } + + static void setDelegateNullSafe(JSObject *obj) { + if (obj) + obj->setDelegate(); + } + + bool isSystem() const { + return (classword & jsuword(2)) != jsuword(0); + } + + void setSystem() { + classword |= jsuword(2); + } + + JSObject *getProto() const { + return JSVAL_TO_OBJECT(fslots[JSSLOT_PROTO]); + } + + void clearProto() { + fslots[JSSLOT_PROTO] = JSVAL_NULL; + } + + void setProto(JSObject *newProto) { + setDelegateNullSafe(newProto); + fslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(newProto); + } + + JSObject *getParent() const { + return JSVAL_TO_OBJECT(fslots[JSSLOT_PARENT]); + } + + void clearParent() { + fslots[JSSLOT_PARENT] = JSVAL_NULL; + } + + void setParent(JSObject *newParent) { + setDelegateNullSafe(newParent); + fslots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(newParent); + } + + void traceProtoAndParent(JSTracer *trc) const { + JSObject *proto = getProto(); + if (proto) + JS_CALL_OBJECT_TRACER(trc, proto, "__proto__"); + + JSObject *parent = getParent(); + if (parent) + JS_CALL_OBJECT_TRACER(trc, parent, "__parent__"); + } + + void *getPrivate() const { + JS_ASSERT(getClass()->flags & JSCLASS_HAS_PRIVATE); + jsval v = fslots[JSSLOT_PRIVATE]; + JS_ASSERT((v & jsval(1)) == jsval(0)); + return reinterpret_cast(v); + } + + void setPrivate(void *data) { + JS_ASSERT(getClass()->flags & JSCLASS_HAS_PRIVATE); + jsval v = reinterpret_cast(data); + JS_ASSERT((v & jsval(1)) == jsval(0)); + fslots[JSSLOT_PRIVATE] = v; + } + + static jsval defaultPrivate(JSClass *clasp) { + return (clasp->flags & JSCLASS_HAS_PRIVATE) + ? JSVAL_NULL + : JSVAL_VOID; + } + + /* The map field is not initialized here and should be set separately. */ + void init(JSClass *clasp, JSObject *proto, JSObject *parent, + jsval privateSlotValue) { + JS_ASSERT(((jsuword) clasp & 3) == 0); + JS_STATIC_ASSERT(JSSLOT_PRIVATE + 3 == JS_INITIAL_NSLOTS); + JS_ASSERT_IF(clasp->flags & JSCLASS_HAS_PRIVATE, + (privateSlotValue & jsval(1)) == jsval(0)); + + classword = jsuword(clasp); + JS_ASSERT(!isDelegate()); + JS_ASSERT(!isSystem()); + + setProto(proto); + setParent(parent); + fslots[JSSLOT_PRIVATE] = privateSlotValue; + fslots[JSSLOT_PRIVATE + 1] = JSVAL_VOID; + fslots[JSSLOT_PRIVATE + 2] = JSVAL_VOID; + dslots = NULL; + } + + /* + * Like init, but also initializes map. The catch: proto must be the result + * of a call to js_InitClass(...clasp, ...). + */ + inline void initSharingEmptyScope(JSClass *clasp, JSObject *proto, JSObject *parent, + jsval privateSlotValue); + + JSBool lookupProperty(JSContext *cx, jsid id, + JSObject **objp, JSProperty **propp) { + return map->ops->lookupProperty(cx, this, id, objp, propp); + } + + JSBool defineProperty(JSContext *cx, jsid id, jsval value, + JSPropertyOp getter = JS_PropertyStub, + JSPropertyOp setter = JS_PropertyStub, + uintN attrs = JSPROP_ENUMERATE) { + return map->ops->defineProperty(cx, this, id, value, getter, setter, attrs); + } + + JSBool getProperty(JSContext *cx, jsid id, jsval *vp) { + return map->ops->getProperty(cx, this, id, vp); + } + + JSBool setProperty(JSContext *cx, jsid id, jsval *vp) { + return map->ops->setProperty(cx, this, id, vp); + } + + JSBool getAttributes(JSContext *cx, jsid id, JSProperty *prop, + uintN *attrsp) { + return map->ops->getAttributes(cx, this, id, prop, attrsp); + } + + JSBool setAttributes(JSContext *cx, jsid id, JSProperty *prop, + uintN *attrsp) { + return map->ops->setAttributes(cx, this, id, prop, attrsp); + } + + JSBool deleteProperty(JSContext *cx, jsid id, jsval *rval) { + return map->ops->deleteProperty(cx, this, id, rval); + } + + JSBool defaultValue(JSContext *cx, JSType hint, jsval *vp) { + return map->ops->defaultValue(cx, this, hint, vp); + } + + JSBool enumerate(JSContext *cx, JSIterateOp op, jsval *statep, + jsid *idp) { + return map->ops->enumerate(cx, this, op, statep, idp); + } + + JSBool checkAccess(JSContext *cx, jsid id, JSAccessMode mode, jsval *vp, + uintN *attrsp) { + return map->ops->checkAccess(cx, this, id, mode, vp, attrsp); + } + + /* These four are time-optimized to avoid stub calls. */ + JSObject *thisObject(JSContext *cx) { + return map->ops->thisObject ? map->ops->thisObject(cx, this) : this; + } + + void dropProperty(JSContext *cx, JSProperty *prop) { + if (map->ops->dropProperty) + map->ops->dropProperty(cx, this, prop); + } +}; + +/* Compatibility macros. */ +#define STOBJ_GET_PROTO(obj) ((obj)->getProto()) +#define STOBJ_SET_PROTO(obj,proto) ((obj)->setProto(proto)) +#define STOBJ_CLEAR_PROTO(obj) ((obj)->clearProto()) + +#define STOBJ_GET_PARENT(obj) ((obj)->getParent()) +#define STOBJ_SET_PARENT(obj,parent) ((obj)->setParent(parent)) +#define STOBJ_CLEAR_PARENT(obj) ((obj)->clearParent()) + +#define OBJ_GET_PROTO(cx,obj) STOBJ_GET_PROTO(obj) +#define OBJ_SET_PROTO(cx,obj,proto) STOBJ_SET_PROTO(obj, proto) +#define OBJ_CLEAR_PROTO(cx,obj) STOBJ_CLEAR_PROTO(obj) + +#define OBJ_GET_PARENT(cx,obj) STOBJ_GET_PARENT(obj) +#define OBJ_SET_PARENT(cx,obj,parent) STOBJ_SET_PARENT(obj, parent) +#define OBJ_CLEAR_PARENT(cx,obj) STOBJ_CLEAR_PARENT(obj) + +#define JSSLOT_START(clasp) (((clasp)->flags & JSCLASS_HAS_PRIVATE) \ + ? JSSLOT_PRIVATE + 1 \ + : JSSLOT_PRIVATE) + +#define JSSLOT_FREE(clasp) (JSSLOT_START(clasp) \ + + JSCLASS_RESERVED_SLOTS(clasp)) + +/* + * Maximum capacity of the obj->dslots vector, net of the hidden slot at + * obj->dslots[-1] that is used to store the length of the vector biased by + * JS_INITIAL_NSLOTS (and again net of the slot at index -1). + */ +#define MAX_DSLOTS_LENGTH (JS_MAX(~uint32(0), ~size_t(0)) / sizeof(jsval) - 1) +#define MAX_DSLOTS_LENGTH32 (~uint32(0) / sizeof(jsval) - 1) + +/* + * STOBJ prefix means Single Threaded Object. Use the following fast macros to + * directly manipulate slots in obj when only one thread can access obj, or + * when accessing read-only slots within JS_INITIAL_NSLOTS. + */ + +#define STOBJ_NSLOTS(obj) \ + ((obj)->dslots ? (uint32)(obj)->dslots[-1] : (uint32)JS_INITIAL_NSLOTS) + +inline jsval& +STOBJ_GET_SLOT(JSObject *obj, uintN slot) +{ + return (slot < JS_INITIAL_NSLOTS) + ? obj->fslots[slot] + : (JS_ASSERT(slot < (uint32)obj->dslots[-1]), + obj->dslots[slot - JS_INITIAL_NSLOTS]); +} + +inline void +STOBJ_SET_SLOT(JSObject *obj, uintN slot, jsval value) +{ + if (slot < JS_INITIAL_NSLOTS) { + obj->fslots[slot] = value; + } else { + JS_ASSERT(slot < (uint32)obj->dslots[-1]); + obj->dslots[slot - JS_INITIAL_NSLOTS] = value; + } +} + +inline JSClass* +STOBJ_GET_CLASS(const JSObject* obj) +{ + return obj->getClass(); +} + +#define OBJ_CHECK_SLOT(obj,slot) \ + (JS_ASSERT(OBJ_IS_NATIVE(obj)), JS_ASSERT(slot < OBJ_SCOPE(obj)->freeslot)) + +#define LOCKED_OBJ_GET_SLOT(obj,slot) \ + (OBJ_CHECK_SLOT(obj, slot), STOBJ_GET_SLOT(obj, slot)) +#define LOCKED_OBJ_SET_SLOT(obj,slot,value) \ + (OBJ_CHECK_SLOT(obj, slot), STOBJ_SET_SLOT(obj, slot, value)) + +#ifdef JS_THREADSAFE + +/* Thread-safe functions and wrapper macros for accessing slots in obj. */ +#define OBJ_GET_SLOT(cx,obj,slot) \ + (OBJ_CHECK_SLOT(obj, slot), \ + (OBJ_SCOPE(obj)->title.ownercx == cx) \ + ? LOCKED_OBJ_GET_SLOT(obj, slot) \ + : js_GetSlotThreadSafe(cx, obj, slot)) + +#define OBJ_SET_SLOT(cx,obj,slot,value) \ + JS_BEGIN_MACRO \ + OBJ_CHECK_SLOT(obj, slot); \ + if (OBJ_SCOPE(obj)->title.ownercx == cx) \ + LOCKED_OBJ_SET_SLOT(obj, slot, value); \ + else \ + js_SetSlotThreadSafe(cx, obj, slot, value); \ + JS_END_MACRO + +/* + * If thread-safe, define an OBJ_GET_SLOT wrapper that bypasses, for a native + * object, the lock-free "fast path" test of (OBJ_SCOPE(obj)->ownercx == cx), + * to avoid needlessly switching from lock-free to lock-full scope when doing + * GC on a different context from the last one to own the scope. The caller + * in this case is probably a JSClass.mark function, e.g., fun_mark, or maybe + * a finalizer. + * + * The GC runs only when all threads except the one on which the GC is active + * are suspended at GC-safe points, so calling STOBJ_GET_SLOT from the GC's + * thread is safe when rt->gcRunning is set. See jsgc.c for details. + */ +#define THREAD_IS_RUNNING_GC(rt, thread) \ + ((rt)->gcRunning && (rt)->gcThread == (thread)) + +#define CX_THREAD_IS_RUNNING_GC(cx) \ + THREAD_IS_RUNNING_GC((cx)->runtime, (cx)->thread) + +#else /* !JS_THREADSAFE */ + +#define OBJ_GET_SLOT(cx,obj,slot) LOCKED_OBJ_GET_SLOT(obj,slot) +#define OBJ_SET_SLOT(cx,obj,slot,value) LOCKED_OBJ_SET_SLOT(obj,slot,value) + +#endif /* !JS_THREADSAFE */ + +/* + * Class is invariant and comes from the fixed clasp member. Thus no locking + * is necessary to read it. Same for the private slot. + */ +#define OBJ_GET_CLASS(cx,obj) STOBJ_GET_CLASS(obj) + +/* + * Test whether the object is native. FIXME bug 492938: consider how it would + * affect the performance to do just the !ops->objectMap check. + */ +#define OPS_IS_NATIVE(ops) \ + JS_LIKELY((ops) == &js_ObjectOps || !(ops)->objectMap) + +#define OBJ_IS_NATIVE(obj) OPS_IS_NATIVE((obj)->map->ops) + +#ifdef __cplusplus +inline void +OBJ_TO_INNER_OBJECT(JSContext *cx, JSObject *&obj) +{ + JSClass *clasp = OBJ_GET_CLASS(cx, obj); + if (clasp->flags & JSCLASS_IS_EXTENDED) { + JSExtendedClass *xclasp = (JSExtendedClass *) clasp; + if (xclasp->innerObject) + obj = xclasp->innerObject(cx, obj); + } +} + +/* + * The following function has been copied to jsd/jsd_val.c. If making changes to + * OBJ_TO_OUTER_OBJECT, please update jsd/jsd_val.c as well. + */ +inline void +OBJ_TO_OUTER_OBJECT(JSContext *cx, JSObject *&obj) +{ + JSClass *clasp = OBJ_GET_CLASS(cx, obj); + if (clasp->flags & JSCLASS_IS_EXTENDED) { + JSExtendedClass *xclasp = (JSExtendedClass *) clasp; + if (xclasp->outerObject) + obj = xclasp->outerObject(cx, obj); + } +} +#endif + +extern JS_FRIEND_DATA(JSObjectOps) js_ObjectOps; +extern JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps; +extern JSClass js_ObjectClass; +extern JSClass js_WithClass; +extern JSClass js_BlockClass; + +/* + * Block scope object macros. The slots reserved by js_BlockClass are: + * + * JSSLOT_PRIVATE JSStackFrame * active frame pointer or null + * JSSLOT_BLOCK_DEPTH int depth of block slots in frame + * + * After JSSLOT_BLOCK_DEPTH come one or more slots for the block locals. + * + * A With object is like a Block object, in that both have one reserved slot + * telling the stack depth of the relevant slots (the slot whose value is the + * object named in the with statement, the slots containing the block's local + * variables); and both have a private slot referring to the JSStackFrame in + * whose activation they were created (or null if the with or block object + * outlives the frame). + */ +#define JSSLOT_BLOCK_DEPTH (JSSLOT_PRIVATE + 1) + +static inline bool +OBJ_IS_CLONED_BLOCK(JSObject *obj) +{ + return obj->getProto() != NULL; +} + +extern JSBool +js_DefineBlockVariable(JSContext *cx, JSObject *obj, jsid id, intN index); + +#define OBJ_BLOCK_COUNT(cx,obj) \ + (OBJ_SCOPE(obj)->entryCount) +#define OBJ_BLOCK_DEPTH(cx,obj) \ + JSVAL_TO_INT(STOBJ_GET_SLOT(obj, JSSLOT_BLOCK_DEPTH)) +#define OBJ_SET_BLOCK_DEPTH(cx,obj,depth) \ + STOBJ_SET_SLOT(obj, JSSLOT_BLOCK_DEPTH, INT_TO_JSVAL(depth)) + +/* + * To make sure this slot is well-defined, always call js_NewWithObject to + * create a With object, don't call js_NewObject directly. When creating a + * With object that does not correspond to a stack slot, pass -1 for depth. + * + * When popping the stack across this object's "with" statement, client code + * must call withobj->setPrivate(NULL). + */ +extern JS_REQUIRES_STACK JSObject * +js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth); + +/* + * Create a new block scope object not linked to any proto or parent object. + * Blocks are created by the compiler to reify let blocks and comprehensions. + * Only when dynamic scope is captured do they need to be cloned and spliced + * into an active scope chain. + */ +extern JSObject * +js_NewBlockObject(JSContext *cx); + +extern JSObject * +js_CloneBlockObject(JSContext *cx, JSObject *proto, JSStackFrame *fp); + +extern JS_REQUIRES_STACK JSBool +js_PutBlockObject(JSContext *cx, JSBool normalUnwind); + +JSBool +js_XDRBlockObject(JSXDRState *xdr, JSObject **objp); + +struct JSSharpObjectMap { + jsrefcount depth; + jsatomid sharpgen; + JSHashTable *table; +}; + +#define SHARP_BIT ((jsatomid) 1) +#define BUSY_BIT ((jsatomid) 2) +#define SHARP_ID_SHIFT 2 +#define IS_SHARP(he) (JS_PTR_TO_UINT32((he)->value) & SHARP_BIT) +#define MAKE_SHARP(he) ((he)->value = JS_UINT32_TO_PTR(JS_PTR_TO_UINT32((he)->value)|SHARP_BIT)) +#define IS_BUSY(he) (JS_PTR_TO_UINT32((he)->value) & BUSY_BIT) +#define MAKE_BUSY(he) ((he)->value = JS_UINT32_TO_PTR(JS_PTR_TO_UINT32((he)->value)|BUSY_BIT)) +#define CLEAR_BUSY(he) ((he)->value = JS_UINT32_TO_PTR(JS_PTR_TO_UINT32((he)->value)&~BUSY_BIT)) + +extern JSHashEntry * +js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, + jschar **sp); + +extern void +js_LeaveSharpObject(JSContext *cx, JSIdArray **idap); + +/* + * Mark objects stored in map if GC happens between js_EnterSharpObject + * and js_LeaveSharpObject. GC calls this when map->depth > 0. + */ +extern void +js_TraceSharpMap(JSTracer *trc, JSSharpObjectMap *map); + +extern JSBool +js_HasOwnPropertyHelper(JSContext *cx, JSLookupPropOp lookup, uintN argc, + jsval *vp); + +extern JSBool +js_HasOwnProperty(JSContext *cx, JSLookupPropOp lookup, JSObject *obj, jsid id, + JSBool *foundp); + +extern JSBool +js_PropertyIsEnumerable(JSContext *cx, JSObject *obj, jsid id, jsval *vp); + +extern JSObject * +js_InitEval(JSContext *cx, JSObject *obj); + +extern JSObject * +js_InitObjectClass(JSContext *cx, JSObject *obj); + +extern JSObject * +js_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, + JSClass *clasp, JSNative constructor, uintN nargs, + JSPropertySpec *ps, JSFunctionSpec *fs, + JSPropertySpec *static_ps, JSFunctionSpec *static_fs); + +/* + * Select Object.prototype method names shared between jsapi.cpp and jsobj.cpp. + */ +extern const char js_watch_str[]; +extern const char js_unwatch_str[]; +extern const char js_hasOwnProperty_str[]; +extern const char js_isPrototypeOf_str[]; +extern const char js_propertyIsEnumerable_str[]; +extern const char js_defineGetter_str[]; +extern const char js_defineSetter_str[]; +extern const char js_lookupGetter_str[]; +extern const char js_lookupSetter_str[]; + +extern JSBool +js_GetClassId(JSContext *cx, JSClass *clasp, jsid *idp); + +extern JSObject * +js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, + JSObject *parent, size_t objectSize = 0); + +/* + * See jsapi.h, JS_NewObjectWithGivenProto. + */ +extern JSObject * +js_NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto, + JSObject *parent, size_t objectSize = 0); + +/* + * Allocate a new native object with the given value of the proto and private + * slots. The parent slot is set to the value of proto's parent slot. + * + * clasp must be a native class. proto must be the result of a call to + * js_InitClass(...clasp, ...). + * + * Note that this is the correct global object for native class instances, but + * not for user-defined functions called as constructors. Functions used as + * constructors must create instances parented by the parent of the function + * object, not by the parent of its .prototype object value. + */ +extern JSObject* +js_NewObjectWithClassProto(JSContext *cx, JSClass *clasp, JSObject *proto, + jsval privateSlotValue); + +/* + * Fast access to immutable standard objects (constructors and prototypes). + */ +extern JSBool +js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, + JSObject **objp); + +extern JSBool +js_SetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject *cobj); + +extern JSBool +js_FindClassObject(JSContext *cx, JSObject *start, jsid id, jsval *vp); + +extern JSObject * +js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, + JSObject *parent, uintN argc, jsval *argv); + +extern JSBool +js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp); + +extern void +js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot); + +extern bool +js_GrowSlots(JSContext *cx, JSObject *obj, size_t nslots); + +extern void +js_ShrinkSlots(JSContext *cx, JSObject *obj, size_t nslots); + +static inline void +js_FreeSlots(JSContext *cx, JSObject *obj) +{ + if (obj->dslots) + js_ShrinkSlots(cx, obj, 0); +} + +/* + * Ensure that the object has at least JSCLASS_RESERVED_SLOTS(clasp)+nreserved + * slots. The function can be called only for native objects just created with + * js_NewObject or its forms. In particular, the object should not be shared + * between threads and its dslots array must be null. nreserved must match the + * value that JSClass.reserveSlots (if any) would return after the object is + * fully initialized. + */ +bool +js_EnsureReservedSlots(JSContext *cx, JSObject *obj, size_t nreserved); + +extern jsid +js_CheckForStringIndex(jsid id); + +/* + * js_PurgeScopeChain does nothing if obj is not itself a prototype or parent + * scope, else it reshapes the scope and prototype chains it links. It calls + * js_PurgeScopeChainHelper, which asserts that obj is flagged as a delegate + * (i.e., obj has ever been on a prototype or parent chain). + */ +extern void +js_PurgeScopeChainHelper(JSContext *cx, JSObject *obj, jsid id); + +#ifdef __cplusplus /* Aargh, libgjs, bug 492720. */ +static JS_INLINE void +js_PurgeScopeChain(JSContext *cx, JSObject *obj, jsid id) +{ + if (obj->isDelegate()) + js_PurgeScopeChainHelper(cx, obj, id); +} +#endif + +/* + * Find or create a property named by id in obj's scope, with the given getter + * and setter, slot, attributes, and other members. + */ +extern JSScopeProperty * +js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id, + JSPropertyOp getter, JSPropertyOp setter, uint32 slot, + uintN attrs, uintN flags, intN shortid); + +/* + * Change sprop to have the given attrs, getter, and setter in scope, morphing + * it into a potentially new JSScopeProperty. Return a pointer to the changed + * or identical property. + */ +extern JSScopeProperty * +js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj, + JSScopeProperty *sprop, uintN attrs, uintN mask, + JSPropertyOp getter, JSPropertyOp setter); + +extern JSBool +js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs); + +/* + * Flags for the defineHow parameter of js_DefineNativeProperty. + */ +const uintN JSDNP_CACHE_RESULT = 1; /* an interpreter call from JSOP_INITPROP */ +const uintN JSDNP_DONT_PURGE = 2; /* suppress js_PurgeScopeChain */ +const uintN JSDNP_SET_METHOD = 4; /* js_{DefineNativeProperty,SetPropertyHelper} + must pass the SPROP_IS_METHOD flag on to + js_AddScopeProperty */ + +/* + * On error, return false. On success, if propp is non-null, return true with + * obj locked and with a held property in *propp; if propp is null, return true + * but release obj's lock first. Therefore all callers who pass non-null propp + * result parameters must later call obj->dropProperty(cx, *propp) both to drop + * the held property, and to release the lock on obj. + */ +extern JSBool +js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs, + uintN flags, intN shortid, JSProperty **propp, + uintN defineHow = 0); + +/* + * Unlike js_DefineNativeProperty, propp must be non-null. On success, and if + * id was found, return true with *objp non-null and locked, and with a held + * property stored in *propp. If successful but id was not found, return true + * with both *objp and *propp null. Therefore all callers who receive a + * non-null *propp must later call (*objp)->dropProperty(cx, *propp). + */ +extern JS_FRIEND_API(JSBool) +js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, + JSProperty **propp); + +/* + * Specialized subroutine that allows caller to preset JSRESOLVE_* flags and + * returns the index along the prototype chain in which *propp was found, or + * the last index if not found, or -1 on error. + */ +extern int +js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, + JSObject **objp, JSProperty **propp); + + +/* + * We cache name lookup results only for the global object or for native + * non-global objects without prototype or with prototype that never mutates, + * see bug 462734 and bug 487039. + */ +static inline bool +js_IsCacheableNonGlobalScope(JSObject *obj) +{ + extern JS_FRIEND_DATA(JSClass) js_CallClass; + extern JS_FRIEND_DATA(JSClass) js_DeclEnvClass; + JS_ASSERT(STOBJ_GET_PARENT(obj)); + + JSClass *clasp = STOBJ_GET_CLASS(obj); + bool cacheable = (clasp == &js_CallClass || + clasp == &js_BlockClass || + clasp == &js_DeclEnvClass); + + JS_ASSERT_IF(cacheable, obj->map->ops->lookupProperty == js_LookupProperty); + return cacheable; +} + +/* + * If cacheResult is false, return JS_NO_PROP_CACHE_FILL on success. + */ +extern JSPropCacheEntry * +js_FindPropertyHelper(JSContext *cx, jsid id, JSBool cacheResult, + JSObject **objp, JSObject **pobjp, JSProperty **propp); + +/* + * Return the index along the scope chain in which id was found, or the last + * index if not found, or -1 on error. + */ +extern JS_FRIEND_API(JSBool) +js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp, + JSProperty **propp); + +extern JS_REQUIRES_STACK JSObject * +js_FindIdentifierBase(JSContext *cx, JSObject *scopeChain, jsid id); + +extern JSObject * +js_FindVariableScope(JSContext *cx, JSFunction **funp); + +/* + * JSGET_CACHE_RESULT is the analogue of JSDNP_CACHE_RESULT for js_GetMethod. + * + * JSGET_METHOD_BARRIER (the default, hence 0 but provided for documentation) + * enables a read barrier that preserves standard function object semantics (by + * default we assume our caller won't leak a joined callee to script, where it + * would create hazardous mutable object sharing as well as observable identity + * according to == and ===. + * + * JSGET_NO_METHOD_BARRIER avoids the performance overhead of the method read + * barrier, which is not needed when invoking a lambda that otherwise does not + * leak its callee reference (via arguments.callee or its name). + */ +const uintN JSGET_CACHE_RESULT = 1; // from a caching interpreter opcode +const uintN JSGET_METHOD_BARRIER = 0; // get can leak joined function object +const uintN JSGET_NO_METHOD_BARRIER = 2; // call to joined function can't leak + +/* + * NB: js_NativeGet and js_NativeSet are called with the scope containing sprop + * (pobj's scope for Get, obj's for Set) locked, and on successful return, that + * scope is again locked. But on failure, both functions return false with the + * scope containing sprop unlocked. + */ +extern JSBool +js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj, + JSScopeProperty *sprop, uintN getHow, jsval *vp); + +extern JSBool +js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, bool added, + jsval *vp); + +extern JSBool +js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN getHow, + jsval *vp); + +extern JSBool +js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); + +extern JSBool +js_GetMethod(JSContext *cx, JSObject *obj, jsid id, uintN getHow, jsval *vp); + +/* + * Check whether it is OK to assign an undeclared property of the global + * object at the current script PC. + */ +extern JS_FRIEND_API(bool) +js_CheckUndeclaredVarAssignment(JSContext *cx); + +extern JSBool +js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow, + jsval *vp); + +extern JSBool +js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); + +extern JSBool +js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, + uintN *attrsp); + +extern JSBool +js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, + uintN *attrsp); + +extern JSBool +js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval); + +extern JSBool +js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp); + +extern JSBool +js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, + jsval *statep, jsid *idp); + +extern void +js_MarkEnumeratorState(JSTracer *trc, JSObject *obj, jsval state); + +extern void +js_PurgeCachedNativeEnumerators(JSContext *cx, JSThreadData *data); + +extern JSBool +js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, + jsval *vp, uintN *attrsp); + +extern JSBool +js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); + +extern JSBool +js_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +extern JSBool +js_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp); + +extern JSBool +js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj, + JSBool checkForCycles); + +extern JSBool +js_IsDelegate(JSContext *cx, JSObject *obj, jsval v, JSBool *bp); + +extern JSBool +js_GetClassPrototype(JSContext *cx, JSObject *scope, jsid id, + JSObject **protop); + +extern JSBool +js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto, + uintN attrs); + +/* + * Wrap boolean, number or string as Boolean, Number or String object. + * *vp must not be an object, null or undefined. + */ +extern JSBool +js_PrimitiveToObject(JSContext *cx, jsval *vp); + +extern JSBool +js_ValueToObject(JSContext *cx, jsval v, JSObject **objp); + +extern JSObject * +js_ValueToNonNullObject(JSContext *cx, jsval v); + +extern JSBool +js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, jsval *rval); + +extern JSBool +js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom, + uintN argc, jsval *argv, jsval *rval); + +extern JSBool +js_XDRObject(JSXDRState *xdr, JSObject **objp); + +extern void +js_TraceObject(JSTracer *trc, JSObject *obj); + +extern void +js_PrintObjectSlotName(JSTracer *trc, char *buf, size_t bufsize); + +extern void +js_Clear(JSContext *cx, JSObject *obj); + +extern bool +js_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval *vp); + +bool +js_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval v); + +/* + * Precondition: obj must be locked. + */ +extern JSBool +js_ReallocSlots(JSContext *cx, JSObject *obj, uint32 nslots, + JSBool exactAllocation); + +extern JSObject * +js_CheckScopeChainValidity(JSContext *cx, JSObject *scopeobj, const char *caller); + +extern JSBool +js_CheckPrincipalsAccess(JSContext *cx, JSObject *scopeobj, + JSPrincipals *principals, JSAtom *caller); + +/* Infallible -- returns its argument if there is no wrapped object. */ +extern JSObject * +js_GetWrappedObject(JSContext *cx, JSObject *obj); + +/* NB: Infallible. */ +extern const char * +js_ComputeFilename(JSContext *cx, JSStackFrame *caller, + JSPrincipals *principals, uintN *linenop); + +/* Infallible, therefore cx is last parameter instead of first. */ +extern JSBool +js_IsCallable(JSObject *obj, JSContext *cx); + +void +js_ReportGetterOnlyAssignment(JSContext *cx); + +extern JS_FRIEND_API(JSBool) +js_GetterOnlyPropertyStub(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + +#ifdef DEBUG +JS_FRIEND_API(void) js_DumpChars(const jschar *s, size_t n); +JS_FRIEND_API(void) js_DumpString(JSString *str); +JS_FRIEND_API(void) js_DumpAtom(JSAtom *atom); +JS_FRIEND_API(void) js_DumpValue(jsval val); +JS_FRIEND_API(void) js_DumpId(jsid id); +JS_FRIEND_API(void) js_DumpObject(JSObject *obj); +JS_FRIEND_API(void) js_DumpStackFrame(JSStackFrame *fp); +#endif + +extern uintN +js_InferFlags(JSContext *cx, uintN defaultFlags); + +/* Object constructor native. Exposed only so the JIT can know its address. */ +JSBool +js_Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); + +JS_END_EXTERN_C + +#endif /* jsobj_h___ */ diff --git a/ape-server/deps/js/src/jsobjinlines.h b/ape-server/deps/js/src/jsobjinlines.h new file mode 100755 index 0000000..1c71ef8 --- /dev/null +++ b/ape-server/deps/js/src/jsobjinlines.h @@ -0,0 +1,59 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=99: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsobjinlines_h___ +#define jsobjinlines_h___ + +#include "jsobj.h" +#include "jsscope.h" + +inline void +JSObject::initSharingEmptyScope(JSClass *clasp, JSObject *proto, JSObject *parent, + jsval privateSlotValue) +{ + init(clasp, proto, parent, privateSlotValue); + + JSEmptyScope *emptyScope = OBJ_SCOPE(proto)->emptyScope; + JS_ASSERT(emptyScope->clasp == clasp); + emptyScope->hold(); + map = emptyScope; +} + +#endif /* jsobjinlines_h___ */ diff --git a/ape-server/deps/js/src/json.cpp b/ape-server/deps/js/src/json.cpp new file mode 100755 index 0000000..3928999 --- /dev/null +++ b/ape-server/deps/js/src/json.cpp @@ -0,0 +1,1254 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=99: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is SpiderMonkey JSON. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Robert Sayre + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include +#include "jsapi.h" +#include "jsarena.h" +#include "jsarray.h" +#include "jsatom.h" +#include "jsbool.h" +#include "jscntxt.h" +#include "jsdtoa.h" +#include "jsfun.h" +#include "jsinterp.h" +#include "jsiter.h" +#include "jsnum.h" +#include "jsobj.h" +#include "jsprf.h" +#include "jsscan.h" +#include "jsstr.h" +#include "jstypes.h" +#include "jsstdint.h" +#include "jsutil.h" +#include "jsxml.h" +#include "jsvector.h" + +#include "json.h" + +#include "jsatominlines.h" + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4351) +#endif + +struct JSONParser +{ + JSONParser(JSContext *cx) + : hexChar(), numHex(), statep(), stateStack(), rootVal(), objectStack(), + objectKey(cx), buffer(cx) + {} + + /* Used while handling \uNNNN in strings */ + jschar hexChar; + uint8 numHex; + + JSONParserState *statep; + JSONParserState stateStack[JSON_MAX_DEPTH]; + jsval *rootVal; + JSObject *objectStack; + js::Vector objectKey; + js::Vector buffer; +}; + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +JSClass js_JSONClass = { + js_JSON_str, + JSCLASS_HAS_CACHED_PROTO(JSProto_JSON), + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +JSBool +js_json_parse(JSContext *cx, uintN argc, jsval *vp) +{ + JSString *s = NULL; + jsval *argv = vp + 2; + jsval reviver = JSVAL_NULL; + JSAutoTempValueRooter tvr(cx, 1, &reviver); + + if (!JS_ConvertArguments(cx, argc, argv, "S / v", &s, &reviver)) + return JS_FALSE; + + JSONParser *jp = js_BeginJSONParse(cx, vp); + JSBool ok = jp != NULL; + if (ok) { + const jschar *chars; + size_t length; + s->getCharsAndLength(chars, length); + ok = js_ConsumeJSONText(cx, jp, chars, length); + ok &= js_FinishJSONParse(cx, jp, reviver); + } + + return ok; +} + +JSBool +js_json_stringify(JSContext *cx, uintN argc, jsval *vp) +{ + jsval *argv = vp + 2; + JSObject *replacer = NULL; + jsval space = JSVAL_NULL; + JSAutoTempValueRooter tvr(cx, replacer); + JSAutoTempValueRooter tvr2(cx, 1, &space); + + // Must throw an Error if there isn't a first arg + if (!JS_ConvertArguments(cx, argc, argv, "v / o v", vp, &replacer, &space)) + return JS_FALSE; + + JSCharBuffer cb(cx); + + if (!js_Stringify(cx, vp, replacer, space, cb)) + return JS_FALSE; + + // XXX This can never happen to nsJSON.cpp, but the JSON object + // needs to support returning undefined. So this is a little awkward + // for the API, because we want to support streaming writers. + if (!cb.empty()) { + JSString *str = js_NewStringFromCharBuffer(cx, cb); + if (!str) + return JS_FALSE; + *vp = STRING_TO_JSVAL(str); + } else { + *vp = JSVAL_VOID; + } + + return JS_TRUE; +} + +JSBool +js_TryJSON(JSContext *cx, jsval *vp) +{ + // Checks whether the return value implements toJSON() + JSBool ok = JS_TRUE; + + if (!JSVAL_IS_PRIMITIVE(*vp)) { + JSObject *obj = JSVAL_TO_OBJECT(*vp); + ok = js_TryMethod(cx, obj, cx->runtime->atomState.toJSONAtom, 0, NULL, vp); + } + + return ok; +} + + +static const char quote = '\"'; +static const char backslash = '\\'; +static const char unicodeEscape[] = "\\u00"; + +static JSBool +write_string(JSContext *cx, JSCharBuffer &cb, const jschar *buf, uint32 len) +{ + if (!cb.append(quote)) + return JS_FALSE; + + uint32 mark = 0; + uint32 i; + for (i = 0; i < len; ++i) { + if (buf[i] == quote || buf[i] == backslash) { + if (!cb.append(&buf[mark], i - mark) || !cb.append(backslash) || + !cb.append(buf[i])) { + return JS_FALSE; + } + mark = i + 1; + } else if (buf[i] <= 31 || buf[i] == 127) { + if (!cb.append(&buf[mark], i - mark) || + !js_AppendLiteral(cb, unicodeEscape)) { + return JS_FALSE; + } + char ubuf[3]; + size_t len = JS_snprintf(ubuf, sizeof(ubuf), "%.2x", buf[i]); + JS_ASSERT(len == 2); + jschar wbuf[3]; + size_t wbufSize = JS_ARRAY_LENGTH(wbuf); + if (!js_InflateStringToBuffer(cx, ubuf, len, wbuf, &wbufSize) || + !cb.append(wbuf, wbufSize)) { + return JS_FALSE; + } + mark = i + 1; + } + } + + if (mark < len && !cb.append(&buf[mark], len - mark)) + return JS_FALSE; + + return cb.append(quote); +} + +class StringifyContext +{ +public: + StringifyContext(JSContext *cx, JSCharBuffer &cb, JSObject *replacer) + : cb(cb), gap(cx), replacer(replacer), depth(0) + {} + + JSCharBuffer &cb; + JSCharBuffer gap; + JSObject *replacer; + uint32 depth; +}; + +static JSBool CallReplacerFunction(JSContext *cx, jsid id, JSObject *holder, + StringifyContext *scx, jsval *vp); +static JSBool Str(JSContext *cx, jsid id, JSObject *holder, + StringifyContext *scx, jsval *vp, bool callReplacer = true); + +static JSBool +WriteIndent(JSContext *cx, StringifyContext *scx, uint32 limit) +{ + if (!scx->gap.empty()) { + if (!scx->cb.append('\n')) + return JS_FALSE; + for (uint32 i = 0; i < limit; i++) { + if (!scx->cb.append(scx->gap.begin(), scx->gap.end())) + return JS_FALSE; + } + } + + return JS_TRUE; +} + +static JSBool +JO(JSContext *cx, jsval *vp, StringifyContext *scx) +{ + JSObject *obj = JSVAL_TO_OBJECT(*vp); + + if (!scx->cb.append('{')) + return JS_FALSE; + + jsval vec[3] = {JSVAL_NULL, JSVAL_NULL, JSVAL_NULL}; + JSAutoTempValueRooter tvr(cx, 3, vec); + jsval& key = vec[0]; + jsval& outputValue = vec[1]; + + JSObject *iterObj = NULL; + jsval *keySource = vp; + bool usingWhitelist = false; + + // if the replacer is an array, we use the keys from it + if (scx->replacer && JS_IsArrayObject(cx, scx->replacer)) { + usingWhitelist = true; + vec[2] = OBJECT_TO_JSVAL(scx->replacer); + keySource = &vec[2]; + } + + if (!js_ValueToIterator(cx, JSITER_ENUMERATE, keySource)) + return JS_FALSE; + iterObj = JSVAL_TO_OBJECT(*keySource); + + JSBool memberWritten = JS_FALSE; + + bool ok = false; + while (true) { + outputValue = JSVAL_VOID; + if (!js_CallIteratorNext(cx, iterObj, &key)) + goto error_break; + if (key == JSVAL_HOLE) + break; + + jsuint index = 0; + if (usingWhitelist) { + // skip non-index properties + if (!js_IdIsIndex(key, &index)) + continue; + + jsval newKey; + if (!scx->replacer->getProperty(cx, key, &newKey)) + goto error_break; + key = newKey; + } + + JSString *ks; + if (JSVAL_IS_STRING(key)) { + ks = JSVAL_TO_STRING(key); + } else { + ks = js_ValueToString(cx, key); + if (!ks) + goto error_break; + } + JSAutoTempValueRooter keyStringRoot(cx, ks); + + // Don't include prototype properties, since this operation is + // supposed to be implemented as if by ES3.1 Object.keys() + jsid id; + JSBool found = JS_FALSE; + if (!js_ValueToStringId(cx, STRING_TO_JSVAL(ks), &id) || + !js_HasOwnProperty(cx, obj->map->ops->lookupProperty, obj, id, &found)) { + goto error_break; + } + + if (!found) + continue; + + if (!JS_GetPropertyById(cx, obj, id, &outputValue)) + goto error_break; + + if (JSVAL_IS_OBJECT(outputValue) && !js_TryJSON(cx, &outputValue)) + goto error_break; + + // call this here, so we don't write out keys if the replacer function + // wants to elide the value. + if (!CallReplacerFunction(cx, id, obj, scx, &outputValue)) + goto error_break; + + JSType type = JS_TypeOfValue(cx, outputValue); + + // elide undefined values and functions and XML + if (outputValue == JSVAL_VOID || type == JSTYPE_FUNCTION || type == JSTYPE_XML) + continue; + + // output a comma unless this is the first member to write + if (memberWritten && !scx->cb.append(',')) + goto error_break; + memberWritten = JS_TRUE; + + if (!WriteIndent(cx, scx, scx->depth)) + goto error_break; + + // Be careful below, this string is weakly rooted + JSString *s = js_ValueToString(cx, key); + if (!s) + goto error_break; + + const jschar *chars; + size_t length; + s->getCharsAndLength(chars, length); + if (!write_string(cx, scx->cb, chars, length) || + !scx->cb.append(':') || + !Str(cx, id, obj, scx, &outputValue, false)) { + goto error_break; + } + } + ok = true; + + error_break: + if (iterObj) { + // Always close the iterator, but make sure not to stomp on OK + JS_ASSERT(OBJECT_TO_JSVAL(iterObj) == *keySource); + ok &= js_CloseIterator(cx, *keySource); + } + + if (!ok) + return JS_FALSE; + + if (memberWritten && !WriteIndent(cx, scx, scx->depth - 1)) + return JS_FALSE; + + return scx->cb.append('}'); +} + +static JSBool +JA(JSContext *cx, jsval *vp, StringifyContext *scx) +{ + JSObject *obj = JSVAL_TO_OBJECT(*vp); + + if (!scx->cb.append('[')) + return JS_FALSE; + + jsuint length; + if (!js_GetLengthProperty(cx, obj, &length)) + return JS_FALSE; + + jsval outputValue = JSVAL_NULL; + JSAutoTempValueRooter tvr(cx, 1, &outputValue); + + jsid id; + jsuint i; + for (i = 0; i < length; i++) { + id = INT_TO_JSID(i); + + if (!obj->getProperty(cx, id, &outputValue)) + return JS_FALSE; + + if (!Str(cx, id, obj, scx, &outputValue)) + return JS_FALSE; + + if (outputValue == JSVAL_VOID) { + if (!js_AppendLiteral(scx->cb, "null")) + return JS_FALSE; + } + + if (i < length - 1) { + if (!scx->cb.append(',')) + return JS_FALSE; + if (!WriteIndent(cx, scx, scx->depth)) + return JS_FALSE; + } + } + + if (length != 0 && !WriteIndent(cx, scx, scx->depth - 1)) + return JS_FALSE; + + return scx->cb.append(']'); +} + +static JSBool +CallReplacerFunction(JSContext *cx, jsid id, JSObject *holder, StringifyContext *scx, jsval *vp) +{ + if (scx->replacer && js_IsCallable(scx->replacer, cx)) { + jsval vec[2] = {ID_TO_VALUE(id), *vp}; + if (!JS_CallFunctionValue(cx, holder, OBJECT_TO_JSVAL(scx->replacer), 2, vec, vp)) + return JS_FALSE; + } + + return JS_TRUE; +} + +static JSBool +Str(JSContext *cx, jsid id, JSObject *holder, StringifyContext *scx, jsval *vp, bool callReplacer) +{ + JS_CHECK_RECURSION(cx, return JS_FALSE); + + if (!holder->getProperty(cx, id, vp)) + return JS_FALSE; + + if (!JSVAL_IS_PRIMITIVE(*vp) && !js_TryJSON(cx, vp)) + return JS_FALSE; + + if (callReplacer && !CallReplacerFunction(cx, id, holder, scx, vp)) + return JS_FALSE; + + // catches string and number objects with no toJSON + if (!JSVAL_IS_PRIMITIVE(*vp)) { + JSClass *clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(*vp)); + if (clasp == &js_StringClass || clasp == &js_NumberClass) + *vp = JSVAL_TO_OBJECT(*vp)->fslots[JSSLOT_PRIMITIVE_THIS]; + } + + if (JSVAL_IS_STRING(*vp)) { + const jschar *chars; + size_t length; + JSVAL_TO_STRING(*vp)->getCharsAndLength(chars, length); + return write_string(cx, scx->cb, chars, length); + } + + if (JSVAL_IS_NULL(*vp)) { + return js_AppendLiteral(scx->cb, "null"); + } + + if (JSVAL_IS_BOOLEAN(*vp)) { + return JSVAL_TO_BOOLEAN(*vp) ? js_AppendLiteral(scx->cb, "true") + : js_AppendLiteral(scx->cb, "false"); + } + + if (JSVAL_IS_NUMBER(*vp)) { + if (JSVAL_IS_DOUBLE(*vp)) { + jsdouble d = *JSVAL_TO_DOUBLE(*vp); + if (!JSDOUBLE_IS_FINITE(d)) + return js_AppendLiteral(scx->cb, "null"); + } + + char numBuf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr; + jsdouble d = JSVAL_IS_INT(*vp) ? jsdouble(JSVAL_TO_INT(*vp)) : *JSVAL_TO_DOUBLE(*vp); + numStr = JS_dtostr(numBuf, sizeof numBuf, DTOSTR_STANDARD, 0, d); + if (!numStr) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + + jschar dstr[DTOSTR_STANDARD_BUFFER_SIZE]; + size_t dbufSize = DTOSTR_STANDARD_BUFFER_SIZE; + if (!js_InflateStringToBuffer(cx, numStr, strlen(numStr), dstr, &dbufSize)) + return JS_FALSE; + + return scx->cb.append(dstr, dbufSize); + } + + if (JSVAL_IS_OBJECT(*vp) && !VALUE_IS_FUNCTION(cx, *vp) && !VALUE_IS_XML(cx, *vp)) { + JSBool ok; + + scx->depth++; + ok = (JS_IsArrayObject(cx, JSVAL_TO_OBJECT(*vp)) ? JA : JO)(cx, vp, scx); + scx->depth--; + + return ok; + } + + *vp = JSVAL_VOID; + return JS_TRUE; +} + +static JSBool +InitializeGap(JSContext *cx, jsval space, JSCharBuffer &cb) +{ + if (!JSVAL_IS_PRIMITIVE(space)) { + JSClass *clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(space)); + if (clasp == &js_StringClass || clasp == &js_NumberClass) + return js_ValueToCharBuffer(cx, space, cb); + } + + if (JSVAL_IS_STRING(space)) + return js_ValueToCharBuffer(cx, space, cb); + + if (JSVAL_IS_NUMBER(space)) { + jsdouble d = JSVAL_IS_INT(space) + ? JSVAL_TO_INT(space) + : js_DoubleToInteger(*JSVAL_TO_DOUBLE(space)); + d = JS_MIN(10, d); + if (d >= 1 && !cb.appendN(' ', uint32(d))) + return JS_FALSE; + } + + return JS_TRUE; +} + +JSBool +js_Stringify(JSContext *cx, jsval *vp, JSObject *replacer, jsval space, + JSCharBuffer &cb) +{ + // XXX stack + JSObject *stack = JS_NewArrayObject(cx, 0, NULL); + if (!stack) + return JS_FALSE; + + StringifyContext scx(cx, cb, replacer); + if (!InitializeGap(cx, space, scx.gap)) + return JS_FALSE; + + JSObject *obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL); + if (!obj) + return JS_FALSE; + + if (!obj->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.emptyAtom), + *vp, NULL, NULL, JSPROP_ENUMERATE)) { + return JS_FALSE; + } + + return Str(cx, ATOM_TO_JSID(cx->runtime->atomState.emptyAtom), obj, &scx, vp); +} + +// helper to determine whether a character could be part of a number +static JSBool IsNumChar(jschar c) +{ + return ((c <= '9' && c >= '0') || c == '.' || c == '-' || c == '+' || c == 'e' || c == 'E'); +} + +static JSBool HandleData(JSContext *cx, JSONParser *jp, JSONDataType type); +static JSBool PopState(JSContext *cx, JSONParser *jp); + +static JSBool +DestroyIdArrayOnError(JSContext *cx, JSIdArray *ida) { + JS_DestroyIdArray(cx, ida); + return JS_FALSE; +} + +static JSBool +Walk(JSContext *cx, jsid id, JSObject *holder, jsval reviver, jsval *vp) +{ + JS_CHECK_RECURSION(cx, return JS_FALSE); + + if (!holder->getProperty(cx, id, vp)) + return JS_FALSE; + + JSObject *obj; + + if (!JSVAL_IS_PRIMITIVE(*vp) && !js_IsCallable(obj = JSVAL_TO_OBJECT(*vp), cx)) { + jsval propValue = JSVAL_NULL; + JSAutoTempValueRooter tvr(cx, 1, &propValue); + + if(OBJ_IS_ARRAY(cx, obj)) { + jsuint length = 0; + if (!js_GetLengthProperty(cx, obj, &length)) + return JS_FALSE; + + for (jsuint i = 0; i < length; i++) { + jsid index; + if (!js_IndexToId(cx, i, &index)) + return JS_FALSE; + + if (!Walk(cx, index, obj, reviver, &propValue)) + return JS_FALSE; + + if (!obj->defineProperty(cx, index, propValue, NULL, NULL, JSPROP_ENUMERATE)) + return JS_FALSE; + } + } else { + JSIdArray *ida = JS_Enumerate(cx, obj); + if (!ida) + return JS_FALSE; + + JSAutoTempValueRooter idaroot(cx, JS_ARRAY_LENGTH(ida), (jsval*)ida); + + for(jsint i = 0; i < ida->length; i++) { + jsid idName = ida->vector[i]; + if (!Walk(cx, idName, obj, reviver, &propValue)) + return DestroyIdArrayOnError(cx, ida); + if (propValue == JSVAL_VOID) { + if (!js_DeleteProperty(cx, obj, idName, &propValue)) + return DestroyIdArrayOnError(cx, ida); + } else { + if (!obj->defineProperty(cx, idName, propValue, NULL, NULL, JSPROP_ENUMERATE)) + return DestroyIdArrayOnError(cx, ida); + } + } + + JS_DestroyIdArray(cx, ida); + } + } + + // return reviver.call(holder, key, value); + jsval value = *vp; + JSString *key = js_ValueToString(cx, ID_TO_VALUE(id)); + if (!key) + return JS_FALSE; + + jsval vec[2] = {STRING_TO_JSVAL(key), value}; + jsval reviverResult; + if (!JS_CallFunctionValue(cx, holder, reviver, 2, vec, &reviverResult)) + return JS_FALSE; + + *vp = reviverResult; + + return JS_TRUE; +} + +static JSBool +Revive(JSContext *cx, jsval reviver, jsval *vp) +{ + + JSObject *obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL); + if (!obj) + return JS_FALSE; + + jsval v = OBJECT_TO_JSVAL(obj); + JSAutoTempValueRooter tvr(cx, 1, &v); + if (!obj->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.emptyAtom), + *vp, NULL, NULL, JSPROP_ENUMERATE)) { + return JS_FALSE; + } + + return Walk(cx, ATOM_TO_JSID(cx->runtime->atomState.emptyAtom), obj, reviver, vp); +} + +JSONParser * +js_BeginJSONParse(JSContext *cx, jsval *rootVal) +{ + if (!cx) + return NULL; + + JSObject *arr = js_NewArrayObject(cx, 0, NULL); + if (!arr) + return NULL; + + JSONParser *jp = cx->create(cx); + if (!jp) + return NULL; + + jp->objectStack = arr; + if (!js_AddRoot(cx, &jp->objectStack, "JSON parse stack")) + goto bad; + + jp->statep = jp->stateStack; + *jp->statep = JSON_PARSE_STATE_INIT; + jp->rootVal = rootVal; + + return jp; + +bad: + js_FinishJSONParse(cx, jp, JSVAL_NULL); + return NULL; +} + +JSBool +js_FinishJSONParse(JSContext *cx, JSONParser *jp, jsval reviver) +{ + if (!jp) + return JS_TRUE; + + JSBool early_ok = JS_TRUE; + + // Check for unprocessed primitives at the root. This doesn't happen for + // strings because a closing quote triggers value processing. + if ((jp->statep - jp->stateStack) == 1) { + if (*jp->statep == JSON_PARSE_STATE_KEYWORD) { + early_ok = HandleData(cx, jp, JSON_DATA_KEYWORD); + if (early_ok) + PopState(cx, jp); + } else if (*jp->statep == JSON_PARSE_STATE_NUMBER) { + early_ok = HandleData(cx, jp, JSON_DATA_NUMBER); + if (early_ok) + PopState(cx, jp); + } + } + + /* This internal API is infallible, in spite of its JSBool return type. */ + js_RemoveRoot(cx->runtime, &jp->objectStack); + + JSBool ok = *jp->statep == JSON_PARSE_STATE_FINISHED; + jsval *vp = jp->rootVal; + + cx->destroy(jp); + + if (!early_ok) + return JS_FALSE; + + if (!ok) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE); + return JS_FALSE; + } + + if (!JSVAL_IS_PRIMITIVE(reviver) && js_IsCallable(JSVAL_TO_OBJECT(reviver), cx)) + ok = Revive(cx, reviver, vp); + + return ok; +} + +static JSBool +PushState(JSContext *cx, JSONParser *jp, JSONParserState state) +{ + if (*jp->statep == JSON_PARSE_STATE_FINISHED) { + // extra input + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE); + return JS_FALSE; + } + + jp->statep++; + if ((uint32)(jp->statep - jp->stateStack) >= JS_ARRAY_LENGTH(jp->stateStack)) { + // too deep + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE); + return JS_FALSE; + } + + *jp->statep = state; + + return JS_TRUE; +} + +static JSBool +PopState(JSContext *cx, JSONParser *jp) +{ + jp->statep--; + if (jp->statep < jp->stateStack) { + jp->statep = jp->stateStack; + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE); + return JS_FALSE; + } + + if (*jp->statep == JSON_PARSE_STATE_INIT) + *jp->statep = JSON_PARSE_STATE_FINISHED; + + return JS_TRUE; +} + +static JSBool +PushValue(JSContext *cx, JSONParser *jp, JSObject *parent, jsval value) +{ + JSBool ok; + if (OBJ_IS_ARRAY(cx, parent)) { + jsuint len; + ok = js_GetLengthProperty(cx, parent, &len); + if (ok) { + jsid index; + if (!js_IndexToId(cx, len, &index)) + return JS_FALSE; + ok = parent->defineProperty(cx, index, value, NULL, NULL, JSPROP_ENUMERATE); + } + } else { + ok = JS_DefineUCProperty(cx, parent, jp->objectKey.begin(), + jp->objectKey.length(), value, + NULL, NULL, JSPROP_ENUMERATE); + jp->objectKey.clear(); + } + + return ok; +} + +static JSBool +PushObject(JSContext *cx, JSONParser *jp, JSObject *obj) +{ + jsuint len; + if (!js_GetLengthProperty(cx, jp->objectStack, &len)) + return JS_FALSE; + if (len >= JSON_MAX_DEPTH) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE); + return JS_FALSE; + } + + jsval v = OBJECT_TO_JSVAL(obj); + JSAutoTempValueRooter tvr(cx, v); + + // Check if this is the root object + if (len == 0) { + *jp->rootVal = v; + // This property must be enumerable to keep the array dense + if (!jp->objectStack->defineProperty(cx, INT_TO_JSID(0), *jp->rootVal, + NULL, NULL, JSPROP_ENUMERATE)) { + return JS_FALSE; + } + return JS_TRUE; + } + + jsval p; + if (!jp->objectStack->getProperty(cx, INT_TO_JSID(len - 1), &p)) + return JS_FALSE; + + JS_ASSERT(JSVAL_IS_OBJECT(p)); + JSObject *parent = JSVAL_TO_OBJECT(p); + if (!PushValue(cx, jp, parent, v)) + return JS_FALSE; + + // This property must be enumerable to keep the array dense + if (!jp->objectStack->defineProperty(cx, INT_TO_JSID(len), v, + NULL, NULL, JSPROP_ENUMERATE)) { + return JS_FALSE; + } + + return JS_TRUE; +} + +static JSBool +OpenObject(JSContext *cx, JSONParser *jp) +{ + JSObject *obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL); + if (!obj) + return JS_FALSE; + + return PushObject(cx, jp, obj); +} + +static JSBool +OpenArray(JSContext *cx, JSONParser *jp) +{ + // Add an array to an existing array or object + JSObject *arr = js_NewArrayObject(cx, 0, NULL); + if (!arr) + return JS_FALSE; + + return PushObject(cx, jp, arr); +} + +static JSBool +CloseObject(JSContext *cx, JSONParser *jp) +{ + jsuint len; + if (!js_GetLengthProperty(cx, jp->objectStack, &len)) + return JS_FALSE; + if (!js_SetLengthProperty(cx, jp->objectStack, len - 1)) + return JS_FALSE; + + return JS_TRUE; +} + +static JSBool +CloseArray(JSContext *cx, JSONParser *jp) +{ + return CloseObject(cx, jp); +} + +static JSBool +PushPrimitive(JSContext *cx, JSONParser *jp, jsval value) +{ + JSAutoTempValueRooter tvr(cx, 1, &value); + + jsuint len; + if (!js_GetLengthProperty(cx, jp->objectStack, &len)) + return JS_FALSE; + + if (len > 0) { + jsval o; + if (!jp->objectStack->getProperty(cx, INT_TO_JSID(len - 1), &o)) + return JS_FALSE; + + JS_ASSERT(!JSVAL_IS_PRIMITIVE(o)); + return PushValue(cx, jp, JSVAL_TO_OBJECT(o), value); + } + + // root value must be primitive + *jp->rootVal = value; + return JS_TRUE; +} + +static JSBool +HandleNumber(JSContext *cx, JSONParser *jp, const jschar *buf, uint32 len) +{ + const jschar *ep; + double val; + if (!js_strtod(cx, buf, buf + len, &ep, &val)) + return JS_FALSE; + if (ep != buf + len) { + // bad number input + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE); + return JS_FALSE; + } + + jsval numVal; + if (!JS_NewNumberValue(cx, val, &numVal)) + return JS_FALSE; + + return PushPrimitive(cx, jp, numVal); +} + +static JSBool +HandleString(JSContext *cx, JSONParser *jp, const jschar *buf, uint32 len) +{ + JSString *str = js_NewStringCopyN(cx, buf, len); + if (!str) + return JS_FALSE; + + return PushPrimitive(cx, jp, STRING_TO_JSVAL(str)); +} + +static JSBool +HandleKeyword(JSContext *cx, JSONParser *jp, const jschar *buf, uint32 len) +{ + jsval keyword; + JSTokenType tt = js_CheckKeyword(buf, len); + if (tt != TOK_PRIMARY) { + // bad keyword + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE); + return JS_FALSE; + } + + if (buf[0] == 'n') { + keyword = JSVAL_NULL; + } else if (buf[0] == 't') { + keyword = JSVAL_TRUE; + } else if (buf[0] == 'f') { + keyword = JSVAL_FALSE; + } else { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE); + return JS_FALSE; + } + + return PushPrimitive(cx, jp, keyword); +} + +static JSBool +HandleData(JSContext *cx, JSONParser *jp, JSONDataType type) +{ + JSBool ok; + + switch (type) { + case JSON_DATA_STRING: + ok = HandleString(cx, jp, jp->buffer.begin(), jp->buffer.length()); + break; + + case JSON_DATA_KEYSTRING: + ok = jp->objectKey.append(jp->buffer.begin(), jp->buffer.end()); + break; + + case JSON_DATA_NUMBER: + ok = HandleNumber(cx, jp, jp->buffer.begin(), jp->buffer.length()); + break; + + default: + JS_ASSERT(type == JSON_DATA_KEYWORD); + ok = HandleKeyword(cx, jp, jp->buffer.begin(), jp->buffer.length()); + break; + } + + if (ok) + jp->buffer.clear(); + return ok; +} + +JSBool +js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len) +{ + uint32 i; + + if (*jp->statep == JSON_PARSE_STATE_INIT) { + PushState(cx, jp, JSON_PARSE_STATE_VALUE); + } + + for (i = 0; i < len; i++) { + jschar c = data[i]; + switch (*jp->statep) { + case JSON_PARSE_STATE_VALUE: + if (c == ']') { + // empty array + if (!PopState(cx, jp)) + return JS_FALSE; + + if (*jp->statep != JSON_PARSE_STATE_ARRAY) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE); + return JS_FALSE; + } + + if (!CloseArray(cx, jp) || !PopState(cx, jp)) + return JS_FALSE; + + break; + } + + if (c == '}') { + // we should only find these in OBJECT_KEY state + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE); + return JS_FALSE; + } + + if (c == '"') { + *jp->statep = JSON_PARSE_STATE_STRING; + break; + } + + if (IsNumChar(c)) { + *jp->statep = JSON_PARSE_STATE_NUMBER; + if (!jp->buffer.append(c)) + return JS_FALSE; + break; + } + + if (JS7_ISLET(c)) { + *jp->statep = JSON_PARSE_STATE_KEYWORD; + if (!jp->buffer.append(c)) + return JS_FALSE; + break; + } + + // fall through in case the value is an object or array + case JSON_PARSE_STATE_OBJECT_VALUE: + if (c == '{') { + *jp->statep = JSON_PARSE_STATE_OBJECT; + if (!OpenObject(cx, jp) || !PushState(cx, jp, JSON_PARSE_STATE_OBJECT_PAIR)) + return JS_FALSE; + } else if (c == '[') { + *jp->statep = JSON_PARSE_STATE_ARRAY; + if (!OpenArray(cx, jp) || !PushState(cx, jp, JSON_PARSE_STATE_VALUE)) + return JS_FALSE; + } else if (!JS_ISXMLSPACE(c)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE); + return JS_FALSE; + } + break; + + case JSON_PARSE_STATE_OBJECT: + if (c == '}') { + if (!CloseObject(cx, jp) || !PopState(cx, jp)) + return JS_FALSE; + } else if (c == ',') { + if (!PushState(cx, jp, JSON_PARSE_STATE_OBJECT_PAIR)) + return JS_FALSE; + } else if (c == ']' || !JS_ISXMLSPACE(c)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE); + return JS_FALSE; + } + break; + + case JSON_PARSE_STATE_ARRAY : + if (c == ']') { + if (!CloseArray(cx, jp) || !PopState(cx, jp)) + return JS_FALSE; + } else if (c == ',') { + if (!PushState(cx, jp, JSON_PARSE_STATE_VALUE)) + return JS_FALSE; + } else if (!JS_ISXMLSPACE(c)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE); + return JS_FALSE; + } + break; + + case JSON_PARSE_STATE_OBJECT_PAIR : + if (c == '"') { + // we want to be waiting for a : when the string has been read + *jp->statep = JSON_PARSE_STATE_OBJECT_IN_PAIR; + if (!PushState(cx, jp, JSON_PARSE_STATE_STRING)) + return JS_FALSE; + } else if (c == '}') { + // pop off the object pair state and the object state + if (!CloseObject(cx, jp) || !PopState(cx, jp) || !PopState(cx, jp)) + return JS_FALSE; + } else if (c == ']' || !JS_ISXMLSPACE(c)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE); + return JS_FALSE; + } + break; + + case JSON_PARSE_STATE_OBJECT_IN_PAIR: + if (c == ':') { + *jp->statep = JSON_PARSE_STATE_VALUE; + } else if (!JS_ISXMLSPACE(c)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE); + return JS_FALSE; + } + break; + + case JSON_PARSE_STATE_STRING: + if (c == '"') { + if (!PopState(cx, jp)) + return JS_FALSE; + JSONDataType jdt; + if (*jp->statep == JSON_PARSE_STATE_OBJECT_IN_PAIR) { + jdt = JSON_DATA_KEYSTRING; + } else { + jdt = JSON_DATA_STRING; + } + if (!HandleData(cx, jp, jdt)) + return JS_FALSE; + } else if (c == '\\') { + *jp->statep = JSON_PARSE_STATE_STRING_ESCAPE; + } else { + if (!jp->buffer.append(c)) + return JS_FALSE; + } + break; + + case JSON_PARSE_STATE_STRING_ESCAPE: + switch (c) { + case '"': + case '\\': + case '/': + break; + case 'b' : c = '\b'; break; + case 'f' : c = '\f'; break; + case 'n' : c = '\n'; break; + case 'r' : c = '\r'; break; + case 't' : c = '\t'; break; + default : + if (c == 'u') { + jp->numHex = 0; + jp->hexChar = 0; + *jp->statep = JSON_PARSE_STATE_STRING_HEX; + continue; + } else { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE); + return JS_FALSE; + } + } + + if (!jp->buffer.append(c)) + return JS_FALSE; + *jp->statep = JSON_PARSE_STATE_STRING; + break; + + case JSON_PARSE_STATE_STRING_HEX: + if (('0' <= c) && (c <= '9')) { + jp->hexChar = (jp->hexChar << 4) | (c - '0'); + } else if (('a' <= c) && (c <= 'f')) { + jp->hexChar = (jp->hexChar << 4) | (c - 'a' + 0x0a); + } else if (('A' <= c) && (c <= 'F')) { + jp->hexChar = (jp->hexChar << 4) | (c - 'A' + 0x0a); + } else { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE); + return JS_FALSE; + } + + if (++(jp->numHex) == 4) { + if (!jp->buffer.append(jp->hexChar)) + return JS_FALSE; + jp->hexChar = 0; + jp->numHex = 0; + *jp->statep = JSON_PARSE_STATE_STRING; + } + break; + + case JSON_PARSE_STATE_KEYWORD: + if (JS7_ISLET(c)) { + if (!jp->buffer.append(c)) + return JS_FALSE; + } else { + // this character isn't part of the keyword, process it again + i--; + if (!PopState(cx, jp)) + return JS_FALSE; + + if (!HandleData(cx, jp, JSON_DATA_KEYWORD)) + return JS_FALSE; + } + break; + + case JSON_PARSE_STATE_NUMBER: + if (IsNumChar(c)) { + if (!jp->buffer.append(c)) + return JS_FALSE; + } else { + // this character isn't part of the number, process it again + i--; + if (!PopState(cx, jp)) + return JS_FALSE; + if (!HandleData(cx, jp, JSON_DATA_NUMBER)) + return JS_FALSE; + } + break; + + case JSON_PARSE_STATE_FINISHED: + if (!JS_ISXMLSPACE(c)) { + // extra input + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE); + return JS_FALSE; + } + break; + + default: + JS_NOT_REACHED("Invalid JSON parser state"); + } + } + + return JS_TRUE; +} + +#if JS_HAS_TOSOURCE +static JSBool +json_toSource(JSContext *cx, uintN argc, jsval *vp) +{ + *vp = ATOM_KEY(CLASS_ATOM(cx, JSON)); + return JS_TRUE; +} +#endif + +static JSFunctionSpec json_static_methods[] = { +#if JS_HAS_TOSOURCE + JS_FN(js_toSource_str, json_toSource, 0, 0), +#endif + JS_FN("parse", js_json_parse, 1, 0), + JS_FN("stringify", js_json_stringify, 1, 0), + JS_FS_END +}; + +JSObject * +js_InitJSONClass(JSContext *cx, JSObject *obj) +{ + JSObject *JSON; + + JSON = JS_NewObject(cx, &js_JSONClass, NULL, obj); + if (!JSON) + return NULL; + if (!JS_DefineProperty(cx, obj, js_JSON_str, OBJECT_TO_JSVAL(JSON), + JS_PropertyStub, JS_PropertyStub, 0)) + return NULL; + + if (!JS_DefineFunctions(cx, JSON, json_static_methods)) + return NULL; + + return JSON; +} diff --git a/ape-server/deps/js/src/json.h b/ape-server/deps/js/src/json.h new file mode 100755 index 0000000..84f3924 --- /dev/null +++ b/ape-server/deps/js/src/json.h @@ -0,0 +1,97 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is SpiderMonkey JSON. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Robert Sayre + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef json_h___ +#define json_h___ +/* + * JS JSON functions. + */ +#include "jsscan.h" + +#define JSON_MAX_DEPTH 2048 +#define JSON_PARSER_BUFSIZE 1024 + +JS_BEGIN_EXTERN_C + +extern JSClass js_JSONClass; + +extern JSObject * +js_InitJSONClass(JSContext *cx, JSObject *obj); + +extern JSBool +js_Stringify(JSContext *cx, jsval *vp, JSObject *replacer, jsval space, + JSCharBuffer &cb); + +extern JSBool js_TryJSON(JSContext *cx, jsval *vp); + +enum JSONParserState { + JSON_PARSE_STATE_INIT, + JSON_PARSE_STATE_OBJECT_VALUE, + JSON_PARSE_STATE_VALUE, + JSON_PARSE_STATE_OBJECT, + JSON_PARSE_STATE_OBJECT_PAIR, + JSON_PARSE_STATE_OBJECT_IN_PAIR, + JSON_PARSE_STATE_ARRAY, + JSON_PARSE_STATE_STRING, + JSON_PARSE_STATE_STRING_ESCAPE, + JSON_PARSE_STATE_STRING_HEX, + JSON_PARSE_STATE_NUMBER, + JSON_PARSE_STATE_KEYWORD, + JSON_PARSE_STATE_FINISHED +}; + +enum JSONDataType { + JSON_DATA_STRING, + JSON_DATA_KEYSTRING, + JSON_DATA_NUMBER, + JSON_DATA_KEYWORD +}; + +struct JSONParser; + +extern JSONParser * +js_BeginJSONParse(JSContext *cx, jsval *rootVal); + +extern JSBool +js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len); + +extern JSBool +js_FinishJSONParse(JSContext *cx, JSONParser *jp, jsval reviver); + +JS_END_EXTERN_C + +#endif /* json_h___ */ diff --git a/ape-server/deps/js/src/jsopcode.cpp b/ape-server/deps/js/src/jsopcode.cpp new file mode 100755 index 0000000..e81b5ca --- /dev/null +++ b/ape-server/deps/js/src/jsopcode.cpp @@ -0,0 +1,5594 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set sw=4 ts=8 et tw=99: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * JS bytecode descriptors, disassemblers, and decompilers. + */ +#ifdef HAVE_MEMORY_H +#include +#endif +#include +#include +#include +#include +#include "jstypes.h" +#include "jsstdint.h" +#include "jsarena.h" /* Added by JSIFY */ +#include "jsutil.h" /* Added by JSIFY */ +#include "jsdtoa.h" +#include "jsprf.h" +#include "jsapi.h" +#include "jsarray.h" +#include "jsatom.h" +#include "jscntxt.h" +#include "jsversion.h" +#include "jsdbgapi.h" +#include "jsemit.h" +#include "jsfun.h" +#include "jsiter.h" +#include "jsnum.h" +#include "jsobj.h" +#include "jsopcode.h" +#include "jsregexp.h" +#include "jsscan.h" +#include "jsscope.h" +#include "jsscript.h" +#include "jsstr.h" +#include "jsstaticcheck.h" +#include "jstracer.h" +#include "jsvector.h" + +#include "jsscriptinlines.h" + +#include "jsautooplen.h" + +/* + * Index limit must stay within 32 bits. + */ +JS_STATIC_ASSERT(sizeof(uint32) * JS_BITS_PER_BYTE >= INDEX_LIMIT_LOG2 + 1); + +/* Verify JSOP_XXX_LENGTH constant definitions. */ +#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ + JS_STATIC_ASSERT(op##_LENGTH == length); +#include "jsopcode.tbl" +#undef OPDEF + +static const char js_incop_strs[][3] = {"++", "--"}; +static const char js_for_each_str[] = "for each"; + +const JSCodeSpec js_CodeSpec[] = { +#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ + {length,nuses,ndefs,prec,format}, +#include "jsopcode.tbl" +#undef OPDEF +}; + +uintN js_NumCodeSpecs = JS_ARRAY_LENGTH(js_CodeSpec); + +/* + * Each element of the array is either a source literal associated with JS + * bytecode or null. + */ +static const char *CodeToken[] = { +#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ + token, +#include "jsopcode.tbl" +#undef OPDEF +}; + +#if defined(DEBUG) || defined(JS_JIT_SPEW) +/* + * Array of JS bytecode names used by DEBUG-only js_Disassemble and by + * JIT debug spew. + */ +const char *js_CodeName[] = { +#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ + name, +#include "jsopcode.tbl" +#undef OPDEF +}; +#endif + +/************************************************************************/ + +static ptrdiff_t +GetJumpOffset(jsbytecode *pc, jsbytecode *pc2) +{ + uint32 type; + + type = JOF_OPTYPE(*pc); + if (JOF_TYPE_IS_EXTENDED_JUMP(type)) + return GET_JUMPX_OFFSET(pc2); + return GET_JUMP_OFFSET(pc2); +} + +uintN +js_GetIndexFromBytecode(JSContext *cx, JSScript *script, jsbytecode *pc, + ptrdiff_t pcoff) +{ + JSOp op; + uintN span, base; + + op = js_GetOpcode(cx, script, pc); + JS_ASSERT(js_CodeSpec[op].length >= 1 + pcoff + UINT16_LEN); + + /* + * We need to detect index base prefix. It presents when resetbase + * follows the bytecode. + */ + span = js_CodeSpec[op].length; + base = 0; + if (pc - script->code + span < script->length) { + if (pc[span] == JSOP_RESETBASE) { + base = GET_INDEXBASE(pc - JSOP_INDEXBASE_LENGTH); + } else if (pc[span] == JSOP_RESETBASE0) { + JS_ASSERT(JSOP_INDEXBASE1 <= pc[-1] || pc[-1] <= JSOP_INDEXBASE3); + base = (pc[-1] - JSOP_INDEXBASE1 + 1) << 16; + } + } + return base + GET_UINT16(pc + pcoff); +} + +uintN +js_GetVariableBytecodeLength(jsbytecode *pc) +{ + JSOp op; + uintN jmplen, ncases; + jsint low, high; + + op = (JSOp) *pc; + JS_ASSERT(js_CodeSpec[op].length == -1); + switch (op) { + case JSOP_TABLESWITCHX: + jmplen = JUMPX_OFFSET_LEN; + goto do_table; + case JSOP_TABLESWITCH: + jmplen = JUMP_OFFSET_LEN; + do_table: + /* Structure: default-jump case-low case-high case1-jump ... */ + pc += jmplen; + low = GET_JUMP_OFFSET(pc); + pc += JUMP_OFFSET_LEN; + high = GET_JUMP_OFFSET(pc); + ncases = (uintN)(high - low + 1); + return 1 + jmplen + INDEX_LEN + INDEX_LEN + ncases * jmplen; + + case JSOP_LOOKUPSWITCHX: + jmplen = JUMPX_OFFSET_LEN; + goto do_lookup; + default: + JS_ASSERT(op == JSOP_LOOKUPSWITCH); + jmplen = JUMP_OFFSET_LEN; + do_lookup: + /* Structure: default-jump case-count (case1-value case1-jump) ... */ + pc += jmplen; + ncases = GET_UINT16(pc); + return 1 + jmplen + INDEX_LEN + ncases * (INDEX_LEN + jmplen); + } +} + +uintN +js_GetVariableStackUses(JSOp op, jsbytecode *pc) +{ + JS_ASSERT(*pc == op || *pc == JSOP_TRAP); + JS_ASSERT(js_CodeSpec[op].nuses == -1); + switch (op) { + case JSOP_POPN: + return GET_UINT16(pc); + case JSOP_CONCATN: + return GET_UINT16(pc); + case JSOP_LEAVEBLOCK: + return GET_UINT16(pc); + case JSOP_LEAVEBLOCKEXPR: + return GET_UINT16(pc) + 1; + case JSOP_NEWARRAY: + return GET_UINT16(pc); + default: + /* stack: fun, this, [argc arguments] */ + JS_ASSERT(op == JSOP_NEW || op == JSOP_CALL || + op == JSOP_EVAL || op == JSOP_SETCALL || + op == JSOP_APPLY); + return 2 + GET_ARGC(pc); + } +} + +uintN +js_GetEnterBlockStackDefs(JSContext *cx, JSScript *script, jsbytecode *pc) +{ + JSObject *obj; + + JS_ASSERT(*pc == JSOP_ENTERBLOCK || *pc == JSOP_TRAP); + GET_OBJECT_FROM_BYTECODE(script, pc, 0, obj); + return OBJ_BLOCK_COUNT(cx, obj); +} + +#ifdef DEBUG + +JS_FRIEND_API(JSBool) +js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, FILE *fp) +{ + jsbytecode *pc, *end; + uintN len; + + pc = script->code; + end = pc + script->length; + while (pc < end) { + if (pc == script->main) + fputs("main:\n", fp); + len = js_Disassemble1(cx, script, pc, + pc - script->code, + lines, fp); + if (!len) + return JS_FALSE; + pc += len; + } + return JS_TRUE; +} + +JSBool +js_DumpScript(JSContext *cx, JSScript *script) +{ + return js_Disassemble(cx, script, true, stdout); +} + +const char * +ToDisassemblySource(JSContext *cx, jsval v) +{ + if (!JSVAL_IS_PRIMITIVE(v)) { + JSObject *obj = JSVAL_TO_OBJECT(v); + JSClass *clasp = OBJ_GET_CLASS(cx, obj); + + if (clasp == &js_BlockClass) { + char *source = JS_sprintf_append(NULL, "depth %d {", OBJ_BLOCK_DEPTH(cx, obj)); + for (JSScopeProperty *sprop = OBJ_SCOPE(obj)->lastProperty(); + sprop; + sprop = sprop->parent) { + const char *bytes = js_AtomToPrintableString(cx, JSID_TO_ATOM(sprop->id)); + if (!bytes) + return NULL; + source = JS_sprintf_append(source, "%s: %d%s", + bytes, sprop->shortid, + sprop->parent ? ", " : ""); + } + + source = JS_sprintf_append(source, "}"); + if (!source) + return NULL; + + JSString *str = JS_NewString(cx, source, strlen(source)); + if (!str) + return NULL; + return js_GetStringBytes(cx, str); + } + + if (clasp == &js_FunctionClass) { + JSFunction *fun = GET_FUNCTION_PRIVATE(cx, obj); + JSString *str = JS_DecompileFunction(cx, fun, JS_DONT_PRETTY_PRINT); + if (!str) + return NULL; + return js_GetStringBytes(cx, str); + } + + if (clasp == &js_RegExpClass) { + JSAutoTempValueRooter tvr(cx); + if (!js_regexp_toString(cx, obj, tvr.addr())) + return NULL; + return js_GetStringBytes(cx, JSVAL_TO_STRING(tvr.value())); + } + } + + return js_ValueToPrintableSource(cx, v); +} + +JS_FRIEND_API(uintN) +js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, + uintN loc, JSBool lines, FILE *fp) +{ + JSOp op; + const JSCodeSpec *cs; + ptrdiff_t len, off, jmplen; + uint32 type; + JSAtom *atom; + uintN index; + JSObject *obj; + jsval v; + const char *bytes; + jsint i; + + op = (JSOp)*pc; + if (op >= JSOP_LIMIT) { + char numBuf1[12], numBuf2[12]; + JS_snprintf(numBuf1, sizeof numBuf1, "%d", op); + JS_snprintf(numBuf2, sizeof numBuf2, "%d", JSOP_LIMIT); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BYTECODE_TOO_BIG, numBuf1, numBuf2); + return 0; + } + cs = &js_CodeSpec[op]; + len = (ptrdiff_t) cs->length; + fprintf(fp, "%05u:", loc); + if (lines) + fprintf(fp, "%4u", JS_PCToLineNumber(cx, script, pc)); + fprintf(fp, " %s", js_CodeName[op]); + type = JOF_TYPE(cs->format); + switch (type) { + case JOF_BYTE: + if (op == JSOP_TRAP) { + op = JS_GetTrapOpcode(cx, script, pc); + len = (ptrdiff_t) js_CodeSpec[op].length; + } + break; + + case JOF_JUMP: + case JOF_JUMPX: + off = GetJumpOffset(pc, pc); + fprintf(fp, " %u (%d)", loc + (intN) off, (intN) off); + break; + + case JOF_ATOM: + case JOF_OBJECT: + case JOF_REGEXP: + index = js_GetIndexFromBytecode(cx, script, pc, 0); + if (type == JOF_ATOM) { + JS_GET_SCRIPT_ATOM(script, pc, index, atom); + v = ATOM_KEY(atom); + } else { + if (type == JOF_OBJECT) + obj = script->getObject(index); + else + obj = script->getRegExp(index); + v = OBJECT_TO_JSVAL(obj); + } + bytes = ToDisassemblySource(cx, v); + if (!bytes) + return 0; + fprintf(fp, " %s", bytes); + break; + + case JOF_UINT16PAIR: + i = (jsint)GET_UINT16(pc); + fprintf(fp, " %d", i); + /* FALL THROUGH */ + + case JOF_UINT16: + i = (jsint)GET_UINT16(pc); + goto print_int; + + case JOF_TABLESWITCH: + case JOF_TABLESWITCHX: + { + jsbytecode *pc2; + jsint i, low, high; + + jmplen = (type == JOF_TABLESWITCH) ? JUMP_OFFSET_LEN + : JUMPX_OFFSET_LEN; + pc2 = pc; + off = GetJumpOffset(pc, pc2); + pc2 += jmplen; + low = GET_JUMP_OFFSET(pc2); + pc2 += JUMP_OFFSET_LEN; + high = GET_JUMP_OFFSET(pc2); + pc2 += JUMP_OFFSET_LEN; + fprintf(fp, " defaultOffset %d low %d high %d", (intN) off, low, high); + for (i = low; i <= high; i++) { + off = GetJumpOffset(pc, pc2); + fprintf(fp, "\n\t%d: %d", i, (intN) off); + pc2 += jmplen; + } + len = 1 + pc2 - pc; + break; + } + + case JOF_LOOKUPSWITCH: + case JOF_LOOKUPSWITCHX: + { + jsbytecode *pc2; + jsatomid npairs; + + jmplen = (type == JOF_LOOKUPSWITCH) ? JUMP_OFFSET_LEN + : JUMPX_OFFSET_LEN; + pc2 = pc; + off = GetJumpOffset(pc, pc2); + pc2 += jmplen; + npairs = GET_UINT16(pc2); + pc2 += UINT16_LEN; + fprintf(fp, " offset %d npairs %u", (intN) off, (uintN) npairs); + while (npairs) { + JS_GET_SCRIPT_ATOM(script, pc, GET_INDEX(pc2), atom); + pc2 += INDEX_LEN; + off = GetJumpOffset(pc, pc2); + pc2 += jmplen; + + bytes = ToDisassemblySource(cx, ATOM_KEY(atom)); + if (!bytes) + return 0; + fprintf(fp, "\n\t%s: %d", bytes, (intN) off); + npairs--; + } + len = 1 + pc2 - pc; + break; + } + + case JOF_QARG: + fprintf(fp, " %u", GET_ARGNO(pc)); + break; + + case JOF_LOCAL: + fprintf(fp, " %u", GET_SLOTNO(pc)); + break; + + case JOF_SLOTATOM: + case JOF_SLOTOBJECT: + fprintf(fp, " %u", GET_SLOTNO(pc)); + index = js_GetIndexFromBytecode(cx, script, pc, SLOTNO_LEN); + if (type == JOF_SLOTATOM) { + JS_GET_SCRIPT_ATOM(script, pc, index, atom); + v = ATOM_KEY(atom); + } else { + obj = script->getObject(index); + v = OBJECT_TO_JSVAL(obj); + } + bytes = ToDisassemblySource(cx, v); + if (!bytes) + return 0; + fprintf(fp, " %s", bytes); + break; + + case JOF_UINT24: + JS_ASSERT(op == JSOP_UINT24 || op == JSOP_NEWARRAY); + i = (jsint)GET_UINT24(pc); + goto print_int; + + case JOF_UINT8: + i = pc[1]; + goto print_int; + + case JOF_INT8: + i = GET_INT8(pc); + goto print_int; + + case JOF_INT32: + JS_ASSERT(op == JSOP_INT32); + i = GET_INT32(pc); + print_int: + fprintf(fp, " %d", i); + break; + + default: { + char numBuf[12]; + JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) cs->format); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_UNKNOWN_FORMAT, numBuf); + return 0; + } + } + fputs("\n", fp); + return len; +} + +#endif /* DEBUG */ + +/************************************************************************/ + +/* + * Sprintf, but with unlimited and automatically allocated buffering. + */ +typedef struct Sprinter { + JSContext *context; /* context executing the decompiler */ + JSArenaPool *pool; /* string allocation pool */ + char *base; /* base address of buffer in pool */ + size_t size; /* size of buffer allocated at base */ + ptrdiff_t offset; /* offset of next free char in buffer */ +} Sprinter; + +#define INIT_SPRINTER(cx, sp, ap, off) \ + ((sp)->context = cx, (sp)->pool = ap, (sp)->base = NULL, (sp)->size = 0, \ + (sp)->offset = off) + +#define OFF2STR(sp,off) ((sp)->base + (off)) +#define STR2OFF(sp,str) ((str) - (sp)->base) +#define RETRACT(sp,str) ((sp)->offset = STR2OFF(sp, str)) + +static JSBool +SprintEnsureBuffer(Sprinter *sp, size_t len) +{ + ptrdiff_t nb; + char *base; + + nb = (sp->offset + len + 1) - sp->size; + if (nb < 0) + return JS_TRUE; + base = sp->base; + if (!base) { + JS_ARENA_ALLOCATE_CAST(base, char *, sp->pool, nb); + } else { + JS_ARENA_GROW_CAST(base, char *, sp->pool, sp->size, nb); + } + if (!base) { + js_ReportOutOfScriptQuota(sp->context); + return JS_FALSE; + } + sp->base = base; + sp->size += nb; + return JS_TRUE; +} + +static ptrdiff_t +SprintPut(Sprinter *sp, const char *s, size_t len) +{ + ptrdiff_t offset; + char *bp; + + /* Allocate space for s, including the '\0' at the end. */ + if (!SprintEnsureBuffer(sp, len)) + return -1; + + /* Advance offset and copy s into sp's buffer. */ + offset = sp->offset; + sp->offset += len; + bp = sp->base + offset; + memmove(bp, s, len); + bp[len] = 0; + return offset; +} + +static ptrdiff_t +SprintCString(Sprinter *sp, const char *s) +{ + return SprintPut(sp, s, strlen(s)); +} + +static ptrdiff_t +SprintString(Sprinter *sp, JSString *str) +{ + const jschar *chars; + size_t length, size; + ptrdiff_t offset; + + str->getCharsAndLength(chars, length); + if (length == 0) + return sp->offset; + + size = js_GetDeflatedStringLength(sp->context, chars, length); + if (size == (size_t)-1 || !SprintEnsureBuffer(sp, size)) + return -1; + + offset = sp->offset; + sp->offset += size; + js_DeflateStringToBuffer(sp->context, chars, length, sp->base + offset, + &size); + sp->base[sp->offset] = 0; + return offset; +} + + +static ptrdiff_t +Sprint(Sprinter *sp, const char *format, ...) +{ + va_list ap; + char *bp; + ptrdiff_t offset; + + va_start(ap, format); + bp = JS_vsmprintf(format, ap); /* XXX vsaprintf */ + va_end(ap); + if (!bp) { + JS_ReportOutOfMemory(sp->context); + return -1; + } + offset = SprintCString(sp, bp); + js_free(bp); + return offset; +} + +const char js_EscapeMap[] = { + '\b', 'b', + '\f', 'f', + '\n', 'n', + '\r', 'r', + '\t', 't', + '\v', 'v', + '"', '"', + '\'', '\'', + '\\', '\\', + '\0', '0' +}; + +#define DONT_ESCAPE 0x10000 + +static char * +QuoteString(Sprinter *sp, JSString *str, uint32 quote) +{ + JSBool dontEscape, ok; + jschar qc, c; + ptrdiff_t off, len; + const jschar *s, *t, *z; + const char *e; + char *bp; + + /* Sample off first for later return value pointer computation. */ + dontEscape = (quote & DONT_ESCAPE) != 0; + qc = (jschar) quote; + off = sp->offset; + if (qc && Sprint(sp, "%c", (char)qc) < 0) + return NULL; + + /* Loop control variables: z points at end of string sentinel. */ + str->getCharsAndEnd(s, z); + for (t = s; t < z; s = ++t) { + /* Move t forward from s past un-quote-worthy characters. */ + c = *t; + while (JS_ISPRINT(c) && c != qc && c != '\\' && c != '\t' && + !(c >> 8)) { + c = *++t; + if (t == z) + break; + } + len = t - s; + + /* Allocate space for s, including the '\0' at the end. */ + if (!SprintEnsureBuffer(sp, len)) + return NULL; + + /* Advance sp->offset and copy s into sp's buffer. */ + bp = sp->base + sp->offset; + sp->offset += len; + while (--len >= 0) + *bp++ = (char) *s++; + *bp = '\0'; + + if (t == z) + break; + + /* Use js_EscapeMap, \u, or \x only if necessary. */ + if (!(c >> 8) && (e = strchr(js_EscapeMap, (int)c)) != NULL) { + ok = dontEscape + ? Sprint(sp, "%c", (char)c) >= 0 + : Sprint(sp, "\\%c", e[1]) >= 0; + } else { + ok = Sprint(sp, (c >> 8) ? "\\u%04X" : "\\x%02X", c) >= 0; + } + if (!ok) + return NULL; + } + + /* Sprint the closing quote and return the quoted string. */ + if (qc && Sprint(sp, "%c", (char)qc) < 0) + return NULL; + + /* + * If we haven't Sprint'd anything yet, Sprint an empty string so that + * the OFF2STR below gives a valid result. + */ + if (off == sp->offset && Sprint(sp, "") < 0) + return NULL; + return OFF2STR(sp, off); +} + +JSString * +js_QuoteString(JSContext *cx, JSString *str, jschar quote) +{ + void *mark; + Sprinter sprinter; + char *bytes; + JSString *escstr; + + mark = JS_ARENA_MARK(&cx->tempPool); + INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); + bytes = QuoteString(&sprinter, str, quote); + escstr = bytes ? JS_NewStringCopyZ(cx, bytes) : NULL; + JS_ARENA_RELEASE(&cx->tempPool, mark); + return escstr; +} + +/************************************************************************/ + +struct JSPrinter { + Sprinter sprinter; /* base class state */ + JSArenaPool pool; /* string allocation pool */ + uintN indent; /* indentation in spaces */ + bool pretty; /* pretty-print: indent, use newlines */ + bool grouped; /* in parenthesized expression context */ + bool strict; /* in code marked strict */ + JSScript *script; /* script being printed */ + jsbytecode *dvgfence; /* DecompileExpression fencepost */ + jsbytecode **pcstack; /* DecompileExpression modeled stack */ + JSFunction *fun; /* interpreted function */ + jsuword *localNames; /* argument and variable names */ +}; + +JSPrinter * +js_NewPrinter(JSContext *cx, const char *name, JSFunction *fun, + uintN indent, JSBool pretty, JSBool grouped, JSBool strict) +{ + JSPrinter *jp; + + jp = (JSPrinter *) cx->malloc(sizeof(JSPrinter)); + if (!jp) + return NULL; + INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0); + JS_InitArenaPool(&jp->pool, name, 256, 1, &cx->scriptStackQuota); + jp->indent = indent; + jp->pretty = pretty; + jp->grouped = grouped; + jp->strict = strict; + jp->script = NULL; + jp->dvgfence = NULL; + jp->pcstack = NULL; + jp->fun = fun; + jp->localNames = NULL; + if (fun && FUN_INTERPRETED(fun) && fun->hasLocalNames()) { + jp->localNames = js_GetLocalNameArray(cx, fun, &jp->pool); + if (!jp->localNames) { + js_DestroyPrinter(jp); + return NULL; + } + } + return jp; +} + +void +js_DestroyPrinter(JSPrinter *jp) +{ + JS_FinishArenaPool(&jp->pool); + jp->sprinter.context->free(jp); +} + +JSString * +js_GetPrinterOutput(JSPrinter *jp) +{ + JSContext *cx; + JSString *str; + + cx = jp->sprinter.context; + if (!jp->sprinter.base) + return cx->runtime->emptyString; + str = JS_NewStringCopyZ(cx, jp->sprinter.base); + if (!str) + return NULL; + JS_FreeArenaPool(&jp->pool); + INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0); + return str; +} + +/* + * NB: Indexed by SRC_DECL_* defines from jsemit.h. + */ +static const char * const var_prefix[] = {"var ", "const ", "let "}; + +static const char * +VarPrefix(jssrcnote *sn) +{ + if (sn && (SN_TYPE(sn) == SRC_DECL || SN_TYPE(sn) == SRC_GROUPASSIGN)) { + ptrdiff_t type = js_GetSrcNoteOffset(sn, 0); + if ((uintN)type <= SRC_DECL_LET) + return var_prefix[type]; + } + return ""; +} + +int +js_printf(JSPrinter *jp, const char *format, ...) +{ + va_list ap; + char *bp, *fp; + int cc; + + if (*format == '\0') + return 0; + + va_start(ap, format); + + /* If pretty-printing, expand magic tab into a run of jp->indent spaces. */ + if (*format == '\t') { + format++; + if (jp->pretty && Sprint(&jp->sprinter, "%*s", jp->indent, "") < 0) { + va_end(ap); + return -1; + } + } + + /* Suppress newlines (must be once per format, at the end) if not pretty. */ + fp = NULL; + if (!jp->pretty && format[cc = strlen(format) - 1] == '\n') { + fp = JS_strdup(jp->sprinter.context, format); + if (!fp) { + va_end(ap); + return -1; + } + fp[cc] = '\0'; + format = fp; + } + + /* Allocate temp space, convert format, and put. */ + bp = JS_vsmprintf(format, ap); /* XXX vsaprintf */ + if (fp) { + jp->sprinter.context->free(fp); + format = NULL; + } + if (!bp) { + JS_ReportOutOfMemory(jp->sprinter.context); + va_end(ap); + return -1; + } + + cc = strlen(bp); + if (SprintPut(&jp->sprinter, bp, (size_t)cc) < 0) + cc = -1; + js_free(bp); + + va_end(ap); + return cc; +} + +JSBool +js_puts(JSPrinter *jp, const char *s) +{ + return SprintCString(&jp->sprinter, s) >= 0; +} + +/************************************************************************/ + +typedef struct SprintStack { + Sprinter sprinter; /* sprinter for postfix to infix buffering */ + ptrdiff_t *offsets; /* stack of postfix string offsets */ + jsbytecode *opcodes; /* parallel stack of JS opcodes */ + uintN top; /* top of stack index */ + uintN inArrayInit; /* array initialiser/comprehension level */ + JSBool inGenExp; /* in generator expression */ + JSPrinter *printer; /* permanent output goes here */ +} SprintStack; + +/* + * Find the depth of the operand stack when the interpreter reaches the given + * pc in script. pcstack must have space for least script->depth elements. On + * return it will contain pointers to opcodes that populated the interpreter's + * current operand stack. + * + * This function cannot raise an exception or error. However, due to a risk of + * potential bugs when modeling the stack, the function returns -1 if it + * detects an inconsistency in the model. Such an inconsistency triggers an + * assert in a debug build. + */ +static intN +ReconstructPCStack(JSContext *cx, JSScript *script, jsbytecode *pc, + jsbytecode **pcstack); + +#define FAILED_EXPRESSION_DECOMPILER ((char *) 1) + +/* + * Decompile a part of expression up to the given pc. The function returns + * NULL on out-of-memory, or the FAILED_EXPRESSION_DECOMPILER sentinel when + * the decompiler fails due to a bug and/or unimplemented feature, or the + * decompiled string on success. + */ +static char * +DecompileExpression(JSContext *cx, JSScript *script, JSFunction *fun, + jsbytecode *pc); + +/* + * Get a stacked offset from ss->sprinter.base, or if the stacked value |off| + * is negative, fetch the generating pc from printer->pcstack[-2 - off] and + * decompile the code that generated the missing value. This is used when + * reporting errors, where the model stack will lack |pcdepth| non-negative + * offsets (see DecompileExpression and DecompileCode). + * + * If the stacked offset is -1, return 0 to index the NUL padding at the start + * of ss->sprinter.base. If this happens, it means there is a decompiler bug + * to fix, but it won't violate memory safety. + */ +static ptrdiff_t +GetOff(SprintStack *ss, uintN i) +{ + ptrdiff_t off; + jsbytecode *pc; + char *bytes; + + off = ss->offsets[i]; + if (off >= 0) + return off; + + JS_ASSERT(off <= -2); + JS_ASSERT(ss->printer->pcstack); + if (off <= -2 && ss->printer->pcstack) { + pc = ss->printer->pcstack[-2 - off]; + bytes = DecompileExpression(ss->sprinter.context, ss->printer->script, + ss->printer->fun, pc); + if (!bytes) + return 0; + if (bytes != FAILED_EXPRESSION_DECOMPILER) { + off = SprintCString(&ss->sprinter, bytes); + if (off < 0) + off = 0; + ss->offsets[i] = off; + ss->sprinter.context->free(bytes); + return off; + } + if (!ss->sprinter.base && SprintPut(&ss->sprinter, "", 0) >= 0) { + memset(ss->sprinter.base, 0, ss->sprinter.offset); + ss->offsets[i] = -1; + } + } + return 0; +} + +static const char * +GetStr(SprintStack *ss, uintN i) +{ + ptrdiff_t off; + + /* + * Must call GetOff before using ss->sprinter.base, since it may be null + * until bootstrapped by GetOff. + */ + off = GetOff(ss, i); + return OFF2STR(&ss->sprinter, off); +} + +/* + * Gap between stacked strings to allow for insertion of parens and commas + * when auto-parenthesizing expressions and decompiling array initialisers + * (see the JSOP_NEWARRAY case in Decompile). + */ +#define PAREN_SLOP (2 + 1) + +/* + * These pseudo-ops help js_DecompileValueGenerator decompile JSOP_SETNAME, + * JSOP_SETPROP, and JSOP_SETELEM, respectively. They are never stored in + * bytecode, so they don't preempt valid opcodes. + */ +#define JSOP_GETPROP2 JSOP_LIMIT +#define JSOP_GETELEM2 JSOP_LIMIT + 1 +JS_STATIC_ASSERT(JSOP_GETELEM2 <= 255); + +static void +AddParenSlop(SprintStack *ss) +{ + memset(OFF2STR(&ss->sprinter, ss->sprinter.offset), 0, PAREN_SLOP); + ss->sprinter.offset += PAREN_SLOP; +} + +static JSBool +PushOff(SprintStack *ss, ptrdiff_t off, JSOp op) +{ + uintN top; + + if (!SprintEnsureBuffer(&ss->sprinter, PAREN_SLOP)) + return JS_FALSE; + + /* ss->top points to the next free slot; be paranoid about overflow. */ + top = ss->top; + JS_ASSERT(top < StackDepth(ss->printer->script)); + if (top >= StackDepth(ss->printer->script)) { + JS_ReportOutOfMemory(ss->sprinter.context); + return JS_FALSE; + } + + /* The opcodes stack must contain real bytecodes that index js_CodeSpec. */ + ss->offsets[top] = off; + ss->opcodes[top] = jsbytecode((op == JSOP_GETPROP2) ? JSOP_GETPROP + : (op == JSOP_GETELEM2) ? JSOP_GETELEM + : op); + ss->top = ++top; + AddParenSlop(ss); + return JS_TRUE; +} + +static ptrdiff_t +PopOffPrec(SprintStack *ss, uint8 prec) +{ + uintN top; + const JSCodeSpec *topcs; + ptrdiff_t off; + + /* ss->top points to the next free slot; be paranoid about underflow. */ + top = ss->top; + JS_ASSERT(top != 0); + if (top == 0) + return 0; + + ss->top = --top; + off = GetOff(ss, top); + topcs = &js_CodeSpec[ss->opcodes[top]]; + if (topcs->prec != 0 && topcs->prec < prec) { + ss->sprinter.offset = ss->offsets[top] = off - 2; + off = Sprint(&ss->sprinter, "(%s)", OFF2STR(&ss->sprinter, off)); + } else { + ss->sprinter.offset = off; + } + return off; +} + +static const char * +PopStrPrec(SprintStack *ss, uint8 prec) +{ + ptrdiff_t off; + + off = PopOffPrec(ss, prec); + return OFF2STR(&ss->sprinter, off); +} + +static ptrdiff_t +PopOff(SprintStack *ss, JSOp op) +{ + return PopOffPrec(ss, js_CodeSpec[op].prec); +} + +static const char * +PopStr(SprintStack *ss, JSOp op) +{ + return PopStrPrec(ss, js_CodeSpec[op].prec); +} + +typedef struct TableEntry { + jsval key; + ptrdiff_t offset; + JSAtom *label; + jsint order; /* source order for stable tableswitch sort */ +} TableEntry; + +static JSBool +CompareOffsets(void *arg, const void *v1, const void *v2, int *result) +{ + ptrdiff_t offset_diff; + const TableEntry *te1 = (const TableEntry *) v1, + *te2 = (const TableEntry *) v2; + + offset_diff = te1->offset - te2->offset; + *result = (offset_diff == 0 ? te1->order - te2->order + : offset_diff < 0 ? -1 + : 1); + return JS_TRUE; +} + +static ptrdiff_t +SprintDoubleValue(Sprinter *sp, jsval v, JSOp *opp) +{ + jsdouble d; + ptrdiff_t todo; + char *s, buf[DTOSTR_STANDARD_BUFFER_SIZE]; + + JS_ASSERT(JSVAL_IS_DOUBLE(v)); + d = *JSVAL_TO_DOUBLE(v); + if (JSDOUBLE_IS_NEGZERO(d)) { + todo = SprintCString(sp, "-0"); + *opp = JSOP_NEG; + } else if (!JSDOUBLE_IS_FINITE(d)) { + /* Don't use Infinity and NaN, they're mutable. */ + todo = SprintCString(sp, + JSDOUBLE_IS_NaN(d) + ? "0 / 0" + : (d < 0) + ? "1 / -0" + : "1 / 0"); + *opp = JSOP_DIV; + } else { + s = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, d); + if (!s) { + JS_ReportOutOfMemory(sp->context); + return -1; + } + JS_ASSERT(strcmp(s, js_Infinity_str) && + (*s != '-' || + strcmp(s + 1, js_Infinity_str)) && + strcmp(s, js_NaN_str)); + todo = Sprint(sp, s); + } + return todo; +} + +static jsbytecode * +Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop); + +static JSBool +DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, + jsbytecode *pc, ptrdiff_t switchLength, + ptrdiff_t defaultOffset, JSBool isCondSwitch) +{ + JSContext *cx; + JSPrinter *jp; + ptrdiff_t off, off2, diff, caseExprOff, todo; + char *lval, *rval; + uintN i; + jsval key; + JSString *str; + + cx = ss->sprinter.context; + jp = ss->printer; + + /* JSOP_CONDSWITCH doesn't pop, unlike JSOP_{LOOKUP,TABLE}SWITCH. */ + off = isCondSwitch ? GetOff(ss, ss->top-1) : PopOff(ss, JSOP_NOP); + lval = OFF2STR(&ss->sprinter, off); + + js_printf(jp, "\tswitch (%s) {\n", lval); + + if (tableLength) { + diff = table[0].offset - defaultOffset; + if (diff > 0) { + jp->indent += 2; + js_printf(jp, "\t%s:\n", js_default_str); + jp->indent += 2; + if (!Decompile(ss, pc + defaultOffset, diff, JSOP_NOP)) + return JS_FALSE; + jp->indent -= 4; + } + + caseExprOff = isCondSwitch ? JSOP_CONDSWITCH_LENGTH : 0; + + for (i = 0; i < tableLength; i++) { + off = table[i].offset; + off2 = (i + 1 < tableLength) ? table[i + 1].offset : switchLength; + + key = table[i].key; + if (isCondSwitch) { + ptrdiff_t nextCaseExprOff; + + /* + * key encodes the JSOP_CASE bytecode's offset from switchtop. + * The next case expression follows immediately, unless we are + * at the last case. + */ + nextCaseExprOff = (ptrdiff_t)JSVAL_TO_INT(key); + nextCaseExprOff += js_CodeSpec[pc[nextCaseExprOff]].length; + jp->indent += 2; + if (!Decompile(ss, pc + caseExprOff, + nextCaseExprOff - caseExprOff, JSOP_NOP)) { + return JS_FALSE; + } + caseExprOff = nextCaseExprOff; + + /* Balance the stack as if this JSOP_CASE matched. */ + --ss->top; + } else { + /* + * key comes from an atom, not the decompiler, so we need to + * quote it if it's a string literal. But if table[i].label + * is non-null, key was constant-propagated and label is the + * name of the const we should show as the case label. We set + * key to undefined so this identifier is escaped, if required + * by non-ASCII characters, but not quoted, by QuoteString. + */ + todo = -1; + if (table[i].label) { + str = ATOM_TO_STRING(table[i].label); + key = JSVAL_VOID; + } else if (JSVAL_IS_DOUBLE(key)) { + JSOp junk; + + todo = SprintDoubleValue(&ss->sprinter, key, &junk); + str = NULL; + } else { + str = js_ValueToString(cx, key); + if (!str) + return JS_FALSE; + } + if (todo >= 0) { + rval = OFF2STR(&ss->sprinter, todo); + } else { + rval = QuoteString(&ss->sprinter, str, (jschar) + (JSVAL_IS_STRING(key) ? '"' : 0)); + if (!rval) + return JS_FALSE; + } + RETRACT(&ss->sprinter, rval); + jp->indent += 2; + js_printf(jp, "\tcase %s:\n", rval); + } + + jp->indent += 2; + if (off <= defaultOffset && defaultOffset < off2) { + diff = defaultOffset - off; + if (diff != 0) { + if (!Decompile(ss, pc + off, diff, JSOP_NOP)) + return JS_FALSE; + off = defaultOffset; + } + jp->indent -= 2; + js_printf(jp, "\t%s:\n", js_default_str); + jp->indent += 2; + } + if (!Decompile(ss, pc + off, off2 - off, JSOP_NOP)) + return JS_FALSE; + jp->indent -= 4; + + /* Re-balance as if last JSOP_CASE or JSOP_DEFAULT mismatched. */ + if (isCondSwitch) + ++ss->top; + } + } + + if (defaultOffset == switchLength) { + jp->indent += 2; + js_printf(jp, "\t%s:;\n", js_default_str); + jp->indent -= 2; + } + js_printf(jp, "\t}\n"); + + /* By the end of a JSOP_CONDSWITCH, the discriminant has been popped. */ + if (isCondSwitch) + --ss->top; + return JS_TRUE; +} + +#define LOCAL_ASSERT_CUSTOM(expr, BAD_EXIT) \ + JS_BEGIN_MACRO \ + JS_ASSERT(expr); \ + if (!(expr)) { BAD_EXIT; } \ + JS_END_MACRO + +#define LOCAL_ASSERT_RV(expr, rv) \ + LOCAL_ASSERT_CUSTOM(expr, return (rv)) + +static JSAtom * +GetArgOrVarAtom(JSPrinter *jp, uintN slot) +{ + JSAtom *name; + + LOCAL_ASSERT_RV(jp->fun, NULL); + LOCAL_ASSERT_RV(slot < jp->fun->countLocalNames(), NULL); + name = JS_LOCAL_NAME_TO_ATOM(jp->localNames[slot]); +#if !JS_HAS_DESTRUCTURING + LOCAL_ASSERT_RV(name, NULL); +#endif + return name; +} + +const char * +GetLocal(SprintStack *ss, jsint i) +{ + ptrdiff_t off; + JSContext *cx; + JSScript *script; + jsatomid j, n; + JSAtom *atom; + JSObject *obj; + jsint depth, count; + JSScopeProperty *sprop; + const char *rval; + +#define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, "") + + off = ss->offsets[i]; + if (off >= 0) + return OFF2STR(&ss->sprinter, off); + + /* + * We must be called from js_DecompileValueGenerator (via Decompile) when + * dereferencing a local that's undefined or null. Search script->objects + * for the block containing this local by its stack index, i. + * + * In case of destructuring's use of JSOP_GETLOCAL, however, there may be + * no such local. This could mean no blocks (no script objects at all, or + * none of the script's object literals are blocks), or the stack slot i is + * not in a block. In either case, return GetStr(ss, i). + */ + cx = ss->sprinter.context; + script = ss->printer->script; + if (script->objectsOffset == 0) + return GetStr(ss, i); + for (j = 0, n = script->objects()->length; ; j++) { + if (j == n) + return GetStr(ss, i); + obj = script->getObject(j); + if (OBJ_GET_CLASS(cx, obj) == &js_BlockClass) { + depth = OBJ_BLOCK_DEPTH(cx, obj); + count = OBJ_BLOCK_COUNT(cx, obj); + if ((jsuint)(i - depth) < (jsuint)count) + break; + } + } + + i -= depth; + for (sprop = OBJ_SCOPE(obj)->lastProperty(); sprop; sprop = sprop->parent) { + if (sprop->shortid == i) + break; + } + + LOCAL_ASSERT(sprop && JSID_IS_ATOM(sprop->id)); + atom = JSID_TO_ATOM(sprop->id); + rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); + if (!rval) + return NULL; + RETRACT(&ss->sprinter, rval); + return rval; + +#undef LOCAL_ASSERT +} + +static JSBool +IsVarSlot(JSPrinter *jp, jsbytecode *pc, jsint *indexp) +{ + uintN slot; + + slot = GET_SLOTNO(pc); + if (slot < jp->script->nfixed) { + /* The slot refers to a variable with name stored in jp->localNames. */ + *indexp = jp->fun->nargs + slot; + return JS_TRUE; + } + + /* We have a local which index is relative to the stack base. */ + slot -= jp->script->nfixed; + JS_ASSERT(slot < StackDepth(jp->script)); + *indexp = slot; + return JS_FALSE; +} + +#define LOAD_ATOM(PCOFF) \ + GET_ATOM_FROM_BYTECODE(jp->script, pc, PCOFF, atom) + +#if JS_HAS_DESTRUCTURING + +#define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL) +#define LOAD_OP_DATA(pc) (oplen = (cs = &js_CodeSpec[op=(JSOp)*pc])->length) + +static jsbytecode * +DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc); + +static jsbytecode * +DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, + JSBool *hole) +{ + JSContext *cx; + JSPrinter *jp; + JSOp op; + const JSCodeSpec *cs; + uintN oplen; + jsint i; + const char *lval, *xval; + ptrdiff_t todo; + JSAtom *atom; + + *hole = JS_FALSE; + cx = ss->sprinter.context; + jp = ss->printer; + LOAD_OP_DATA(pc); + + switch (op) { + case JSOP_POP: + *hole = JS_TRUE; + todo = SprintPut(&ss->sprinter, ", ", 2); + break; + + case JSOP_DUP: + pc = DecompileDestructuring(ss, pc, endpc); + if (!pc) + return NULL; + if (pc == endpc) + return pc; + LOAD_OP_DATA(pc); + lval = PopStr(ss, JSOP_NOP); + todo = SprintCString(&ss->sprinter, lval); + if (op == JSOP_POPN) + return pc; + LOCAL_ASSERT(*pc == JSOP_POP); + break; + + case JSOP_SETARG: + case JSOP_SETGVAR: + case JSOP_SETLOCAL: + LOCAL_ASSERT(pc[oplen] == JSOP_POP || pc[oplen] == JSOP_POPN); + /* FALL THROUGH */ + + case JSOP_SETLOCALPOP: + atom = NULL; + lval = NULL; + if (op == JSOP_SETARG) { + atom = GetArgOrVarAtom(jp, GET_SLOTNO(pc)); + LOCAL_ASSERT(atom); + } else if (op == JSOP_SETGVAR) { + LOAD_ATOM(0); + } else if (IsVarSlot(jp, pc, &i)) { + atom = GetArgOrVarAtom(jp, i); + LOCAL_ASSERT(atom); + } else { + lval = GetLocal(ss, i); + } + if (atom) + lval = js_AtomToPrintableString(cx, atom); + LOCAL_ASSERT(lval); + todo = SprintCString(&ss->sprinter, lval); + if (op != JSOP_SETLOCALPOP) { + pc += oplen; + if (pc == endpc) + return pc; + LOAD_OP_DATA(pc); + if (op == JSOP_POPN) + return pc; + LOCAL_ASSERT(op == JSOP_POP); + } + break; + + default: + /* + * We may need to auto-parenthesize the left-most value decompiled + * here, so add back PAREN_SLOP temporarily. Then decompile until the + * opcode that would reduce the stack depth to (ss->top-1), which we + * pass to Decompile encoded as -(ss->top-1) - 1 or just -ss->top for + * the nb parameter. + */ + todo = ss->sprinter.offset; + ss->sprinter.offset = todo + PAREN_SLOP; + pc = Decompile(ss, pc, -((intN)ss->top), JSOP_NOP); + if (!pc) + return NULL; + if (pc == endpc) + return pc; + LOAD_OP_DATA(pc); + LOCAL_ASSERT(op == JSOP_ENUMELEM || op == JSOP_ENUMCONSTELEM); + xval = PopStr(ss, JSOP_NOP); + lval = PopStr(ss, JSOP_GETPROP); + ss->sprinter.offset = todo; + if (*lval == '\0') { + /* lval is from JSOP_BINDNAME, so just print xval. */ + todo = SprintCString(&ss->sprinter, xval); + } else if (*xval == '\0') { + /* xval is from JSOP_SETCALL or JSOP_BINDXMLNAME, print lval. */ + todo = SprintCString(&ss->sprinter, lval); + } else { + todo = Sprint(&ss->sprinter, + (JOF_OPMODE(ss->opcodes[ss->top+1]) == JOF_XMLNAME) + ? "%s.%s" + : "%s[%s]", + lval, xval); + } + break; + } + + if (todo < 0) + return NULL; + + LOCAL_ASSERT(pc < endpc); + pc += oplen; + return pc; +} + +/* + * Starting with a SRC_DESTRUCT-annotated JSOP_DUP, decompile a destructuring + * left-hand side object or array initialiser, including nested destructuring + * initialisers. On successful return, the decompilation will be pushed on ss + * and the return value will point to the POP or GROUP bytecode following the + * destructuring expression. + * + * At any point, if pc is equal to endpc and would otherwise advance, we stop + * immediately and return endpc. + */ +static jsbytecode * +DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc) +{ + ptrdiff_t head; + JSContext *cx; + JSPrinter *jp; + JSOp op, saveop; + const JSCodeSpec *cs; + uintN oplen; + jsint i, lasti; + jsdouble d; + const char *lval; + JSAtom *atom; + jssrcnote *sn; + JSString *str; + JSBool hole; + + LOCAL_ASSERT(*pc == JSOP_DUP); + pc += JSOP_DUP_LENGTH; + + /* + * Set head so we can rewrite '[' to '{' as needed. Back up PAREN_SLOP + * chars so the destructuring decompilation accumulates contiguously in + * ss->sprinter starting with "[". + */ + head = SprintPut(&ss->sprinter, "[", 1); + if (head < 0 || !PushOff(ss, head, JSOP_NOP)) + return NULL; + ss->sprinter.offset -= PAREN_SLOP; + LOCAL_ASSERT(head == ss->sprinter.offset - 1); + LOCAL_ASSERT(*OFF2STR(&ss->sprinter, head) == '['); + + cx = ss->sprinter.context; + jp = ss->printer; + lasti = -1; + + while (pc < endpc) { +#if JS_HAS_DESTRUCTURING_SHORTHAND + ptrdiff_t nameoff = -1; +#endif + + LOAD_OP_DATA(pc); + saveop = op; + + switch (op) { + case JSOP_POP: + pc += oplen; + goto out; + + /* Handle the optimized number-pushing opcodes. */ + case JSOP_ZERO: d = i = 0; goto do_getelem; + case JSOP_ONE: d = i = 1; goto do_getelem; + case JSOP_UINT16: d = i = GET_UINT16(pc); goto do_getelem; + case JSOP_UINT24: d = i = GET_UINT24(pc); goto do_getelem; + case JSOP_INT8: d = i = GET_INT8(pc); goto do_getelem; + case JSOP_INT32: d = i = GET_INT32(pc); goto do_getelem; + + case JSOP_DOUBLE: + GET_DOUBLE_FROM_BYTECODE(jp->script, pc, 0, atom); + d = *ATOM_TO_DOUBLE(atom); + LOCAL_ASSERT(JSDOUBLE_IS_FINITE(d) && !JSDOUBLE_IS_NEGZERO(d)); + i = (jsint)d; + + do_getelem: + sn = js_GetSrcNote(jp->script, pc); + pc += oplen; + if (pc == endpc) + return pc; + LOAD_OP_DATA(pc); + LOCAL_ASSERT(op == JSOP_GETELEM); + + /* Distinguish object from array by opcode or source note. */ + if (sn && SN_TYPE(sn) == SRC_INITPROP) { + *OFF2STR(&ss->sprinter, head) = '{'; + if (Sprint(&ss->sprinter, "%g: ", d) < 0) + return NULL; + } else { + /* Sanity check for the gnarly control flow above. */ + LOCAL_ASSERT(i == d); + + /* Fill in any holes (holes at the end don't matter). */ + while (++lasti < i) { + if (SprintPut(&ss->sprinter, ", ", 2) < 0) + return NULL; + } + } + break; + + case JSOP_LENGTH: + atom = cx->runtime->atomState.lengthAtom; + goto do_destructure_atom; + + case JSOP_CALLPROP: + case JSOP_GETPROP: + LOAD_ATOM(0); + do_destructure_atom: + *OFF2STR(&ss->sprinter, head) = '{'; + str = ATOM_TO_STRING(atom); +#if JS_HAS_DESTRUCTURING_SHORTHAND + nameoff = ss->sprinter.offset; +#endif + if (!QuoteString(&ss->sprinter, str, + js_IsIdentifier(str) ? 0 : (jschar)'\'')) { + return NULL; + } + if (SprintPut(&ss->sprinter, ": ", 2) < 0) + return NULL; + break; + + default: + LOCAL_ASSERT(0); + } + + pc += oplen; + if (pc == endpc) + return pc; + + /* + * Decompile the left-hand side expression whose bytecode starts at pc + * and continues for a bounded number of bytecodes or stack operations + * (and which in any event stops before endpc). + */ + pc = DecompileDestructuringLHS(ss, pc, endpc, &hole); + if (!pc) + return NULL; + +#if JS_HAS_DESTRUCTURING_SHORTHAND + if (nameoff >= 0) { + ptrdiff_t offset, initlen; + + offset = ss->sprinter.offset; + LOCAL_ASSERT(*OFF2STR(&ss->sprinter, offset) == '\0'); + initlen = offset - nameoff; + LOCAL_ASSERT(initlen >= 4); + + /* Early check to rule out odd "name: lval" length. */ + if (((size_t)initlen & 1) == 0) { + size_t namelen; + const char *name; + + /* + * Even "name: lval" string length: check for "x: x" and the + * like, and apply the shorthand if we can. + */ + namelen = (size_t)(initlen - 2) >> 1; + name = OFF2STR(&ss->sprinter, nameoff); + if (!strncmp(name + namelen, ": ", 2) && + !strncmp(name, name + namelen + 2, namelen)) { + offset -= namelen + 2; + *OFF2STR(&ss->sprinter, offset) = '\0'; + ss->sprinter.offset = offset; + } + } + } +#endif + + if (pc == endpc || *pc != JSOP_DUP) + break; + + /* + * We should stop if JSOP_DUP is either without notes or its note is + * not SRC_CONTINUE. The former happens when JSOP_DUP duplicates the + * last destructuring reference implementing an op= assignment like in + * '([t] = z).y += x'. In the latter case the note is SRC_DESTRUCT and + * means another destructuring initialiser abuts this one like in + * '[a] = [b] = c'. + */ + sn = js_GetSrcNote(jp->script, pc); + if (!sn) + break; + if (SN_TYPE(sn) != SRC_CONTINUE) { + LOCAL_ASSERT(SN_TYPE(sn) == SRC_DESTRUCT); + break; + } + + if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0) + return NULL; + + pc += JSOP_DUP_LENGTH; + } + +out: + lval = OFF2STR(&ss->sprinter, head); + if (SprintPut(&ss->sprinter, (*lval == '[') ? "]" : "}", 1) < 0) + return NULL; + return pc; +} + +static jsbytecode * +DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, + jssrcnote *sn, ptrdiff_t *todop) +{ + JSOp op; + const JSCodeSpec *cs; + uintN oplen, start, end, i; + ptrdiff_t todo; + JSBool hole; + const char *rval; + + LOAD_OP_DATA(pc); + LOCAL_ASSERT(op == JSOP_PUSH || op == JSOP_GETLOCAL); + + todo = Sprint(&ss->sprinter, "%s[", VarPrefix(sn)); + if (todo < 0 || !PushOff(ss, todo, JSOP_NOP)) + return NULL; + ss->sprinter.offset -= PAREN_SLOP; + + for (;;) { + pc += oplen; + if (pc == endpc) + return pc; + pc = DecompileDestructuringLHS(ss, pc, endpc, &hole); + if (!pc) + return NULL; + if (pc == endpc) + return pc; + LOAD_OP_DATA(pc); + if (op != JSOP_PUSH && op != JSOP_GETLOCAL) + break; + if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0) + return NULL; + } + + LOCAL_ASSERT(op == JSOP_POPN); + if (SprintPut(&ss->sprinter, "] = [", 5) < 0) + return NULL; + + end = ss->top - 1; + start = end - GET_UINT16(pc); + for (i = start; i < end; i++) { + rval = GetStr(ss, i); + if (Sprint(&ss->sprinter, + (i == start) ? "%s" : ", %s", + (i == end - 1 && *rval == '\0') ? ", " : rval) < 0) { + return NULL; + } + } + + if (SprintPut(&ss->sprinter, "]", 1) < 0) + return NULL; + ss->sprinter.offset = ss->offsets[i]; + ss->top = start; + *todop = todo; + return pc; +} + +#undef LOCAL_ASSERT +#undef LOAD_OP_DATA + +#endif /* JS_HAS_DESTRUCTURING */ + +static JSBool +InitSprintStack(JSContext *cx, SprintStack *ss, JSPrinter *jp, uintN depth) +{ + size_t offsetsz, opcodesz; + void *space; + + INIT_SPRINTER(cx, &ss->sprinter, &cx->tempPool, PAREN_SLOP); + + /* Allocate the parallel (to avoid padding) offset and opcode stacks. */ + offsetsz = depth * sizeof(ptrdiff_t); + opcodesz = depth * sizeof(jsbytecode); + JS_ARENA_ALLOCATE(space, &cx->tempPool, offsetsz + opcodesz); + if (!space) { + js_ReportOutOfScriptQuota(cx); + return JS_FALSE; + } + ss->offsets = (ptrdiff_t *) space; + ss->opcodes = (jsbytecode *) ((char *)space + offsetsz); + + ss->top = ss->inArrayInit = 0; + ss->inGenExp = JS_FALSE; + ss->printer = jp; + return JS_TRUE; +} + +/* + * If nb is non-negative, decompile nb bytecodes starting at pc. Otherwise + * the decompiler starts at pc and continues until it reaches an opcode for + * which decompiling would result in the stack depth equaling -(nb + 1). + * + * The nextop parameter is either JSOP_NOP or the "next" opcode in order of + * abstract interpretation (not necessarily physically next in a bytecode + * vector). So nextop is JSOP_POP for the last operand in a comma expression, + * or JSOP_AND for the right operand of &&. + */ +static jsbytecode * +Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) +{ + JSContext *cx; + JSPrinter *jp, *jp2; + jsbytecode *startpc, *endpc, *pc2, *done; + ptrdiff_t tail, todo, len, oplen, cond, next; + JSOp op, lastop, saveop; + const JSCodeSpec *cs; + jssrcnote *sn, *sn2; + const char *lval, *rval, *xval, *fmt, *token; + uintN nuses; + jsint i, argc; + char **argv; + JSAtom *atom; + JSObject *obj; + JSFunction *fun; + JSString *str; + JSBool ok; +#if JS_HAS_XML_SUPPORT + JSBool foreach, inXML, quoteAttr; +#else +#define inXML JS_FALSE +#endif + jsval val; + + static const char exception_cookie[] = "/*EXCEPTION*/"; + static const char retsub_pc_cookie[] = "/*RETSUB_PC*/"; + static const char iter_cookie[] = "/*ITER*/"; + static const char forelem_cookie[] = "/*FORELEM*/"; + static const char with_cookie[] = "/*WITH*/"; + static const char dot_format[] = "%s.%s"; + static const char index_format[] = "%s[%s]"; + static const char predot_format[] = "%s%s.%s"; + static const char postdot_format[] = "%s.%s%s"; + static const char preindex_format[] = "%s%s[%s]"; + static const char postindex_format[] = "%s[%s]%s"; + static const char ss_format[] = "%s%s"; + static const char sss_format[] = "%s%s%s"; + + /* Argument and variables decompilation uses the following to share code. */ + JS_STATIC_ASSERT(ARGNO_LEN == SLOTNO_LEN); + +/* + * Local macros + */ +#define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL) +#define DECOMPILE_CODE(pc,nb) if (!Decompile(ss, pc, nb, JSOP_NOP)) return NULL +#define NEXT_OP(pc) (((pc) + (len) == endpc) ? nextop : pc[len]) +#define TOP_STR() GetStr(ss, ss->top - 1) +#define POP_STR() PopStr(ss, op) +#define POP_STR_PREC(prec) PopStrPrec(ss, prec) + +/* + * Pop a condition expression for if/while. JSOP_IFEQ's precedence forces + * extra parens around assignment, which avoids a strict-mode warning. + */ +#define POP_COND_STR() \ + PopStr(ss, (js_CodeSpec[ss->opcodes[ss->top - 1]].format & JOF_SET) \ + ? JSOP_IFEQ \ + : JSOP_NOP) + +/* + * Callers know that ATOM_IS_STRING(atom), and we leave it to the optimizer to + * common ATOM_TO_STRING(atom) here and near the call sites. + */ +#define ATOM_IS_IDENTIFIER(atom) js_IsIdentifier(ATOM_TO_STRING(atom)) +#define ATOM_IS_KEYWORD(atom) \ + (js_CheckKeyword(ATOM_TO_STRING(atom)->chars(), \ + ATOM_TO_STRING(atom)->length()) != TOK_EOF) + +/* + * Given an atom already fetched from jp->script's atom map, quote/escape its + * string appropriately into rval, and select fmt from the quoted and unquoted + * alternatives. + */ +#define GET_QUOTE_AND_FMT(qfmt, ufmt, rval) \ + JS_BEGIN_MACRO \ + jschar quote_; \ + if (!ATOM_IS_IDENTIFIER(atom)) { \ + quote_ = '\''; \ + fmt = qfmt; \ + } else { \ + quote_ = 0; \ + fmt = ufmt; \ + } \ + rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), quote_); \ + if (!rval) \ + return NULL; \ + JS_END_MACRO + +#define LOAD_OBJECT(PCOFF) \ + GET_OBJECT_FROM_BYTECODE(jp->script, pc, PCOFF, obj) + +#define LOAD_FUNCTION(PCOFF) \ + GET_FUNCTION_FROM_BYTECODE(jp->script, pc, PCOFF, fun) + +#define LOAD_REGEXP(PCOFF) \ + GET_REGEXP_FROM_BYTECODE(jp->script, pc, PCOFF, obj) + +#define GET_SOURCE_NOTE_ATOM(sn, atom) \ + JS_BEGIN_MACRO \ + jsatomid atomIndex_ = (jsatomid) js_GetSrcNoteOffset((sn), 0); \ + \ + LOCAL_ASSERT(atomIndex_ < jp->script->atomMap.length); \ + (atom) = jp->script->atomMap.vector[atomIndex_]; \ + JS_END_MACRO + +/* + * Get atom from jp->script's atom map, quote/escape its string appropriately + * into rval, and select fmt from the quoted and unquoted alternatives. + */ +#define GET_ATOM_QUOTE_AND_FMT(qfmt, ufmt, rval) \ + JS_BEGIN_MACRO \ + LOAD_ATOM(0); \ + GET_QUOTE_AND_FMT(qfmt, ufmt, rval); \ + JS_END_MACRO + +/* + * Per spec, new x(y).z means (new x(y))).z. For example new (x(y).z) must + * decompile with the constructor parenthesized, but new x.z should not. The + * normal rules give x(y).z and x.z identical precedence: both are produced by + * JSOP_GETPROP. + * + * Therefore, we need to know in case JSOP_NEW whether the constructor + * expression contains any unparenthesized function calls. So when building a + * MemberExpression or CallExpression, we set ss->opcodes[n] to JSOP_CALL if + * this is true. x(y).z gets JSOP_CALL, not JSOP_GETPROP. + */ +#define PROPAGATE_CALLNESS() \ + JS_BEGIN_MACRO \ + if (ss->opcodes[ss->top - 1] == JSOP_CALL) \ + saveop = JSOP_CALL; \ + JS_END_MACRO + + cx = ss->sprinter.context; + JS_CHECK_RECURSION(cx, return NULL); + + jp = ss->printer; + startpc = pc; + endpc = (nb < 0) ? jp->script->code + jp->script->length : pc + nb; + tail = -1; + todo = -2; /* NB: different from Sprint() error return. */ + saveop = JSOP_NOP; + sn = NULL; + rval = NULL; +#if JS_HAS_XML_SUPPORT + foreach = inXML = quoteAttr = JS_FALSE; +#endif + + while (nb < 0 || pc < endpc) { + /* + * Move saveop to lastop so prefixed bytecodes can take special action + * while sharing maximal code. Set op and saveop to the new bytecode, + * use op in POP_STR to trigger automatic parenthesization, but push + * saveop at the bottom of the loop if this op pushes. Thus op may be + * set to nop or otherwise mutated to suppress auto-parens. + */ + lastop = saveop; + op = (JSOp) *pc; + cs = &js_CodeSpec[op]; + if (cs->format & JOF_INDEXBASE) { + /* + * The decompiler uses js_GetIndexFromBytecode to get atoms and + * objects and ignores these suffix/prefix bytecodes, thus + * simplifying code that must process JSOP_GETTER/JSOP_SETTER + * prefixes. + */ + pc += cs->length; + if (pc >= endpc) + break; + op = (JSOp) *pc; + cs = &js_CodeSpec[op]; + } + saveop = op; + len = oplen = cs->length; + nuses = js_GetStackUses(cs, op, pc); + + /* + * Here it is possible that nuses > ss->top when the op has a hidden + * source note. But when nb < 0 we assume that the caller knows that + * Decompile would never meet such opcodes. + */ + if (nb < 0) { + LOCAL_ASSERT(ss->top >= nuses); + uintN ndefs = js_GetStackDefs(cx, cs, op, jp->script, pc); + if ((uintN) -(nb + 1) == ss->top - nuses + ndefs) + return pc; + } + + /* + * Save source literal associated with JS now before the following + * rewrite changes op. See bug 380197. + */ + token = CodeToken[op]; + + if (pc + oplen == jp->dvgfence) { + JSStackFrame *fp; + uint32 format, mode, type; + + /* + * Rewrite non-get ops to their "get" format if the error is in + * the bytecode at pc, so we don't decompile more than the error + * expression. + */ + fp = js_GetScriptedCaller(cx, NULL); + format = cs->format; + if (((fp && fp->regs && pc == fp->regs->pc) || + (pc == startpc && nuses != 0)) && + format & (JOF_SET|JOF_DEL|JOF_INCDEC|JOF_FOR|JOF_VARPROP)) { + mode = JOF_MODE(format); + if (mode == JOF_NAME) { + /* + * JOF_NAME does not imply JOF_ATOM, so we must check for + * the QARG and QVAR format types, and translate those to + * JSOP_GETARG or JSOP_GETLOCAL appropriately, instead of + * to JSOP_NAME. + */ + type = JOF_TYPE(format); + op = (type == JOF_QARG) + ? JSOP_GETARG + : (type == JOF_LOCAL) + ? JSOP_GETLOCAL + : JSOP_NAME; + + JS_ASSERT(js_CodeSpec[op].nuses >= 0); + i = nuses - js_CodeSpec[op].nuses; + while (--i >= 0) + PopOff(ss, JSOP_NOP); + } else { + /* + * We must replace the faulting pc's bytecode with a + * corresponding JSOP_GET* code. For JSOP_SET{PROP,ELEM}, + * we must use the "2nd" form of JSOP_GET{PROP,ELEM}, to + * throw away the assignment op's right-hand operand and + * decompile it as if it were a GET of its left-hand + * operand. + */ + if (mode == JOF_PROP) { + op = (JSOp) ((format & JOF_SET) + ? JSOP_GETPROP2 + : JSOP_GETPROP); + } else if (mode == JOF_ELEM) { + op = (JSOp) ((format & JOF_SET) + ? JSOP_GETELEM2 + : JSOP_GETELEM); + } else { + /* + * Unknown mode (including mode 0) means that op is + * uncategorized for our purposes, so we must write + * per-op special case code here. + */ + switch (op) { + case JSOP_ENUMELEM: + case JSOP_ENUMCONSTELEM: + op = JSOP_GETELEM; + break; + case JSOP_SETCALL: + op = JSOP_CALL; + break; + case JSOP_GETTHISPROP: + /* + * NB: JSOP_GETTHISPROP can't fail due to |this| + * being null or undefined at runtime (beware that + * this may change for ES4). Therefore any error + * resulting from this op must be due to the value + * of the property accessed via |this|, so do not + * rewrite op to JSOP_THIS. + * + * The next two cases should not change op if + * js_DecompileValueGenerator was called from the + * the property getter. They should rewrite only + * if the base object in the arg/var/local is null + * or undefined. FIXME: bug 431569. + */ + break; + case JSOP_GETARGPROP: + op = JSOP_GETARG; + break; + case JSOP_GETLOCALPROP: + op = JSOP_GETLOCAL; + break; + default: + LOCAL_ASSERT(0); + } + } + } + } + + saveop = op; + if (op >= JSOP_LIMIT) { + switch (op) { + case JSOP_GETPROP2: + saveop = JSOP_GETPROP; + break; + case JSOP_GETELEM2: + saveop = JSOP_GETELEM; + break; + default:; + } + } + LOCAL_ASSERT(js_CodeSpec[saveop].length == oplen || + JOF_TYPE(format) == JOF_SLOTATOM); + + jp->dvgfence = NULL; + } + + if (token) { + switch (nuses) { + case 2: + sn = js_GetSrcNote(jp->script, pc); + if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) { + /* + * Avoid over-parenthesizing y in x op= y based on its + * expansion: x = x op y (replace y by z = w to see the + * problem). + */ + op = (JSOp) pc[oplen]; + rval = POP_STR(); + lval = POP_STR(); + /* Print only the right operand of the assignment-op. */ + todo = SprintCString(&ss->sprinter, rval); + op = saveop; + } else if (!inXML) { + rval = POP_STR_PREC(cs->prec + !!(cs->format & JOF_LEFTASSOC)); + lval = POP_STR_PREC(cs->prec + !(cs->format & JOF_LEFTASSOC)); + todo = Sprint(&ss->sprinter, "%s %s %s", + lval, token, rval); + } else { + /* In XML, just concatenate the two operands. */ + LOCAL_ASSERT(op == JSOP_ADD); + rval = POP_STR(); + lval = POP_STR(); + todo = Sprint(&ss->sprinter, ss_format, lval, rval); + } + break; + + case 1: + rval = POP_STR(); + todo = Sprint(&ss->sprinter, ss_format, token, rval); + break; + + case 0: + todo = SprintCString(&ss->sprinter, token); + break; + + default: + todo = -2; + break; + } + } else { + switch (op) { + case JSOP_NOP: + /* + * Check for a do-while loop, a for-loop with an empty + * initializer part, a labeled statement, a function + * definition, or try/finally. + */ + sn = js_GetSrcNote(jp->script, pc); + todo = -2; + switch (sn ? SN_TYPE(sn) : SRC_NULL) { + case SRC_WHILE: + ++pc; + tail = js_GetSrcNoteOffset(sn, 0) - 1; + LOCAL_ASSERT(pc[tail] == JSOP_IFNE || + pc[tail] == JSOP_IFNEX); + js_printf(jp, "\tdo {\n"); + jp->indent += 4; + DECOMPILE_CODE(pc, tail); + jp->indent -= 4; + js_printf(jp, "\t} while (%s);\n", POP_COND_STR()); + pc += tail; + len = js_CodeSpec[*pc].length; + todo = -2; + break; + + case SRC_FOR: + rval = ""; + + do_forloop: + JS_ASSERT(SN_TYPE(sn) == SRC_FOR); + + /* Skip the JSOP_NOP or JSOP_POP bytecode. */ + pc += JSOP_NOP_LENGTH; + + /* Get the cond, next, and loop-closing tail offsets. */ + cond = js_GetSrcNoteOffset(sn, 0); + next = js_GetSrcNoteOffset(sn, 1); + tail = js_GetSrcNoteOffset(sn, 2); + + /* + * If this loop has a condition, then pc points at a goto + * targeting the condition. + */ + pc2 = pc; + if (cond != tail) { + LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX); + pc2 += (*pc == JSOP_GOTO) ? JSOP_GOTO_LENGTH : JSOP_GOTOX_LENGTH; + } + LOCAL_ASSERT(tail + GetJumpOffset(pc+tail, pc+tail) == pc2 - pc); + + /* Print the keyword and the possibly empty init-part. */ + js_printf(jp, "\tfor (%s;", rval); + + if (cond != tail) { + /* Decompile the loop condition. */ + DECOMPILE_CODE(pc + cond, tail - cond); + js_printf(jp, " %s", POP_STR()); + } + + /* Need a semicolon whether or not there was a cond. */ + js_puts(jp, ";"); + + if (next != cond) { + /* + * Decompile the loop updater. It may end in a JSOP_POP + * that we skip; or in a JSOP_POPN that we do not skip, + * followed by a JSOP_NOP (skipped as if it's a POP). + * We cope with the difference between these two cases + * by checking for stack imbalance and popping if there + * is an rval. + */ + uintN saveTop = ss->top; + + DECOMPILE_CODE(pc + next, cond - next - JSOP_POP_LENGTH); + LOCAL_ASSERT(ss->top - saveTop <= 1U); + rval = (ss->top == saveTop) + ? ss->sprinter.base + ss->sprinter.offset + : POP_STR(); + js_printf(jp, " %s", rval); + } + + /* Do the loop body. */ + js_printf(jp, ") {\n"); + jp->indent += 4; + next -= pc2 - pc; + DECOMPILE_CODE(pc2, next); + jp->indent -= 4; + js_printf(jp, "\t}\n"); + + /* Set len so pc skips over the entire loop. */ + len = tail + js_CodeSpec[pc[tail]].length; + break; + + case SRC_LABEL: + GET_SOURCE_NOTE_ATOM(sn, atom); + jp->indent -= 4; + rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); + if (!rval) + return NULL; + RETRACT(&ss->sprinter, rval); + js_printf(jp, "\t%s:\n", rval); + jp->indent += 4; + break; + + case SRC_LABELBRACE: + GET_SOURCE_NOTE_ATOM(sn, atom); + rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); + if (!rval) + return NULL; + RETRACT(&ss->sprinter, rval); + js_printf(jp, "\t%s: {\n", rval); + jp->indent += 4; + break; + + case SRC_ENDBRACE: + jp->indent -= 4; + js_printf(jp, "\t}\n"); + break; + + case SRC_FUNCDEF: + fun = jp->script->getFunction(js_GetSrcNoteOffset(sn, 0)); + do_function: + js_puts(jp, "\n"); + jp2 = js_NewPrinter(cx, "nested_function", fun, + jp->indent, jp->pretty, jp->grouped, + jp->strict); + if (!jp2) + return NULL; + ok = js_DecompileFunction(jp2); + if (ok && jp2->sprinter.base) + js_puts(jp, jp2->sprinter.base); + js_DestroyPrinter(jp2); + if (!ok) + return NULL; + js_puts(jp, "\n\n"); + break; + + case SRC_BRACE: + js_printf(jp, "\t{\n"); + jp->indent += 4; + len = js_GetSrcNoteOffset(sn, 0); + DECOMPILE_CODE(pc + oplen, len - oplen); + jp->indent -= 4; + js_printf(jp, "\t}\n"); + break; + + default:; + } + break; + + case JSOP_PUSH: +#if JS_HAS_DESTRUCTURING + sn = js_GetSrcNote(jp->script, pc); + if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) { + pc = DecompileGroupAssignment(ss, pc, endpc, sn, &todo); + if (!pc) + return NULL; + LOCAL_ASSERT(*pc == JSOP_POPN); + len = oplen = JSOP_POPN_LENGTH; + goto end_groupassignment; + } +#endif + /* FALL THROUGH */ + + case JSOP_BINDNAME: + todo = Sprint(&ss->sprinter, ""); + break; + + case JSOP_TRY: + js_printf(jp, "\ttry {\n"); + jp->indent += 4; + todo = -2; + break; + + case JSOP_FINALLY: + jp->indent -= 4; + js_printf(jp, "\t} finally {\n"); + jp->indent += 4; + + /* + * We push push the pair of exception/restsub cookies to + * simulate the effects [gosub] or control transfer during + * exception capturing on the stack. + */ + todo = Sprint(&ss->sprinter, exception_cookie); + if (todo < 0 || !PushOff(ss, todo, op)) + return NULL; + todo = Sprint(&ss->sprinter, retsub_pc_cookie); + break; + + case JSOP_RETSUB: + rval = POP_STR(); + LOCAL_ASSERT(strcmp(rval, retsub_pc_cookie) == 0); + lval = POP_STR(); + LOCAL_ASSERT(strcmp(lval, exception_cookie) == 0); + todo = -2; + break; + + case JSOP_GOSUB: + case JSOP_GOSUBX: + /* + * JSOP_GOSUB and GOSUBX have no effect on the decompiler's + * string stack because the next op in bytecode order finds + * the stack balanced by a JSOP_RETSUB executed elsewhere. + */ + todo = -2; + break; + + case JSOP_POPN: + { + uintN newtop, oldtop; + + /* + * The compiler models operand stack depth and fixes the stack + * pointer on entry to a catch clause based on its depth model. + * The decompiler must match the code generator's model, which + * is why JSOP_FINALLY pushes a cookie that JSOP_RETSUB pops. + */ + oldtop = ss->top; + newtop = oldtop - GET_UINT16(pc); + LOCAL_ASSERT(newtop <= oldtop); + todo = -2; + + sn = js_GetSrcNote(jp->script, pc); + if (sn && SN_TYPE(sn) == SRC_HIDDEN) + break; +#if JS_HAS_DESTRUCTURING + if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) { + todo = Sprint(&ss->sprinter, "%s[] = [", + VarPrefix(sn)); + if (todo < 0) + return NULL; + for (uintN i = newtop; i < oldtop; i++) { + rval = OFF2STR(&ss->sprinter, ss->offsets[i]); + if (Sprint(&ss->sprinter, ss_format, + (i == newtop) ? "" : ", ", + (i == oldtop - 1 && *rval == '\0') + ? ", " : rval) < 0) { + return NULL; + } + } + if (SprintPut(&ss->sprinter, "]", 1) < 0) + return NULL; + + /* + * If this is an empty group assignment, we have no stack + * budget into which we can push our result string. Adjust + * ss->sprinter.offset so that our consumer can find the + * empty group assignment decompilation. + */ + if (newtop == oldtop) { + ss->sprinter.offset = todo; + } else { + /* + * Kill newtop before the end_groupassignment: label by + * retracting/popping early. Control will either jump + * to do_forloop: or do_letheadbody: or else break from + * our case JSOP_POPN: after the switch (*pc2) below. + */ + LOCAL_ASSERT(newtop < oldtop); + ss->sprinter.offset = GetOff(ss, newtop); + ss->top = newtop; + } + + end_groupassignment: + LOCAL_ASSERT(*pc == JSOP_POPN); + + /* + * Thread directly to the next opcode if we can, to handle + * the special cases of a group assignment in the first or + * last part of a for(;;) loop head, or in a let block or + * expression head. + * + * NB: todo at this point indexes space in ss->sprinter + * that is liable to be overwritten. The code below knows + * exactly how long rval lives, or else copies it down via + * SprintCString. + */ + rval = OFF2STR(&ss->sprinter, todo); + todo = -2; + pc2 = pc + oplen; + if (*pc2 == JSOP_NOP) { + sn = js_GetSrcNote(jp->script, pc2); + if (sn) { + if (SN_TYPE(sn) == SRC_FOR) { + op = JSOP_NOP; + pc = pc2; + goto do_forloop; + } + + if (SN_TYPE(sn) == SRC_DECL) { + if (ss->top == StackDepth(jp->script)) { + /* + * This must be an empty destructuring + * in the head of a let whose body block + * is also empty. + */ + pc = pc2 + JSOP_NOP_LENGTH; + len = js_GetSrcNoteOffset(sn, 0); + LOCAL_ASSERT(pc[len] == JSOP_LEAVEBLOCK); + js_printf(jp, "\tlet (%s) {\n", rval); + js_printf(jp, "\t}\n"); + break; + } + todo = SprintCString(&ss->sprinter, rval); + if (todo < 0 || !PushOff(ss, todo, JSOP_NOP)) + return NULL; + op = JSOP_POP; + pc = pc2 + JSOP_NOP_LENGTH; + goto do_letheadbody; + } + } else { + /* + * An unnannotated NOP following a POPN must be the + * third part of for(;;) loop head. If the POPN's + * immediate operand is 0, then we may have no slot + * on the sprint-stack in which to push our result + * string. In this case the result can be recovered + * at ss->sprinter.base + ss->sprinter.offset. + */ + if (GET_UINT16(pc) == 0) + break; + todo = SprintCString(&ss->sprinter, rval); + saveop = JSOP_NOP; + } + } + + /* + * If control flow reaches this point with todo still -2, + * just print rval as an expression statement. + */ + if (todo == -2) + js_printf(jp, "\t%s;\n", rval); + break; + } +#endif + if (newtop < oldtop) { + ss->sprinter.offset = GetOff(ss, newtop); + ss->top = newtop; + } + break; + } + + case JSOP_EXCEPTION: + /* The catch decompiler handles this op itself. */ + LOCAL_ASSERT(JS_FALSE); + break; + + case JSOP_POP: + /* + * By default, do not automatically parenthesize when popping + * a stacked expression decompilation. We auto-parenthesize + * only when JSOP_POP is annotated with SRC_PCDELTA, meaning + * comma operator. + */ + op = JSOP_POPV; + /* FALL THROUGH */ + + case JSOP_POPV: + sn = js_GetSrcNote(jp->script, pc); + switch (sn ? SN_TYPE(sn) : SRC_NULL) { + case SRC_FOR: + /* Force parens around 'in' expression at 'for' front. */ + if (ss->opcodes[ss->top-1] == JSOP_IN) + op = JSOP_LSH; + rval = POP_STR(); + todo = -2; + goto do_forloop; + + case SRC_PCDELTA: + /* Comma operator: use JSOP_POP for correct precedence. */ + op = JSOP_POP; + + /* Pop and save to avoid blowing stack depth budget. */ + lval = JS_strdup(cx, POP_STR()); + if (!lval) + return NULL; + + /* + * The offset tells distance to the end of the right-hand + * operand of the comma operator. + */ + done = pc + len; + pc += js_GetSrcNoteOffset(sn, 0); + len = 0; + + if (!Decompile(ss, done, pc - done, JSOP_POP)) { + cx->free((char *)lval); + return NULL; + } + + /* Pop Decompile result and print comma expression. */ + rval = POP_STR(); + todo = Sprint(&ss->sprinter, "%s, %s", lval, rval); + cx->free((char *)lval); + break; + + case SRC_HIDDEN: + /* Hide this pop, it's from a goto in a with or for/in. */ + todo = -2; + break; + + case SRC_DECL: + /* This pop is at the end of the let block/expr head. */ + pc += JSOP_POP_LENGTH; +#if JS_HAS_DESTRUCTURING + do_letheadbody: +#endif + len = js_GetSrcNoteOffset(sn, 0); + if (pc[len] == JSOP_LEAVEBLOCK) { + js_printf(jp, "\tlet (%s) {\n", POP_STR()); + jp->indent += 4; + DECOMPILE_CODE(pc, len); + jp->indent -= 4; + js_printf(jp, "\t}\n"); + todo = -2; + } else { + LOCAL_ASSERT(pc[len] == JSOP_LEAVEBLOCKEXPR); + + lval = JS_strdup(cx, PopStr(ss, JSOP_NOP)); + if (!lval) + return NULL; + + /* Set saveop to reflect what we will push. */ + saveop = JSOP_LEAVEBLOCKEXPR; + if (!Decompile(ss, pc, len, saveop)) { + cx->free((char *)lval); + return NULL; + } + rval = PopStr(ss, JSOP_SETNAME); + todo = Sprint(&ss->sprinter, + (*rval == '{') + ? "let (%s) (%s)" + : "let (%s) %s", + lval, rval); + cx->free((char *)lval); + } + break; + + default: + /* Turn off parens around a yield statement. */ + if (ss->opcodes[ss->top-1] == JSOP_YIELD) + op = JSOP_NOP; + + rval = POP_STR(); + + /* + * Don't emit decompiler-pushed strings that are not + * handled by other opcodes. They are pushed onto the + * stack to help model the interpreter stack and should + * not appear in the decompiler's output. + */ + if (*rval != '\0' && (rval[0] != '/' || rval[1] != '*')) { + js_printf(jp, + (*rval == '{' || + (strncmp(rval, js_function_str, 8) == 0 && + rval[8] == ' ')) + ? "\t(%s);\n" + : "\t%s;\n", + rval); + } else { + LOCAL_ASSERT(*rval == '\0' || + strcmp(rval, exception_cookie) == 0); + } + todo = -2; + break; + } + sn = NULL; + break; + + case JSOP_ENTERWITH: + LOCAL_ASSERT(!js_GetSrcNote(jp->script, pc)); + rval = POP_STR(); + js_printf(jp, "\twith (%s) {\n", rval); + jp->indent += 4; + todo = Sprint(&ss->sprinter, with_cookie); + break; + + case JSOP_LEAVEWITH: + sn = js_GetSrcNote(jp->script, pc); + todo = -2; + if (sn && SN_TYPE(sn) == SRC_HIDDEN) + break; + rval = POP_STR(); + LOCAL_ASSERT(strcmp(rval, with_cookie) == 0); + jp->indent -= 4; + js_printf(jp, "\t}\n"); + break; + + case JSOP_ENTERBLOCK: + { + JSAtom **atomv, *smallv[5]; + JSScopeProperty *sprop; + + LOAD_OBJECT(0); + argc = OBJ_BLOCK_COUNT(cx, obj); + if ((size_t)argc <= JS_ARRAY_LENGTH(smallv)) { + atomv = smallv; + } else { + atomv = (JSAtom **) cx->malloc(argc * sizeof(JSAtom *)); + if (!atomv) + return NULL; + } + + MUST_FLOW_THROUGH("enterblock_out"); +#define LOCAL_ASSERT_OUT(expr) LOCAL_ASSERT_CUSTOM(expr, ok = JS_FALSE; \ + goto enterblock_out) + for (sprop = OBJ_SCOPE(obj)->lastProperty(); sprop; + sprop = sprop->parent) { + if (!(sprop->flags & SPROP_HAS_SHORTID)) + continue; + LOCAL_ASSERT_OUT(sprop->shortid < argc); + atomv[sprop->shortid] = JSID_TO_ATOM(sprop->id); + } + ok = JS_TRUE; + for (i = 0; i < argc; i++) { + atom = atomv[i]; + rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); + if (!rval || + !PushOff(ss, STR2OFF(&ss->sprinter, rval), op)) { + ok = JS_FALSE; + goto enterblock_out; + } + } + + sn = js_GetSrcNote(jp->script, pc); + switch (sn ? SN_TYPE(sn) : SRC_NULL) { +#if JS_HAS_BLOCK_SCOPE + case SRC_BRACE: + js_printf(jp, "\t{\n"); + jp->indent += 4; + len = js_GetSrcNoteOffset(sn, 0); + ok = Decompile(ss, pc + oplen, len - oplen, JSOP_NOP) + != NULL; + if (!ok) + goto enterblock_out; + jp->indent -= 4; + js_printf(jp, "\t}\n"); + break; +#endif + + case SRC_CATCH: + jp->indent -= 4; + js_printf(jp, "\t} catch ("); + + pc2 = pc; + pc += oplen; + LOCAL_ASSERT_OUT(*pc == JSOP_EXCEPTION); + pc += JSOP_EXCEPTION_LENGTH; + todo = Sprint(&ss->sprinter, exception_cookie); + if (todo < 0 || !PushOff(ss, todo, JSOP_EXCEPTION)) { + ok = JS_FALSE; + goto enterblock_out; + } + + if (*pc == JSOP_DUP) { + sn2 = js_GetSrcNote(jp->script, pc); + if (!sn2 || SN_TYPE(sn2) != SRC_DESTRUCT) { + /* + * This is a dup to save the exception for later. + * It is emitted only when the catch head contains + * an exception guard. + */ + LOCAL_ASSERT_OUT(js_GetSrcNoteOffset(sn, 0) != 0); + pc += JSOP_DUP_LENGTH; + todo = Sprint(&ss->sprinter, exception_cookie); + if (todo < 0 || + !PushOff(ss, todo, JSOP_EXCEPTION)) { + ok = JS_FALSE; + goto enterblock_out; + } + } + } + +#if JS_HAS_DESTRUCTURING + if (*pc == JSOP_DUP) { + pc = DecompileDestructuring(ss, pc, endpc); + if (!pc) { + ok = JS_FALSE; + goto enterblock_out; + } + LOCAL_ASSERT_OUT(*pc == JSOP_POP); + pc += JSOP_POP_LENGTH; + lval = PopStr(ss, JSOP_NOP); + js_puts(jp, lval); + } else { +#endif + LOCAL_ASSERT_OUT(*pc == JSOP_SETLOCALPOP); + i = GET_SLOTNO(pc) - jp->script->nfixed; + pc += JSOP_SETLOCALPOP_LENGTH; + atom = atomv[i - OBJ_BLOCK_DEPTH(cx, obj)]; + str = ATOM_TO_STRING(atom); + if (!QuoteString(&jp->sprinter, str, 0)) { + ok = JS_FALSE; + goto enterblock_out; + } +#if JS_HAS_DESTRUCTURING + } +#endif + + /* + * Pop the exception_cookie (or its dup in the case of a + * guarded catch head) off the stack now. + */ + rval = PopStr(ss, JSOP_NOP); + LOCAL_ASSERT_OUT(strcmp(rval, exception_cookie) == 0); + + len = js_GetSrcNoteOffset(sn, 0); + if (len) { + len -= pc - pc2; + LOCAL_ASSERT_OUT(len > 0); + js_printf(jp, " if "); + ok = Decompile(ss, pc, len, JSOP_NOP) != NULL; + if (!ok) + goto enterblock_out; + js_printf(jp, "%s", POP_STR()); + pc += len; + LOCAL_ASSERT_OUT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX); + pc += js_CodeSpec[*pc].length; + } + + js_printf(jp, ") {\n"); + jp->indent += 4; + len = 0; + break; + default: + break; + } + + todo = -2; + +#undef LOCAL_ASSERT_OUT + enterblock_out: + if (atomv != smallv) + cx->free(atomv); + if (!ok) + return NULL; + } + break; + + case JSOP_LEAVEBLOCK: + case JSOP_LEAVEBLOCKEXPR: + { + uintN top, depth; + + sn = js_GetSrcNote(jp->script, pc); + todo = -2; + if (op == JSOP_LEAVEBLOCKEXPR) { + LOCAL_ASSERT(SN_TYPE(sn) == SRC_PCBASE); + rval = POP_STR(); + } else if (sn) { + LOCAL_ASSERT(op == JSOP_LEAVEBLOCK); + if (SN_TYPE(sn) == SRC_HIDDEN) + break; + + /* + * This JSOP_LEAVEBLOCK must be for a catch block. If sn's + * offset does not equal the model stack depth, there must + * be a copy of the exception value on the stack due to a + * catch guard (see above, the JSOP_ENTERBLOCK + SRC_CATCH + * case code). + */ + LOCAL_ASSERT(SN_TYPE(sn) == SRC_CATCH); + if ((uintN)js_GetSrcNoteOffset(sn, 0) != ss->top) { + LOCAL_ASSERT((uintN)js_GetSrcNoteOffset(sn, 0) + == ss->top - 1); + rval = POP_STR(); + LOCAL_ASSERT(strcmp(rval, exception_cookie) == 0); + } + } + top = ss->top; + depth = GET_UINT16(pc); + LOCAL_ASSERT(top >= depth); + top -= depth; + ss->top = top; + ss->sprinter.offset = GetOff(ss, top); + if (op == JSOP_LEAVEBLOCKEXPR) + todo = SprintCString(&ss->sprinter, rval); + break; + } + + case JSOP_GETUPVAR: + case JSOP_CALLUPVAR: + case JSOP_GETUPVAR_DBG: + case JSOP_CALLUPVAR_DBG: + case JSOP_GETDSLOT: + case JSOP_CALLDSLOT: + { + if (!jp->fun) { + JS_ASSERT(jp->script->savedCallerFun); + jp->fun = jp->script->getFunction(0); + } + + if (!jp->localNames) + jp->localNames = js_GetLocalNameArray(cx, jp->fun, &jp->pool); + + uintN index = GET_UINT16(pc); + if (index < jp->fun->u.i.nupvars) { + index += jp->fun->countArgsAndVars(); + } else { + JSUpvarArray *uva; +#ifdef DEBUG + /* + * We must be in an eval called from jp->fun, where + * jp->script is the eval-compiled script. + * + * However, it's possible that a js_Invoke already + * pushed a frame trying to call js_Construct on an + * object that's not a constructor, causing us to be + * called with an intervening frame on the stack. + */ + JSStackFrame *fp = js_GetTopStackFrame(cx); + if (fp) { + while (!(fp->flags & JSFRAME_EVAL)) + fp = fp->down; + JS_ASSERT(fp->script == jp->script); + JS_ASSERT(fp->down->fun == jp->fun); + JS_ASSERT(FUN_INTERPRETED(jp->fun)); + JS_ASSERT(jp->script != jp->fun->u.i.script); + JS_ASSERT(jp->script->upvarsOffset != 0); + } +#endif + uva = jp->script->upvars(); + index = UPVAR_FRAME_SLOT(uva->vector[index]); + } + atom = GetArgOrVarAtom(jp, index); + goto do_name; + } + + case JSOP_CALLLOCAL: + case JSOP_GETLOCAL: + if (IsVarSlot(jp, pc, &i)) { + atom = GetArgOrVarAtom(jp, i); + LOCAL_ASSERT(atom); + goto do_name; + } + LOCAL_ASSERT((uintN)i < ss->top); + sn = js_GetSrcNote(jp->script, pc); + +#if JS_HAS_DESTRUCTURING + if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) { + /* + * Distinguish a js_DecompileValueGenerator call that + * targets op alone, from decompilation of a full group + * assignment sequence, triggered by SRC_GROUPASSIGN + * annotating the first JSOP_GETLOCAL in the sequence. + */ + if (endpc - pc > JSOP_GETLOCAL_LENGTH || pc > startpc) { + pc = DecompileGroupAssignment(ss, pc, endpc, sn, &todo); + if (!pc) + return NULL; + LOCAL_ASSERT(*pc == JSOP_POPN); + len = oplen = JSOP_POPN_LENGTH; + goto end_groupassignment; + } + + /* Null sn to prevent bogus VarPrefix'ing below. */ + sn = NULL; + } +#endif + + rval = GetLocal(ss, i); + todo = Sprint(&ss->sprinter, ss_format, VarPrefix(sn), rval); + break; + + case JSOP_SETLOCAL: + case JSOP_SETLOCALPOP: + if (IsVarSlot(jp, pc, &i)) { + atom = GetArgOrVarAtom(jp, i); + LOCAL_ASSERT(atom); + goto do_setname; + } + lval = GetLocal(ss, i); + rval = POP_STR(); + goto do_setlval; + + case JSOP_INCLOCAL: + case JSOP_DECLOCAL: + if (IsVarSlot(jp, pc, &i)) { + atom = GetArgOrVarAtom(jp, i); + LOCAL_ASSERT(atom); + goto do_incatom; + } + lval = GetLocal(ss, i); + goto do_inclval; + + case JSOP_LOCALINC: + case JSOP_LOCALDEC: + if (IsVarSlot(jp, pc, &i)) { + atom = GetArgOrVarAtom(jp, i); + LOCAL_ASSERT(atom); + goto do_atominc; + } + lval = GetLocal(ss, i); + goto do_lvalinc; + + case JSOP_RETRVAL: + todo = -2; + break; + + case JSOP_RETURN: + LOCAL_ASSERT(jp->fun); + fun = jp->fun; + if (fun->flags & JSFUN_EXPR_CLOSURE) { + /* Turn on parens around comma-expression here. */ + op = JSOP_SETNAME; + rval = POP_STR(); + js_printf(jp, (*rval == '{') ? "(%s)%s" : ss_format, + rval, + ((fun->flags & JSFUN_LAMBDA) || !fun->atom) + ? "" + : ";"); + todo = -2; + break; + } + /* FALL THROUGH */ + + case JSOP_SETRVAL: + rval = POP_STR(); + if (*rval != '\0') + js_printf(jp, "\t%s %s;\n", js_return_str, rval); + else + js_printf(jp, "\t%s;\n", js_return_str); + todo = -2; + break; + +#if JS_HAS_GENERATORS + case JSOP_YIELD: +#if JS_HAS_GENERATOR_EXPRS + if (!ss->inGenExp || !(sn = js_GetSrcNote(jp->script, pc))) +#endif + { + /* Turn off most parens. */ + op = JSOP_SETNAME; + rval = POP_STR(); + todo = (*rval != '\0') + ? Sprint(&ss->sprinter, + (strncmp(rval, js_yield_str, 5) == 0 && + (rval[5] == ' ' || rval[5] == '\0')) + ? "%s (%s)" + : "%s %s", + js_yield_str, rval) + : SprintCString(&ss->sprinter, js_yield_str); + break; + } + +#if JS_HAS_GENERATOR_EXPRS + LOCAL_ASSERT(SN_TYPE(sn) == SRC_HIDDEN); + /* FALL THROUGH */ +#endif + + case JSOP_ARRAYPUSH: + { + uintN pos, forpos; + ptrdiff_t start; + + /* Turn off most parens. */ + op = JSOP_SETNAME; + + /* Pop the expression being pushed or yielded. */ + rval = POP_STR(); + + /* + * Skip the for loop head stacked by JSOP_FORLOCAL until we hit + * a block local slot (note empty destructuring patterns result + * in unit-count blocks). + */ + pos = ss->top; + while (pos != 0) { + op = (JSOp) ss->opcodes[--pos]; + if (op == JSOP_ENTERBLOCK) + break; + } + JS_ASSERT(op == JSOP_ENTERBLOCK); + + /* + * Here, forpos must index the space before the left-most |for| + * in the single string of accumulated |for| heads and optional + * final |if (condition)|. + */ + forpos = pos + 2; + LOCAL_ASSERT(forpos < ss->top); + + /* + * Now move pos downward over the block's local slots. Even an + * empty destructuring pattern has one (dummy) local. + */ + while (ss->opcodes[pos] == JSOP_ENTERBLOCK) { + if (pos == 0) + break; + --pos; + } + +#if JS_HAS_GENERATOR_EXPRS + if (saveop == JSOP_YIELD) { + /* + * Generator expression: decompile just rval followed by + * the string starting at forpos. Leave the result string + * in ss->offsets[0] so it can be recovered by our caller + * (the JSOP_ANONFUNOBJ with SRC_GENEXP case). Bump the + * top of stack to balance yield, which is an expression + * (so has neutral stack balance). + */ + LOCAL_ASSERT(pos == 0); + xval = OFF2STR(&ss->sprinter, ss->offsets[forpos]); + ss->sprinter.offset = PAREN_SLOP; + todo = Sprint(&ss->sprinter, ss_format, rval, xval); + if (todo < 0) + return NULL; + ss->offsets[0] = todo; + ++ss->top; + return pc; + } +#endif /* JS_HAS_GENERATOR_EXPRS */ + + /* + * Array comprehension: retract the sprinter to the beginning + * of the array initialiser and decompile "[ for ...]". + */ + JS_ASSERT(jp->script->nfixed + pos == GET_UINT16(pc)); + LOCAL_ASSERT(ss->opcodes[pos] == JSOP_NEWINIT); + + start = ss->offsets[pos]; + LOCAL_ASSERT(ss->sprinter.base[start] == '[' || + ss->sprinter.base[start] == '#'); + LOCAL_ASSERT(forpos < ss->top); + xval = OFF2STR(&ss->sprinter, ss->offsets[forpos]); + lval = OFF2STR(&ss->sprinter, start); + RETRACT(&ss->sprinter, lval); + + todo = Sprint(&ss->sprinter, sss_format, lval, rval, xval); + if (todo < 0) + return NULL; + ss->offsets[pos] = todo; + todo = -2; + break; + } +#endif /* JS_HAS_GENERATORS */ + + case JSOP_THROWING: + todo = -2; + break; + + case JSOP_THROW: + sn = js_GetSrcNote(jp->script, pc); + todo = -2; + if (sn && SN_TYPE(sn) == SRC_HIDDEN) + break; + rval = POP_STR(); + js_printf(jp, "\t%s %s;\n", js_throw_str, rval); + break; + + case JSOP_ITER: + foreach = (pc[1] & (JSITER_FOREACH | JSITER_KEYVALUE)) == + JSITER_FOREACH; + todo = SprintCString(&ss->sprinter, iter_cookie); + break; + + case JSOP_NEXTITER: + JS_NOT_REACHED("JSOP_NEXTITER"); + break; + + case JSOP_ENDITER: + sn = js_GetSrcNote(jp->script, pc); + todo = -2; + if (sn && SN_TYPE(sn) == SRC_HIDDEN) + break; + (void) PopOff(ss, op); + (void) PopOff(ss, op); + break; + + case JSOP_GOTO: + case JSOP_GOTOX: + sn = js_GetSrcNote(jp->script, pc); + switch (sn ? SN_TYPE(sn) : SRC_NULL) { + case SRC_FOR_IN: + /* + * The loop back-edge carries +1 stack balance, for the + * flag processed by JSOP_IFNE. We do not decompile the + * JSOP_IFNE, and instead push the left-hand side of 'in' + * after the loop edge in this stack slot (the JSOP_FOR* + * opcodes' decompilers do this pushing). + */ + cond = GetJumpOffset(pc, pc); + next = js_GetSrcNoteOffset(sn, 0); + tail = js_GetSrcNoteOffset(sn, 1); + JS_ASSERT(pc[cond] == JSOP_NEXTITER); + DECOMPILE_CODE(pc + oplen, next - oplen); + lval = POP_STR(); + LOCAL_ASSERT(ss->top >= 2); + + if (ss->inArrayInit || ss->inGenExp) { + (void) PopOff(ss, JSOP_NOP); + rval = TOP_STR(); + if (ss->top >= 2 && ss->opcodes[ss->top - 2] == JSOP_FORLOCAL) { + ss->sprinter.offset = ss->offsets[ss->top - 1] - PAREN_SLOP; + if (Sprint(&ss->sprinter, " %s (%s in %s)", + foreach ? js_for_each_str : js_for_str, + lval, rval) < 0) { + return NULL; + } + + /* + * Do not AddParentSlop here, as we will push the + * top-most offset again, which will add paren slop + * for us. We must push to balance the stack budget + * when nesting for heads in a comprehension. + */ + todo = ss->offsets[ss->top - 1]; + } else { + todo = Sprint(&ss->sprinter, " %s (%s in %s)", + foreach ? js_for_each_str : js_for_str, + lval, rval); + } + if (todo < 0 || !PushOff(ss, todo, JSOP_FORLOCAL)) + return NULL; + DECOMPILE_CODE(pc + next, cond - next); + } else { + /* + * As above, rval or an extension of it must remain + * stacked during loop body decompilation. + */ + rval = GetStr(ss, ss->top - 2); + js_printf(jp, "\t%s (%s in %s) {\n", + foreach ? js_for_each_str : js_for_str, + lval, rval); + jp->indent += 4; + DECOMPILE_CODE(pc + next, cond - next); + jp->indent -= 4; + js_printf(jp, "\t}\n"); + } + + pc += tail; + LOCAL_ASSERT(*pc == JSOP_IFNE || *pc == JSOP_IFNEX); + len = js_CodeSpec[*pc].length; + break; + + case SRC_WHILE: + cond = GetJumpOffset(pc, pc); + tail = js_GetSrcNoteOffset(sn, 0); + DECOMPILE_CODE(pc + cond, tail - cond); + js_printf(jp, "\twhile (%s) {\n", POP_COND_STR()); + jp->indent += 4; + DECOMPILE_CODE(pc + oplen, cond - oplen); + jp->indent -= 4; + js_printf(jp, "\t}\n"); + pc += tail; + LOCAL_ASSERT(*pc == JSOP_IFNE || *pc == JSOP_IFNEX); + len = js_CodeSpec[*pc].length; + todo = -2; + break; + + case SRC_CONT2LABEL: + GET_SOURCE_NOTE_ATOM(sn, atom); + rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); + if (!rval) + return NULL; + RETRACT(&ss->sprinter, rval); + js_printf(jp, "\tcontinue %s;\n", rval); + break; + + case SRC_CONTINUE: + js_printf(jp, "\tcontinue;\n"); + break; + + case SRC_BREAK2LABEL: + GET_SOURCE_NOTE_ATOM(sn, atom); + rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); + if (!rval) + return NULL; + RETRACT(&ss->sprinter, rval); + js_printf(jp, "\tbreak %s;\n", rval); + break; + + case SRC_HIDDEN: + break; + + default: + js_printf(jp, "\tbreak;\n"); + break; + } + todo = -2; + break; + + case JSOP_IFEQ: + case JSOP_IFEQX: + { + JSBool elseif = JS_FALSE; + + if_again: + len = GetJumpOffset(pc, pc); + sn = js_GetSrcNote(jp->script, pc); + + switch (sn ? SN_TYPE(sn) : SRC_NULL) { + case SRC_IF: + case SRC_IF_ELSE: + rval = POP_COND_STR(); + if (ss->inArrayInit || ss->inGenExp) { + LOCAL_ASSERT(SN_TYPE(sn) == SRC_IF); + ss->sprinter.offset -= PAREN_SLOP; + if (Sprint(&ss->sprinter, " if (%s)", rval) < 0) + return NULL; + AddParenSlop(ss); + } else { + js_printf(jp, + elseif ? " if (%s) {\n" : "\tif (%s) {\n", + rval); + jp->indent += 4; + } + + if (SN_TYPE(sn) == SRC_IF) { + DECOMPILE_CODE(pc + oplen, len - oplen); + } else { + LOCAL_ASSERT(!ss->inArrayInit && !ss->inGenExp); + tail = js_GetSrcNoteOffset(sn, 0); + DECOMPILE_CODE(pc + oplen, tail - oplen); + jp->indent -= 4; + pc += tail; + LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX); + oplen = js_CodeSpec[*pc].length; + len = GetJumpOffset(pc, pc); + js_printf(jp, "\t} else"); + + /* + * If the second offset for sn is non-zero, it tells + * the distance from the goto around the else, to the + * ifeq for the if inside the else that forms an "if + * else if" chain. Thus cond spans the condition of + * the second if, so we simply decompile it and start + * over at label if_again. + */ + cond = js_GetSrcNoteOffset(sn, 1); + if (cond != 0) { + DECOMPILE_CODE(pc + oplen, cond - oplen); + pc += cond; + elseif = JS_TRUE; + goto if_again; + } + + js_printf(jp, " {\n"); + jp->indent += 4; + DECOMPILE_CODE(pc + oplen, len - oplen); + } + + if (!ss->inArrayInit && !ss->inGenExp) { + jp->indent -= 4; + js_printf(jp, "\t}\n"); + } + todo = -2; + break; + + case SRC_COND: + xval = JS_strdup(cx, POP_STR()); + if (!xval) + return NULL; + len = js_GetSrcNoteOffset(sn, 0); + DECOMPILE_CODE(pc + oplen, len - oplen); + lval = JS_strdup(cx, POP_STR()); + if (!lval) { + cx->free((void *)xval); + return NULL; + } + pc += len; + LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX); + oplen = js_CodeSpec[*pc].length; + len = GetJumpOffset(pc, pc); + DECOMPILE_CODE(pc + oplen, len - oplen); + rval = POP_STR(); + todo = Sprint(&ss->sprinter, "%s ? %s : %s", + xval, lval, rval); + cx->free((void *)xval); + cx->free((void *)lval); + break; + + default: + break; + } + break; + } + + case JSOP_IFNE: + case JSOP_IFNEX: + LOCAL_ASSERT(0); + break; + + case JSOP_OR: + case JSOP_ORX: + xval = "||"; + + do_logical_connective: + /* Top of stack is the first clause in a disjunction (||). */ + lval = JS_strdup(cx, POP_STR()); + if (!lval) + return NULL; + done = pc + GetJumpOffset(pc, pc); + pc += len; + len = done - pc; + if (!Decompile(ss, pc, len, op)) { + cx->free((char *)lval); + return NULL; + } + rval = POP_STR(); + if (jp->pretty && + jp->indent + 4 + strlen(lval) + 4 + strlen(rval) > 75) { + rval = JS_strdup(cx, rval); + if (!rval) { + tail = -1; + } else { + todo = Sprint(&ss->sprinter, "%s %s\n", lval, xval); + tail = Sprint(&ss->sprinter, "%*s%s", + jp->indent + 4, "", rval); + cx->free((char *)rval); + } + if (tail < 0) + todo = -1; + } else { + todo = Sprint(&ss->sprinter, "%s %s %s", lval, xval, rval); + } + cx->free((char *)lval); + break; + + case JSOP_AND: + case JSOP_ANDX: + xval = "&&"; + goto do_logical_connective; + + case JSOP_FORARG: + sn = NULL; + i = GET_ARGNO(pc); + goto do_forvarslot; + + case JSOP_FORLOCAL: + sn = js_GetSrcNote(jp->script, pc); + if (!IsVarSlot(jp, pc, &i)) { + JS_ASSERT(op == JSOP_FORLOCAL); + todo = Sprint(&ss->sprinter, ss_format, VarPrefix(sn), GetStr(ss, i)); + break; + } + + do_forvarslot: + atom = GetArgOrVarAtom(jp, i); + LOCAL_ASSERT(atom); + todo = SprintCString(&ss->sprinter, VarPrefix(sn)); + if (todo < 0 || !QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0)) + return NULL; + break; + + case JSOP_FORNAME: + LOAD_ATOM(0); + sn = js_GetSrcNote(jp->script, pc); + todo = SprintCString(&ss->sprinter, VarPrefix(sn)); + if (todo < 0 || !QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0)) + return NULL; + break; + + case JSOP_FORPROP: + xval = NULL; + LOAD_ATOM(0); + if (!ATOM_IS_IDENTIFIER(atom)) { + xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), + (jschar)'\''); + if (!xval) + return NULL; + } + lval = POP_STR(); + if (xval) { + JS_ASSERT(*lval); + todo = Sprint(&ss->sprinter, index_format, lval, xval); + } else { + todo = Sprint(&ss->sprinter, ss_format, lval, *lval ? "." : ""); + if (todo < 0) + return NULL; + if (!QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0)) + return NULL; + } + break; + + case JSOP_FORELEM: + todo = SprintCString(&ss->sprinter, forelem_cookie); + break; + + case JSOP_ENUMELEM: + case JSOP_ENUMCONSTELEM: + /* + * The stack has the object under the (top) index expression. + * The "rval" property id is underneath those two on the stack. + * The for loop body net and gross lengths can now be adjusted + * to account for the length of the indexing expression that + * came after JSOP_FORELEM and before JSOP_ENUMELEM. + */ + atom = NULL; + op = JSOP_NOP; /* turn off parens around xval */ + xval = POP_STR(); + op = JSOP_GETELEM; /* lval must have high precedence */ + lval = POP_STR(); + op = saveop; + rval = POP_STR(); + LOCAL_ASSERT(strcmp(rval, forelem_cookie) == 0); + if (*xval == '\0') { + todo = SprintCString(&ss->sprinter, lval); + } else { + todo = Sprint(&ss->sprinter, + (JOF_OPMODE(lastop) == JOF_XMLNAME) + ? dot_format + : index_format, + lval, xval); + } + break; + +#if JS_HAS_GETTER_SETTER + case JSOP_GETTER: + case JSOP_SETTER: + todo = -2; + break; +#endif + + case JSOP_DUP2: + rval = GetStr(ss, ss->top-2); + todo = SprintCString(&ss->sprinter, rval); + if (todo < 0 || !PushOff(ss, todo, + (JSOp) ss->opcodes[ss->top-2])) { + return NULL; + } + /* FALL THROUGH */ + + case JSOP_DUP: +#if JS_HAS_DESTRUCTURING + sn = js_GetSrcNote(jp->script, pc); + if (sn) { + LOCAL_ASSERT(SN_TYPE(sn) == SRC_DESTRUCT); + pc = DecompileDestructuring(ss, pc, endpc); + if (!pc) + return NULL; + len = 0; + lval = POP_STR(); + op = saveop = JSOP_ENUMELEM; + rval = POP_STR(); + + if (strcmp(rval, forelem_cookie) == 0) { + todo = Sprint(&ss->sprinter, ss_format, + VarPrefix(sn), lval); + + // Skip POP so the SRC_FOR_IN code can pop for itself. + if (*pc == JSOP_POP) + len = JSOP_POP_LENGTH; + } else { + todo = Sprint(&ss->sprinter, "%s%s = %s", + VarPrefix(sn), lval, rval); + } + break; + } +#endif + + rval = GetStr(ss, ss->top-1); + saveop = (JSOp) ss->opcodes[ss->top-1]; + todo = SprintCString(&ss->sprinter, rval); + break; + + case JSOP_SETARG: + atom = GetArgOrVarAtom(jp, GET_ARGNO(pc)); + LOCAL_ASSERT(atom); + goto do_setname; + + case JSOP_SETCONST: + case JSOP_SETNAME: + case JSOP_SETGVAR: + LOAD_ATOM(0); + + do_setname: + lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); + if (!lval) + return NULL; + rval = POP_STR(); + if (op == JSOP_SETNAME) + (void) PopOff(ss, op); + + do_setlval: + sn = js_GetSrcNote(jp->script, pc - 1); + if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) { + todo = Sprint(&ss->sprinter, "%s %s= %s", + lval, + (lastop == JSOP_GETTER) + ? js_getter_str + : (lastop == JSOP_SETTER) + ? js_setter_str + : CodeToken[lastop], + rval); + } else { + sn = js_GetSrcNote(jp->script, pc); + todo = Sprint(&ss->sprinter, "%s%s = %s", + VarPrefix(sn), lval, rval); + } + if (op == JSOP_SETLOCALPOP) { + if (!PushOff(ss, todo, saveop)) + return NULL; + rval = POP_STR(); + LOCAL_ASSERT(*rval != '\0'); + js_printf(jp, "\t%s;\n", rval); + todo = -2; + } + break; + + case JSOP_CONCATN: + { + argc = GET_UINT16(pc); + JS_ASSERT(argc > 0); + + js::Vector argv(cx); + if (!argv.resize(argc)) + return NULL; + + MUST_FLOW_THROUGH("out"); + ok = JS_FALSE; + + for (i = argc - 1; i >= 0; i--) { + argv[i] = JS_strdup(cx, POP_STR()); + if (!argv[i]) + goto out; + } + + todo = Sprint(&ss->sprinter, "%s", argv[0]); + if (todo < 0) + goto out; + for (i = 1; i < argc; i++) { + if (Sprint(&ss->sprinter, " + %s", argv[i]) < 0) + goto out; + } + + /* + * The only way that our next op could be a JSOP_ADD is + * if we are about to concatenate at least one non-string + * literal. Deal with that here in order to avoid extra + * parentheses (because JSOP_ADD is left-associative). + */ + if (pc[len] == JSOP_ADD) + saveop = JSOP_NOP; + + ok = JS_TRUE; + + out: + for (i = 0; i < argc; i++) + JS_free(cx, argv[i]); + if (!ok) + return NULL; + break; + } + + case JSOP_NEW: + case JSOP_CALL: + case JSOP_EVAL: + case JSOP_APPLY: + case JSOP_SETCALL: + argc = GET_ARGC(pc); + argv = (char **) + cx->malloc((size_t)(argc + 1) * sizeof *argv); + if (!argv) + return NULL; + + op = JSOP_SETNAME; + ok = JS_TRUE; + for (i = argc; i > 0; i--) + argv[i] = JS_strdup(cx, POP_STR()); + + /* Skip the JSOP_PUSHOBJ-created empty string. */ + LOCAL_ASSERT(ss->top >= 2); + (void) PopOff(ss, op); + + /* + * Special case: new (x(y)(z)) must be parenthesized like so. + * Same for new (x(y).z) -- contrast with new x(y).z. + * See PROPAGATE_CALLNESS. + */ + op = (JSOp) ss->opcodes[ss->top - 1]; + lval = PopStr(ss, + (saveop == JSOP_NEW && + (op == JSOP_CALL || + op == JSOP_EVAL || + op == JSOP_APPLY || + (js_CodeSpec[op].format & JOF_CALLOP))) + ? JSOP_NAME + : saveop); + op = saveop; + + argv[0] = JS_strdup(cx, lval); + if (!argv[0]) + ok = JS_FALSE; + + lval = "(", rval = ")"; + if (op == JSOP_NEW) { + if (argc == 0) + lval = rval = ""; + todo = Sprint(&ss->sprinter, "%s %s%s", + js_new_str, argv[0], lval); + } else { + todo = Sprint(&ss->sprinter, ss_format, + argv[0], lval); + } + if (todo < 0) + ok = JS_FALSE; + + for (i = 1; i <= argc; i++) { + if (!argv[i] || + Sprint(&ss->sprinter, ss_format, + argv[i], (i < argc) ? ", " : "") < 0) { + ok = JS_FALSE; + break; + } + } + if (Sprint(&ss->sprinter, rval) < 0) + ok = JS_FALSE; + + for (i = 0; i <= argc; i++) + cx->free(argv[i]); + cx->free(argv); + if (!ok) + return NULL; + if (op == JSOP_SETCALL) { + if (!PushOff(ss, todo, op)) + return NULL; + todo = Sprint(&ss->sprinter, ""); + } + break; + + case JSOP_DELNAME: + LOAD_ATOM(0); + lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); + if (!lval) + return NULL; + RETRACT(&ss->sprinter, lval); + do_delete_lval: + todo = Sprint(&ss->sprinter, "%s %s", js_delete_str, lval); + break; + + case JSOP_DELPROP: + GET_ATOM_QUOTE_AND_FMT("%s %s[%s]", "%s %s.%s", rval); + op = JSOP_GETPROP; + lval = POP_STR(); + todo = Sprint(&ss->sprinter, fmt, js_delete_str, lval, rval); + break; + + case JSOP_DELELEM: + op = JSOP_NOP; /* turn off parens */ + xval = POP_STR(); + op = JSOP_GETPROP; + lval = POP_STR(); + if (*xval == '\0') + goto do_delete_lval; + todo = Sprint(&ss->sprinter, + (JOF_OPMODE(lastop) == JOF_XMLNAME) + ? "%s %s.%s" + : "%s %s[%s]", + js_delete_str, lval, xval); + break; + +#if JS_HAS_XML_SUPPORT + case JSOP_DELDESC: + xval = POP_STR(); + op = JSOP_GETPROP; + lval = POP_STR(); + todo = Sprint(&ss->sprinter, "%s %s..%s", + js_delete_str, lval, xval); + break; +#endif + + case JSOP_TYPEOFEXPR: + case JSOP_TYPEOF: + case JSOP_VOID: + rval = POP_STR(); + todo = Sprint(&ss->sprinter, "%s %s", + (op == JSOP_VOID) ? js_void_str : js_typeof_str, + rval); + break; + + case JSOP_INCARG: + case JSOP_DECARG: + atom = GetArgOrVarAtom(jp, GET_ARGNO(pc)); + LOCAL_ASSERT(atom); + goto do_incatom; + + case JSOP_INCNAME: + case JSOP_DECNAME: + case JSOP_INCGVAR: + case JSOP_DECGVAR: + LOAD_ATOM(0); + do_incatom: + lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); + if (!lval) + return NULL; + RETRACT(&ss->sprinter, lval); + do_inclval: + todo = Sprint(&ss->sprinter, ss_format, + js_incop_strs[!(cs->format & JOF_INC)], lval); + break; + + case JSOP_INCPROP: + case JSOP_DECPROP: + GET_ATOM_QUOTE_AND_FMT(preindex_format, predot_format, rval); + + /* + * Force precedence below the numeric literal opcodes, so that + * 42..foo or 10000..toString(16), e.g., decompile with parens + * around the left-hand side of dot. + */ + op = JSOP_GETPROP; + lval = POP_STR(); + todo = Sprint(&ss->sprinter, fmt, + js_incop_strs[!(cs->format & JOF_INC)], + lval, rval); + break; + + case JSOP_INCELEM: + case JSOP_DECELEM: + op = JSOP_NOP; /* turn off parens */ + xval = POP_STR(); + op = JSOP_GETELEM; + lval = POP_STR(); + if (*xval != '\0') { + todo = Sprint(&ss->sprinter, + (JOF_OPMODE(lastop) == JOF_XMLNAME) + ? predot_format + : preindex_format, + js_incop_strs[!(cs->format & JOF_INC)], + lval, xval); + } else { + todo = Sprint(&ss->sprinter, ss_format, + js_incop_strs[!(cs->format & JOF_INC)], lval); + } + break; + + case JSOP_ARGINC: + case JSOP_ARGDEC: + atom = GetArgOrVarAtom(jp, GET_ARGNO(pc)); + LOCAL_ASSERT(atom); + goto do_atominc; + + case JSOP_NAMEINC: + case JSOP_NAMEDEC: + case JSOP_GVARINC: + case JSOP_GVARDEC: + LOAD_ATOM(0); + do_atominc: + lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); + if (!lval) + return NULL; + RETRACT(&ss->sprinter, lval); + do_lvalinc: + todo = Sprint(&ss->sprinter, ss_format, + lval, js_incop_strs[!(cs->format & JOF_INC)]); + break; + + case JSOP_PROPINC: + case JSOP_PROPDEC: + GET_ATOM_QUOTE_AND_FMT(postindex_format, postdot_format, rval); + + /* + * Force precedence below the numeric literal opcodes, so that + * 42..foo or 10000..toString(16), e.g., decompile with parens + * around the left-hand side of dot. + */ + op = JSOP_GETPROP; + lval = POP_STR(); + todo = Sprint(&ss->sprinter, fmt, lval, rval, + js_incop_strs[!(cs->format & JOF_INC)]); + break; + + case JSOP_ELEMINC: + case JSOP_ELEMDEC: + op = JSOP_NOP; /* turn off parens */ + xval = POP_STR(); + op = JSOP_GETELEM; + lval = POP_STR(); + if (*xval != '\0') { + todo = Sprint(&ss->sprinter, + (JOF_OPMODE(lastop) == JOF_XMLNAME) + ? postdot_format + : postindex_format, + lval, xval, + js_incop_strs[!(cs->format & JOF_INC)]); + } else { + todo = Sprint(&ss->sprinter, ss_format, + lval, js_incop_strs[!(cs->format & JOF_INC)]); + } + break; + + case JSOP_LENGTH: + fmt = dot_format; + rval = js_length_str; + goto do_getprop_lval; + + case JSOP_GETPROP2: + op = JSOP_GETPROP; + (void) PopOff(ss, lastop); + /* FALL THROUGH */ + + case JSOP_CALLPROP: + case JSOP_GETPROP: + case JSOP_GETXPROP: + LOAD_ATOM(0); + + do_getprop: + GET_QUOTE_AND_FMT(index_format, dot_format, rval); + do_getprop_lval: + PROPAGATE_CALLNESS(); + lval = POP_STR(); + todo = Sprint(&ss->sprinter, fmt, lval, rval); + break; + + case JSOP_GETTHISPROP: + LOAD_ATOM(0); + GET_QUOTE_AND_FMT(index_format, dot_format, rval); + todo = Sprint(&ss->sprinter, fmt, js_this_str, rval); + break; + + case JSOP_GETARGPROP: + /* Get the name of the argument or variable. */ + i = GET_ARGNO(pc); + + do_getarg_prop: + atom = GetArgOrVarAtom(ss->printer, i); + LOCAL_ASSERT(atom); + LOCAL_ASSERT(ATOM_IS_STRING(atom)); + lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); + if (!lval || !PushOff(ss, STR2OFF(&ss->sprinter, lval), op)) + return NULL; + + /* Get the name of the property. */ + LOAD_ATOM(ARGNO_LEN); + goto do_getprop; + + case JSOP_GETLOCALPROP: + if (IsVarSlot(jp, pc, &i)) + goto do_getarg_prop; + LOCAL_ASSERT((uintN)i < ss->top); + lval = GetLocal(ss, i); + if (!lval) + return NULL; + todo = SprintCString(&ss->sprinter, lval); + if (todo < 0 || !PushOff(ss, todo, op)) + return NULL; + LOAD_ATOM(2); + goto do_getprop; + + case JSOP_SETPROP: + case JSOP_SETMETHOD: + LOAD_ATOM(0); + GET_QUOTE_AND_FMT("%s[%s] %s= %s", "%s.%s %s= %s", xval); + rval = POP_STR(); + + /* + * Force precedence below the numeric literal opcodes, so that + * 42..foo or 10000..toString(16), e.g., decompile with parens + * around the left-hand side of dot. + */ + op = JSOP_GETPROP; + lval = POP_STR(); + sn = js_GetSrcNote(jp->script, pc - 1); + todo = Sprint(&ss->sprinter, fmt, lval, xval, + (sn && SN_TYPE(sn) == SRC_ASSIGNOP) + ? (lastop == JSOP_GETTER) + ? js_getter_str + : (lastop == JSOP_SETTER) + ? js_setter_str + : CodeToken[lastop] + : "", + rval); + break; + + case JSOP_GETELEM2: + op = JSOP_GETELEM; + (void) PopOff(ss, lastop); + /* FALL THROUGH */ + + case JSOP_CALLELEM: + case JSOP_GETELEM: + op = JSOP_NOP; /* turn off parens */ + xval = POP_STR(); + op = saveop; + PROPAGATE_CALLNESS(); + lval = POP_STR(); + if (*xval == '\0') { + todo = Sprint(&ss->sprinter, "%s", lval); + } else { + todo = Sprint(&ss->sprinter, + (JOF_OPMODE(lastop) == JOF_XMLNAME) + ? dot_format + : index_format, + lval, xval); + } + break; + + case JSOP_SETELEM: + rval = POP_STR(); + op = JSOP_NOP; /* turn off parens */ + xval = POP_STR(); + cs = &js_CodeSpec[ss->opcodes[ss->top]]; + op = JSOP_GETELEM; /* lval must have high precedence */ + lval = POP_STR(); + op = saveop; + if (*xval == '\0') + goto do_setlval; + sn = js_GetSrcNote(jp->script, pc - 1); + todo = Sprint(&ss->sprinter, + (JOF_MODE(cs->format) == JOF_XMLNAME) + ? "%s.%s %s= %s" + : "%s[%s] %s= %s", + lval, xval, + (sn && SN_TYPE(sn) == SRC_ASSIGNOP) + ? (lastop == JSOP_GETTER) + ? js_getter_str + : (lastop == JSOP_SETTER) + ? js_setter_str + : CodeToken[lastop] + : "", + rval); + break; + + case JSOP_ARGSUB: + i = (jsint) GET_ARGNO(pc); + todo = Sprint(&ss->sprinter, "%s[%d]", + js_arguments_str, (int) i); + break; + + case JSOP_ARGCNT: + todo = Sprint(&ss->sprinter, dot_format, + js_arguments_str, js_length_str); + break; + + case JSOP_CALLARG: + case JSOP_GETARG: + i = GET_ARGNO(pc); + atom = GetArgOrVarAtom(jp, i); +#if JS_HAS_DESTRUCTURING + if (!atom) { + todo = Sprint(&ss->sprinter, "%s[%d]", js_arguments_str, i); + break; + } +#else + LOCAL_ASSERT(atom); +#endif + goto do_name; + + case JSOP_CALLNAME: + case JSOP_NAME: + case JSOP_GETGVAR: + case JSOP_CALLGVAR: + LOAD_ATOM(0); + do_name: + lval = ""; +#if JS_HAS_XML_SUPPORT + do_qname: +#endif + sn = js_GetSrcNote(jp->script, pc); + rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), + inXML ? DONT_ESCAPE : 0); + if (!rval) + return NULL; + RETRACT(&ss->sprinter, rval); + todo = Sprint(&ss->sprinter, sss_format, + VarPrefix(sn), lval, rval); + break; + + case JSOP_UINT16: + i = (jsint) GET_UINT16(pc); + goto do_sprint_int; + + case JSOP_UINT24: + i = (jsint) GET_UINT24(pc); + goto do_sprint_int; + + case JSOP_INT8: + i = GET_INT8(pc); + goto do_sprint_int; + + case JSOP_INT32: + i = GET_INT32(pc); + do_sprint_int: + todo = Sprint(&ss->sprinter, "%d", i); + break; + + case JSOP_DOUBLE: + GET_DOUBLE_FROM_BYTECODE(jp->script, pc, 0, atom); + val = ATOM_KEY(atom); + LOCAL_ASSERT(JSVAL_IS_DOUBLE(val)); + todo = SprintDoubleValue(&ss->sprinter, val, &saveop); + break; + + case JSOP_STRING: + LOAD_ATOM(0); + rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), + inXML ? DONT_ESCAPE : '"'); + if (!rval) + return NULL; + todo = STR2OFF(&ss->sprinter, rval); + break; + + case JSOP_LAMBDA: + case JSOP_LAMBDA_FC: + case JSOP_LAMBDA_DBGFC: +#if JS_HAS_GENERATOR_EXPRS + sn = js_GetSrcNote(jp->script, pc); + if (sn && SN_TYPE(sn) == SRC_GENEXP) { + void *mark; + jsuword *innerLocalNames, *outerLocalNames; + JSScript *inner, *outer; + SprintStack ss2; + JSFunction *outerfun; + + LOAD_FUNCTION(0); + + /* + * All allocation when decompiling is LIFO, using malloc + * or, more commonly, arena-allocating from cx->tempPool. + * Therefore after InitSprintStack succeeds, we must + * release to mark before returning. + */ + mark = JS_ARENA_MARK(&cx->tempPool); + if (!fun->hasLocalNames()) { + innerLocalNames = NULL; + } else { + innerLocalNames = js_GetLocalNameArray(cx, fun, &cx->tempPool); + if (!innerLocalNames) + return NULL; + } + inner = fun->u.i.script; + if (!InitSprintStack(cx, &ss2, jp, StackDepth(inner))) { + JS_ARENA_RELEASE(&cx->tempPool, mark); + return NULL; + } + ss2.inGenExp = JS_TRUE; + + /* + * Recursively decompile this generator function as an + * un-parenthesized generator expression. The ss->inGenExp + * special case of JSOP_YIELD shares array comprehension + * decompilation code that leaves the result as the single + * string pushed on ss2. + */ + outer = jp->script; + outerfun = jp->fun; + outerLocalNames = jp->localNames; + LOCAL_ASSERT(JS_UPTRDIFF(pc, outer->code) <= outer->length); + jp->script = inner; + jp->fun = fun; + jp->localNames = innerLocalNames; + ok = Decompile(&ss2, inner->code, inner->length, JSOP_NOP) != NULL; + jp->script = outer; + jp->fun = outerfun; + jp->localNames = outerLocalNames; + if (!ok) { + JS_ARENA_RELEASE(&cx->tempPool, mark); + return NULL; + } + + /* + * Advance over this op and its global |this| push, and + * arrange to advance over the call to this lambda. + */ + pc += len; + LOCAL_ASSERT(*pc == JSOP_NULL); + pc += JSOP_NULL_LENGTH; + LOCAL_ASSERT(*pc == JSOP_CALL); + LOCAL_ASSERT(GET_ARGC(pc) == 0); + len = JSOP_CALL_LENGTH; + + /* + * Arrange to parenthesize this genexp unless: + * + * 1. It is the complete expression consumed by a control + * flow bytecode such as JSOP_TABLESWITCH whose syntax + * always parenthesizes the controlling expression. + * 2. It is the sole argument to a function call. + * + * But if this genexp runs up against endpc, parenthesize + * regardless. (This can happen if we are called from + * DecompileExpression or recursively from case + * JSOP_{NOP,AND,OR}.) + * + * There's no special case for |if (genexp)| because the + * compiler optimizes that to |if (true)|. + */ + pc2 = pc + len; + op = JSOp(*pc2); + if (op == JSOP_TRACE || op == JSOP_NOP) + pc2 += JSOP_NOP_LENGTH; + LOCAL_ASSERT(pc2 < endpc || + endpc < outer->code + outer->length); + LOCAL_ASSERT(ss2.top == 1); + ss2.opcodes[0] = JSOP_POP; + if (pc2 == endpc) { + op = JSOP_SETNAME; + } else { + op = (JSOp) *pc2; + op = ((js_CodeSpec[op].format & JOF_PARENHEAD) || + ((js_CodeSpec[op].format & JOF_INVOKE) && GET_ARGC(pc2) == 1)) + ? JSOP_POP + : JSOP_SETNAME; + + /* + * Stack this result as if it's a name and not an + * anonymous function, so it doesn't get decompiled as + * a generator function in a getter or setter context. + * The precedence level is the same for JSOP_NAME and + * JSOP_LAMBDA. + */ + LOCAL_ASSERT(js_CodeSpec[JSOP_NAME].prec == + js_CodeSpec[saveop].prec); + saveop = JSOP_NAME; + } + + /* + * Alas, we have to malloc a copy of the result left on + * the top of ss2 because both ss and ss2 arena-allocate + * from cx's tempPool. + */ + rval = JS_strdup(cx, PopStr(&ss2, op)); + JS_ARENA_RELEASE(&cx->tempPool, mark); + if (!rval) + return NULL; + todo = SprintCString(&ss->sprinter, rval); + cx->free((void *)rval); + break; + } +#endif /* JS_HAS_GENERATOR_EXPRS */ + /* FALL THROUGH */ + + LOAD_FUNCTION(0); + { + /* + * Always parenthesize expression closures. We can't force + * saveop to a low-precedence op to arrange for auto-magic + * parenthesization without confusing getter/setter code + * that checks for JSOP_LAMBDA. + */ + bool grouped = !(fun->flags & JSFUN_EXPR_CLOSURE); + bool strict = jp->script->strictModeCode; + str = js_DecompileToString(cx, "lambda", fun, 0, + false, grouped, strict, + js_DecompileFunction); + if (!str) + return NULL; + } + sprint_string: + todo = SprintString(&ss->sprinter, str); + break; + + case JSOP_CALLEE: + JS_ASSERT(jp->fun && jp->fun->atom); + todo = SprintString(&ss->sprinter, ATOM_TO_STRING(jp->fun->atom)); + break; + + case JSOP_OBJECT: + LOAD_OBJECT(0); + LOCAL_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_RegExpClass); + goto do_regexp; + + case JSOP_REGEXP: + GET_REGEXP_FROM_BYTECODE(jp->script, pc, 0, obj); + do_regexp: + if (!js_regexp_toString(cx, obj, &val)) + return NULL; + str = JSVAL_TO_STRING(val); + goto sprint_string; + + case JSOP_TABLESWITCH: + case JSOP_TABLESWITCHX: + { + ptrdiff_t jmplen, off, off2; + jsint j, n, low, high; + TableEntry *table, *tmp; + + sn = js_GetSrcNote(jp->script, pc); + LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH); + len = js_GetSrcNoteOffset(sn, 0); + jmplen = (op == JSOP_TABLESWITCH) ? JUMP_OFFSET_LEN + : JUMPX_OFFSET_LEN; + pc2 = pc; + off = GetJumpOffset(pc, pc2); + pc2 += jmplen; + low = GET_JUMP_OFFSET(pc2); + pc2 += JUMP_OFFSET_LEN; + high = GET_JUMP_OFFSET(pc2); + pc2 += JUMP_OFFSET_LEN; + + n = high - low + 1; + if (n == 0) { + table = NULL; + j = 0; + ok = JS_TRUE; + } else { + table = (TableEntry *) + cx->malloc((size_t)n * sizeof *table); + if (!table) + return NULL; + for (i = j = 0; i < n; i++) { + table[j].label = NULL; + off2 = GetJumpOffset(pc, pc2); + if (off2) { + sn = js_GetSrcNote(jp->script, pc2); + if (sn) { + LOCAL_ASSERT(SN_TYPE(sn) == SRC_LABEL); + GET_SOURCE_NOTE_ATOM(sn, table[j].label); + } + table[j].key = INT_TO_JSVAL(low + i); + table[j].offset = off2; + table[j].order = j; + j++; + } + pc2 += jmplen; + } + tmp = (TableEntry *) + cx->malloc((size_t)j * sizeof *table); + if (tmp) { + VOUCH_DOES_NOT_REQUIRE_STACK(); + ok = js_MergeSort(table, (size_t)j, sizeof(TableEntry), + CompareOffsets, NULL, tmp); + cx->free(tmp); + } else { + ok = JS_FALSE; + } + } + + if (ok) { + ok = DecompileSwitch(ss, table, (uintN)j, pc, len, off, + JS_FALSE); + } + cx->free(table); + if (!ok) + return NULL; + todo = -2; + break; + } + + case JSOP_LOOKUPSWITCH: + case JSOP_LOOKUPSWITCHX: + { + ptrdiff_t jmplen, off, off2; + jsatomid npairs, k; + TableEntry *table; + + sn = js_GetSrcNote(jp->script, pc); + LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH); + len = js_GetSrcNoteOffset(sn, 0); + jmplen = (op == JSOP_LOOKUPSWITCH) ? JUMP_OFFSET_LEN + : JUMPX_OFFSET_LEN; + pc2 = pc; + off = GetJumpOffset(pc, pc2); + pc2 += jmplen; + npairs = GET_UINT16(pc2); + pc2 += UINT16_LEN; + + table = (TableEntry *) + cx->malloc((size_t)npairs * sizeof *table); + if (!table) + return NULL; + for (k = 0; k < npairs; k++) { + sn = js_GetSrcNote(jp->script, pc2); + if (sn) { + LOCAL_ASSERT(SN_TYPE(sn) == SRC_LABEL); + GET_SOURCE_NOTE_ATOM(sn, table[k].label); + } else { + table[k].label = NULL; + } + JS_GET_SCRIPT_ATOM(jp->script, pc, GET_INDEX(pc2), atom); + pc2 += INDEX_LEN; + off2 = GetJumpOffset(pc, pc2); + pc2 += jmplen; + table[k].key = ATOM_KEY(atom); + table[k].offset = off2; + } + + ok = DecompileSwitch(ss, table, (uintN)npairs, pc, len, off, + JS_FALSE); + cx->free(table); + if (!ok) + return NULL; + todo = -2; + break; + } + + case JSOP_CONDSWITCH: + { + ptrdiff_t off, off2, caseOff; + jsint ncases; + TableEntry *table; + + sn = js_GetSrcNote(jp->script, pc); + LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH); + len = js_GetSrcNoteOffset(sn, 0); + off = js_GetSrcNoteOffset(sn, 1); + + /* + * Count the cases using offsets from switch to first case, + * and case to case, stored in srcnote immediates. + */ + pc2 = pc; + off2 = off; + for (ncases = 0; off2 != 0; ncases++) { + pc2 += off2; + LOCAL_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT || + *pc2 == JSOP_CASEX || *pc2 == JSOP_DEFAULTX); + if (*pc2 == JSOP_DEFAULT || *pc2 == JSOP_DEFAULTX) { + /* End of cases, but count default as a case. */ + off2 = 0; + } else { + sn = js_GetSrcNote(jp->script, pc2); + LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA); + off2 = js_GetSrcNoteOffset(sn, 0); + } + } + + /* + * Allocate table and rescan the cases using their srcnotes, + * stashing each case's delta from switch top in table[i].key, + * and the distance to its statements in table[i].offset. + */ + table = (TableEntry *) + cx->malloc((size_t)ncases * sizeof *table); + if (!table) + return NULL; + pc2 = pc; + off2 = off; + for (i = 0; i < ncases; i++) { + pc2 += off2; + LOCAL_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT || + *pc2 == JSOP_CASEX || *pc2 == JSOP_DEFAULTX); + caseOff = pc2 - pc; + table[i].key = INT_TO_JSVAL((jsint) caseOff); + table[i].offset = caseOff + GetJumpOffset(pc2, pc2); + if (*pc2 == JSOP_CASE || *pc2 == JSOP_CASEX) { + sn = js_GetSrcNote(jp->script, pc2); + LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA); + off2 = js_GetSrcNoteOffset(sn, 0); + } + } + + /* + * Find offset of default code by fetching the default offset + * from the end of table. JSOP_CONDSWITCH always has a default + * case at the end. + */ + off = JSVAL_TO_INT(table[ncases-1].key); + pc2 = pc + off; + off += GetJumpOffset(pc2, pc2); + + ok = DecompileSwitch(ss, table, (uintN)ncases, pc, len, off, + JS_TRUE); + cx->free(table); + if (!ok) + return NULL; + todo = -2; + break; + } + + case JSOP_CASE: + case JSOP_CASEX: + { + lval = POP_STR(); + if (!lval) + return NULL; + js_printf(jp, "\tcase %s:\n", lval); + todo = -2; + break; + } + + case JSOP_DEFFUN: + case JSOP_DEFFUN_FC: + case JSOP_DEFFUN_DBGFC: + LOAD_FUNCTION(0); + todo = -2; + goto do_function; + break; + + case JSOP_TRAP: + saveop = op = JS_GetTrapOpcode(cx, jp->script, pc); + *pc = op; + cs = &js_CodeSpec[op]; + len = cs->length; + DECOMPILE_CODE(pc, len); + *pc = JSOP_TRAP; + todo = -2; + break; + + case JSOP_HOLE: + todo = SprintPut(&ss->sprinter, "", 0); + break; + + case JSOP_NEWARRAY: + argc = GET_UINT16(pc); + LOCAL_ASSERT(ss->top >= (uintN) argc); + if (argc == 0) { + todo = SprintCString(&ss->sprinter, "[]"); + break; + } + + argv = (char **) cx->malloc(size_t(argc) * sizeof *argv); + if (!argv) + return NULL; + + op = JSOP_SETNAME; + ok = JS_TRUE; + i = argc; + while (i > 0) + argv[--i] = JS_strdup(cx, POP_STR()); + + todo = SprintCString(&ss->sprinter, "["); + if (todo < 0) + break; + + for (i = 0; i < argc; i++) { + if (!argv[i] || + Sprint(&ss->sprinter, ss_format, + argv[i], (i < argc - 1) ? ", " : "") < 0) { + ok = JS_FALSE; + break; + } + } + + for (i = 0; i < argc; i++) + cx->free(argv[i]); + cx->free(argv); + if (!ok) + return NULL; + + sn = js_GetSrcNote(jp->script, pc); + if (sn && SN_TYPE(sn) == SRC_CONTINUE && SprintCString(&ss->sprinter, ", ") < 0) + return NULL; + if (SprintCString(&ss->sprinter, "]") < 0) + return NULL; + break; + + case JSOP_NEWINIT: + { + i = GET_INT8(pc); + LOCAL_ASSERT(i == JSProto_Array || i == JSProto_Object); + + todo = ss->sprinter.offset; +#if JS_HAS_SHARP_VARS + op = (JSOp)pc[len]; + if (op == JSOP_SHARPINIT) + op = (JSOp)pc[len += JSOP_SHARPINIT_LENGTH]; + if (op == JSOP_DEFSHARP) { + pc += len; + cs = &js_CodeSpec[op]; + len = cs->length; + if (Sprint(&ss->sprinter, "#%u=", + (unsigned) (jsint) GET_UINT16(pc + UINT16_LEN)) + < 0) { + return NULL; + } + } +#endif /* JS_HAS_SHARP_VARS */ + if (i == JSProto_Array) { + ++ss->inArrayInit; + if (SprintCString(&ss->sprinter, "[") < 0) + return NULL; + } else { + if (SprintCString(&ss->sprinter, "{") < 0) + return NULL; + } + break; + } + + case JSOP_ENDINIT: + { + JSBool inArray; + + op = JSOP_NOP; /* turn off parens */ + rval = POP_STR(); + sn = js_GetSrcNote(jp->script, pc); + + /* Skip any #n= prefix to find the opening bracket. */ + for (xval = rval; *xval != '[' && *xval != '{'; xval++) + continue; + inArray = (*xval == '['); + if (inArray) + --ss->inArrayInit; + todo = Sprint(&ss->sprinter, "%s%s%c", + rval, + (sn && SN_TYPE(sn) == SRC_CONTINUE) ? ", " : "", + inArray ? ']' : '}'); + break; + } + + { + JSBool isFirst; + const char *maybeComma; + + case JSOP_INITELEM: + isFirst = (ss->opcodes[ss->top - 3] == JSOP_NEWINIT); + + /* Turn off most parens. */ + op = JSOP_SETNAME; + rval = POP_STR(); + + /* Turn off all parens for xval and lval, which we control. */ + op = JSOP_NOP; + xval = POP_STR(); + lval = POP_STR(); + sn = js_GetSrcNote(jp->script, pc); + + if (sn && SN_TYPE(sn) == SRC_INITPROP) { + atom = NULL; + goto do_initprop; + } + maybeComma = isFirst ? "" : ", "; + todo = Sprint(&ss->sprinter, sss_format, + lval, + maybeComma, + rval); + break; + + case JSOP_INITPROP: + case JSOP_INITMETHOD: + LOAD_ATOM(0); + xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), + (jschar) + (ATOM_IS_IDENTIFIER(atom) ? 0 : '\'')); + if (!xval) + return NULL; + isFirst = (ss->opcodes[ss->top - 2] == JSOP_NEWINIT); + rval = POP_STR(); + lval = POP_STR(); + /* fall through */ + + do_initprop: + maybeComma = isFirst ? "" : ", "; +#ifdef OLD_GETTER_SETTER + todo = Sprint(&ss->sprinter, "%s%s%s%s%s%s%s:%s", + lval, + maybeComma, + xval, + (lastop == JSOP_GETTER || lastop == JSOP_SETTER) + ? " " : "", + (lastop == JSOP_GETTER) ? js_getter_str : + (lastop == JSOP_SETTER) ? js_setter_str : + "", + rval); +#else + if (lastop == JSOP_GETTER || lastop == JSOP_SETTER) { + if (!atom || + !ATOM_IS_STRING(atom) || + !ATOM_IS_IDENTIFIER(atom) || + ATOM_IS_KEYWORD(atom) || + (ss->opcodes[ss->top+1] != JSOP_LAMBDA && + ss->opcodes[ss->top+1] != JSOP_LAMBDA_FC)) { + todo = Sprint(&ss->sprinter, "%s%s%s %s: %s", + lval, + maybeComma, + xval, + (lastop == JSOP_GETTER) ? js_getter_str : + (lastop == JSOP_SETTER) ? js_setter_str : + "", + rval); + } else { + const char *end = rval + strlen(rval); + + if (*rval == '(') + ++rval, --end; + LOCAL_ASSERT(strncmp(rval, js_function_str, 8) == 0); + LOCAL_ASSERT(rval[8] == ' '); + rval += 8 + 1; + LOCAL_ASSERT(*end ? *end == ')' : end[-1] == '}'); + todo = Sprint(&ss->sprinter, "%s%s%s %s%s%.*s", + lval, + maybeComma, + (lastop == JSOP_GETTER) + ? js_get_str : js_set_str, + xval, + (rval[0] != '(') ? " " : "", + end - rval, rval); + } + } else { + todo = Sprint(&ss->sprinter, "%s%s%s: %s", + lval, maybeComma, xval, rval); + } +#endif + break; + } + +#if JS_HAS_SHARP_VARS + case JSOP_DEFSHARP: + i = (jsint) GET_UINT16(pc + UINT16_LEN); + rval = POP_STR(); + todo = Sprint(&ss->sprinter, "#%u=%s", (unsigned) i, rval); + break; + + case JSOP_USESHARP: + i = (jsint) GET_UINT16(pc + UINT16_LEN); + todo = Sprint(&ss->sprinter, "#%u#", (unsigned) i); + break; +#endif /* JS_HAS_SHARP_VARS */ + +#if JS_HAS_DEBUGGER_KEYWORD + case JSOP_DEBUGGER: + js_printf(jp, "\tdebugger;\n"); + todo = -2; + break; +#endif /* JS_HAS_DEBUGGER_KEYWORD */ + +#if JS_HAS_XML_SUPPORT + case JSOP_STARTXML: + case JSOP_STARTXMLEXPR: + inXML = op == JSOP_STARTXML; + todo = -2; + break; + + case JSOP_DEFXMLNS: + rval = POP_STR(); + js_printf(jp, "\t%s %s %s = %s;\n", + js_default_str, js_xml_str, js_namespace_str, rval); + todo = -2; + break; + + case JSOP_ANYNAME: + if (pc[JSOP_ANYNAME_LENGTH] == JSOP_TOATTRNAME) { + len += JSOP_TOATTRNAME_LENGTH; + todo = SprintPut(&ss->sprinter, "@*", 2); + } else { + todo = SprintPut(&ss->sprinter, "*", 1); + } + break; + + case JSOP_QNAMEPART: + LOAD_ATOM(0); + if (pc[JSOP_QNAMEPART_LENGTH] == JSOP_TOATTRNAME) { + saveop = JSOP_TOATTRNAME; + len += JSOP_TOATTRNAME_LENGTH; + lval = "@"; + goto do_qname; + } + goto do_name; + + case JSOP_QNAMECONST: + LOAD_ATOM(0); + rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); + if (!rval) + return NULL; + RETRACT(&ss->sprinter, rval); + lval = POP_STR(); + todo = Sprint(&ss->sprinter, "%s::%s", lval, rval); + break; + + case JSOP_QNAME: + rval = POP_STR(); + lval = POP_STR(); + todo = Sprint(&ss->sprinter, "%s::[%s]", lval, rval); + break; + + case JSOP_TOATTRNAME: + op = JSOP_NOP; /* turn off parens */ + rval = POP_STR(); + todo = Sprint(&ss->sprinter, "@[%s]", rval); + break; + + case JSOP_TOATTRVAL: + todo = -2; + break; + + case JSOP_ADDATTRNAME: + rval = POP_STR(); + lval = POP_STR(); + todo = Sprint(&ss->sprinter, "%s %s", lval, rval); + /* This gets reset by all XML tag expressions. */ + quoteAttr = JS_TRUE; + break; + + case JSOP_ADDATTRVAL: + rval = POP_STR(); + lval = POP_STR(); + if (quoteAttr) + todo = Sprint(&ss->sprinter, "%s=\"%s\"", lval, rval); + else + todo = Sprint(&ss->sprinter, "%s=%s", lval, rval); + break; + + case JSOP_BINDXMLNAME: + /* Leave the name stacked and push a dummy string. */ + todo = Sprint(&ss->sprinter, ""); + break; + + case JSOP_SETXMLNAME: + /* Pop the r.h.s., the dummy string, and the name. */ + rval = POP_STR(); + (void) PopOff(ss, op); + lval = POP_STR(); + goto do_setlval; + + case JSOP_XMLELTEXPR: + case JSOP_XMLTAGEXPR: + todo = Sprint(&ss->sprinter, "{%s}", POP_STR()); + inXML = JS_TRUE; + /* If we're an attribute value, we shouldn't quote this. */ + quoteAttr = JS_FALSE; + break; + + case JSOP_TOXMLLIST: + op = JSOP_NOP; /* turn off parens */ + todo = Sprint(&ss->sprinter, "<>%s", POP_STR()); + inXML = JS_FALSE; + break; + + case JSOP_TOXML: + case JSOP_CALLXMLNAME: + case JSOP_XMLNAME: + case JSOP_FILTER: + /* These ops indicate the end of XML expressions. */ + inXML = JS_FALSE; + todo = -2; + break; + + case JSOP_ENDFILTER: + rval = POP_STR(); + PROPAGATE_CALLNESS(); + lval = POP_STR(); + todo = Sprint(&ss->sprinter, "%s.(%s)", lval, rval); + break; + + case JSOP_DESCENDANTS: + rval = POP_STR(); + PROPAGATE_CALLNESS(); + lval = POP_STR(); + todo = Sprint(&ss->sprinter, "%s..%s", lval, rval); + break; + + case JSOP_XMLOBJECT: + LOAD_OBJECT(0); + todo = Sprint(&ss->sprinter, "", obj); + break; + + case JSOP_XMLCDATA: + LOAD_ATOM(0); + todo = SprintPut(&ss->sprinter, "sprinter, ATOM_TO_STRING(atom), + DONT_ESCAPE)) + return NULL; + SprintPut(&ss->sprinter, "]]>", 3); + break; + + case JSOP_XMLCOMMENT: + LOAD_ATOM(0); + todo = SprintPut(&ss->sprinter, "", 3); + break; + + case JSOP_XMLPI: + LOAD_ATOM(0); + rval = JS_strdup(cx, POP_STR()); + if (!rval) + return NULL; + todo = SprintPut(&ss->sprinter, "sprinter, ATOM_TO_STRING(atom), 0) && + (*rval == '\0' || + (SprintPut(&ss->sprinter, " ", 1) >= 0 && + SprintCString(&ss->sprinter, rval))); + cx->free((char *)rval); + if (!ok) + return NULL; + SprintPut(&ss->sprinter, "?>", 2); + break; + + case JSOP_GETFUNNS: + todo = SprintPut(&ss->sprinter, js_function_str, 8); + break; +#endif /* JS_HAS_XML_SUPPORT */ + + default: + todo = -2; + break; + } + } + + if (todo < 0) { + /* -2 means "don't push", -1 means reported error. */ + if (todo == -1) + return NULL; + } else { + if (!PushOff(ss, todo, saveop)) + return NULL; + } + + if (cs->format & JOF_CALLOP) { + todo = Sprint(&ss->sprinter, ""); + if (todo < 0 || !PushOff(ss, todo, saveop)) + return NULL; + } + + pc += len; + } + +/* + * Undefine local macros. + */ +#undef inXML +#undef DECOMPILE_CODE +#undef NEXT_OP +#undef TOP_STR +#undef POP_STR +#undef POP_STR_PREC +#undef LOCAL_ASSERT +#undef ATOM_IS_IDENTIFIER +#undef GET_QUOTE_AND_FMT +#undef GET_ATOM_QUOTE_AND_FMT + + return pc; +} + +static JSBool +DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len, + uintN pcdepth) +{ + uintN depth, i; + SprintStack ss; + JSContext *cx; + void *mark; + JSBool ok; + JSScript *oldscript; + jsbytecode *oldcode, *oldmain, *code; + char *last; + + depth = StackDepth(script); + JS_ASSERT(pcdepth <= depth); + + /* Initialize a sprinter for use with the offset stack. */ + cx = jp->sprinter.context; + mark = JS_ARENA_MARK(&cx->tempPool); + ok = InitSprintStack(cx, &ss, jp, depth); + if (!ok) + goto out; + + /* + * If we are called from js_DecompileValueGenerator with a portion of + * script's bytecode that starts with a non-zero model stack depth given + * by pcdepth, attempt to initialize the missing string offsets in ss to + * |spindex| negative indexes from fp->sp for the activation fp in which + * the error arose. + * + * See js_DecompileValueGenerator for how its |spindex| parameter is used, + * and see also GetOff, which makes use of the ss.offsets[i] < -1 that are + * potentially stored below. + */ + ss.top = pcdepth; + if (pcdepth != 0) { + for (i = 0; i < pcdepth; i++) { + ss.offsets[i] = -2 - (ptrdiff_t)i; + ss.opcodes[i] = *jp->pcstack[i]; + } + } + + /* Call recursive subroutine to do the hard work. */ + oldscript = jp->script; + jp->script = script; + oldcode = jp->script->code; + oldmain = jp->script->main; + code = js_UntrapScriptCode(cx, jp->script); + if (code != oldcode) { + jp->script->code = code; + jp->script->main = code + (oldmain - oldcode); + pc = code + (pc - oldcode); + } + + ok = Decompile(&ss, pc, len, JSOP_NOP) != NULL; + if (code != oldcode) { + cx->free(jp->script->code); + jp->script->code = oldcode; + jp->script->main = oldmain; + } + jp->script = oldscript; + + /* If the given code didn't empty the stack, do it now. */ + if (ok && ss.top) { + do { + last = OFF2STR(&ss.sprinter, PopOff(&ss, JSOP_POP)); + } while (ss.top > pcdepth); + js_printf(jp, "%s", last); + } + +out: + /* Free all temporary stuff allocated under this call. */ + JS_ARENA_RELEASE(&cx->tempPool, mark); + return ok; +} + +JSBool +js_DecompileScript(JSPrinter *jp, JSScript *script) +{ + return DecompileCode(jp, script, script->code, (uintN)script->length, 0); +} + +JSString * +js_DecompileToString(JSContext *cx, const char *name, JSFunction *fun, + uintN indent, JSBool pretty, JSBool grouped, JSBool strict, + JSBool (*decompiler)(JSPrinter *jp)) +{ + JSPrinter *jp; + JSString *str; + + jp = js_NewPrinter(cx, name, fun, indent, pretty, grouped, strict); + if (!jp) + return NULL; + if (decompiler(jp)) + str = js_GetPrinterOutput(jp); + else + str = NULL; + js_DestroyPrinter(jp); + return str; +} + +static const char native_code_str[] = "\t[native code]\n"; + +JSBool +js_DecompileFunctionBody(JSPrinter *jp) +{ + JSScript *script; + + JS_ASSERT(jp->fun); + JS_ASSERT(!jp->script); + if (!FUN_INTERPRETED(jp->fun)) { + js_printf(jp, native_code_str); + return JS_TRUE; + } + + script = jp->fun->u.i.script; + return DecompileCode(jp, script, script->code, (uintN)script->length, 0); +} + +JSBool +js_DecompileFunction(JSPrinter *jp) +{ + JSFunction *fun; + uintN i; + JSAtom *param; + jsbytecode *pc, *endpc; + ptrdiff_t len; + JSBool ok; + + fun = jp->fun; + JS_ASSERT(fun); + JS_ASSERT(!jp->script); + + /* + * If pretty, conform to ECMA-262 Edition 3, 15.3.4.2, by decompiling a + * FunctionDeclaration. Otherwise, check the JSFUN_LAMBDA flag and force + * an expression by parenthesizing. + */ + if (jp->pretty) { + js_printf(jp, "\t"); + } else { + if (!jp->grouped && (fun->flags & JSFUN_LAMBDA)) + js_puts(jp, "("); + } + if (JSFUN_GETTER_TEST(fun->flags)) + js_printf(jp, "%s ", js_getter_str); + else if (JSFUN_SETTER_TEST(fun->flags)) + js_printf(jp, "%s ", js_setter_str); + + js_printf(jp, "%s ", js_function_str); + if (fun->atom && !QuoteString(&jp->sprinter, ATOM_TO_STRING(fun->atom), 0)) + return JS_FALSE; + js_puts(jp, "("); + + if (!FUN_INTERPRETED(fun)) { + js_printf(jp, ") {\n"); + jp->indent += 4; + js_printf(jp, native_code_str); + jp->indent -= 4; + js_printf(jp, "\t}"); + } else { + JSScript *script = fun->u.i.script; +#if JS_HAS_DESTRUCTURING + SprintStack ss; + void *mark; +#endif + + /* Print the parameters. */ + pc = script->main; + endpc = pc + script->length; + ok = JS_TRUE; + + /* Skip trace hint if it appears here. */ +#if JS_HAS_GENERATORS + if (js_GetOpcode(jp->sprinter.context, script, script->code) != JSOP_GENERATOR) +#endif + { + JSOp op = js_GetOpcode(jp->sprinter.context, script, pc); + if (op == JSOP_TRACE || op == JSOP_NOP) { + JS_STATIC_ASSERT(JSOP_TRACE_LENGTH == JSOP_NOP_LENGTH); + pc += JSOP_TRACE_LENGTH; + } else { + JS_ASSERT(op == JSOP_STOP); /* empty script singleton */ + } + } + +#if JS_HAS_DESTRUCTURING + ss.printer = NULL; + jp->script = script; + mark = JS_ARENA_MARK(&jp->sprinter.context->tempPool); +#endif + + for (i = 0; i < fun->nargs; i++) { + if (i > 0) + js_puts(jp, ", "); + + param = GetArgOrVarAtom(jp, i); + +#if JS_HAS_DESTRUCTURING +#define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, JS_FALSE) + + if (!param) { + ptrdiff_t todo; + const char *lval; + + LOCAL_ASSERT(*pc == JSOP_GETARG); + pc += JSOP_GETARG_LENGTH; + LOCAL_ASSERT(*pc == JSOP_DUP); + if (!ss.printer) { + ok = InitSprintStack(jp->sprinter.context, &ss, jp, StackDepth(script)); + if (!ok) + break; + } + pc = DecompileDestructuring(&ss, pc, endpc); + if (!pc) { + ok = JS_FALSE; + break; + } + LOCAL_ASSERT(*pc == JSOP_POP); + pc += JSOP_POP_LENGTH; + lval = PopStr(&ss, JSOP_NOP); + todo = SprintCString(&jp->sprinter, lval); + if (todo < 0) { + ok = JS_FALSE; + break; + } + continue; + } + +#undef LOCAL_ASSERT +#endif + + if (!QuoteString(&jp->sprinter, ATOM_TO_STRING(param), 0)) { + ok = JS_FALSE; + break; + } + } + +#if JS_HAS_DESTRUCTURING + jp->script = NULL; + JS_ARENA_RELEASE(&jp->sprinter.context->tempPool, mark); +#endif + if (!ok) + return JS_FALSE; + if (fun->flags & JSFUN_EXPR_CLOSURE) { + js_printf(jp, ") "); + if (fun->u.i.script->strictModeCode && !jp->strict) { + /* + * We have no syntax for strict function expressions; + * at least give a hint. + */ + js_printf(jp, "\t/* use strict */ \n"); + jp->strict = true; + } + + } else { + js_printf(jp, ") {\n"); + jp->indent += 4; + if (fun->u.i.script->strictModeCode && !jp->strict) { + js_printf(jp, "\t'use strict';\n"); + jp->strict = true; + } + } + + len = script->code + script->length - pc; + ok = DecompileCode(jp, script, pc, (uintN)len, 0); + if (!ok) + return JS_FALSE; + + if (!(fun->flags & JSFUN_EXPR_CLOSURE)) { + jp->indent -= 4; + js_printf(jp, "\t}"); + } + } + + if (!jp->pretty && !jp->grouped && (fun->flags & JSFUN_LAMBDA)) + js_puts(jp, ")"); + + return JS_TRUE; +} + +char * +js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v, + JSString *fallback) +{ + JSStackFrame *fp; + jsbytecode *pc; + JSScript *script; + JSFrameRegs *regs; + intN pcdepth; + jsval *sp, *stackBase; + char *name; + + JS_ASSERT(spindex < 0 || + spindex == JSDVG_IGNORE_STACK || + spindex == JSDVG_SEARCH_STACK); + + fp = js_GetScriptedCaller(cx, NULL); + if (!fp || !fp->regs || !fp->regs->sp) + goto do_fallback; + + script = fp->script; + regs = fp->regs; + pc = fp->imacpc ? fp->imacpc : regs->pc; + if (pc < script->main || script->code + script->length <= pc) { + JS_NOT_REACHED("bug"); + goto do_fallback; + } + + if (spindex != JSDVG_IGNORE_STACK) { + jsbytecode **pcstack; + + /* + * Prepare computing pcstack containing pointers to opcodes that + * populated interpreter's stack with its current content. + */ + pcstack = (jsbytecode **) + cx->malloc(StackDepth(script) * sizeof *pcstack); + if (!pcstack) + return NULL; + pcdepth = ReconstructPCStack(cx, script, pc, pcstack); + if (pcdepth < 0) + goto release_pcstack; + + if (spindex != JSDVG_SEARCH_STACK) { + JS_ASSERT(spindex < 0); + pcdepth += spindex; + if (pcdepth < 0) + goto release_pcstack; + pc = pcstack[pcdepth]; + } else { + /* + * We search from fp->sp to base to find the most recently + * calculated value matching v under assumption that it is + * it that caused exception, see bug 328664. + */ + stackBase = StackBase(fp); + sp = regs->sp; + do { + if (sp == stackBase) { + pcdepth = -1; + goto release_pcstack; + } + } while (*--sp != v); + + /* + * The value may have come from beyond stackBase + pcdepth, + * meaning that it came from a temporary slot that the + * interpreter uses for GC roots or when JSOP_APPLY extended + * the stack to fit the argument array elements. Only update pc + * if beneath stackBase + pcdepth; otherwise blame existing + * (current) PC. + */ + if (sp < stackBase + pcdepth) + pc = pcstack[sp - stackBase]; + } + + release_pcstack: + cx->free(pcstack); + if (pcdepth < 0) + goto do_fallback; + } + + { + jsbytecode* savepc = regs->pc; + jsbytecode* imacpc = fp->imacpc; + if (imacpc) { + regs->pc = imacpc; + fp->imacpc = NULL; + } + + /* + * FIXME: bug 489843. Stack reconstruction may have returned a pc + * value *inside* an imacro; this would confuse the decompiler. + */ + if (imacpc && size_t(pc - script->code) >= script->length) + name = FAILED_EXPRESSION_DECOMPILER; + else + name = DecompileExpression(cx, script, fp->fun, pc); + + if (imacpc) { + regs->pc = savepc; + fp->imacpc = imacpc; + } + } + if (name != FAILED_EXPRESSION_DECOMPILER) + return name; + + do_fallback: + if (!fallback) { + fallback = js_ValueToSource(cx, v); + if (!fallback) + return NULL; + } + return js_DeflateString(cx, fallback->chars(), fallback->length()); +} + +static char * +DecompileExpression(JSContext *cx, JSScript *script, JSFunction *fun, + jsbytecode *pc) +{ + jsbytecode *code, *oldcode, *oldmain; + JSOp op; + const JSCodeSpec *cs; + jsbytecode *begin, *end; + jssrcnote *sn; + ptrdiff_t len; + jsbytecode **pcstack; + intN pcdepth; + JSPrinter *jp; + char *name; + + JS_ASSERT(script->main <= pc && pc < script->code + script->length); + + pcstack = NULL; + oldcode = script->code; + oldmain = script->main; + + MUST_FLOW_THROUGH("out"); + code = js_UntrapScriptCode(cx, script); + if (code != oldcode) { + script->code = code; + script->main = code + (oldmain - oldcode); + pc = code + (pc - oldcode); + } + + op = (JSOp) *pc; + + /* None of these stack-writing ops generates novel values. */ + JS_ASSERT(op != JSOP_CASE && op != JSOP_CASEX && + op != JSOP_DUP && op != JSOP_DUP2); + + /* JSOP_PUSH is used to generate undefined for group assignment holes. */ + if (op == JSOP_PUSH) { + name = JS_strdup(cx, js_undefined_str); + goto out; + } + + /* + * |this| could convert to a very long object initialiser, so cite it by + * its keyword name instead. + */ + if (op == JSOP_THIS) { + name = JS_strdup(cx, js_this_str); + goto out; + } + + /* + * JSOP_BINDNAME is special: it generates a value, the base object of a + * reference. But if it is the generating op for a diagnostic produced by + * js_DecompileValueGenerator, the name being bound is irrelevant. Just + * fall back to the base object. + */ + if (op == JSOP_BINDNAME) { + name = FAILED_EXPRESSION_DECOMPILER; + goto out; + } + + /* NAME ops are self-contained, others require left or right context. */ + cs = &js_CodeSpec[op]; + begin = pc; + end = pc + cs->length; + switch (JOF_MODE(cs->format)) { + case JOF_PROP: + case JOF_ELEM: + case JOF_XMLNAME: + case 0: + sn = js_GetSrcNote(script, pc); + if (!sn) { + name = FAILED_EXPRESSION_DECOMPILER; + goto out; + } + switch (SN_TYPE(sn)) { + case SRC_PCBASE: + begin -= js_GetSrcNoteOffset(sn, 0); + break; + case SRC_PCDELTA: + end = begin + js_GetSrcNoteOffset(sn, 0); + begin += cs->length; + break; + default: + name = FAILED_EXPRESSION_DECOMPILER; + goto out; + } + break; + default:; + } + len = end - begin; + if (len <= 0) { + name = FAILED_EXPRESSION_DECOMPILER; + goto out; + } + + pcstack = (jsbytecode **) + cx->malloc(StackDepth(script) * sizeof *pcstack); + if (!pcstack) { + name = NULL; + goto out; + } + + MUST_FLOW_THROUGH("out"); + pcdepth = ReconstructPCStack(cx, script, begin, pcstack); + if (pcdepth < 0) { + name = FAILED_EXPRESSION_DECOMPILER; + goto out; + } + + name = NULL; + jp = js_NewPrinter(cx, "js_DecompileValueGenerator", fun, 0, + false, false, false); + if (jp) { + jp->dvgfence = end; + jp->pcstack = pcstack; + if (DecompileCode(jp, script, begin, (uintN) len, (uintN) pcdepth)) { + name = (jp->sprinter.base) ? jp->sprinter.base : (char *) ""; + name = JS_strdup(cx, name); + } + js_DestroyPrinter(jp); + } + + out: + if (code != oldcode) { + cx->free(script->code); + script->code = oldcode; + script->main = oldmain; + } + + cx->free(pcstack); + return name; +} + +uintN +js_ReconstructStackDepth(JSContext *cx, JSScript *script, jsbytecode *pc) +{ + return ReconstructPCStack(cx, script, pc, NULL); +} + +#define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, -1); + +static intN +SimulateOp(JSContext *cx, JSScript *script, JSOp op, const JSCodeSpec *cs, + jsbytecode *pc, jsbytecode **pcstack, uintN &pcdepth) +{ + uintN nuses = js_GetStackUses(cs, op, pc); + uintN ndefs = js_GetStackDefs(cx, cs, op, script, pc); + LOCAL_ASSERT(pcdepth >= nuses); + pcdepth -= nuses; + LOCAL_ASSERT(pcdepth + ndefs <= StackDepth(script)); + + /* + * Fill the slots that the opcode defines withs its pc unless it just + * reshuffles the stack. In the latter case we want to preserve the + * opcode that generated the original value. + */ + switch (op) { + default: + if (pcstack) { + for (uintN i = 0; i != ndefs; ++i) + pcstack[pcdepth + i] = pc; + } + break; + + case JSOP_CASE: + case JSOP_CASEX: + /* Keep the switch value. */ + JS_ASSERT(ndefs == 1); + break; + + case JSOP_DUP: + JS_ASSERT(ndefs == 2); + if (pcstack) + pcstack[pcdepth + 1] = pcstack[pcdepth]; + break; + + case JSOP_DUP2: + JS_ASSERT(ndefs == 4); + if (pcstack) { + pcstack[pcdepth + 2] = pcstack[pcdepth]; + pcstack[pcdepth + 3] = pcstack[pcdepth + 1]; + } + break; + + case JSOP_SWAP: + JS_ASSERT(ndefs == 2); + if (pcstack) { + jsbytecode *tmp = pcstack[pcdepth + 1]; + pcstack[pcdepth + 1] = pcstack[pcdepth]; + pcstack[pcdepth] = tmp; + } + break; + } + pcdepth += ndefs; + return pcdepth; +} + +#ifdef JS_TRACER + +#undef LOCAL_ASSERT +#define LOCAL_ASSERT(expr) LOCAL_ASSERT_CUSTOM(expr, goto failure); + +static intN +SimulateImacroCFG(JSContext *cx, JSScript *script, + uintN pcdepth, jsbytecode *pc, jsbytecode *target, + jsbytecode **pcstack) +{ + size_t nbytes = StackDepth(script) * sizeof *pcstack; + jsbytecode** tmp_pcstack = (jsbytecode **) cx->malloc(nbytes); + if (!tmp_pcstack) + return -1; + memcpy(tmp_pcstack, pcstack, nbytes); + + ptrdiff_t oplen; + for (; pc < target; pc += oplen) { + JSOp op = js_GetOpcode(cx, script, pc); + const JSCodeSpec *cs = &js_CodeSpec[op]; + oplen = cs->length; + if (oplen < 0) + oplen = js_GetVariableBytecodeLength(pc); + + if (SimulateOp(cx, script, op, cs, pc, tmp_pcstack, pcdepth) < 0) + goto failure; + + uint32 type = cs->format & JOF_TYPEMASK; + if (type == JOF_JUMP || type == JOF_JUMPX) { + ptrdiff_t jmpoff = (type == JOF_JUMP) ? GET_JUMP_OFFSET(pc) + : GET_JUMPX_OFFSET(pc); + LOCAL_ASSERT(jmpoff >= 0); + intN tmp_pcdepth = SimulateImacroCFG(cx, script, pcdepth, pc + jmpoff, + target, tmp_pcstack); + if (tmp_pcdepth >= 0) { + pcdepth = uintN(tmp_pcdepth); + goto success; + } + + if (op == JSOP_GOTO || op == JSOP_GOTOX) + goto failure; + } + } + + if (pc > target) + goto failure; + + LOCAL_ASSERT(pc == target); + + success: + memcpy(pcstack, tmp_pcstack, nbytes); + cx->free(tmp_pcstack); + return pcdepth; + + failure: + cx->free(tmp_pcstack); + return -1; +} + +#undef LOCAL_ASSERT +#define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, -1); + +static intN +ReconstructPCStack(JSContext *cx, JSScript *script, jsbytecode *target, + jsbytecode **pcstack); + +static intN +ReconstructImacroPCStack(JSContext *cx, JSScript *script, + jsbytecode *imacstart, jsbytecode *target, + jsbytecode **pcstack) +{ + /* + * Begin with a recursive call back to ReconstructPCStack to pick up + * the state-of-the-world at the *start* of the imacro. + */ + JSStackFrame *fp = js_GetScriptedCaller(cx, NULL); + JS_ASSERT(fp->imacpc); + intN pcdepth = ReconstructPCStack(cx, script, fp->imacpc, pcstack); + if (pcdepth < 0) + return pcdepth; + return SimulateImacroCFG(cx, script, pcdepth, imacstart, target, pcstack); +} + +extern jsbytecode* js_GetImacroStart(jsbytecode* pc); +#endif + +static intN +ReconstructPCStack(JSContext *cx, JSScript *script, jsbytecode *target, + jsbytecode **pcstack) +{ + /* + * Walk forward from script->main and compute the stack depth and stack of + * operand-generating opcode PCs in pcstack. + * + * FIXME: Code to compute oplen copied from js_Disassemble1 and reduced. + * FIXME: Optimize to use last empty-stack sequence point. + */ +#ifdef JS_TRACER + jsbytecode *imacstart = js_GetImacroStart(target); + + if (imacstart) + return ReconstructImacroPCStack(cx, script, imacstart, target, pcstack); +#endif + + LOCAL_ASSERT(script->main <= target && target < script->code + script->length); + jsbytecode *pc = script->main; + uintN pcdepth = 0; + ptrdiff_t oplen; + for (; pc < target; pc += oplen) { + JSOp op = js_GetOpcode(cx, script, pc); + const JSCodeSpec *cs = &js_CodeSpec[op]; + oplen = cs->length; + if (oplen < 0) + oplen = js_GetVariableBytecodeLength(pc); + + /* + * A (C ? T : E) expression requires skipping either T (if target is in + * E) or both T and E (if target is after the whole expression) before + * adjusting pcdepth based on the JSOP_IFEQ or JSOP_IFEQX at pc that + * tests condition C. We know that the stack depth can't change from + * what it was with C on top of stack. + */ + jssrcnote *sn = js_GetSrcNote(script, pc); + if (sn && SN_TYPE(sn) == SRC_COND) { + ptrdiff_t jmpoff = js_GetSrcNoteOffset(sn, 0); + if (pc + jmpoff < target) { + pc += jmpoff; + op = js_GetOpcode(cx, script, pc); + JS_ASSERT(op == JSOP_GOTO || op == JSOP_GOTOX); + cs = &js_CodeSpec[op]; + oplen = cs->length; + JS_ASSERT(oplen > 0); + ptrdiff_t jmplen = GetJumpOffset(pc, pc); + if (pc + jmplen < target) { + oplen = (uintN) jmplen; + continue; + } + + /* + * Ok, target lies in E. Manually pop C off the model stack, + * since we have moved beyond the IFEQ now. + */ + LOCAL_ASSERT(pcdepth != 0); + --pcdepth; + } + } + + if (sn && SN_TYPE(sn) == SRC_HIDDEN) + continue; + + if (SimulateOp(cx, script, op, cs, pc, pcstack, pcdepth) < 0) + return -1; + + } + LOCAL_ASSERT(pc == target); + return pcdepth; + +#undef LOCAL_ASSERT +} + +#undef LOCAL_ASSERT_RV diff --git a/ape-server/deps/js/src/jsopcode.h b/ape-server/deps/js/src/jsopcode.h new file mode 100755 index 0000000..abd005d --- /dev/null +++ b/ape-server/deps/js/src/jsopcode.h @@ -0,0 +1,460 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsopcode_h___ +#define jsopcode_h___ +/* + * JS bytecode definitions. + */ +#include +#include "jsprvtd.h" +#include "jspubtd.h" +#include "jsutil.h" + +JS_BEGIN_EXTERN_C + +/* + * JS operation bytecodes. + */ +typedef enum JSOp { +#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ + op = val, +#include "jsopcode.tbl" +#undef OPDEF + JSOP_LIMIT +} JSOp; + +/* + * JS bytecode formats. + */ +#define JOF_BYTE 0 /* single bytecode, no immediates */ +#define JOF_JUMP 1 /* signed 16-bit jump offset immediate */ +#define JOF_ATOM 2 /* unsigned 16-bit constant index */ +#define JOF_UINT16 3 /* unsigned 16-bit immediate operand */ +#define JOF_TABLESWITCH 4 /* table switch */ +#define JOF_LOOKUPSWITCH 5 /* lookup switch */ +#define JOF_QARG 6 /* quickened get/set function argument ops */ +#define JOF_LOCAL 7 /* var or block-local variable */ +#define JOF_SLOTATOM 8 /* uint16 slot + constant index */ +#define JOF_JUMPX 9 /* signed 32-bit jump offset immediate */ +#define JOF_TABLESWITCHX 10 /* extended (32-bit offset) table switch */ +#define JOF_LOOKUPSWITCHX 11 /* extended (32-bit offset) lookup switch */ +#define JOF_UINT24 12 /* extended unsigned 24-bit literal (index) */ +#define JOF_UINT8 13 /* uint8 immediate, e.g. top 8 bits of 24-bit + atom index */ +#define JOF_INT32 14 /* int32 immediate operand */ +#define JOF_OBJECT 15 /* unsigned 16-bit object index */ +#define JOF_SLOTOBJECT 16 /* uint16 slot index + object index */ +#define JOF_REGEXP 17 /* unsigned 16-bit regexp index */ +#define JOF_INT8 18 /* int8 immediate operand */ +#define JOF_ATOMOBJECT 19 /* uint16 constant index + object index */ +#define JOF_UINT16PAIR 20 /* pair of uint16 immediates */ +#define JOF_TYPEMASK 0x001f /* mask for above immediate types */ + +#define JOF_NAME (1U<<5) /* name operation */ +#define JOF_PROP (2U<<5) /* obj.prop operation */ +#define JOF_ELEM (3U<<5) /* obj[index] operation */ +#define JOF_XMLNAME (4U<<5) /* XML name: *, a::b, @a, @a::b, etc. */ +#define JOF_VARPROP (5U<<5) /* x.prop for this, arg, var, or local x */ +#define JOF_MODEMASK (7U<<5) /* mask for above addressing modes */ +#define JOF_SET (1U<<8) /* set (i.e., assignment) operation */ +#define JOF_DEL (1U<<9) /* delete operation */ +#define JOF_DEC (1U<<10) /* decrement (--, not ++) opcode */ +#define JOF_INC (2U<<10) /* increment (++, not --) opcode */ +#define JOF_INCDEC (3U<<10) /* increment or decrement opcode */ +#define JOF_POST (1U<<12) /* postorder increment or decrement */ +#define JOF_FOR (1U<<13) /* for-in property op (akin to JOF_SET) */ +#define JOF_ASSIGNING JOF_SET /* hint for JSClass.resolve, used for ops + that do simplex assignment */ +#define JOF_DETECTING (1U<<14) /* object detection for JSNewResolveOp */ +#define JOF_BACKPATCH (1U<<15) /* backpatch placeholder during codegen */ +#define JOF_LEFTASSOC (1U<<16) /* left-associative operator */ +#define JOF_DECLARING (1U<<17) /* var, const, or function declaration op */ +#define JOF_INDEXBASE (1U<<18) /* atom segment base setting prefix op */ +#define JOF_CALLOP (1U<<19) /* call operation that pushes function and + this */ +#define JOF_PARENHEAD (1U<<20) /* opcode consumes value of expression in + parenthesized statement head */ +#define JOF_INVOKE (1U<<21) /* JSOP_CALL, JSOP_NEW, JSOP_EVAL */ +#define JOF_TMPSLOT (1U<<22) /* interpreter uses extra temporary slot + to root intermediate objects besides + the slots opcode uses */ +#define JOF_TMPSLOT2 (2U<<22) /* interpreter uses extra 2 temporary slot + besides the slots opcode uses */ +#define JOF_TMPSLOT_SHIFT 22 +#define JOF_TMPSLOT_MASK (JS_BITMASK(2) << JOF_TMPSLOT_SHIFT) + +#define JOF_SHARPSLOT (1U<<24) /* first immediate is uint16 stack slot no. + that needs fixup when in global code (see + JSCompiler::compileScript) */ + +/* Shorthands for type from format and type from opcode. */ +#define JOF_TYPE(fmt) ((fmt) & JOF_TYPEMASK) +#define JOF_OPTYPE(op) JOF_TYPE(js_CodeSpec[op].format) + +/* Shorthands for mode from format and mode from opcode. */ +#define JOF_MODE(fmt) ((fmt) & JOF_MODEMASK) +#define JOF_OPMODE(op) JOF_MODE(js_CodeSpec[op].format) + +#define JOF_TYPE_IS_EXTENDED_JUMP(t) \ + ((unsigned)((t) - JOF_JUMPX) <= (unsigned)(JOF_LOOKUPSWITCHX - JOF_JUMPX)) + +/* + * Immediate operand getters, setters, and bounds. + */ + +/* Common uint16 immediate format helpers. */ +#define UINT16_LEN 2 +#define UINT16_HI(i) ((jsbytecode)((i) >> 8)) +#define UINT16_LO(i) ((jsbytecode)(i)) +#define GET_UINT16(pc) ((uintN)(((pc)[1] << 8) | (pc)[2])) +#define SET_UINT16(pc,i) ((pc)[1] = UINT16_HI(i), (pc)[2] = UINT16_LO(i)) +#define UINT16_LIMIT ((uintN)1 << 16) + +/* Short (2-byte signed offset) relative jump macros. */ +#define JUMP_OFFSET_LEN 2 +#define JUMP_OFFSET_HI(off) ((jsbytecode)((off) >> 8)) +#define JUMP_OFFSET_LO(off) ((jsbytecode)(off)) +#define GET_JUMP_OFFSET(pc) ((int16)GET_UINT16(pc)) +#define SET_JUMP_OFFSET(pc,off) ((pc)[1] = JUMP_OFFSET_HI(off), \ + (pc)[2] = JUMP_OFFSET_LO(off)) +#define JUMP_OFFSET_MIN ((int16)0x8000) +#define JUMP_OFFSET_MAX ((int16)0x7fff) + +/* + * When a short jump won't hold a relative offset, its 2-byte immediate offset + * operand is an unsigned index of a span-dependency record, maintained until + * code generation finishes -- after which some (but we hope not nearly all) + * span-dependent jumps must be extended (see OptimizeSpanDeps in jsemit.c). + * + * If the span-dependency record index overflows SPANDEP_INDEX_MAX, the jump + * offset will contain SPANDEP_INDEX_HUGE, indicating that the record must be + * found (via binary search) by its "before span-dependency optimization" pc + * offset (from script main entry point). + */ +#define GET_SPANDEP_INDEX(pc) ((uint16)GET_UINT16(pc)) +#define SET_SPANDEP_INDEX(pc,i) ((pc)[1] = JUMP_OFFSET_HI(i), \ + (pc)[2] = JUMP_OFFSET_LO(i)) +#define SPANDEP_INDEX_MAX ((uint16)0xfffe) +#define SPANDEP_INDEX_HUGE ((uint16)0xffff) + +/* Ultimately, if short jumps won't do, emit long (4-byte signed) offsets. */ +#define JUMPX_OFFSET_LEN 4 +#define JUMPX_OFFSET_B3(off) ((jsbytecode)((off) >> 24)) +#define JUMPX_OFFSET_B2(off) ((jsbytecode)((off) >> 16)) +#define JUMPX_OFFSET_B1(off) ((jsbytecode)((off) >> 8)) +#define JUMPX_OFFSET_B0(off) ((jsbytecode)(off)) +#define GET_JUMPX_OFFSET(pc) ((int32)(((pc)[1] << 24) | ((pc)[2] << 16) \ + | ((pc)[3] << 8) | (pc)[4])) +#define SET_JUMPX_OFFSET(pc,off)((pc)[1] = JUMPX_OFFSET_B3(off), \ + (pc)[2] = JUMPX_OFFSET_B2(off), \ + (pc)[3] = JUMPX_OFFSET_B1(off), \ + (pc)[4] = JUMPX_OFFSET_B0(off)) +#define JUMPX_OFFSET_MIN ((int32)0x80000000) +#define JUMPX_OFFSET_MAX ((int32)0x7fffffff) + +/* + * A literal is indexed by a per-script atom or object maps. Most scripts + * have relatively few literals, so the standard JOF_ATOM, JOF_OBJECT and + * JOF_REGEXP formats specifies a fixed 16 bits of immediate operand index. + * A script with more than 64K literals must wrap the bytecode into + * JSOP_INDEXBASE and JSOP_RESETBASE pair. + */ +#define INDEX_LEN 2 +#define INDEX_HI(i) ((jsbytecode)((i) >> 8)) +#define INDEX_LO(i) ((jsbytecode)(i)) +#define GET_INDEX(pc) GET_UINT16(pc) +#define SET_INDEX(pc,i) ((pc)[1] = INDEX_HI(i), (pc)[2] = INDEX_LO(i)) + +#define GET_INDEXBASE(pc) (JS_ASSERT(*(pc) == JSOP_INDEXBASE), \ + ((uintN)((pc)[1])) << 16) +#define INDEXBASE_LEN 1 + +#define UINT24_HI(i) ((jsbytecode)((i) >> 16)) +#define UINT24_MID(i) ((jsbytecode)((i) >> 8)) +#define UINT24_LO(i) ((jsbytecode)(i)) +#define GET_UINT24(pc) ((jsatomid)(((pc)[1] << 16) | \ + ((pc)[2] << 8) | \ + (pc)[3])) +#define SET_UINT24(pc,i) ((pc)[1] = UINT24_HI(i), \ + (pc)[2] = UINT24_MID(i), \ + (pc)[3] = UINT24_LO(i)) + +#define GET_INT8(pc) ((jsint)(int8)(pc)[1]) + +#define GET_INT32(pc) ((jsint)(((uint32)((pc)[1]) << 24) | \ + ((uint32)((pc)[2]) << 16) | \ + ((uint32)((pc)[3]) << 8) | \ + (uint32)(pc)[4])) +#define SET_INT32(pc,i) ((pc)[1] = (jsbytecode)((uint32)(i) >> 24), \ + (pc)[2] = (jsbytecode)((uint32)(i) >> 16), \ + (pc)[3] = (jsbytecode)((uint32)(i) >> 8), \ + (pc)[4] = (jsbytecode)(uint32)(i)) + +/* Index limit is determined by SN_3BYTE_OFFSET_FLAG, see jsemit.h. */ +#define INDEX_LIMIT_LOG2 23 +#define INDEX_LIMIT ((uint32)1 << INDEX_LIMIT_LOG2) + +/* Actual argument count operand format helpers. */ +#define ARGC_HI(argc) UINT16_HI(argc) +#define ARGC_LO(argc) UINT16_LO(argc) +#define GET_ARGC(pc) GET_UINT16(pc) +#define ARGC_LIMIT UINT16_LIMIT + +/* Synonyms for quick JOF_QARG and JOF_LOCAL bytecodes. */ +#define GET_ARGNO(pc) GET_UINT16(pc) +#define SET_ARGNO(pc,argno) SET_UINT16(pc,argno) +#define ARGNO_LEN 2 +#define ARGNO_LIMIT UINT16_LIMIT + +#define GET_SLOTNO(pc) GET_UINT16(pc) +#define SET_SLOTNO(pc,varno) SET_UINT16(pc,varno) +#define SLOTNO_LEN 2 +#define SLOTNO_LIMIT UINT16_LIMIT + +struct JSCodeSpec { + int8 length; /* length including opcode byte */ + int8 nuses; /* arity, -1 if variadic */ + int8 ndefs; /* number of stack results */ + uint8 prec; /* operator precedence */ + uint32 format; /* immediate operand format */ +}; + +extern const JSCodeSpec js_CodeSpec[]; +extern uintN js_NumCodeSpecs; +extern const char *js_CodeName[]; +extern const char js_EscapeMap[]; + +/* + * Return a GC'ed string containing the chars in str, with any non-printing + * chars or quotes (' or " as specified by the quote argument) escaped, and + * with the quote character at the beginning and end of the result string. + */ +extern JSString * +js_QuoteString(JSContext *cx, JSString *str, jschar quote); + +/* + * JSPrinter operations, for printf style message formatting. The return + * value from js_GetPrinterOutput() is the printer's cumulative output, in + * a GC'ed string. + * + * strict is true if the context in which the output will appear has + * already been marked as strict, thus indicating that nested + * functions need not be re-marked with a strict directive. It should + * be false in the outermost printer. + */ + +extern JSPrinter * +js_NewPrinter(JSContext *cx, const char *name, JSFunction *fun, + uintN indent, JSBool pretty, JSBool grouped, JSBool strict); + +extern void +js_DestroyPrinter(JSPrinter *jp); + +extern JSString * +js_GetPrinterOutput(JSPrinter *jp); + +extern int +js_printf(JSPrinter *jp, const char *format, ...); + +extern JSBool +js_puts(JSPrinter *jp, const char *s); + +/* + * Get index operand from the bytecode using a bytecode analysis to deduce the + * the index register. This function is infallible, in spite of taking cx as + * its first parameter; it uses only cx->runtime when calling JS_GetTrapOpcode. + * The GET_*_FROM_BYTECODE macros that call it pick up cx from their caller's + * lexical environments. + */ +uintN +js_GetIndexFromBytecode(JSContext *cx, JSScript *script, jsbytecode *pc, + ptrdiff_t pcoff); + +/* + * A slower version of GET_ATOM when the caller does not want to maintain + * the index segment register itself. + */ +#define GET_ATOM_FROM_BYTECODE(script, pc, pcoff, atom) \ + JS_BEGIN_MACRO \ + JS_ASSERT(*(pc) != JSOP_DOUBLE); \ + uintN index_ = js_GetIndexFromBytecode(cx, (script), (pc), (pcoff)); \ + JS_GET_SCRIPT_ATOM(script, pc, index_, atom); \ + JS_END_MACRO + +/* + * Variant for getting a double atom when we might be in an imacro. Bytecodes + * with literals that are only ever doubles must use this macro, and never use + * GET_ATOM_FROM_BYTECODE or JS_GET_SCRIPT_ATOM. + * + * Unfortunately some bytecodes such as JSOP_LOOKUPSWITCH have immediates that + * might be string or double atoms. Those opcodes cannot be used from imacros. + * See the assertions in the JSOP_DOUBLE and JSOP_LOOKUPSWTICH* opcode cases in + * jsops.cpp. + */ +#define GET_DOUBLE_FROM_BYTECODE(script, pc, pcoff, atom) \ + JS_BEGIN_MACRO \ + uintN index_ = js_GetIndexFromBytecode(cx, (script), (pc), (pcoff)); \ + JS_ASSERT(index_ < (script)->atomMap.length); \ + (atom) = (script)->atomMap.vector[index_]; \ + JS_ASSERT(ATOM_IS_DOUBLE(atom)); \ + JS_END_MACRO + +#define GET_OBJECT_FROM_BYTECODE(script, pc, pcoff, obj) \ + JS_BEGIN_MACRO \ + uintN index_ = js_GetIndexFromBytecode(cx, (script), (pc), (pcoff)); \ + obj = (script)->getObject(index_); \ + JS_END_MACRO + +#define GET_FUNCTION_FROM_BYTECODE(script, pc, pcoff, fun) \ + JS_BEGIN_MACRO \ + uintN index_ = js_GetIndexFromBytecode(cx, (script), (pc), (pcoff)); \ + fun = (script)->getFunction(index_); \ + JS_END_MACRO + +#define GET_REGEXP_FROM_BYTECODE(script, pc, pcoff, obj) \ + JS_BEGIN_MACRO \ + uintN index_ = js_GetIndexFromBytecode(cx, (script), (pc), (pcoff)); \ + obj = (script)->getRegExp(index_); \ + JS_END_MACRO + +/* + * Get the length of variable-length bytecode like JSOP_TABLESWITCH. + */ +extern uintN +js_GetVariableBytecodeLength(jsbytecode *pc); + +/* + * Find the number of stack slots used by a variadic opcode such as JSOP_CALL + * or JSOP_NEWARRAY (for such ops, JSCodeSpec.nuses is -1). + */ +extern uintN +js_GetVariableStackUses(JSOp op, jsbytecode *pc); + +/* + * Find the number of stack slots defined by JSOP_ENTERBLOCK (for this op, + * JSCodeSpec.ndefs is -1). + */ +extern uintN +js_GetEnterBlockStackDefs(JSContext *cx, JSScript *script, jsbytecode *pc); + +#ifdef __cplusplus /* Aargh, libgjs, bug 492720. */ +static JS_INLINE uintN +js_GetStackUses(const JSCodeSpec *cs, JSOp op, jsbytecode *pc) +{ + JS_ASSERT(cs == &js_CodeSpec[op]); + if (cs->nuses >= 0) + return cs->nuses; + return js_GetVariableStackUses(op, pc); +} + +static JS_INLINE uintN +js_GetStackDefs(JSContext *cx, const JSCodeSpec *cs, JSOp op, JSScript *script, + jsbytecode *pc) +{ + JS_ASSERT(cs == &js_CodeSpec[op]); + if (cs->ndefs >= 0) + return cs->ndefs; + + /* Only JSOP_ENTERBLOCK has a variable number of stack defs. */ + JS_ASSERT(op == JSOP_ENTERBLOCK); + return js_GetEnterBlockStackDefs(cx, script, pc); +} +#endif + +#ifdef DEBUG +/* + * Disassemblers, for debugging only. + */ +#include + +extern JS_FRIEND_API(JSBool) +js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, FILE *fp); + +extern JS_FRIEND_API(uintN) +js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc, + JSBool lines, FILE *fp); +#endif /* DEBUG */ + +/* + * Decompilers, for script, function, and expression pretty-printing. + */ +extern JSBool +js_DecompileScript(JSPrinter *jp, JSScript *script); + +extern JSBool +js_DecompileFunctionBody(JSPrinter *jp); + +extern JSBool +js_DecompileFunction(JSPrinter *jp); + +extern JSString * +js_DecompileToString(JSContext *cx, const char *name, JSFunction *fun, + uintN indent, JSBool pretty, JSBool grouped, JSBool strict, + JSBool (*decompiler)(JSPrinter *jp)); + +/* + * Find the source expression that resulted in v, and return a newly allocated + * C-string containing it. Fall back on v's string conversion (fallback) if we + * can't find the bytecode that generated and pushed v on the operand stack. + * + * Search the current stack frame if spindex is JSDVG_SEARCH_STACK. Don't + * look for v on the stack if spindex is JSDVG_IGNORE_STACK. Otherwise, + * spindex is the negative index of v, measured from cx->fp->sp, or from a + * lower frame's sp if cx->fp is native. + * + * The caller must call JS_free on the result after a succsesful call. + */ +extern char * +js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v, + JSString *fallback); + +#define JSDVG_IGNORE_STACK 0 +#define JSDVG_SEARCH_STACK 1 + +/* + * Given bytecode address pc in script's main program code, return the operand + * stack depth just before (JSOp) *pc executes. + */ +extern uintN +js_ReconstructStackDepth(JSContext *cx, JSScript *script, jsbytecode *pc); + +JS_END_EXTERN_C + +#endif /* jsopcode_h___ */ diff --git a/ape-server/deps/js/src/jsopcode.tbl b/ape-server/deps/js/src/jsopcode.tbl new file mode 100755 index 0000000..ec7adc4 --- /dev/null +++ b/ape-server/deps/js/src/jsopcode.tbl @@ -0,0 +1,605 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=0 ft=c: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * JavaScript operation bytecodes. If you need to allocate a bytecode, look + * for a name of the form JSOP_UNUSED* and claim it. Otherwise, always add at + * the end of the table. + * + * Includers must define an OPDEF macro of the following form: + * + * #define OPDEF(op,val,name,image,length,nuses,ndefs,prec,format) ... + * + * Selected arguments can be expanded in initializers. The op argument is + * expanded followed by comma in the JSOp enum (jsopcode.h), e.g. The value + * field must be dense for now, because jsopcode.c uses an OPDEF() expansion + * inside the js_CodeSpec[] initializer. + * + * Field Description + * op Bytecode name, which is the JSOp enumerator name + * value Bytecode value, which is the JSOp enumerator value + * name C string containing name for disassembler + * image C string containing "image" for pretty-printer, null if ugly + * length Number of bytes including any immediate operands + * nuses Number of stack slots consumed by bytecode, -1 if variadic + * ndefs Number of stack slots produced by bytecode, -1 if variadic + * prec Operator precedence, zero if not an operator + * format Bytecode plus immediate operand encoding format + * + * Precedence Operators Opcodes + * 1 yield w JSOP_YIELD + * 2 , JSOP_POP with SRC_PCDELTA, JSOP_RETURN + * 3 =, +=, etc. JSOP_SETNAME, etc. (all JOF_SET); + * let (...) ... and JSOP_LEAVEBLOCKEXPR + * 4 ?: JSOP_IFEQ, JSOP_IFEQX + * 5 || JSOP_OR, JSOP_ORX + * 6 && JSOP_AND, JSOP_ANDX + * 7 | JSOP_BITOR + * 8 ^ JSOP_BITXOR + * 9 & JSOP_BITAND + * 10 ==, !=, etc. JSOP_EQ, JSOP_NE, etc. + * 11 <, in, etc. JSOP_LT, JSOP_IN, etc. + * 12 <<, >>, >>> JSOP_LSH, JSOP_RSH, JSOP_URSH + * 13 +, -, etc. JSOP_ADD, JSOP_SUB, etc. + * 14 *, /, % JSOP_MUL, JSOP_DIV, JSOP_MOD + * 15 !, ~, delete, etc. JSOP_NOT, JSOP_BITNOT, JSOP_DEL*, etc. + * 16 3.14, 0, etc. JSOP_DOUBLE, JSOP_ZERO, etc. + * 17 new JSOP_NEW + * 18 x.y, f(), etc. JSOP_GETPROP, JSOP_CALL, etc. + * 19 x, null, JSOP_NAME, JSOP_NULL, etc.; + * function (...) ... and JSOP_LAMBDA + * + * The push-numeric-constant operators, JSOP_ZERO, JSOP_DOUBLE, etc., have + * lower precedence than the member operators emitted for the . operator, to + * cause the decompiler to parenthesize the . left operand, e.g. (0).foo. + * Otherwise the . could be taken as a decimal point. + * + * Let expressions are "primary" when viewed from the left, but they eat up ops + * to the right as if assignment expressions and therefore have precedence 3. + * This makes the decompiler retain the parentheses in (let (a=0) x) ? a : 0 + * but omit the superfluous ones in (let (a=0) x), a. + * + * Yield expressions must be parenthesized even in comma-expressions and + * argument lists, so they have the lowest precedence. + * + * This file is best viewed with 128 columns: +12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678 + */ + +/* legend: op val name image len use def prec format */ + +/* + * Generic nop for the decompiler. + */ +OPDEF(JSOP_NOP, 0, "nop", NULL, 1, 0, 0, 0, JOF_BYTE) + +/* Long-standing JavaScript bytecodes. */ +OPDEF(JSOP_PUSH, 1, "push", NULL, 1, 0, 1, 0, JOF_BYTE) +OPDEF(JSOP_POPV, 2, "popv", NULL, 1, 1, 0, 2, JOF_BYTE) +OPDEF(JSOP_ENTERWITH, 3, "enterwith", NULL, 1, 1, 1, 0, JOF_BYTE|JOF_PARENHEAD) +OPDEF(JSOP_LEAVEWITH, 4, "leavewith", NULL, 1, 1, 0, 0, JOF_BYTE) +OPDEF(JSOP_RETURN, 5, "return", NULL, 1, 1, 0, 2, JOF_BYTE) +OPDEF(JSOP_GOTO, 6, "goto", NULL, 3, 0, 0, 0, JOF_JUMP) +OPDEF(JSOP_IFEQ, 7, "ifeq", NULL, 3, 1, 0, 4, JOF_JUMP|JOF_DETECTING) +OPDEF(JSOP_IFNE, 8, "ifne", NULL, 3, 1, 0, 0, JOF_JUMP|JOF_PARENHEAD) + +/* Get the arguments object for the current, lightweight function activation. */ +OPDEF(JSOP_ARGUMENTS, 9, js_arguments_str, js_arguments_str, 1, 0, 1, 18, JOF_BYTE) + +/* ECMA-compliant for-in loop with argument or local loop control. */ +OPDEF(JSOP_FORARG, 10, "forarg", NULL, 3, 2, 2, 19, JOF_QARG|JOF_NAME|JOF_FOR) +OPDEF(JSOP_FORLOCAL, 11, "forlocal", NULL, 3, 2, 2, 19, JOF_LOCAL|JOF_NAME|JOF_FOR) + +/* More long-standing bytecodes. */ +OPDEF(JSOP_DUP, 12, "dup", NULL, 1, 1, 2, 0, JOF_BYTE) +OPDEF(JSOP_DUP2, 13, "dup2", NULL, 1, 2, 4, 0, JOF_BYTE) +OPDEF(JSOP_SETCONST, 14, "setconst", NULL, 3, 1, 1, 3, JOF_ATOM|JOF_NAME|JOF_SET) +OPDEF(JSOP_BITOR, 15, "bitor", "|", 1, 2, 1, 7, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_BITXOR, 16, "bitxor", "^", 1, 2, 1, 8, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_BITAND, 17, "bitand", "&", 1, 2, 1, 9, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_EQ, 18, "eq", "==", 1, 2, 1, 10, JOF_BYTE|JOF_LEFTASSOC|JOF_DETECTING) +OPDEF(JSOP_NE, 19, "ne", "!=", 1, 2, 1, 10, JOF_BYTE|JOF_LEFTASSOC|JOF_DETECTING) +OPDEF(JSOP_LT, 20, "lt", "<", 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_LE, 21, "le", "<=", 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_GT, 22, "gt", ">", 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_GE, 23, "ge", ">=", 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_LSH, 24, "lsh", "<<", 1, 2, 1, 12, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_RSH, 25, "rsh", ">>", 1, 2, 1, 12, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_URSH, 26, "ursh", ">>>", 1, 2, 1, 12, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_ADD, 27, "add", "+", 1, 2, 1, 13, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_SUB, 28, "sub", "-", 1, 2, 1, 13, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_MUL, 29, "mul", "*", 1, 2, 1, 14, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_DIV, 30, "div", "/", 1, 2, 1, 14, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_MOD, 31, "mod", "%", 1, 2, 1, 14, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_NOT, 32, "not", "!", 1, 1, 1, 15, JOF_BYTE|JOF_DETECTING) +OPDEF(JSOP_BITNOT, 33, "bitnot", "~", 1, 1, 1, 15, JOF_BYTE) +OPDEF(JSOP_NEG, 34, "neg", "- ", 1, 1, 1, 15, JOF_BYTE) +OPDEF(JSOP_POS, 35, "pos", "+ ", 1, 1, 1, 15, JOF_BYTE) +OPDEF(JSOP_DELNAME, 36, "delname", NULL, 3, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_DEL) +OPDEF(JSOP_DELPROP, 37, "delprop", NULL, 3, 1, 1, 15, JOF_ATOM|JOF_PROP|JOF_DEL) +OPDEF(JSOP_DELELEM, 38, "delelem", NULL, 1, 2, 1, 15, JOF_BYTE |JOF_ELEM|JOF_DEL) +OPDEF(JSOP_TYPEOF, 39, js_typeof_str,NULL, 1, 1, 1, 15, JOF_BYTE|JOF_DETECTING) +OPDEF(JSOP_VOID, 40, js_void_str, NULL, 1, 1, 1, 15, JOF_BYTE) + +OPDEF(JSOP_INCNAME, 41, "incname", NULL, 3, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_INC|JOF_TMPSLOT2) +OPDEF(JSOP_INCPROP, 42, "incprop", NULL, 3, 1, 1, 15, JOF_ATOM|JOF_PROP|JOF_INC|JOF_TMPSLOT2) +OPDEF(JSOP_INCELEM, 43, "incelem", NULL, 1, 2, 1, 15, JOF_BYTE |JOF_ELEM|JOF_INC|JOF_TMPSLOT2) +OPDEF(JSOP_DECNAME, 44, "decname", NULL, 3, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_DEC|JOF_TMPSLOT2) +OPDEF(JSOP_DECPROP, 45, "decprop", NULL, 3, 1, 1, 15, JOF_ATOM|JOF_PROP|JOF_DEC|JOF_TMPSLOT2) +OPDEF(JSOP_DECELEM, 46, "decelem", NULL, 1, 2, 1, 15, JOF_BYTE |JOF_ELEM|JOF_DEC|JOF_TMPSLOT2) +OPDEF(JSOP_NAMEINC, 47, "nameinc", NULL, 3, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT2) +OPDEF(JSOP_PROPINC, 48, "propinc", NULL, 3, 1, 1, 15, JOF_ATOM|JOF_PROP|JOF_INC|JOF_POST|JOF_TMPSLOT2) +OPDEF(JSOP_ELEMINC, 49, "eleminc", NULL, 1, 2, 1, 15, JOF_BYTE |JOF_ELEM|JOF_INC|JOF_POST|JOF_TMPSLOT2) +OPDEF(JSOP_NAMEDEC, 50, "namedec", NULL, 3, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT2) +OPDEF(JSOP_PROPDEC, 51, "propdec", NULL, 3, 1, 1, 15, JOF_ATOM|JOF_PROP|JOF_DEC|JOF_POST|JOF_TMPSLOT2) +OPDEF(JSOP_ELEMDEC, 52, "elemdec", NULL, 1, 2, 1, 15, JOF_BYTE |JOF_ELEM|JOF_DEC|JOF_POST|JOF_TMPSLOT2) + +OPDEF(JSOP_GETPROP, 53, "getprop", NULL, 3, 1, 1, 18, JOF_ATOM|JOF_PROP) +OPDEF(JSOP_SETPROP, 54, "setprop", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) +OPDEF(JSOP_GETELEM, 55, "getelem", NULL, 1, 2, 1, 18, JOF_BYTE |JOF_ELEM|JOF_LEFTASSOC) +OPDEF(JSOP_SETELEM, 56, "setelem", NULL, 1, 3, 1, 3, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_DETECTING) +OPDEF(JSOP_CALLNAME, 57, "callname", NULL, 3, 0, 2, 19, JOF_ATOM|JOF_NAME|JOF_CALLOP) +OPDEF(JSOP_CALL, 58, "call", NULL, 3, -1, 1, 18, JOF_UINT16|JOF_INVOKE) +OPDEF(JSOP_NAME, 59, "name", NULL, 3, 0, 1, 19, JOF_ATOM|JOF_NAME) +OPDEF(JSOP_DOUBLE, 60, "double", NULL, 3, 0, 1, 16, JOF_ATOM) +OPDEF(JSOP_STRING, 61, "string", NULL, 3, 0, 1, 19, JOF_ATOM) +OPDEF(JSOP_ZERO, 62, "zero", "0", 1, 0, 1, 16, JOF_BYTE) +OPDEF(JSOP_ONE, 63, "one", "1", 1, 0, 1, 16, JOF_BYTE) +OPDEF(JSOP_NULL, 64, js_null_str, js_null_str, 1, 0, 1, 19, JOF_BYTE) +OPDEF(JSOP_THIS, 65, js_this_str, js_this_str, 1, 0, 1, 19, JOF_BYTE) +OPDEF(JSOP_FALSE, 66, js_false_str, js_false_str, 1, 0, 1, 19, JOF_BYTE) +OPDEF(JSOP_TRUE, 67, js_true_str, js_true_str, 1, 0, 1, 19, JOF_BYTE) +OPDEF(JSOP_OR, 68, "or", NULL, 3, 1, 0, 5, JOF_JUMP|JOF_DETECTING|JOF_LEFTASSOC) +OPDEF(JSOP_AND, 69, "and", NULL, 3, 1, 0, 6, JOF_JUMP|JOF_DETECTING|JOF_LEFTASSOC) + +/* The switch bytecodes have variable length. */ +OPDEF(JSOP_TABLESWITCH, 70, "tableswitch", NULL, -1, 1, 0, 0, JOF_TABLESWITCH|JOF_DETECTING|JOF_PARENHEAD) +OPDEF(JSOP_LOOKUPSWITCH, 71, "lookupswitch", NULL, -1, 1, 0, 0, JOF_LOOKUPSWITCH|JOF_DETECTING|JOF_PARENHEAD) + +/* New, infallible/transitive identity ops. */ +OPDEF(JSOP_STRICTEQ, 72, "stricteq", "===", 1, 2, 1, 10, JOF_BYTE|JOF_DETECTING|JOF_LEFTASSOC) +OPDEF(JSOP_STRICTNE, 73, "strictne", "!==", 1, 2, 1, 10, JOF_BYTE|JOF_DETECTING|JOF_LEFTASSOC) + +/* + * Host object extension: given 'o.item(i) = j', the left-hand side compiles + * JSOP_SETCALL, rather than JSOP_CALL. + */ +OPDEF(JSOP_SETCALL, 74, "setcall", NULL, 3, -1, 2, 18, JOF_UINT16|JOF_SET) + +/* + * JSOP_ITER sets up a for-in or for-each-in loop using the JSITER_* flag bits + * in this op's uint8 immediate operand. It replaces the top of stack object + * with an iterator for that object, and pushes a slot used by JSOP_NEXTITER. + * + * JSOP_NEXTITER stores the next iterated value in the top of stack slot which + * was allocated by JSOP_ITER and pushes true, or stores JSVAL_HOLE and pushes + * false. It is followed immediately by JSOP_IFNE{,X}. + * + * JSOP_ENDITER cleans up after the loop. It uses the slot above the iterator + * for temporary GC rooting. + */ +OPDEF(JSOP_ITER, 75, "iter", NULL, 2, 1, 2, 0, JOF_UINT8) +OPDEF(JSOP_NEXTITER, 76, "nextiter", NULL, 1, 2, 3, 0, JOF_BYTE) +OPDEF(JSOP_ENDITER, 77, "enditer", NULL, 1, 2, 0, 0, JOF_BYTE) + +OPDEF(JSOP_APPLY, 78, "apply", NULL, 3, -1, 1, 18, JOF_UINT16|JOF_INVOKE) +OPDEF(JSOP_SWAP, 79, "swap", NULL, 1, 2, 2, 0, JOF_BYTE) + +/* Push object literal. */ +OPDEF(JSOP_OBJECT, 80, "object", NULL, 3, 0, 1, 19, JOF_OBJECT) + +/* Pop value and discard it. */ +OPDEF(JSOP_POP, 81, "pop", NULL, 1, 1, 0, 2, JOF_BYTE) + +/* Convert value to number, for unary +. */ +OPDEF(JSOP_NEW, 82, js_new_str, NULL, 3, -1, 1, 17, JOF_UINT16|JOF_INVOKE) + +/* Trap into debugger for breakpoint, etc. */ +OPDEF(JSOP_TRAP, 83, "trap", NULL, 1, 0, 0, 0, JOF_BYTE) + +/* Fast get/set ops for function arguments and local variables. */ +OPDEF(JSOP_GETARG, 84, "getarg", NULL, 3, 0, 1, 19, JOF_QARG |JOF_NAME) +OPDEF(JSOP_SETARG, 85, "setarg", NULL, 3, 1, 1, 3, JOF_QARG |JOF_NAME|JOF_SET) +OPDEF(JSOP_GETLOCAL, 86,"getlocal", NULL, 3, 0, 1, 19, JOF_LOCAL|JOF_NAME) +OPDEF(JSOP_SETLOCAL, 87,"setlocal", NULL, 3, 1, 1, 3, JOF_LOCAL|JOF_NAME|JOF_SET|JOF_DETECTING) + +/* Push unsigned 16-bit int constant. */ +OPDEF(JSOP_UINT16, 88, "uint16", NULL, 3, 0, 1, 16, JOF_UINT16) + +/* Object and array literal support. */ +OPDEF(JSOP_NEWINIT, 89, "newinit", NULL, 2, 0, 1, 19, JOF_INT8) +OPDEF(JSOP_ENDINIT, 90, "endinit", NULL, 1, 0, 0, 19, JOF_BYTE) +OPDEF(JSOP_INITPROP, 91, "initprop", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) +OPDEF(JSOP_INITELEM, 92, "initelem", NULL, 1, 3, 1, 3, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_DETECTING) +OPDEF(JSOP_DEFSHARP, 93, "defsharp", NULL, 5, 0, 0, 0, JOF_UINT16PAIR|JOF_SHARPSLOT) +OPDEF(JSOP_USESHARP, 94, "usesharp", NULL, 5, 0, 1, 0, JOF_UINT16PAIR|JOF_SHARPSLOT) + +/* Fast inc/dec ops for args and locals. */ +OPDEF(JSOP_INCARG, 95, "incarg", NULL, 3, 0, 1, 15, JOF_QARG |JOF_NAME|JOF_INC) +OPDEF(JSOP_DECARG, 96, "decarg", NULL, 3, 0, 1, 15, JOF_QARG |JOF_NAME|JOF_DEC) +OPDEF(JSOP_ARGINC, 97, "arginc", NULL, 3, 0, 1, 15, JOF_QARG |JOF_NAME|JOF_INC|JOF_POST) +OPDEF(JSOP_ARGDEC, 98, "argdec", NULL, 3, 0, 1, 15, JOF_QARG |JOF_NAME|JOF_DEC|JOF_POST) + +OPDEF(JSOP_INCLOCAL, 99, "inclocal", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_INC) +OPDEF(JSOP_DECLOCAL, 100,"declocal", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_DEC) +OPDEF(JSOP_LOCALINC, 101,"localinc", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_INC|JOF_POST) +OPDEF(JSOP_LOCALDEC, 102,"localdec", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_DEC|JOF_POST) + +OPDEF(JSOP_IMACOP, 103,"imacop", NULL, 1, 0, 0, 0, JOF_BYTE) + +/* ECMA-compliant for/in ops. */ +OPDEF(JSOP_FORNAME, 104,"forname", NULL, 3, 2, 2, 19, JOF_ATOM|JOF_NAME|JOF_FOR) +OPDEF(JSOP_FORPROP, 105,"forprop", NULL, 3, 3, 2, 18, JOF_ATOM|JOF_PROP|JOF_FOR) +OPDEF(JSOP_FORELEM, 106,"forelem", NULL, 1, 2, 3, 18, JOF_BYTE |JOF_ELEM|JOF_FOR) +OPDEF(JSOP_POPN, 107,"popn", NULL, 3, -1, 0, 0, JOF_UINT16) + +/* ECMA-compliant assignment ops. */ +OPDEF(JSOP_BINDNAME, 108,"bindname", NULL, 3, 0, 1, 0, JOF_ATOM|JOF_NAME|JOF_SET) +OPDEF(JSOP_SETNAME, 109,"setname", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING) + +/* Exception handling ops. */ +OPDEF(JSOP_THROW, 110,js_throw_str, NULL, 1, 1, 0, 0, JOF_BYTE) + +/* 'in' and 'instanceof' ops. */ +OPDEF(JSOP_IN, 111,js_in_str, js_in_str, 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_INSTANCEOF,112,js_instanceof_str,js_instanceof_str,1,2,1,11,JOF_BYTE|JOF_LEFTASSOC) + +/* debugger op */ +OPDEF(JSOP_DEBUGGER, 113,"debugger", NULL, 1, 0, 0, 0, JOF_BYTE) + +/* gosub/retsub for finally handling */ +OPDEF(JSOP_GOSUB, 114,"gosub", NULL, 3, 0, 0, 0, JOF_JUMP) +OPDEF(JSOP_RETSUB, 115,"retsub", NULL, 1, 2, 0, 0, JOF_BYTE) + +/* More exception handling ops. */ +OPDEF(JSOP_EXCEPTION, 116,"exception", NULL, 1, 0, 1, 0, JOF_BYTE) + +/* Embedded lineno to speedup pc->line mapping. */ +OPDEF(JSOP_LINENO, 117,"lineno", NULL, 3, 0, 0, 0, JOF_UINT16) + +/* + * ECMA-compliant switch statement ops. + * CONDSWITCH is a decompilable NOP; CASE is ===, POP, jump if true, re-push + * lval if false; and DEFAULT is POP lval and GOTO. + */ +OPDEF(JSOP_CONDSWITCH,118,"condswitch", NULL, 1, 0, 0, 0, JOF_BYTE|JOF_PARENHEAD) +OPDEF(JSOP_CASE, 119,"case", NULL, 3, 2, 1, 0, JOF_JUMP) +OPDEF(JSOP_DEFAULT, 120,"default", NULL, 3, 1, 0, 0, JOF_JUMP) + +/* + * ECMA-compliant call to eval op + */ +OPDEF(JSOP_EVAL, 121,"eval", NULL, 3, -1, 1, 18, JOF_UINT16|JOF_INVOKE) + +/* + * ECMA-compliant helper for 'for (x[i] in o)' loops. + */ +OPDEF(JSOP_ENUMELEM, 122,"enumelem", NULL, 1, 3, 0, 3, JOF_BYTE |JOF_SET) + +/* + * Getter and setter prefix bytecodes. These modify the next bytecode, either + * an assignment or a property initializer code, which then defines a property + * getter or setter. + */ +OPDEF(JSOP_GETTER, 123,js_getter_str,NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_SETTER, 124,js_setter_str,NULL, 1, 0, 0, 0, JOF_BYTE) + +/* + * Prolog bytecodes for defining function, var, and const names. + */ +OPDEF(JSOP_DEFFUN, 125,"deffun", NULL, 3, 0, 0, 0, JOF_OBJECT|JOF_DECLARING) +OPDEF(JSOP_DEFCONST, 126,"defconst", NULL, 3, 0, 0, 0, JOF_ATOM|JOF_DECLARING) +OPDEF(JSOP_DEFVAR, 127,"defvar", NULL, 3, 0, 0, 0, JOF_ATOM|JOF_DECLARING) + +/* Push a closure for a named or anonymous function expression. */ +OPDEF(JSOP_LAMBDA, 128, "lambda", NULL, 3, 0, 1, 19, JOF_OBJECT) + +/* Used for named function expression self-naming, if lightweight. */ +OPDEF(JSOP_CALLEE, 129, "callee", NULL, 1, 0, 1, 19, JOF_BYTE) + +/* + * Like JSOP_SETLOCAL, but specialized to avoid requiring JSOP_POP immediately + * after to throw away the exception value. + */ +OPDEF(JSOP_SETLOCALPOP, 130, "setlocalpop", NULL, 3, 1, 0, 3, JOF_LOCAL|JOF_NAME|JOF_SET) + +/* Pick an element from the stack. */ +OPDEF(JSOP_PICK, 131, "pick", NULL, 2, 0, 0, 0, JOF_UINT8) + +/* + * Exception handling no-op, for more economical byte-coding than SRC_TRYFIN + * srcnote-annotated JSOP_NOPs and to simply stack balance handling. + */ +OPDEF(JSOP_TRY, 132,"try", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_FINALLY, 133,"finally", NULL, 1, 0, 2, 0, JOF_BYTE) + +/* + * Get a dynamic slot from an object known to have at least one greater than + * the slot index number of values at obj->dslots. The CALL variant computes + * the callee and this-object in preparation for a JSOP_CALL. + */ +OPDEF(JSOP_GETDSLOT, 134,"getdslot", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME) +OPDEF(JSOP_CALLDSLOT, 135,"calldslot", NULL, 3, 0, 2, 19, JOF_UINT16|JOF_NAME|JOF_CALLOP) + +/* + * Bytecodes that avoid making an arguments object in most cases: + * JSOP_ARGSUB gets arguments[i] from fp->argv, iff i is in [0, fp->argc-1]. + * JSOP_ARGCNT returns fp->argc. + */ +OPDEF(JSOP_ARGSUB, 136,"argsub", NULL, 3, 0, 1, 18, JOF_QARG |JOF_NAME) +OPDEF(JSOP_ARGCNT, 137,"argcnt", NULL, 1, 0, 1, 18, JOF_BYTE) + +/* + * Define a local function object as a local variable. + * The local variable's slot number is the first immediate two-byte operand. + * The function object's atom index is the second immediate operand. + */ +OPDEF(JSOP_DEFLOCALFUN, 138,"deflocalfun",NULL, 5, 0, 0, 0, JOF_SLOTOBJECT|JOF_DECLARING) + +/* Extended jumps. */ +OPDEF(JSOP_GOTOX, 139,"gotox", NULL, 5, 0, 0, 0, JOF_JUMPX) +OPDEF(JSOP_IFEQX, 140,"ifeqx", NULL, 5, 1, 0, 4, JOF_JUMPX|JOF_DETECTING) +OPDEF(JSOP_IFNEX, 141,"ifnex", NULL, 5, 1, 0, 0, JOF_JUMPX|JOF_PARENHEAD) +OPDEF(JSOP_ORX, 142,"orx", NULL, 5, 1, 0, 5, JOF_JUMPX|JOF_DETECTING) +OPDEF(JSOP_ANDX, 143,"andx", NULL, 5, 1, 0, 6, JOF_JUMPX|JOF_DETECTING) +OPDEF(JSOP_GOSUBX, 144,"gosubx", NULL, 5, 0, 0, 0, JOF_JUMPX) +OPDEF(JSOP_CASEX, 145,"casex", NULL, 5, 2, 1, 0, JOF_JUMPX) +OPDEF(JSOP_DEFAULTX, 146,"defaultx", NULL, 5, 1, 0, 0, JOF_JUMPX) +OPDEF(JSOP_TABLESWITCHX, 147,"tableswitchx",NULL, -1, 1, 0, 0, JOF_TABLESWITCHX|JOF_DETECTING|JOF_PARENHEAD) +OPDEF(JSOP_LOOKUPSWITCHX, 148,"lookupswitchx",NULL, -1, 1, 0, 0, JOF_LOOKUPSWITCHX|JOF_DETECTING|JOF_PARENHEAD) + +/* Placeholders for a real jump opcode set during backpatch chain fixup. */ +OPDEF(JSOP_BACKPATCH, 149,"backpatch",NULL, 3, 0, 0, 0, JOF_JUMP|JOF_BACKPATCH) +OPDEF(JSOP_BACKPATCH_POP, 150,"backpatch_pop",NULL, 3, 1, 0, 0, JOF_JUMP|JOF_BACKPATCH) + +/* Set pending exception from the stack, to trigger rethrow. */ +OPDEF(JSOP_THROWING, 151,"throwing", NULL, 1, 1, 0, 0, JOF_BYTE) + +/* Set and get return value pseudo-register in stack frame. */ +OPDEF(JSOP_SETRVAL, 152,"setrval", NULL, 1, 1, 0, 2, JOF_BYTE) +OPDEF(JSOP_RETRVAL, 153,"retrval", NULL, 1, 0, 0, 0, JOF_BYTE) + +/* Optimized global variable ops (we don't bother doing a JSOP_FORGVAR op). */ +OPDEF(JSOP_GETGVAR, 154,"getgvar", NULL, 3, 0, 1, 19, JOF_ATOM|JOF_NAME) +OPDEF(JSOP_SETGVAR, 155,"setgvar", NULL, 3, 1, 1, 3, JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING) +OPDEF(JSOP_INCGVAR, 156,"incgvar", NULL, 3, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_INC|JOF_TMPSLOT2) +OPDEF(JSOP_DECGVAR, 157,"decgvar", NULL, 3, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_DEC|JOF_TMPSLOT2) +OPDEF(JSOP_GVARINC, 158,"gvarinc", NULL, 3, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT2) +OPDEF(JSOP_GVARDEC, 159,"gvardec", NULL, 3, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT2) + +/* Regular expression literal requiring special "fork on exec" handling. */ +OPDEF(JSOP_REGEXP, 160,"regexp", NULL, 3, 0, 1, 19, JOF_REGEXP) + +/* XML (ECMA-357, a.k.a. "E4X") support. */ +OPDEF(JSOP_DEFXMLNS, 161,"defxmlns", NULL, 1, 1, 0, 0, JOF_BYTE) +OPDEF(JSOP_ANYNAME, 162,"anyname", NULL, 1, 0, 1, 19, JOF_BYTE|JOF_XMLNAME) +OPDEF(JSOP_QNAMEPART, 163,"qnamepart", NULL, 3, 0, 1, 19, JOF_ATOM|JOF_XMLNAME) +OPDEF(JSOP_QNAMECONST, 164,"qnameconst", NULL, 3, 1, 1, 19, JOF_ATOM|JOF_XMLNAME) +OPDEF(JSOP_QNAME, 165,"qname", NULL, 1, 2, 1, 0, JOF_BYTE|JOF_XMLNAME) +OPDEF(JSOP_TOATTRNAME, 166,"toattrname", NULL, 1, 1, 1, 19, JOF_BYTE|JOF_XMLNAME) +OPDEF(JSOP_TOATTRVAL, 167,"toattrval", NULL, 1, 1, 1, 19, JOF_BYTE) +OPDEF(JSOP_ADDATTRNAME, 168,"addattrname",NULL, 1, 2, 1, 13, JOF_BYTE) +OPDEF(JSOP_ADDATTRVAL, 169,"addattrval", NULL, 1, 2, 1, 13, JOF_BYTE) +OPDEF(JSOP_BINDXMLNAME, 170,"bindxmlname",NULL, 1, 1, 2, 3, JOF_BYTE|JOF_SET) +OPDEF(JSOP_SETXMLNAME, 171,"setxmlname", NULL, 1, 3, 1, 3, JOF_BYTE|JOF_SET|JOF_DETECTING) +OPDEF(JSOP_XMLNAME, 172,"xmlname", NULL, 1, 1, 1, 19, JOF_BYTE) +OPDEF(JSOP_DESCENDANTS, 173,"descendants",NULL, 1, 2, 1, 18, JOF_BYTE) +OPDEF(JSOP_FILTER, 174,"filter", NULL, 3, 1, 1, 0, JOF_JUMP) +OPDEF(JSOP_ENDFILTER, 175,"endfilter", NULL, 3, 2, 1, 18, JOF_JUMP) +OPDEF(JSOP_TOXML, 176,"toxml", NULL, 1, 1, 1, 19, JOF_BYTE) +OPDEF(JSOP_TOXMLLIST, 177,"toxmllist", NULL, 1, 1, 1, 19, JOF_BYTE) +OPDEF(JSOP_XMLTAGEXPR, 178,"xmltagexpr", NULL, 1, 1, 1, 0, JOF_BYTE) +OPDEF(JSOP_XMLELTEXPR, 179,"xmleltexpr", NULL, 1, 1, 1, 0, JOF_BYTE) +OPDEF(JSOP_XMLOBJECT, 180,"xmlobject", NULL, 3, 0, 1, 19, JOF_OBJECT) +OPDEF(JSOP_XMLCDATA, 181,"xmlcdata", NULL, 3, 0, 1, 19, JOF_ATOM) +OPDEF(JSOP_XMLCOMMENT, 182,"xmlcomment", NULL, 3, 0, 1, 19, JOF_ATOM) +OPDEF(JSOP_XMLPI, 183,"xmlpi", NULL, 3, 1, 1, 19, JOF_ATOM) +OPDEF(JSOP_CALLPROP, 184,"callprop", NULL, 3, 1, 2, 18, JOF_ATOM|JOF_PROP|JOF_CALLOP) + +/* + * Get a display (free) variable from the closure's reserved slots. + */ +OPDEF(JSOP_GETUPVAR, 185,"getupvar", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME) +OPDEF(JSOP_CALLUPVAR, 186,"callupvar", NULL, 3, 0, 2, 19, JOF_UINT16|JOF_NAME|JOF_CALLOP) + +OPDEF(JSOP_DELDESC, 187,"deldesc", NULL, 1, 2, 1, 15, JOF_BYTE|JOF_ELEM|JOF_DEL) + +/* + * Opcode to hold 24-bit immediate integer operands. + */ +OPDEF(JSOP_UINT24, 188,"uint24", NULL, 4, 0, 1, 16, JOF_UINT24) + +/* + * Opcodes to allow 24-bit atom or object indexes. Whenever an index exceeds + * the 16-bit limit, the index-accessing bytecode must be bracketed by + * JSOP_INDEXBASE and JSOP_RESETBASE to provide the upper bits of the index. + * See jsemit.c, EmitIndexOp. + */ +OPDEF(JSOP_INDEXBASE, 189,"atombase", NULL, 2, 0, 0, 0, JOF_UINT8|JOF_INDEXBASE) +OPDEF(JSOP_RESETBASE, 190,"resetbase", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_RESETBASE0, 191,"resetbase0", NULL, 1, 0, 0, 0, JOF_BYTE) + +/* + * Opcodes to help the decompiler deal with XML. + */ +OPDEF(JSOP_STARTXML, 192,"startxml", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_STARTXMLEXPR, 193,"startxmlexpr",NULL, 1, 0, 0, 0, JOF_BYTE) + +OPDEF(JSOP_CALLELEM, 194, "callelem", NULL, 1, 2, 2, 18, JOF_BYTE |JOF_ELEM|JOF_LEFTASSOC|JOF_CALLOP) + +/* + * Stop interpretation, emitted at end of script to save the threaded bytecode + * interpreter an extra branch test on every DO_NEXT_OP (see jsinterp.c). + */ +OPDEF(JSOP_STOP, 195,"stop", NULL, 1, 0, 0, 0, JOF_BYTE) + +/* + * Get an extant property value, throwing ReferenceError if the identified + * property does not exist. + */ +OPDEF(JSOP_GETXPROP, 196,"getxprop", NULL, 3, 1, 1, 18, JOF_ATOM|JOF_PROP) + +OPDEF(JSOP_CALLXMLNAME, 197, "callxmlname", NULL, 1, 1, 2, 19, JOF_BYTE|JOF_CALLOP) + +/* + * Specialized JSOP_TYPEOF to avoid reporting undefined for typeof(0, undef). + */ +OPDEF(JSOP_TYPEOFEXPR, 198,"typeofexpr", NULL, 1, 1, 1, 15, JOF_BYTE|JOF_DETECTING) + +/* + * Block-local scope support. + */ +OPDEF(JSOP_ENTERBLOCK, 199,"enterblock", NULL, 3, 0, -1, 0, JOF_OBJECT) +OPDEF(JSOP_LEAVEBLOCK, 200,"leaveblock", NULL, 3, -1, 0, 0, JOF_UINT16) + +/* Jump to target if top of stack value is of primitive type. */ +OPDEF(JSOP_IFPRIMTOP, 201,"ifprimtop", NULL, 3, 1, 1, 0, JOF_JUMP|JOF_DETECTING) + +/* Throws a TypeError if the value at the top of the stack is not primitive. */ +OPDEF(JSOP_PRIMTOP, 202,"primtop", NULL, 2, 1, 1, 0, JOF_INT8) + +/* + * Generator and array comprehension support. + */ +OPDEF(JSOP_GENERATOR, 203,"generator", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_YIELD, 204,"yield", NULL, 1, 1, 1, 1, JOF_BYTE) +OPDEF(JSOP_ARRAYPUSH, 205,"arraypush", NULL, 3, 1, 0, 3, JOF_LOCAL) + +/* + * Get the built-in function::foo namespace and push it. + */ +OPDEF(JSOP_GETFUNNS, 206,"getfunns", NULL, 1, 0, 1, 19, JOF_BYTE) + +/* + * Variant of JSOP_ENUMELEM for destructuring const (const [a, b] = ...). + */ +OPDEF(JSOP_ENUMCONSTELEM, 207,"enumconstelem",NULL, 1, 3, 0, 3, JOF_BYTE|JOF_SET) + +/* + * Variant of JSOP_LEAVEBLOCK has a result on the stack above the locals, + * which must be moved down when the block pops. + */ +OPDEF(JSOP_LEAVEBLOCKEXPR,208,"leaveblockexpr",NULL, 3, -1, 1, 3, JOF_UINT16) + +/* + * Optimize common JSOP_{THIS,GET{ARG,LOCAL}} -> JSOP_GETPROP cliches. + */ +OPDEF(JSOP_GETTHISPROP, 209,"getthisprop", NULL, 3, 0, 1, 18, JOF_ATOM|JOF_VARPROP) +OPDEF(JSOP_GETARGPROP, 210,"getargprop", NULL, 5, 0, 1, 18, JOF_SLOTATOM|JOF_VARPROP) +OPDEF(JSOP_GETLOCALPROP, 211,"getlocalprop", NULL, 5, 0, 1, 18, JOF_SLOTATOM|JOF_VARPROP) + +/* + * Optimize atom segments 1-3. These must be followed by JSOP_RESETBASE0 after + * the opcode that they prefix. + */ +OPDEF(JSOP_INDEXBASE1, 212,"atombase1", NULL, 1, 0, 0, 0, JOF_BYTE |JOF_INDEXBASE) +OPDEF(JSOP_INDEXBASE2, 213,"atombase2", NULL, 1, 0, 0, 0, JOF_BYTE |JOF_INDEXBASE) +OPDEF(JSOP_INDEXBASE3, 214,"atombase3", NULL, 1, 0, 0, 0, JOF_BYTE |JOF_INDEXBASE) + +OPDEF(JSOP_CALLGVAR, 215, "callgvar", NULL, 3, 0, 2, 19, JOF_ATOM|JOF_NAME|JOF_CALLOP) +OPDEF(JSOP_CALLLOCAL, 216, "calllocal", NULL, 3, 0, 2, 19, JOF_LOCAL|JOF_NAME|JOF_CALLOP) +OPDEF(JSOP_CALLARG, 217, "callarg", NULL, 3, 0, 2, 19, JOF_QARG |JOF_NAME|JOF_CALLOP) +OPDEF(JSOP_CALLBUILTIN, 218, "callbuiltin", NULL, 3, 1, 2, 0, JOF_UINT16) + +/* + * Opcodes to hold 8-bit and 32-bit immediate integer operands. + */ +OPDEF(JSOP_INT8, 219, "int8", NULL, 2, 0, 1, 16, JOF_INT8) +OPDEF(JSOP_INT32, 220, "int32", NULL, 5, 0, 1, 16, JOF_INT32) + +/* + * Get the value of the 'length' property from a stacked object. + */ +OPDEF(JSOP_LENGTH, 221, "length", NULL, 1, 1, 1, 18, JOF_BYTE|JOF_PROP) + +/* + * Construct a new dense array whose contents are the values provided on the + * stack, consuming those values and replacing them with the newly-constructed + * array. The topmost value is the last value in the new array, and the + * bottommost value is the first value in the array; the array length is a + * 16-bit immediate operand to the instruction. + */ +OPDEF(JSOP_NEWARRAY, 222, "newarray", NULL, 3, -1, 1, 19, JOF_UINT16) + +/* + * Push a JSVAL_HOLE value onto the stack, representing an omitted property in + * an array literal (e.g. property 0 in the array [, 1]). This opcode is used + * with the JSOP_NEWARRAY and JSOP_NEWINIT opcodes. + */ +OPDEF(JSOP_HOLE, 223, "hole", NULL, 1, 0, 1, 0, JOF_BYTE) + +/* + * Variants of JSOP_{DEF{,LOCAL}FUN,LAMBDA} optimized for the flat closure case. + */ +OPDEF(JSOP_DEFFUN_FC, 224,"deffun_fc", NULL, 3, 0, 0, 0, JOF_OBJECT|JOF_DECLARING) +OPDEF(JSOP_DEFLOCALFUN_FC,225,"deflocalfun_fc",NULL, 5, 0, 0, 0, JOF_SLOTOBJECT|JOF_DECLARING) +OPDEF(JSOP_LAMBDA_FC, 226,"lambda_fc", NULL, 3, 0, 1, 19, JOF_OBJECT) + +/* + * Ensure that the value on the top of the stack is an object. The one + * argument is an error message, defined in js.msg, that takes one parameter + * (the decompilation of the primitive value). + */ +OPDEF(JSOP_OBJTOP, 227,"objtop", NULL, 3, 0, 0, 0, JOF_UINT16) + +OPDEF(JSOP_TRACE, 228, "trace", NULL, 1, 0, 0, 0, JOF_BYTE) + +/* + * Debugger versions of JSOP_{GET,CALL}UPVAR and the flat closure (_FC) ops. + */ +OPDEF(JSOP_GETUPVAR_DBG, 229,"getupvar_dbg", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME) +OPDEF(JSOP_CALLUPVAR_DBG, 230,"callupvar_dbg", NULL, 3, 0, 2, 19, JOF_UINT16|JOF_NAME|JOF_CALLOP) +OPDEF(JSOP_DEFFUN_DBGFC, 231,"deffun_dbgfc", NULL, 3, 0, 0, 0, JOF_OBJECT|JOF_DECLARING) +OPDEF(JSOP_DEFLOCALFUN_DBGFC,232,"deflocalfun_dbgfc",NULL, 5, 0, 0, 0, JOF_SLOTOBJECT|JOF_DECLARING) +OPDEF(JSOP_LAMBDA_DBGFC, 233,"lambda_dbgfc", NULL, 3, 0, 1, 19, JOF_OBJECT) + +/* + * Concatenate N values, coercing to string if necessary, where N is concatn's + * immediate. See record_JSOP_CONCATN for recording behavior. + */ +OPDEF(JSOP_CONCATN, 234,"concatn", NULL, 3, -1, 1, 13, JOF_UINT16|JOF_TMPSLOT2) + +/* + * Joined function object as method optimization support. + */ +OPDEF(JSOP_SETMETHOD, 235,"setmethod", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) +OPDEF(JSOP_INITMETHOD, 236,"initmethod", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) + +OPDEF(JSOP_SHARPINIT, 237,"sharpinit", NULL, 3, 0, 0, 0, JOF_UINT16|JOF_SHARPSLOT) diff --git a/ape-server/deps/js/src/jsoplengen.cpp b/ape-server/deps/js/src/jsoplengen.cpp new file mode 100755 index 0000000..683ea24 --- /dev/null +++ b/ape-server/deps/js/src/jsoplengen.cpp @@ -0,0 +1,121 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set sw=4 ts=8 et tw=80: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is String Switch Generator for JavaScript Keywords, + * released 2005-12-09. + * + * The Initial Developer of the Original Code is + * Igor Bukanov. + * Portions created by the Initial Developer are Copyright (C) 2005-2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include +#include +#include + +static const struct { + const char *name; + int length; +} pairs[] = { +#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ + { #op, length } , +#include "jsopcode.tbl" +#undef OPDEF +}; + +int +main(int argc, char **argv) +{ + FILE *fp; + size_t maxNameWidth, i, nameWidth, tabStop; + int lengthGap; + + static const char prefix[] = "#define "; + static const char suffix[] = "_LENGTH"; + static const size_t tabWidth = 8; + static const size_t prefixWidth = sizeof(prefix) - 1; + static const size_t suffixWidth = sizeof(suffix) - 1; + + if (argc != 2) { + fputs("Bad usage\n", stderr); + return EXIT_FAILURE; + } + + fp = fopen(argv[1], "w"); + if (!fp) { + perror("fopen"); + return EXIT_FAILURE; + } + fputs("/*\n" + " * Automatically generated header with JS opcode length constants.\n" + " *\n" + " * Do not edit it, alter jsopcode.tbl instead.\n" + " */\n", + fp); + + /* + * Print + * + * #define name_LENGTH length + * + * with all length values aligned on the same column. The column is at the + * second character position after a tab-stop with the first position + * reserved for the minus sign of variable-length opcodes. + */ + maxNameWidth = 0; + for (i = 0; i != sizeof pairs / sizeof pairs[0]; ++i) { + nameWidth = strlen(pairs[i].name); + if (maxNameWidth < nameWidth) + maxNameWidth = nameWidth; + } + + tabStop = prefixWidth + maxNameWidth + suffixWidth + 1; + tabStop = (tabStop + tabWidth - 1) / tabWidth * tabWidth; + for (i = 0; i != sizeof pairs / sizeof pairs[0]; ++i) { + lengthGap = (int) (tabStop - prefixWidth - strlen(pairs[i].name) - + suffixWidth); + fprintf(fp, "%s%s%s%*c%2d\n", + prefix, pairs[i].name, suffix, lengthGap, ' ', + pairs[i].length); + if (ferror(fp)) { + perror("fclose"); + exit(EXIT_FAILURE); + } + } + + if (fclose(fp)) { + perror("fclose"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/ape-server/deps/js/src/jsops.cpp b/ape-server/deps/js/src/jsops.cpp new file mode 100755 index 0000000..997bc9e --- /dev/null +++ b/ape-server/deps/js/src/jsops.cpp @@ -0,0 +1,4297 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=79: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* This file needs to be included in possibly multiple places. */ + +#if JS_THREADED_INTERP + interrupt: +#else /* !JS_THREADED_INTERP */ + case -1: + JS_ASSERT(switchMask == -1); +#endif /* !JS_THREADED_INTERP */ + { + bool moreInterrupts = false; + JSTrapHandler handler = cx->debugHooks->interruptHandler; + if (handler) { +#ifdef JS_TRACER + if (TRACE_RECORDER(cx)) + js_AbortRecording(cx, "interrupt handler"); +#endif + switch (handler(cx, script, regs.pc, &rval, + cx->debugHooks->interruptHandlerData)) { + case JSTRAP_ERROR: + goto error; + case JSTRAP_CONTINUE: + break; + case JSTRAP_RETURN: + fp->rval = rval; + ok = JS_TRUE; + goto forced_return; + case JSTRAP_THROW: + cx->throwing = JS_TRUE; + cx->exception = rval; + goto error; + default:; + } + moreInterrupts = true; + } + +#ifdef JS_TRACER + if (TraceRecorder* tr = TRACE_RECORDER(cx)) { + AbortableRecordingStatus status = tr->monitorRecording(op); + switch (status) { + case ARECORD_CONTINUE: + moreInterrupts = true; + break; + case ARECORD_IMACRO: + atoms = COMMON_ATOMS_START(&rt->atomState); + op = JSOp(*regs.pc); + DO_OP(); /* keep interrupting for op. */ + break; + case ARECORD_ERROR: + // The code at 'error:' aborts the recording. + goto error; + case ARECORD_ABORTED: + case ARECORD_COMPLETED: + break; + case ARECORD_STOP: + /* A 'stop' error should have already aborted recording. */ + default: + JS_NOT_REACHED("Bad recording status"); + } + } +#endif /* !JS_TRACER */ + +#if JS_THREADED_INTERP +#ifdef MOZ_TRACEVIS + if (!moreInterrupts) + js_ExitTraceVisState(cx, R_ABORT); +#endif + jumpTable = moreInterrupts ? interruptJumpTable : normalJumpTable; + JS_EXTENSION_(goto *normalJumpTable[op]); +#else + switchMask = moreInterrupts ? -1 : 0; + switchOp = intN(op); + goto do_switch; +#endif + } + +/* No-ops for ease of decompilation. */ +ADD_EMPTY_CASE(JSOP_NOP) +ADD_EMPTY_CASE(JSOP_CONDSWITCH) +ADD_EMPTY_CASE(JSOP_TRY) +ADD_EMPTY_CASE(JSOP_TRACE) +#if JS_HAS_XML_SUPPORT +ADD_EMPTY_CASE(JSOP_STARTXML) +ADD_EMPTY_CASE(JSOP_STARTXMLEXPR) +#endif +END_EMPTY_CASES + +/* ADD_EMPTY_CASE is not used here as JSOP_LINENO_LENGTH == 3. */ +BEGIN_CASE(JSOP_LINENO) +END_CASE(JSOP_LINENO) + +BEGIN_CASE(JSOP_PUSH) + PUSH_OPND(JSVAL_VOID); +END_CASE(JSOP_PUSH) + +BEGIN_CASE(JSOP_POP) + regs.sp--; +END_CASE(JSOP_POP) + +BEGIN_CASE(JSOP_POPN) + regs.sp -= GET_UINT16(regs.pc); +#ifdef DEBUG + JS_ASSERT(StackBase(fp) <= regs.sp); + obj = fp->blockChain; + JS_ASSERT_IF(obj, + OBJ_BLOCK_DEPTH(cx, obj) + OBJ_BLOCK_COUNT(cx, obj) + <= (size_t) (regs.sp - StackBase(fp))); + for (obj = fp->scopeChain; obj; obj = OBJ_GET_PARENT(cx, obj)) { + clasp = OBJ_GET_CLASS(cx, obj); + if (clasp != &js_BlockClass && clasp != &js_WithClass) + continue; + if (obj->getPrivate() != fp) + break; + JS_ASSERT(StackBase(fp) + OBJ_BLOCK_DEPTH(cx, obj) + + ((clasp == &js_BlockClass) + ? OBJ_BLOCK_COUNT(cx, obj) + : 1) + <= regs.sp); + } +#endif +END_CASE(JSOP_POPN) + +BEGIN_CASE(JSOP_SETRVAL) +BEGIN_CASE(JSOP_POPV) + ASSERT_NOT_THROWING(cx); + fp->rval = POP_OPND(); +END_CASE(JSOP_POPV) + +BEGIN_CASE(JSOP_ENTERWITH) + if (!js_EnterWith(cx, -1)) + goto error; + + /* + * We must ensure that different "with" blocks have different stack depth + * associated with them. This allows the try handler search to properly + * recover the scope chain. Thus we must keep the stack at least at the + * current level. + * + * We set sp[-1] to the current "with" object to help asserting the + * enter/leave balance in [leavewith]. + */ + regs.sp[-1] = OBJECT_TO_JSVAL(fp->scopeChain); +END_CASE(JSOP_ENTERWITH) + +BEGIN_CASE(JSOP_LEAVEWITH) + JS_ASSERT(regs.sp[-1] == OBJECT_TO_JSVAL(fp->scopeChain)); + regs.sp--; + js_LeaveWith(cx); +END_CASE(JSOP_LEAVEWITH) + +BEGIN_CASE(JSOP_RETURN) + fp->rval = POP_OPND(); + /* FALL THROUGH */ + +BEGIN_CASE(JSOP_RETRVAL) /* fp->rval already set */ +BEGIN_CASE(JSOP_STOP) + /* + * When the inlined frame exits with an exception or an error, ok will be + * false after the inline_return label. + */ + ASSERT_NOT_THROWING(cx); + CHECK_BRANCH(); + + if (fp->imacpc) { + /* + * If we are at the end of an imacro, return to its caller in the + * current frame. + */ + JS_ASSERT(op == JSOP_STOP); + + end_imacro: + JS_ASSERT((uintN)(regs.sp - fp->slots) <= script->nslots); + regs.pc = fp->imacpc + js_CodeSpec[*fp->imacpc].length; + fp->imacpc = NULL; + atoms = script->atomMap.vector; + op = JSOp(*regs.pc); + DO_OP(); + } + + JS_ASSERT(regs.sp == StackBase(fp)); + if ((fp->flags & JSFRAME_CONSTRUCTING) && + JSVAL_IS_PRIMITIVE(fp->rval)) { + if (!fp->fun) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_NEW_RESULT, + js_ValueToPrintableString(cx, rval)); + goto error; + } + fp->rval = fp->thisv; + } + ok = JS_TRUE; + if (inlineCallCount) + inline_return: + { + JSInlineFrame *ifp = (JSInlineFrame *) fp; + void *hookData = ifp->hookData; + + JS_ASSERT(!fp->blockChain); + JS_ASSERT(!js_IsActiveWithOrBlock(cx, fp->scopeChain, 0)); + + if (script->staticLevel < JS_DISPLAY_SIZE) + cx->display[script->staticLevel] = fp->displaySave; + + if (hookData) { + JSInterpreterHook hook; + JSBool status; + + hook = cx->debugHooks->callHook; + if (hook) { + /* + * Do not pass &ok directly as exposing the address inhibits + * optimizations and uninitialised warnings. + */ + status = ok; + hook(cx, fp, JS_FALSE, &status, hookData); + ok = status; + CHECK_INTERRUPT_HANDLER(); + } + } + + /* + * If fp has a call object, sync values and clear the back- + * pointer. This can happen for a lightweight function if it calls eval + * unexpectedly (in a way that is hidden from the compiler). See bug + * 325540. + */ + fp->putActivationObjects(cx); + +#ifdef INCLUDE_MOZILLA_DTRACE + /* DTrace function return, inlines */ + if (JAVASCRIPT_FUNCTION_RVAL_ENABLED()) + jsdtrace_function_rval(cx, fp, fp->fun, &fp->rval); + if (JAVASCRIPT_FUNCTION_RETURN_ENABLED()) + jsdtrace_function_return(cx, fp, fp->fun); +#endif + + /* Restore context version only if callee hasn't set version. */ + if (JS_LIKELY(cx->version == currentVersion)) { + currentVersion = ifp->callerVersion; + if (currentVersion != cx->version) + js_SetVersion(cx, currentVersion); + } + + /* + * If inline-constructing, replace primitive rval with the new object + * passed in via |this|, and instrument this constructor invocation. + */ + if (fp->flags & JSFRAME_CONSTRUCTING) { + if (JSVAL_IS_PRIMITIVE(fp->rval)) + fp->rval = fp->thisv; + JS_RUNTIME_METER(cx->runtime, constructs); + } + + /* Restore caller's registers. */ + regs = ifp->callerRegs; + + /* Store the return value in the caller's operand frame. */ + regs.sp -= 1 + (size_t) ifp->frame.argc; + regs.sp[-1] = fp->rval; + + bool recursive = fp->script == fp->down->script; + + /* Restore cx->fp and release the inline frame's space. */ + cx->fp = fp = fp->down; + JS_ASSERT(fp->regs == &ifp->callerRegs); + fp->regs = ®s; + JS_ARENA_RELEASE(&cx->stackPool, ifp->mark); + + /* Restore the calling script's interpreter registers. */ + script = fp->script; + atoms = FrameAtomBase(cx, fp); + + /* Resume execution in the calling frame. */ + inlineCallCount--; + if (JS_LIKELY(ok)) { + JS_ASSERT(js_CodeSpec[js_GetOpcode(cx, script, regs.pc)].length + == JSOP_CALL_LENGTH); + TRACE_0(LeaveFrame); + if (!TRACE_RECORDER(cx) && recursive) { + if (*(regs.pc + JSOP_CALL_LENGTH) == JSOP_TRACE) { + regs.pc += JSOP_CALL_LENGTH; + MONITOR_BRANCH(Record_LeaveFrame); + op = (JSOp)*regs.pc; + DO_OP(); + } + } + if (*(regs.pc + JSOP_CALL_LENGTH) == JSOP_TRACE || + *(regs.pc + JSOP_CALL_LENGTH) == JSOP_NOP) { + JS_STATIC_ASSERT(JSOP_TRACE_LENGTH == JSOP_NOP_LENGTH); + regs.pc += JSOP_CALL_LENGTH; + len = JSOP_TRACE_LENGTH; + } else { + len = JSOP_CALL_LENGTH; + } + DO_NEXT_OP(len); + } + goto error; + } + goto exit; + +BEGIN_CASE(JSOP_DEFAULT) + (void) POP(); + /* FALL THROUGH */ +BEGIN_CASE(JSOP_GOTO) + len = GET_JUMP_OFFSET(regs.pc); + BRANCH(len); +END_CASE(JSOP_GOTO) + +BEGIN_CASE(JSOP_IFEQ) + POP_BOOLEAN(cx, rval, cond); + if (cond == JS_FALSE) { + len = GET_JUMP_OFFSET(regs.pc); + BRANCH(len); + } +END_CASE(JSOP_IFEQ) + +BEGIN_CASE(JSOP_IFNE) + POP_BOOLEAN(cx, rval, cond); + if (cond != JS_FALSE) { + len = GET_JUMP_OFFSET(regs.pc); + BRANCH(len); + } +END_CASE(JSOP_IFNE) + +BEGIN_CASE(JSOP_OR) + POP_BOOLEAN(cx, rval, cond); + if (cond == JS_TRUE) { + len = GET_JUMP_OFFSET(regs.pc); + PUSH_OPND(rval); + DO_NEXT_OP(len); + } +END_CASE(JSOP_OR) + +BEGIN_CASE(JSOP_AND) + POP_BOOLEAN(cx, rval, cond); + if (cond == JS_FALSE) { + len = GET_JUMP_OFFSET(regs.pc); + PUSH_OPND(rval); + DO_NEXT_OP(len); + } +END_CASE(JSOP_AND) + +BEGIN_CASE(JSOP_DEFAULTX) + (void) POP(); + /* FALL THROUGH */ +BEGIN_CASE(JSOP_GOTOX) + len = GET_JUMPX_OFFSET(regs.pc); + BRANCH(len); +END_CASE(JSOP_GOTOX); + +BEGIN_CASE(JSOP_IFEQX) + POP_BOOLEAN(cx, rval, cond); + if (cond == JS_FALSE) { + len = GET_JUMPX_OFFSET(regs.pc); + BRANCH(len); + } +END_CASE(JSOP_IFEQX) + +BEGIN_CASE(JSOP_IFNEX) + POP_BOOLEAN(cx, rval, cond); + if (cond != JS_FALSE) { + len = GET_JUMPX_OFFSET(regs.pc); + BRANCH(len); + } +END_CASE(JSOP_IFNEX) + +BEGIN_CASE(JSOP_ORX) + POP_BOOLEAN(cx, rval, cond); + if (cond == JS_TRUE) { + len = GET_JUMPX_OFFSET(regs.pc); + PUSH_OPND(rval); + DO_NEXT_OP(len); + } +END_CASE(JSOP_ORX) + +BEGIN_CASE(JSOP_ANDX) + POP_BOOLEAN(cx, rval, cond); + if (cond == JS_FALSE) { + len = GET_JUMPX_OFFSET(regs.pc); + PUSH_OPND(rval); + DO_NEXT_OP(len); + } +END_CASE(JSOP_ANDX) + +/* + * If the index value at sp[n] is not an int that fits in a jsval, it could + * be an object (an XML QName, AttributeName, or AnyName), but only if we are + * compiling with JS_HAS_XML_SUPPORT. Otherwise convert the index value to a + * string atom id. + */ +#define FETCH_ELEMENT_ID(obj, n, id) \ + JS_BEGIN_MACRO \ + jsval idval_ = FETCH_OPND(n); \ + if (JSVAL_IS_INT(idval_)) { \ + id = INT_JSVAL_TO_JSID(idval_); \ + } else { \ + if (!js_InternNonIntElementId(cx, obj, idval_, &id)) \ + goto error; \ + regs.sp[n] = ID_TO_VALUE(id); \ + } \ + JS_END_MACRO + +#define TRY_BRANCH_AFTER_COND(cond,spdec) \ + JS_BEGIN_MACRO \ + uintN diff_; \ + JS_ASSERT(js_CodeSpec[op].length == 1); \ + diff_ = (uintN) regs.pc[1] - (uintN) JSOP_IFEQ; \ + if (diff_ <= 1) { \ + regs.sp -= spdec; \ + if (cond == (diff_ != 0)) { \ + ++regs.pc; \ + len = GET_JUMP_OFFSET(regs.pc); \ + BRANCH(len); \ + } \ + len = 1 + JSOP_IFEQ_LENGTH; \ + DO_NEXT_OP(len); \ + } \ + JS_END_MACRO + +BEGIN_CASE(JSOP_IN) + rval = FETCH_OPND(-1); + if (JSVAL_IS_PRIMITIVE(rval)) { + js_ReportValueError(cx, JSMSG_IN_NOT_OBJECT, -1, rval, NULL); + goto error; + } + obj = JSVAL_TO_OBJECT(rval); + FETCH_ELEMENT_ID(obj, -2, id); + if (!obj->lookupProperty(cx, id, &obj2, &prop)) + goto error; + cond = prop != NULL; + if (prop) + obj2->dropProperty(cx, prop); + TRY_BRANCH_AFTER_COND(cond, 2); + regs.sp--; + STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); +END_CASE(JSOP_IN) + +BEGIN_CASE(JSOP_ITER) + JS_ASSERT(regs.sp > StackBase(fp)); + flags = regs.pc[1]; + if (!js_ValueToIterator(cx, flags, ®s.sp[-1])) + goto error; + CHECK_INTERRUPT_HANDLER(); + JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-1])); + PUSH(JSVAL_VOID); +END_CASE(JSOP_ITER) + +BEGIN_CASE(JSOP_NEXTITER) + JS_ASSERT(regs.sp - 2 >= StackBase(fp)); + JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-2])); + if (!js_CallIteratorNext(cx, JSVAL_TO_OBJECT(regs.sp[-2]), ®s.sp[-1])) + goto error; + CHECK_INTERRUPT_HANDLER(); + rval = BOOLEAN_TO_JSVAL(regs.sp[-1] != JSVAL_HOLE); + PUSH(rval); +END_CASE(JSOP_NEXTITER) + +BEGIN_CASE(JSOP_ENDITER) + /* + * Decrease the stack pointer even when !ok -- see comments in the + * exception capturing code for details. + */ + JS_ASSERT(regs.sp - 2 >= StackBase(fp)); + ok = js_CloseIterator(cx, regs.sp[-2]); + regs.sp -= 2; + if (!ok) + goto error; +END_CASE(JSOP_ENDITER) + +BEGIN_CASE(JSOP_FORARG) + JS_ASSERT(regs.sp - 2 >= StackBase(fp)); + slot = GET_ARGNO(regs.pc); + JS_ASSERT(slot < fp->fun->nargs); + fp->argv[slot] = regs.sp[-1]; +END_CASE(JSOP_FORARG) + +BEGIN_CASE(JSOP_FORLOCAL) + JS_ASSERT(regs.sp - 2 >= StackBase(fp)); + slot = GET_SLOTNO(regs.pc); + JS_ASSERT(slot < fp->script->nslots); + fp->slots[slot] = regs.sp[-1]; +END_CASE(JSOP_FORLOCAL) + +BEGIN_CASE(JSOP_FORNAME) + JS_ASSERT(regs.sp - 2 >= StackBase(fp)); + LOAD_ATOM(0); + id = ATOM_TO_JSID(atom); + if (!js_FindProperty(cx, id, &obj, &obj2, &prop)) + goto error; + if (prop) + obj2->dropProperty(cx, prop); + ok = obj->setProperty(cx, id, ®s.sp[-1]); + if (!ok) + goto error; +END_CASE(JSOP_FORNAME) + +BEGIN_CASE(JSOP_FORPROP) + JS_ASSERT(regs.sp - 2 >= StackBase(fp)); + LOAD_ATOM(0); + id = ATOM_TO_JSID(atom); + FETCH_OBJECT(cx, -1, lval, obj); + ok = obj->setProperty(cx, id, ®s.sp[-2]); + if (!ok) + goto error; + regs.sp--; +END_CASE(JSOP_FORPROP) + +BEGIN_CASE(JSOP_FORELEM) + /* + * JSOP_FORELEM simply dups the property identifier at top of stack and + * lets the subsequent JSOP_ENUMELEM opcode sequence handle the left-hand + * side expression evaluation and assignment. This opcode exists solely to + * help the decompiler. + */ + JS_ASSERT(regs.sp - 2 >= StackBase(fp)); + rval = FETCH_OPND(-1); + PUSH(rval); +END_CASE(JSOP_FORELEM) + +BEGIN_CASE(JSOP_DUP) + JS_ASSERT(regs.sp > StackBase(fp)); + rval = FETCH_OPND(-1); + PUSH(rval); +END_CASE(JSOP_DUP) + +BEGIN_CASE(JSOP_DUP2) + JS_ASSERT(regs.sp - 2 >= StackBase(fp)); + lval = FETCH_OPND(-2); + rval = FETCH_OPND(-1); + PUSH(lval); + PUSH(rval); +END_CASE(JSOP_DUP2) + +BEGIN_CASE(JSOP_SWAP) + JS_ASSERT(regs.sp - 2 >= StackBase(fp)); + lval = FETCH_OPND(-2); + rval = FETCH_OPND(-1); + STORE_OPND(-1, lval); + STORE_OPND(-2, rval); +END_CASE(JSOP_SWAP) + +BEGIN_CASE(JSOP_PICK) + i = regs.pc[1]; + JS_ASSERT(regs.sp - (i+1) >= StackBase(fp)); + lval = regs.sp[-(i+1)]; + memmove(regs.sp - (i+1), regs.sp - i, sizeof(jsval)*i); + regs.sp[-1] = lval; +END_CASE(JSOP_PICK) + +#define PROPERTY_OP(n, call) \ + JS_BEGIN_MACRO \ + /* Fetch the left part and resolve it to a non-null object. */ \ + FETCH_OBJECT(cx, n, lval, obj); \ + \ + /* Get or set the property. */ \ + if (!call) \ + goto error; \ + JS_END_MACRO + +#define ELEMENT_OP(n, call) \ + JS_BEGIN_MACRO \ + /* Fetch the left part and resolve it to a non-null object. */ \ + FETCH_OBJECT(cx, n - 1, lval, obj); \ + \ + /* Fetch index and convert it to id suitable for use with obj. */ \ + FETCH_ELEMENT_ID(obj, n, id); \ + \ + /* Get or set the element. */ \ + if (!call) \ + goto error; \ + JS_END_MACRO + +#define NATIVE_GET(cx,obj,pobj,sprop,getHow,vp) \ + JS_BEGIN_MACRO \ + if (SPROP_HAS_STUB_GETTER(sprop)) { \ + /* Fast path for Object instance properties. */ \ + JS_ASSERT((sprop)->slot != SPROP_INVALID_SLOT || \ + !SPROP_HAS_STUB_SETTER(sprop)); \ + *vp = ((sprop)->slot != SPROP_INVALID_SLOT) \ + ? LOCKED_OBJ_GET_SLOT(pobj, (sprop)->slot) \ + : JSVAL_VOID; \ + } else { \ + if (!js_NativeGet(cx, obj, pobj, sprop, getHow, vp)) \ + goto error; \ + } \ + JS_END_MACRO + +#define NATIVE_SET(cx,obj,sprop,entry,vp) \ + JS_BEGIN_MACRO \ + TRACE_2(SetPropHit, entry, sprop); \ + if (SPROP_HAS_STUB_SETTER(sprop) && \ + (sprop)->slot != SPROP_INVALID_SLOT && \ + !OBJ_SCOPE(obj)->brandedOrHasMethodBarrier()) { \ + /* Fast path for, e.g., plain Object instance properties. */ \ + LOCKED_OBJ_SET_SLOT(obj, (sprop)->slot, *vp); \ + } else { \ + if (!js_NativeSet(cx, obj, sprop, false, vp)) \ + goto error; \ + } \ + JS_END_MACRO + +/* + * Skip the JSOP_POP typically found after a JSOP_SET* opcode, where oplen is + * the constant length of the SET opcode sequence, and spdec is the constant + * by which to decrease the stack pointer to pop all of the SET op's operands. + * + * NB: unlike macros that could conceivably be replaced by functions (ignoring + * goto error), where a call should not have to be braced in order to expand + * correctly (e.g., in if (cond) FOO(); else BAR()), these three macros lack + * JS_{BEGIN,END}_MACRO brackets. They are also indented so as to align with + * nearby opcode code. + */ +#define SKIP_POP_AFTER_SET(oplen,spdec) \ + if (regs.pc[oplen] == JSOP_POP) { \ + regs.sp -= spdec; \ + regs.pc += oplen + JSOP_POP_LENGTH; \ + op = (JSOp) *regs.pc; \ + DO_OP(); \ + } + +#define END_SET_CASE(OP) \ + SKIP_POP_AFTER_SET(OP##_LENGTH, 1); \ + END_CASE(OP) + +#define END_SET_CASE_STORE_RVAL(OP,spdec) \ + SKIP_POP_AFTER_SET(OP##_LENGTH, spdec); \ + rval = FETCH_OPND(-1); \ + regs.sp -= (spdec) - 1; \ + STORE_OPND(-1, rval); \ + END_CASE(OP) + +BEGIN_CASE(JSOP_SETCONST) + LOAD_ATOM(0); + obj = fp->varobj; + rval = FETCH_OPND(-1); + if (!obj->defineProperty(cx, ATOM_TO_JSID(atom), rval, + JS_PropertyStub, JS_PropertyStub, + JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY)) { + goto error; + } +END_SET_CASE(JSOP_SETCONST); + +#if JS_HAS_DESTRUCTURING +BEGIN_CASE(JSOP_ENUMCONSTELEM) + rval = FETCH_OPND(-3); + FETCH_OBJECT(cx, -2, lval, obj); + FETCH_ELEMENT_ID(obj, -1, id); + if (!obj->defineProperty(cx, id, rval, + JS_PropertyStub, JS_PropertyStub, + JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY)) { + goto error; + } + regs.sp -= 3; +END_CASE(JSOP_ENUMCONSTELEM) +#endif + +BEGIN_CASE(JSOP_BINDNAME) + do { + JSPropCacheEntry *entry; + + /* + * We can skip the property lookup for the global object. If the + * property does not exist anywhere on the scope chain, JSOP_SETNAME + * adds the property to the global. + * + * As a consequence of this optimization for the global object we run + * its JSRESOLVE_ASSIGNING-tolerant resolve hooks only in JSOP_SETNAME, + * after the interpreter evaluates the right- hand-side of the + * assignment, and not here. + * + * This should be transparent to the hooks because the script, instead + * of name = rhs, could have used global.name = rhs given a global + * object reference, which also calls the hooks only after evaluating + * the rhs. We desire such resolve hook equivalence between the two + * forms. + */ + obj = fp->scopeChain; + if (!OBJ_GET_PARENT(cx, obj)) + break; + if (JS_LIKELY(OBJ_IS_NATIVE(obj))) { + PROPERTY_CACHE_TEST(cx, regs.pc, obj, obj2, entry, atom); + if (!atom) { + ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry); + JS_UNLOCK_OBJ(cx, obj2); + break; + } + } else { + entry = NULL; + LOAD_ATOM(0); + } + id = ATOM_TO_JSID(atom); + obj = js_FindIdentifierBase(cx, fp->scopeChain, id); + if (!obj) + goto error; + } while (0); + PUSH_OPND(OBJECT_TO_JSVAL(obj)); +END_CASE(JSOP_BINDNAME) + +BEGIN_CASE(JSOP_IMACOP) + JS_ASSERT(JS_UPTRDIFF(fp->imacpc, script->code) < script->length); + op = JSOp(*fp->imacpc); + DO_OP(); + +#define BITWISE_OP(OP) \ + JS_BEGIN_MACRO \ + FETCH_INT(cx, -2, i); \ + FETCH_INT(cx, -1, j); \ + i = i OP j; \ + regs.sp--; \ + STORE_INT(cx, -1, i); \ + JS_END_MACRO + +BEGIN_CASE(JSOP_BITOR) + BITWISE_OP(|); +END_CASE(JSOP_BITOR) + +BEGIN_CASE(JSOP_BITXOR) + BITWISE_OP(^); +END_CASE(JSOP_BITXOR) + +BEGIN_CASE(JSOP_BITAND) + BITWISE_OP(&); +END_CASE(JSOP_BITAND) + +#define RELATIONAL_OP(OP) \ + JS_BEGIN_MACRO \ + rval = FETCH_OPND(-1); \ + lval = FETCH_OPND(-2); \ + /* Optimize for two int-tagged operands (typical loop control). */ \ + if ((lval & rval) & JSVAL_INT) { \ + cond = JSVAL_TO_INT(lval) OP JSVAL_TO_INT(rval); \ + } else { \ + if (!JSVAL_IS_PRIMITIVE(lval)) \ + DEFAULT_VALUE(cx, -2, JSTYPE_NUMBER, lval); \ + if (!JSVAL_IS_PRIMITIVE(rval)) \ + DEFAULT_VALUE(cx, -1, JSTYPE_NUMBER, rval); \ + if (JSVAL_IS_STRING(lval) && JSVAL_IS_STRING(rval)) { \ + str = JSVAL_TO_STRING(lval); \ + str2 = JSVAL_TO_STRING(rval); \ + cond = js_CompareStrings(str, str2) OP 0; \ + } else { \ + VALUE_TO_NUMBER(cx, -2, lval, d); \ + VALUE_TO_NUMBER(cx, -1, rval, d2); \ + cond = JSDOUBLE_COMPARE(d, OP, d2, JS_FALSE); \ + } \ + } \ + TRY_BRANCH_AFTER_COND(cond, 2); \ + regs.sp--; \ + STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \ + JS_END_MACRO + +/* + * NB: These macros can't use JS_BEGIN_MACRO/JS_END_MACRO around their bodies + * because they begin if/else chains, so callers must not put semicolons after + * the call expressions! + */ +#if JS_HAS_XML_SUPPORT +#define XML_EQUALITY_OP(OP) \ + if ((ltmp == JSVAL_OBJECT && \ + (obj2 = JSVAL_TO_OBJECT(lval)) && \ + OBJECT_IS_XML(cx, obj2)) || \ + (rtmp == JSVAL_OBJECT && \ + (obj2 = JSVAL_TO_OBJECT(rval)) && \ + OBJECT_IS_XML(cx, obj2))) { \ + if (JSVAL_IS_OBJECT(rval) && obj2 == JSVAL_TO_OBJECT(rval)) \ + rval = lval; \ + if (!js_TestXMLEquality(cx, obj2, rval, &cond)) \ + goto error; \ + cond = cond OP JS_TRUE; \ + } else + +#define EXTENDED_EQUALITY_OP(OP) \ + if (ltmp == JSVAL_OBJECT && \ + (obj2 = JSVAL_TO_OBJECT(lval)) && \ + ((clasp = OBJ_GET_CLASS(cx, obj2))->flags & JSCLASS_IS_EXTENDED)) { \ + JSExtendedClass *xclasp; \ + \ + xclasp = (JSExtendedClass *) clasp; \ + if (!xclasp->equality(cx, obj2, rval, &cond)) \ + goto error; \ + cond = cond OP JS_TRUE; \ + } else +#else +#define XML_EQUALITY_OP(OP) /* nothing */ +#define EXTENDED_EQUALITY_OP(OP) /* nothing */ +#endif + +#define EQUALITY_OP(OP, IFNAN) \ + JS_BEGIN_MACRO \ + rval = FETCH_OPND(-1); \ + lval = FETCH_OPND(-2); \ + ltmp = JSVAL_TAG(lval); \ + rtmp = JSVAL_TAG(rval); \ + XML_EQUALITY_OP(OP) \ + if (ltmp == rtmp) { \ + if (ltmp == JSVAL_STRING) { \ + str = JSVAL_TO_STRING(lval); \ + str2 = JSVAL_TO_STRING(rval); \ + cond = js_EqualStrings(str, str2) OP JS_TRUE; \ + } else if (ltmp == JSVAL_DOUBLE) { \ + d = *JSVAL_TO_DOUBLE(lval); \ + d2 = *JSVAL_TO_DOUBLE(rval); \ + cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN); \ + } else { \ + EXTENDED_EQUALITY_OP(OP) \ + /* Handle all undefined (=>NaN) and int combinations. */ \ + cond = lval OP rval; \ + } \ + } else { \ + if (JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval)) { \ + cond = (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) OP 1; \ + } else if (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) { \ + cond = 1 OP 0; \ + } else { \ + if (ltmp == JSVAL_OBJECT) { \ + DEFAULT_VALUE(cx, -2, JSTYPE_VOID, lval); \ + ltmp = JSVAL_TAG(lval); \ + } else if (rtmp == JSVAL_OBJECT) { \ + DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval); \ + rtmp = JSVAL_TAG(rval); \ + } \ + if (ltmp == JSVAL_STRING && rtmp == JSVAL_STRING) { \ + str = JSVAL_TO_STRING(lval); \ + str2 = JSVAL_TO_STRING(rval); \ + cond = js_EqualStrings(str, str2) OP JS_TRUE; \ + } else { \ + VALUE_TO_NUMBER(cx, -2, lval, d); \ + VALUE_TO_NUMBER(cx, -1, rval, d2); \ + cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN); \ + } \ + } \ + } \ + TRY_BRANCH_AFTER_COND(cond, 2); \ + regs.sp--; \ + STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \ + JS_END_MACRO + +BEGIN_CASE(JSOP_EQ) + EQUALITY_OP(==, JS_FALSE); +END_CASE(JSOP_EQ) + +BEGIN_CASE(JSOP_NE) + EQUALITY_OP(!=, JS_TRUE); +END_CASE(JSOP_NE) + +#define STRICT_EQUALITY_OP(OP) \ + JS_BEGIN_MACRO \ + rval = FETCH_OPND(-1); \ + lval = FETCH_OPND(-2); \ + cond = js_StrictlyEqual(cx, lval, rval) OP JS_TRUE; \ + regs.sp--; \ + STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \ + JS_END_MACRO + +BEGIN_CASE(JSOP_STRICTEQ) + STRICT_EQUALITY_OP(==); +END_CASE(JSOP_STRICTEQ) + +BEGIN_CASE(JSOP_STRICTNE) + STRICT_EQUALITY_OP(!=); +END_CASE(JSOP_STRICTNE) + +BEGIN_CASE(JSOP_CASE) + STRICT_EQUALITY_OP(==); + (void) POP(); + if (cond) { + len = GET_JUMP_OFFSET(regs.pc); + BRANCH(len); + } + PUSH(lval); +END_CASE(JSOP_CASE) + +BEGIN_CASE(JSOP_CASEX) + STRICT_EQUALITY_OP(==); + (void) POP(); + if (cond) { + len = GET_JUMPX_OFFSET(regs.pc); + BRANCH(len); + } + PUSH(lval); +END_CASE(JSOP_CASEX) + +BEGIN_CASE(JSOP_LT) + RELATIONAL_OP(<); +END_CASE(JSOP_LT) + +BEGIN_CASE(JSOP_LE) + RELATIONAL_OP(<=); +END_CASE(JSOP_LE) + +BEGIN_CASE(JSOP_GT) + RELATIONAL_OP(>); +END_CASE(JSOP_GT) + +BEGIN_CASE(JSOP_GE) + RELATIONAL_OP(>=); +END_CASE(JSOP_GE) + +#undef EQUALITY_OP +#undef RELATIONAL_OP + +#define SIGNED_SHIFT_OP(OP) \ + JS_BEGIN_MACRO \ + FETCH_INT(cx, -2, i); \ + FETCH_INT(cx, -1, j); \ + i = i OP (j & 31); \ + regs.sp--; \ + STORE_INT(cx, -1, i); \ + JS_END_MACRO + +BEGIN_CASE(JSOP_LSH) + SIGNED_SHIFT_OP(<<); +END_CASE(JSOP_LSH) + +BEGIN_CASE(JSOP_RSH) + SIGNED_SHIFT_OP(>>); +END_CASE(JSOP_RSH) + +BEGIN_CASE(JSOP_URSH) +{ + uint32 u; + + FETCH_UINT(cx, -2, u); + FETCH_INT(cx, -1, j); + u >>= (j & 31); + regs.sp--; + STORE_UINT(cx, -1, u); +} +END_CASE(JSOP_URSH) + +#undef BITWISE_OP +#undef SIGNED_SHIFT_OP + +BEGIN_CASE(JSOP_ADD) + rval = FETCH_OPND(-1); + lval = FETCH_OPND(-2); +#if JS_HAS_XML_SUPPORT + if (!JSVAL_IS_PRIMITIVE(lval) && + (obj2 = JSVAL_TO_OBJECT(lval), OBJECT_IS_XML(cx, obj2)) && + VALUE_IS_XML(cx, rval)) { + if (!js_ConcatenateXML(cx, obj2, rval, &rval)) + goto error; + regs.sp--; + STORE_OPND(-1, rval); + } else +#endif + { + if (!JSVAL_IS_PRIMITIVE(lval)) + DEFAULT_VALUE(cx, -2, JSTYPE_VOID, lval); + if (!JSVAL_IS_PRIMITIVE(rval)) + DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval); + if ((cond = JSVAL_IS_STRING(lval)) || JSVAL_IS_STRING(rval)) { + if (cond) { + str = JSVAL_TO_STRING(lval); + str2 = js_ValueToString(cx, rval); + if (!str2) + goto error; + regs.sp[-1] = STRING_TO_JSVAL(str2); + } else { + str2 = JSVAL_TO_STRING(rval); + str = js_ValueToString(cx, lval); + if (!str) + goto error; + regs.sp[-2] = STRING_TO_JSVAL(str); + } + str = js_ConcatStrings(cx, str, str2); + if (!str) + goto error; + regs.sp--; + STORE_OPND(-1, STRING_TO_JSVAL(str)); + } else { + VALUE_TO_NUMBER(cx, -2, lval, d); + VALUE_TO_NUMBER(cx, -1, rval, d2); + d += d2; + regs.sp--; + STORE_NUMBER(cx, -1, d); + } + } +END_CASE(JSOP_ADD) + +BEGIN_CASE(JSOP_CONCATN) +{ +#ifdef JS_TRACER + JS_ASSERT_IF(fp->imacpc, + *fp->imacpc == JSOP_CONCATN && *regs.pc == JSOP_IMACOP); + + /* + * This instruction can be executed in three contexts. (1) is normal + * execution. (2) is while recording, during an imacro 'imacop'. (3) is + * during a failed recording or when trace execution aborts during a + * recorded imacro. + * 1. !imacro : N args on stack, pc is regs.pc + * 2. imacro && recording : N args on stack, pc is fp->imacpc + * 3. imacro && !recording : N+2 args on stack, pc is fp->imacpc + */ + bool imacro = fp->imacpc != NULL; + bool recording = TRACE_RECORDER(cx) != NULL; + if (imacro) { + argc = GET_ARGC(fp->imacpc); + if (!recording) + js_ConcatPostImacroStackCleanup(argc, regs, NULL); + } else { +#endif /* JS_TRACER */ + argc = GET_ARGC(regs.pc); +#ifdef JS_TRACER + } +#endif /* JS_TRACER */ + + JSCharBuffer buf(cx); + for (vp = regs.sp - argc; vp < regs.sp; vp++) { + if ((!JSVAL_IS_PRIMITIVE(*vp) && + !JSVAL_TO_OBJECT(*vp)->defaultValue(cx, JSTYPE_VOID, vp)) || + !js_ValueToCharBuffer(cx, *vp, buf)) { + goto error; + } + } + + str = js_NewStringFromCharBuffer(cx, buf); + if (!str) + goto error; + + regs.sp -= argc - 1; + STORE_OPND(-1, STRING_TO_JSVAL(str)); + +#ifdef JS_TRACER + if (imacro) { + /* END_CASE does pc += CONCATN_LENGTH. (IMACOP YOU IDIOT!) */ + regs.pc -= JSOP_CONCATN_LENGTH - JSOP_IMACOP_LENGTH; + } +#endif /* JS_TRACER */ +} +END_CASE(JSOP_CONCATN) + +#define BINARY_OP(OP) \ + JS_BEGIN_MACRO \ + FETCH_NUMBER(cx, -2, d); \ + FETCH_NUMBER(cx, -1, d2); \ + d = d OP d2; \ + regs.sp--; \ + STORE_NUMBER(cx, -1, d); \ + JS_END_MACRO + +BEGIN_CASE(JSOP_SUB) + BINARY_OP(-); +END_CASE(JSOP_SUB) + +BEGIN_CASE(JSOP_MUL) + BINARY_OP(*); +END_CASE(JSOP_MUL) + +BEGIN_CASE(JSOP_DIV) + FETCH_NUMBER(cx, -1, d2); + FETCH_NUMBER(cx, -2, d); + regs.sp--; + if (d2 == 0) { +#ifdef XP_WIN + /* XXX MSVC miscompiles such that (NaN == 0) */ + if (JSDOUBLE_IS_NaN(d2)) + rval = rt->NaNValue; + else +#endif + if (d == 0 || JSDOUBLE_IS_NaN(d)) + rval = rt->NaNValue; + else if (JSDOUBLE_IS_NEG(d) != JSDOUBLE_IS_NEG(d2)) + rval = rt->negativeInfinityValue; + else + rval = rt->positiveInfinityValue; + STORE_OPND(-1, rval); + } else { + d /= d2; + STORE_NUMBER(cx, -1, d); + } +END_CASE(JSOP_DIV) + +BEGIN_CASE(JSOP_MOD) + FETCH_NUMBER(cx, -1, d2); + FETCH_NUMBER(cx, -2, d); + regs.sp--; + if (d2 == 0) { + STORE_OPND(-1, rt->NaNValue); + } else { + d = js_fmod(d, d2); + STORE_NUMBER(cx, -1, d); + } +END_CASE(JSOP_MOD) + +BEGIN_CASE(JSOP_NOT) + POP_BOOLEAN(cx, rval, cond); + PUSH_OPND(BOOLEAN_TO_JSVAL(!cond)); +END_CASE(JSOP_NOT) + +BEGIN_CASE(JSOP_BITNOT) + FETCH_INT(cx, -1, i); + i = ~i; + STORE_INT(cx, -1, i); +END_CASE(JSOP_BITNOT) + +BEGIN_CASE(JSOP_NEG) + /* + * When the operand is int jsval, INT_FITS_IN_JSVAL(i) implies + * INT_FITS_IN_JSVAL(-i) unless i is 0 or JSVAL_INT_MIN when the + * results, -0.0 or JSVAL_INT_MAX + 1, are jsdouble values. + */ + rval = FETCH_OPND(-1); + if (JSVAL_IS_INT(rval) && + rval != INT_TO_JSVAL(JSVAL_INT_MIN) && + (i = JSVAL_TO_INT(rval)) != 0) { + JS_STATIC_ASSERT(!INT_FITS_IN_JSVAL(-JSVAL_INT_MIN)); + i = -i; + JS_ASSERT(INT_FITS_IN_JSVAL(i)); + regs.sp[-1] = INT_TO_JSVAL(i); + } else { + if (JSVAL_IS_DOUBLE(rval)) { + d = *JSVAL_TO_DOUBLE(rval); + } else { + d = js_ValueToNumber(cx, ®s.sp[-1]); + if (JSVAL_IS_NULL(regs.sp[-1])) + goto error; + JS_ASSERT(JSVAL_IS_NUMBER(regs.sp[-1]) || + regs.sp[-1] == JSVAL_TRUE); + } + d = -d; + if (!js_NewNumberInRootedValue(cx, d, ®s.sp[-1])) + goto error; + } +END_CASE(JSOP_NEG) + +BEGIN_CASE(JSOP_POS) + rval = FETCH_OPND(-1); + if (!JSVAL_IS_NUMBER(rval)) { + d = js_ValueToNumber(cx, ®s.sp[-1]); + rval = regs.sp[-1]; + if (JSVAL_IS_NULL(rval)) + goto error; + if (rval == JSVAL_TRUE) { + if (!js_NewNumberInRootedValue(cx, d, ®s.sp[-1])) + goto error; + } else { + JS_ASSERT(JSVAL_IS_NUMBER(rval)); + } + } +END_CASE(JSOP_POS) + +BEGIN_CASE(JSOP_DELNAME) + LOAD_ATOM(0); + id = ATOM_TO_JSID(atom); + if (!js_FindProperty(cx, id, &obj, &obj2, &prop)) + goto error; + + /* ECMA says to return true if name is undefined or inherited. */ + PUSH_OPND(JSVAL_TRUE); + if (prop) { + obj2->dropProperty(cx, prop); + if (!obj->deleteProperty(cx, id, ®s.sp[-1])) + goto error; + } +END_CASE(JSOP_DELNAME) + +BEGIN_CASE(JSOP_DELPROP) + LOAD_ATOM(0); + id = ATOM_TO_JSID(atom); + PROPERTY_OP(-1, obj->deleteProperty(cx, id, &rval)); + STORE_OPND(-1, rval); +END_CASE(JSOP_DELPROP) + +BEGIN_CASE(JSOP_DELELEM) + ELEMENT_OP(-1, obj->deleteProperty(cx, id, &rval)); + regs.sp--; + STORE_OPND(-1, rval); +END_CASE(JSOP_DELELEM) + +BEGIN_CASE(JSOP_TYPEOFEXPR) +BEGIN_CASE(JSOP_TYPEOF) + rval = FETCH_OPND(-1); + type = JS_TypeOfValue(cx, rval); + atom = rt->atomState.typeAtoms[type]; + STORE_OPND(-1, ATOM_KEY(atom)); +END_CASE(JSOP_TYPEOF) + +BEGIN_CASE(JSOP_VOID) + STORE_OPND(-1, JSVAL_VOID); +END_CASE(JSOP_VOID) + +BEGIN_CASE(JSOP_INCELEM) +BEGIN_CASE(JSOP_DECELEM) +BEGIN_CASE(JSOP_ELEMINC) +BEGIN_CASE(JSOP_ELEMDEC) + /* + * Delay fetching of id until we have the object to ensure the proper + * evaluation order. See bug 372331. + */ + id = 0; + i = -2; + goto fetch_incop_obj; + +BEGIN_CASE(JSOP_INCPROP) +BEGIN_CASE(JSOP_DECPROP) +BEGIN_CASE(JSOP_PROPINC) +BEGIN_CASE(JSOP_PROPDEC) + LOAD_ATOM(0); + id = ATOM_TO_JSID(atom); + i = -1; + + fetch_incop_obj: + FETCH_OBJECT(cx, i, lval, obj); + if (id == 0) + FETCH_ELEMENT_ID(obj, -1, id); + goto do_incop; + +BEGIN_CASE(JSOP_INCNAME) +BEGIN_CASE(JSOP_DECNAME) +BEGIN_CASE(JSOP_NAMEINC) +BEGIN_CASE(JSOP_NAMEDEC) +{ + JSPropCacheEntry *entry; + + obj = fp->scopeChain; + if (JS_LIKELY(OBJ_IS_NATIVE(obj))) { + PROPERTY_CACHE_TEST(cx, regs.pc, obj, obj2, entry, atom); + if (!atom) { + ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry); + if (obj == obj2 && PCVAL_IS_SLOT(entry->vword)) { + slot = PCVAL_TO_SLOT(entry->vword); + JS_ASSERT(slot < OBJ_SCOPE(obj)->freeslot); + rval = LOCKED_OBJ_GET_SLOT(obj, slot); + if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval))) { + rtmp = rval; + rval += (js_CodeSpec[op].format & JOF_INC) ? 2 : -2; + if (!(js_CodeSpec[op].format & JOF_POST)) + rtmp = rval; + LOCKED_OBJ_SET_SLOT(obj, slot, rval); + JS_UNLOCK_OBJ(cx, obj); + PUSH_OPND(rtmp); + len = JSOP_INCNAME_LENGTH; + DO_NEXT_OP(len); + } + } + JS_UNLOCK_OBJ(cx, obj2); + LOAD_ATOM(0); + } + } else { + LOAD_ATOM(0); + } + id = ATOM_TO_JSID(atom); + if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop)) + goto error; + if (!prop) + goto atom_not_defined; + obj2->dropProperty(cx, prop); +} + +do_incop: +{ + const JSCodeSpec *cs; + jsval v; + + /* + * We need a root to store the value to leave on the stack until + * we have done with obj->setProperty. + */ + PUSH_OPND(JSVAL_NULL); + if (!obj->getProperty(cx, id, ®s.sp[-1])) + goto error; + + cs = &js_CodeSpec[op]; + JS_ASSERT(cs->ndefs == 1); + JS_ASSERT((cs->format & JOF_TMPSLOT_MASK) == JOF_TMPSLOT2); + v = regs.sp[-1]; + if (JS_LIKELY(CAN_DO_FAST_INC_DEC(v))) { + jsval incr; + + incr = (cs->format & JOF_INC) ? 2 : -2; + if (cs->format & JOF_POST) { + regs.sp[-1] = v + incr; + } else { + v += incr; + regs.sp[-1] = v; + } + fp->flags |= JSFRAME_ASSIGNING; + ok = obj->setProperty(cx, id, ®s.sp[-1]); + fp->flags &= ~JSFRAME_ASSIGNING; + if (!ok) + goto error; + + /* + * We must set regs.sp[-1] to v for both post and pre increments + * as the setter overwrites regs.sp[-1]. + */ + regs.sp[-1] = v; + } else { + /* We need an extra root for the result. */ + PUSH_OPND(JSVAL_NULL); + if (!js_DoIncDec(cx, cs, ®s.sp[-2], ®s.sp[-1])) + goto error; + fp->flags |= JSFRAME_ASSIGNING; + ok = obj->setProperty(cx, id, ®s.sp[-1]); + fp->flags &= ~JSFRAME_ASSIGNING; + if (!ok) + goto error; + regs.sp--; + } + + if (cs->nuses == 0) { + /* regs.sp[-1] already contains the result of name increment. */ + } else { + rtmp = regs.sp[-1]; + regs.sp -= cs->nuses; + regs.sp[-1] = rtmp; + } + len = cs->length; + DO_NEXT_OP(len); +} + +{ + jsval incr, incr2; + + /* Position cases so the most frequent i++ does not need a jump. */ +BEGIN_CASE(JSOP_DECARG) + incr = -2; incr2 = -2; goto do_arg_incop; +BEGIN_CASE(JSOP_ARGDEC) + incr = -2; incr2 = 0; goto do_arg_incop; +BEGIN_CASE(JSOP_INCARG) + incr = 2; incr2 = 2; goto do_arg_incop; +BEGIN_CASE(JSOP_ARGINC) + incr = 2; incr2 = 0; + + do_arg_incop: + slot = GET_ARGNO(regs.pc); + JS_ASSERT(slot < fp->fun->nargs); + METER_SLOT_OP(op, slot); + vp = fp->argv + slot; + goto do_int_fast_incop; + +BEGIN_CASE(JSOP_DECLOCAL) + incr = -2; incr2 = -2; goto do_local_incop; +BEGIN_CASE(JSOP_LOCALDEC) + incr = -2; incr2 = 0; goto do_local_incop; +BEGIN_CASE(JSOP_INCLOCAL) + incr = 2; incr2 = 2; goto do_local_incop; +BEGIN_CASE(JSOP_LOCALINC) + incr = 2; incr2 = 0; + + /* + * do_local_incop comes right before do_int_fast_incop as we want to + * avoid an extra jump for variable cases as local++ is more frequent + * than arg++. + */ + do_local_incop: + slot = GET_SLOTNO(regs.pc); + JS_ASSERT(slot < fp->script->nslots); + vp = fp->slots + slot; + METER_SLOT_OP(op, slot); + vp = fp->slots + slot; + + do_int_fast_incop: + rval = *vp; + if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval))) { + *vp = rval + incr; + JS_ASSERT(JSOP_INCARG_LENGTH == js_CodeSpec[op].length); + SKIP_POP_AFTER_SET(JSOP_INCARG_LENGTH, 0); + PUSH_OPND(rval + incr2); + } else { + PUSH_OPND(rval); + if (!js_DoIncDec(cx, &js_CodeSpec[op], ®s.sp[-1], vp)) + goto error; + } + len = JSOP_INCARG_LENGTH; + JS_ASSERT(len == js_CodeSpec[op].length); + DO_NEXT_OP(len); +} + +/* NB: This macro doesn't use JS_BEGIN_MACRO/JS_END_MACRO around its body. */ +#define FAST_GLOBAL_INCREMENT_OP(SLOWOP,INCR,INCR2) \ + op2 = SLOWOP; \ + incr = INCR; \ + incr2 = INCR2; \ + goto do_global_incop + +{ + jsval incr, incr2; + +BEGIN_CASE(JSOP_DECGVAR) + FAST_GLOBAL_INCREMENT_OP(JSOP_DECNAME, -2, -2); +BEGIN_CASE(JSOP_GVARDEC) + FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEDEC, -2, 0); +BEGIN_CASE(JSOP_INCGVAR) + FAST_GLOBAL_INCREMENT_OP(JSOP_INCNAME, 2, 2); +BEGIN_CASE(JSOP_GVARINC) + FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEINC, 2, 0); + +#undef FAST_GLOBAL_INCREMENT_OP + + do_global_incop: + JS_ASSERT((js_CodeSpec[op].format & JOF_TMPSLOT_MASK) == + JOF_TMPSLOT2); + slot = GET_SLOTNO(regs.pc); + JS_ASSERT(slot < GlobalVarCount(fp)); + METER_SLOT_OP(op, slot); + lval = fp->slots[slot]; + if (JSVAL_IS_NULL(lval)) { + op = op2; + DO_OP(); + } + slot = JSVAL_TO_INT(lval); + rval = OBJ_GET_SLOT(cx, fp->varobj, slot); + if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval))) { + PUSH_OPND(rval + incr2); + rval += incr; + } else { + PUSH_OPND(rval); + PUSH_OPND(JSVAL_NULL); /* Extra root */ + if (!js_DoIncDec(cx, &js_CodeSpec[op], ®s.sp[-2], ®s.sp[-1])) + goto error; + rval = regs.sp[-1]; + --regs.sp; + } + OBJ_SET_SLOT(cx, fp->varobj, slot, rval); + len = JSOP_INCGVAR_LENGTH; /* all gvar incops are same length */ + JS_ASSERT(len == js_CodeSpec[op].length); + DO_NEXT_OP(len); +} + +#define COMPUTE_THIS(cx, fp, obj) \ + JS_BEGIN_MACRO \ + if (!(obj = js_ComputeThisForFrame(cx, fp))) \ + goto error; \ + JS_END_MACRO + +BEGIN_CASE(JSOP_THIS) + COMPUTE_THIS(cx, fp, obj); + PUSH_OPND(OBJECT_TO_JSVAL(obj)); +END_CASE(JSOP_THIS) + +BEGIN_CASE(JSOP_GETTHISPROP) + i = 0; + COMPUTE_THIS(cx, fp, obj); + PUSH(JSVAL_NULL); + goto do_getprop_with_obj; + +#undef COMPUTE_THIS + +BEGIN_CASE(JSOP_GETARGPROP) + i = ARGNO_LEN; + slot = GET_ARGNO(regs.pc); + JS_ASSERT(slot < fp->fun->nargs); + PUSH_OPND(fp->argv[slot]); + goto do_getprop_body; + +BEGIN_CASE(JSOP_GETLOCALPROP) + i = SLOTNO_LEN; + slot = GET_SLOTNO(regs.pc); + JS_ASSERT(slot < script->nslots); + PUSH_OPND(fp->slots[slot]); + goto do_getprop_body; + +BEGIN_CASE(JSOP_GETPROP) +BEGIN_CASE(JSOP_GETXPROP) + i = 0; + + do_getprop_body: + lval = FETCH_OPND(-1); + + do_getprop_with_lval: + VALUE_TO_OBJECT(cx, -1, lval, obj); + + do_getprop_with_obj: + do { + JSObject *aobj; + JSPropCacheEntry *entry; + + /* + * We do not impose the method read barrier if in an imacro, + * assuming any property gets it does (e.g., for 'toString' + * from JSOP_NEW) will not be leaked to the calling script. + */ + aobj = js_GetProtoIfDenseArray(cx, obj); + if (JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)) { + PROPERTY_CACHE_TEST(cx, regs.pc, aobj, obj2, entry, atom); + if (!atom) { + ASSERT_VALID_PROPERTY_CACHE_HIT(i, aobj, obj2, entry); + if (PCVAL_IS_OBJECT(entry->vword)) { + rval = PCVAL_OBJECT_TO_JSVAL(entry->vword); + } else if (PCVAL_IS_SLOT(entry->vword)) { + slot = PCVAL_TO_SLOT(entry->vword); + JS_ASSERT(slot < OBJ_SCOPE(obj2)->freeslot); + rval = LOCKED_OBJ_GET_SLOT(obj2, slot); + } else { + JS_ASSERT(PCVAL_IS_SPROP(entry->vword)); + sprop = PCVAL_TO_SPROP(entry->vword); + NATIVE_GET(cx, obj, obj2, sprop, + fp->imacpc ? JSGET_NO_METHOD_BARRIER : JSGET_METHOD_BARRIER, + &rval); + } + JS_UNLOCK_OBJ(cx, obj2); + break; + } + } else { + entry = NULL; + if (i < 0) + atom = rt->atomState.lengthAtom; + else + LOAD_ATOM(i); + } + id = ATOM_TO_JSID(atom); + if (entry + ? !js_GetPropertyHelper(cx, obj, id, + fp->imacpc + ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER + : JSGET_CACHE_RESULT | JSGET_METHOD_BARRIER, + &rval) + : !obj->getProperty(cx, id, &rval)) { + goto error; + } + } while (0); + + STORE_OPND(-1, rval); + JS_ASSERT(JSOP_GETPROP_LENGTH + i == js_CodeSpec[op].length); + len = JSOP_GETPROP_LENGTH + i; +END_VARLEN_CASE + +BEGIN_CASE(JSOP_LENGTH) + lval = FETCH_OPND(-1); + if (JSVAL_IS_STRING(lval)) { + str = JSVAL_TO_STRING(lval); + regs.sp[-1] = INT_TO_JSVAL(str->length()); + } else if (!JSVAL_IS_PRIMITIVE(lval) && + (obj = JSVAL_TO_OBJECT(lval), OBJ_IS_ARRAY(cx, obj))) { + jsuint length; + + /* + * We know that the array is created with only its 'length' private + * data in a fixed slot at JSSLOT_ARRAY_LENGTH. See also + * JSOP_ARRAYPUSH, far below. + */ + length = obj->fslots[JSSLOT_ARRAY_LENGTH]; + if (length <= JSVAL_INT_MAX) { + regs.sp[-1] = INT_TO_JSVAL(length); + } else if (!js_NewDoubleInRootedValue(cx, (jsdouble) length, + ®s.sp[-1])) { + goto error; + } + } else { + i = -2; + goto do_getprop_with_lval; + } +END_CASE(JSOP_LENGTH) + +BEGIN_CASE(JSOP_CALLPROP) +{ + JSObject *aobj; + JSPropCacheEntry *entry; + + lval = FETCH_OPND(-1); + if (!JSVAL_IS_PRIMITIVE(lval)) { + obj = JSVAL_TO_OBJECT(lval); + } else { + if (JSVAL_IS_STRING(lval)) { + i = JSProto_String; + } else if (JSVAL_IS_NUMBER(lval)) { + i = JSProto_Number; + } else if (JSVAL_IS_BOOLEAN(lval)) { + i = JSProto_Boolean; + } else { + JS_ASSERT(JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval)); + js_ReportIsNullOrUndefined(cx, -1, lval, NULL); + goto error; + } + + if (!js_GetClassPrototype(cx, NULL, INT_TO_JSID(i), &obj)) + goto error; + } + + aobj = js_GetProtoIfDenseArray(cx, obj); + if (JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)) { + PROPERTY_CACHE_TEST(cx, regs.pc, aobj, obj2, entry, atom); + if (!atom) { + ASSERT_VALID_PROPERTY_CACHE_HIT(0, aobj, obj2, entry); + if (PCVAL_IS_OBJECT(entry->vword)) { + rval = PCVAL_OBJECT_TO_JSVAL(entry->vword); + } else if (PCVAL_IS_SLOT(entry->vword)) { + slot = PCVAL_TO_SLOT(entry->vword); + JS_ASSERT(slot < OBJ_SCOPE(obj2)->freeslot); + rval = LOCKED_OBJ_GET_SLOT(obj2, slot); + } else { + JS_ASSERT(PCVAL_IS_SPROP(entry->vword)); + sprop = PCVAL_TO_SPROP(entry->vword); + NATIVE_GET(cx, obj, obj2, sprop, JSGET_NO_METHOD_BARRIER, &rval); + } + JS_UNLOCK_OBJ(cx, obj2); + STORE_OPND(-1, rval); + PUSH_OPND(lval); + goto end_callprop; + } + } else { + entry = NULL; + LOAD_ATOM(0); + } + + /* + * Cache miss: use the immediate atom that was loaded for us under + * PROPERTY_CACHE_TEST. + */ + id = ATOM_TO_JSID(atom); + PUSH(JSVAL_NULL); + if (!JSVAL_IS_PRIMITIVE(lval)) { + if (!js_GetMethod(cx, obj, id, + entry + ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER + : JSGET_NO_METHOD_BARRIER, + &rval)) { + goto error; + } + STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); + STORE_OPND(-2, rval); + } else { + JS_ASSERT(obj->map->ops->getProperty == js_GetProperty); + if (!js_GetPropertyHelper(cx, obj, id, + JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER, + &rval)) { + goto error; + } + STORE_OPND(-1, lval); + STORE_OPND(-2, rval); + } + + end_callprop: + /* Wrap primitive lval in object clothing if necessary. */ + if (JSVAL_IS_PRIMITIVE(lval)) { + /* FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=412571 */ + if (!VALUE_IS_FUNCTION(cx, rval) || + (obj = JSVAL_TO_OBJECT(rval), + fun = GET_FUNCTION_PRIVATE(cx, obj), + !PRIMITIVE_THIS_TEST(fun, lval))) { + if (!js_PrimitiveToObject(cx, ®s.sp[-1])) + goto error; + } + } +#if JS_HAS_NO_SUCH_METHOD + if (JS_UNLIKELY(JSVAL_IS_VOID(rval))) { + LOAD_ATOM(0); + regs.sp[-2] = ATOM_KEY(atom); + if (!js_OnUnknownMethod(cx, regs.sp - 2)) + goto error; + } +#endif +} +END_CASE(JSOP_CALLPROP) + +BEGIN_CASE(JSOP_SETNAME) +BEGIN_CASE(JSOP_SETPROP) +BEGIN_CASE(JSOP_SETMETHOD) + rval = FETCH_OPND(-1); + JS_ASSERT_IF(op == JSOP_SETMETHOD, VALUE_IS_FUNCTION(cx, rval)); + lval = FETCH_OPND(-2); + JS_ASSERT_IF(op == JSOP_SETNAME, !JSVAL_IS_PRIMITIVE(lval)); + VALUE_TO_OBJECT(cx, -2, lval, obj); + + do { + JSPropCacheEntry *entry; + + entry = NULL; + atom = NULL; + if (JS_LIKELY(obj->map->ops->setProperty == js_SetProperty)) { + JSPropertyCache *cache = &JS_PROPERTY_CACHE(cx); + uint32 kshape = OBJ_SHAPE(obj); + + /* + * Open-code PROPERTY_CACHE_TEST, specializing for two important + * set-property cases. First: + * + * function f(a, b, c) { + * var o = {p:a, q:b, r:c}; + * return o; + * } + * + * or similar real-world cases, which evolve a newborn native + * object predicatably through some bounded number of property + * additions. And second: + * + * o.p = x; + * + * in a frequently executed method or loop body, where p will + * (possibly after the first iteration) always exist in native + * object o. + */ + entry = &cache->table[PROPERTY_CACHE_HASH_PC(regs.pc, kshape)]; + PCMETER(cache->pctestentry = entry); + PCMETER(cache->tests++); + PCMETER(cache->settests++); + if (entry->kpc == regs.pc && entry->kshape == kshape) { + JS_ASSERT(PCVCAP_TAG(entry->vcap) <= 1); + if (JS_LOCK_OBJ_IF_SHAPE(cx, obj, kshape)) { + JS_ASSERT(PCVAL_IS_SPROP(entry->vword)); + sprop = PCVAL_TO_SPROP(entry->vword); + JS_ASSERT(!(sprop->attrs & JSPROP_READONLY)); + JS_ASSERT_IF(!(sprop->attrs & JSPROP_SHARED), + PCVCAP_TAG(entry->vcap) == 0); + + JSScope *scope = OBJ_SCOPE(obj); + JS_ASSERT(!scope->sealed()); + + /* + * Fastest path: check whether the cached sprop is already + * in scope and call NATIVE_SET and break to get out of the + * do-while(0). But we can call NATIVE_SET only if obj owns + * scope or sprop is shared. + */ + bool checkForAdd; + if (sprop->attrs & JSPROP_SHARED) { + if (PCVCAP_TAG(entry->vcap) == 0 || + ((obj2 = OBJ_GET_PROTO(cx, obj)) && + OBJ_IS_NATIVE(obj2) && + OBJ_SHAPE(obj2) == PCVCAP_SHAPE(entry->vcap))) { + goto fast_set_propcache_hit; + } + + /* The cache entry doesn't apply. vshape mismatch. */ + checkForAdd = false; + } else if (scope->owned()) { + if (sprop == scope->lastProperty() || scope->hasProperty(sprop)) { + fast_set_propcache_hit: + PCMETER(cache->pchits++); + PCMETER(cache->setpchits++); + NATIVE_SET(cx, obj, sprop, entry, &rval); + JS_UNLOCK_SCOPE(cx, scope); + break; + } + checkForAdd = + !(sprop->attrs & JSPROP_SHARED) && + sprop->parent == scope->lastProperty(); + } else { + scope = js_GetMutableScope(cx, obj); + if (!scope) { + JS_UNLOCK_OBJ(cx, obj); + goto error; + } + checkForAdd = !sprop->parent; + } + + if (checkForAdd && + SPROP_HAS_STUB_SETTER(sprop) && + (slot = sprop->slot) == scope->freeslot) { + /* + * Fast path: adding a plain old property that was once + * at the frontier of the property tree, whose slot is + * next to claim among the allocated slots in obj, + * where scope->table has not been created yet. + * + * We may want to remove hazard conditions above and + * inline compensation code here, depending on + * real-world workloads. + */ + JS_ASSERT(!(obj->getClass()->flags & + JSCLASS_SHARE_ALL_PROPERTIES)); + + PCMETER(cache->pchits++); + PCMETER(cache->addpchits++); + + /* + * Beware classes such as Function that use the + * reserveSlots hook to allocate a number of reserved + * slots that may vary with obj. + */ + if (slot < STOBJ_NSLOTS(obj) && + !OBJ_GET_CLASS(cx, obj)->reserveSlots) { + ++scope->freeslot; + } else { + if (!js_AllocSlot(cx, obj, &slot)) { + JS_UNLOCK_SCOPE(cx, scope); + goto error; + } + } + + /* + * If this obj's number of reserved slots differed, or + * if something created a hash table for scope, we must + * pay the price of JSScope::putProperty. + * + * If slot does not match the cached sprop's slot, + * update the cache entry in the hope that obj and + * other instances with the same number of reserved + * slots are now "hot". + */ + if (slot != sprop->slot || scope->table) { + JSScopeProperty *sprop2 = + scope->putProperty(cx, sprop->id, + sprop->getter, sprop->setter, + slot, sprop->attrs, + sprop->flags, sprop->shortid); + if (!sprop2) { + js_FreeSlot(cx, obj, slot); + JS_UNLOCK_SCOPE(cx, scope); + goto error; + } + if (sprop2 != sprop) { + PCMETER(cache->slotchanges++); + JS_ASSERT(slot != sprop->slot && + slot == sprop2->slot && + sprop2->id == sprop->id); + entry->vword = SPROP_TO_PCVAL(sprop2); + } + sprop = sprop2; + } else { + scope->extend(cx, sprop); + } + + /* + * No method change check here because here we are + * adding a new property, not updating an existing + * slot's value that might contain a method of a + * branded scope. + */ + TRACE_2(SetPropHit, entry, sprop); + LOCKED_OBJ_SET_SLOT(obj, slot, rval); + JS_UNLOCK_SCOPE(cx, scope); + + /* + * Purge the property cache of the id we may have just + * shadowed in obj's scope and proto chains. We do this + * after unlocking obj's scope to avoid lock nesting. + */ + js_PurgeScopeChain(cx, obj, sprop->id); + break; + } + JS_UNLOCK_SCOPE(cx, scope); + PCMETER(cache->setpcmisses++); + } + } + + atom = js_FullTestPropertyCache(cx, regs.pc, &obj, &obj2, + &entry); + if (atom) { + PCMETER(cache->misses++); + PCMETER(cache->setmisses++); + } else { + ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry); + sprop = NULL; + if (obj == obj2) { + JS_ASSERT(PCVAL_IS_SPROP(entry->vword)); + sprop = PCVAL_TO_SPROP(entry->vword); + JS_ASSERT(!(sprop->attrs & JSPROP_READONLY)); + JS_ASSERT(!OBJ_SCOPE(obj2)->sealed()); + NATIVE_SET(cx, obj, sprop, entry, &rval); + } + JS_UNLOCK_OBJ(cx, obj2); + if (sprop) + break; + } + } + + if (!atom) + LOAD_ATOM(0); + id = ATOM_TO_JSID(atom); + if (entry) { + uintN defineHow = (op == JSOP_SETMETHOD) + ? JSDNP_CACHE_RESULT | JSDNP_SET_METHOD + : JSDNP_CACHE_RESULT; + if (!js_SetPropertyHelper(cx, obj, id, defineHow, &rval)) + goto error; + } else { + if (!obj->setProperty(cx, id, &rval)) + goto error; + ABORT_RECORDING(cx, "Non-native set"); + } + } while (0); +END_SET_CASE_STORE_RVAL(JSOP_SETPROP, 2); + +BEGIN_CASE(JSOP_GETELEM) + /* Open-coded ELEMENT_OP optimized for strings and dense arrays. */ + lval = FETCH_OPND(-2); + rval = FETCH_OPND(-1); + if (JSVAL_IS_STRING(lval) && JSVAL_IS_INT(rval)) { + str = JSVAL_TO_STRING(lval); + i = JSVAL_TO_INT(rval); + if ((size_t)i < str->length()) { + str = JSString::getUnitString(cx, str, size_t(i)); + if (!str) + goto error; + rval = STRING_TO_JSVAL(str); + goto end_getelem; + } + } + + VALUE_TO_OBJECT(cx, -2, lval, obj); + if (JSVAL_IS_INT(rval)) { + if (OBJ_IS_DENSE_ARRAY(cx, obj)) { + jsuint length; + + length = js_DenseArrayCapacity(obj); + i = JSVAL_TO_INT(rval); + if ((jsuint)i < length && + i < obj->fslots[JSSLOT_ARRAY_LENGTH]) { + rval = obj->dslots[i]; + if (rval != JSVAL_HOLE) + goto end_getelem; + + /* Reload rval from the stack in the rare hole case. */ + rval = FETCH_OPND(-1); + } + } + id = INT_JSVAL_TO_JSID(rval); + } else { + if (!js_InternNonIntElementId(cx, obj, rval, &id)) + goto error; + } + + if (!obj->getProperty(cx, id, &rval)) + goto error; + end_getelem: + regs.sp--; + STORE_OPND(-1, rval); +END_CASE(JSOP_GETELEM) + +BEGIN_CASE(JSOP_CALLELEM) + ELEMENT_OP(-1, js_GetMethod(cx, obj, id, JSGET_NO_METHOD_BARRIER, &rval)); +#if JS_HAS_NO_SUCH_METHOD + if (JS_UNLIKELY(JSVAL_IS_VOID(rval))) { + regs.sp[-2] = regs.sp[-1]; + regs.sp[-1] = OBJECT_TO_JSVAL(obj); + if (!js_OnUnknownMethod(cx, regs.sp - 2)) + goto error; + } else +#endif + { + STORE_OPND(-2, rval); + STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); + } +END_CASE(JSOP_CALLELEM) + +BEGIN_CASE(JSOP_SETELEM) + rval = FETCH_OPND(-1); + FETCH_OBJECT(cx, -3, lval, obj); + FETCH_ELEMENT_ID(obj, -2, id); + do { + if (OBJ_IS_DENSE_ARRAY(cx, obj) && JSID_IS_INT(id)) { + jsuint length; + + length = js_DenseArrayCapacity(obj); + i = JSID_TO_INT(id); + if ((jsuint)i < length) { + if (obj->dslots[i] == JSVAL_HOLE) { + if (js_PrototypeHasIndexedProperties(cx, obj)) + break; + if (i >= obj->fslots[JSSLOT_ARRAY_LENGTH]) + obj->fslots[JSSLOT_ARRAY_LENGTH] = i + 1; + obj->fslots[JSSLOT_ARRAY_COUNT]++; + } + obj->dslots[i] = rval; + goto end_setelem; + } + } + } while (0); + if (!obj->setProperty(cx, id, &rval)) + goto error; + end_setelem: +END_SET_CASE_STORE_RVAL(JSOP_SETELEM, 3) + +BEGIN_CASE(JSOP_ENUMELEM) + /* Funky: the value to set is under the [obj, id] pair. */ + rval = FETCH_OPND(-3); + FETCH_OBJECT(cx, -2, lval, obj); + FETCH_ELEMENT_ID(obj, -1, id); + if (!obj->setProperty(cx, id, &rval)) + goto error; + regs.sp -= 3; +END_CASE(JSOP_ENUMELEM) + +BEGIN_CASE(JSOP_NEW) + /* Get immediate argc and find the constructor function. */ + argc = GET_ARGC(regs.pc); + vp = regs.sp - (2 + argc); + JS_ASSERT(vp >= StackBase(fp)); + + /* + * Assign lval, obj, and fun exactly as the code at inline_call: expects to + * find them, to avoid nesting a js_Interpret call via js_InvokeConstructor. + */ + lval = *vp; + if (VALUE_IS_FUNCTION(cx, lval)) { + obj = JSVAL_TO_OBJECT(lval); + fun = GET_FUNCTION_PRIVATE(cx, obj); + if (FUN_INTERPRETED(fun)) { + /* Root as we go using vp[1]. */ + if (!obj->getProperty(cx, + ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), + &vp[1])) { + goto error; + } + rval = vp[1]; + obj2 = js_NewObject(cx, &js_ObjectClass, + JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : NULL, + OBJ_GET_PARENT(cx, obj)); + if (!obj2) + goto error; + + if (fun->u.i.script->isEmpty()) { + *vp = OBJECT_TO_JSVAL(obj2); + regs.sp = vp + 1; + goto end_new; + } + + vp[1] = OBJECT_TO_JSVAL(obj2); + flags = JSFRAME_CONSTRUCTING; + goto inline_call; + } + } + + if (!js_InvokeConstructor(cx, argc, JS_FALSE, vp)) + goto error; + regs.sp = vp + 1; + CHECK_INTERRUPT_HANDLER(); + TRACE_0(NativeCallComplete); + + end_new: +END_CASE(JSOP_NEW) + +BEGIN_CASE(JSOP_CALL) +BEGIN_CASE(JSOP_EVAL) +BEGIN_CASE(JSOP_APPLY) + argc = GET_ARGC(regs.pc); + vp = regs.sp - (argc + 2); + + lval = *vp; + if (VALUE_IS_FUNCTION(cx, lval)) { + obj = JSVAL_TO_OBJECT(lval); + fun = GET_FUNCTION_PRIVATE(cx, obj); + + /* Clear frame flags since this is not a constructor call. */ + flags = 0; + if (FUN_INTERPRETED(fun)) + inline_call: + { + uintN nframeslots, nvars, missing; + JSArena *a; + jsuword nbytes; + void *newmark; + jsval *newsp; + JSInlineFrame *newifp; + JSInterpreterHook hook; + + script = fun->u.i.script; + if (script->isEmpty()) { + script = fp->script; + *vp = JSVAL_VOID; + regs.sp = vp + 1; + goto end_call; + } + + /* Restrict recursion of lightweight functions. */ + if (inlineCallCount >= JS_MAX_INLINE_CALL_COUNT) { + js_ReportOverRecursed(cx); + script = fp->script; + goto error; + } + + /* Compute the total number of stack slots needed by fun. */ + nframeslots = JS_HOWMANY(sizeof(JSInlineFrame), sizeof(jsval)); + atoms = script->atomMap.vector; + nbytes = (nframeslots + script->nslots) * sizeof(jsval); + + /* Allocate missing expected args adjacent to actuals. */ + a = cx->stackPool.current; + newmark = (void *) a->avail; + if (fun->nargs <= argc) { + missing = 0; + } else { + newsp = vp + 2 + fun->nargs; + JS_ASSERT(newsp > regs.sp); + if ((jsuword) newsp <= a->limit) { + if ((jsuword) newsp > a->avail) + a->avail = (jsuword) newsp; + jsval *argsp = newsp; + do { + *--argsp = JSVAL_VOID; + } while (argsp != regs.sp); + missing = 0; + } else { + missing = fun->nargs - argc; + nbytes += (2 + fun->nargs) * sizeof(jsval); + } + } + + /* Allocate the inline frame with its slots and operands. */ + if (a->avail + nbytes <= a->limit) { + newsp = (jsval *) a->avail; + a->avail += nbytes; + JS_ASSERT(missing == 0); + } else { + JS_ARENA_ALLOCATE_CAST(newsp, jsval *, &cx->stackPool, + nbytes); + if (!newsp) { + js_ReportOutOfScriptQuota(cx); + goto bad_inline_call; + } + + /* + * Move args if the missing ones overflow arena a, then push + * undefined for the missing args. + */ + if (missing) { + memcpy(newsp, vp, (2 + argc) * sizeof(jsval)); + vp = newsp; + newsp = vp + 2 + argc; + do { + *newsp++ = JSVAL_VOID; + } while (--missing != 0); + } + } + + /* Claim space for the stack frame and initialize it. */ + newifp = (JSInlineFrame *) newsp; + newsp += nframeslots; + newifp->frame.callobj = NULL; + newifp->frame.argsobj = NULL; + newifp->frame.varobj = NULL; + newifp->frame.script = script; + newifp->frame.fun = fun; + newifp->frame.argc = argc; + newifp->frame.argv = vp + 2; + newifp->frame.rval = JSVAL_VOID; + newifp->frame.down = fp; + newifp->frame.annotation = NULL; + newifp->frame.scopeChain = parent = OBJ_GET_PARENT(cx, obj); + newifp->frame.flags = flags; + newifp->frame.dormantNext = NULL; + newifp->frame.blockChain = NULL; + if (script->staticLevel < JS_DISPLAY_SIZE) { + JSStackFrame **disp = &cx->display[script->staticLevel]; + newifp->frame.displaySave = *disp; + *disp = &newifp->frame; + } + newifp->mark = newmark; + + /* Compute the 'this' parameter now that argv is set. */ + JS_ASSERT(!JSFUN_BOUND_METHOD_TEST(fun->flags)); + newifp->frame.thisv = vp[1]; + + newifp->frame.regs = NULL; + newifp->frame.imacpc = NULL; + newifp->frame.slots = newsp; + + /* Push void to initialize local variables. */ + nvars = fun->u.i.nvars; + while (nvars--) + *newsp++ = JSVAL_VOID; + + /* Scope with a call object parented by callee's parent. */ + if (JSFUN_HEAVYWEIGHT_TEST(fun->flags) && + !js_GetCallObject(cx, &newifp->frame)) { + goto bad_inline_call; + } + + /* Switch version if currentVersion wasn't overridden. */ + newifp->callerVersion = (JSVersion) cx->version; + if (JS_LIKELY(cx->version == currentVersion)) { + currentVersion = (JSVersion) script->version; + if (currentVersion != cx->version) + js_SetVersion(cx, currentVersion); + } + + /* Push the frame and set interpreter registers. */ + newifp->callerRegs = regs; + fp->regs = &newifp->callerRegs; + regs.sp = newsp; + regs.pc = script->code; + newifp->frame.regs = ®s; + cx->fp = fp = &newifp->frame; + + /* Call the debugger hook if present. */ + hook = cx->debugHooks->callHook; + if (hook) { + newifp->hookData = hook(cx, &newifp->frame, JS_TRUE, 0, + cx->debugHooks->callHookData); + CHECK_INTERRUPT_HANDLER(); + } else { + newifp->hookData = NULL; + } + + inlineCallCount++; + JS_RUNTIME_METER(rt, inlineCalls); + +#ifdef INCLUDE_MOZILLA_DTRACE + /* DTrace function entry, inlines */ + if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED()) + jsdtrace_function_entry(cx, fp, fun); + if (JAVASCRIPT_FUNCTION_INFO_ENABLED()) + jsdtrace_function_info(cx, fp, fp->down, fun); + if (JAVASCRIPT_FUNCTION_ARGS_ENABLED()) + jsdtrace_function_args(cx, fp, fun, fp->argc, fp->argv); +#endif + +#ifdef JS_TRACER + if (TRACE_RECORDER(cx)) { + TRACE_1(EnterFrame, inlineCallCount); + RESTORE_INTERP_VARS(); + } else if (fp->script == fp->down->script && + *fp->down->regs->pc == JSOP_CALL && + *fp->regs->pc == JSOP_TRACE) { + MONITOR_BRANCH(Record_EnterFrame); + } +#endif + + /* Load first op and dispatch it (safe since JSOP_STOP). */ + op = (JSOp) *regs.pc; + DO_OP(); + + bad_inline_call: + JS_ASSERT(fp->regs == ®s); + script = fp->script; + atoms = script->atomMap.vector; + js_FreeRawStack(cx, newmark); + goto error; + } + + if (fun->flags & JSFUN_FAST_NATIVE) { +#ifdef INCLUDE_MOZILLA_DTRACE + /* DTrace function entry, non-inlines */ + if (VALUE_IS_FUNCTION(cx, lval)) { + if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED()) + jsdtrace_function_entry(cx, NULL, fun); + if (JAVASCRIPT_FUNCTION_INFO_ENABLED()) + jsdtrace_function_info(cx, NULL, fp, fun); + if (JAVASCRIPT_FUNCTION_ARGS_ENABLED()) + jsdtrace_function_args(cx, fp, fun, argc, vp+2); + } +#endif + + JS_ASSERT(fun->u.n.extra == 0); + JS_ASSERT(JSVAL_IS_OBJECT(vp[1]) || + PRIMITIVE_THIS_TEST(fun, vp[1])); + ok = ((JSFastNative) fun->u.n.native)(cx, argc, vp); +#ifdef INCLUDE_MOZILLA_DTRACE + if (VALUE_IS_FUNCTION(cx, lval)) { + if (JAVASCRIPT_FUNCTION_RVAL_ENABLED()) + jsdtrace_function_rval(cx, NULL, fun, vp); + if (JAVASCRIPT_FUNCTION_RETURN_ENABLED()) + jsdtrace_function_return(cx, NULL, fun); + } +#endif + regs.sp = vp + 1; + if (!ok) { + /* + * If we are executing the JSOP_NEXTITER imacro and a + * Stopiteration exception is raised, transform it into a + * JSVAL_HOLE return value. The tracer generates equivalent + * code by calling CatchStopIteration_tn. + */ + if (fp->imacpc && *fp->imacpc == JSOP_NEXTITER && + cx->throwing && js_ValueIsStopIteration(cx->exception)) { + // pc may point to JSOP_DUP here due to bug 474854. + JS_ASSERT(*regs.pc == JSOP_CALL || + *regs.pc == JSOP_DUP); + cx->throwing = JS_FALSE; + cx->exception = JSVAL_VOID; + regs.sp[-1] = JSVAL_HOLE; + } else { + goto error; + } + } + TRACE_0(NativeCallComplete); + goto end_call; + } + } + + ok = js_Invoke(cx, argc, vp, 0); + regs.sp = vp + 1; + CHECK_INTERRUPT_HANDLER(); + if (!ok) + goto error; + JS_RUNTIME_METER(rt, nonInlineCalls); + TRACE_0(NativeCallComplete); + + end_call: +END_CASE(JSOP_CALL) + +BEGIN_CASE(JSOP_SETCALL) + argc = GET_ARGC(regs.pc); + vp = regs.sp - argc - 2; + if (js_Invoke(cx, argc, vp, 0)) + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_LEFTSIDE_OF_ASS); + goto error; +END_CASE(JSOP_SETCALL) + +BEGIN_CASE(JSOP_NAME) +BEGIN_CASE(JSOP_CALLNAME) +{ + JSPropCacheEntry *entry; + + obj = fp->scopeChain; + if (JS_LIKELY(OBJ_IS_NATIVE(obj))) { + PROPERTY_CACHE_TEST(cx, regs.pc, obj, obj2, entry, atom); + if (!atom) { + ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry); + if (PCVAL_IS_OBJECT(entry->vword)) { + rval = PCVAL_OBJECT_TO_JSVAL(entry->vword); + JS_UNLOCK_OBJ(cx, obj2); + goto do_push_rval; + } + + if (PCVAL_IS_SLOT(entry->vword)) { + slot = PCVAL_TO_SLOT(entry->vword); + JS_ASSERT(slot < OBJ_SCOPE(obj2)->freeslot); + rval = LOCKED_OBJ_GET_SLOT(obj2, slot); + JS_UNLOCK_OBJ(cx, obj2); + goto do_push_rval; + } + + JS_ASSERT(PCVAL_IS_SPROP(entry->vword)); + sprop = PCVAL_TO_SPROP(entry->vword); + goto do_native_get; + } + } else { + LOAD_ATOM(0); + } + + id = ATOM_TO_JSID(atom); + if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop)) + goto error; + if (!prop) { + /* Kludge to allow (typeof foo == "undefined") tests. */ + endpc = script->code + script->length; + op2 = js_GetOpcode(cx, script, regs.pc + JSOP_NAME_LENGTH); + if (op2 == JSOP_TYPEOF) { + PUSH_OPND(JSVAL_VOID); + len = JSOP_NAME_LENGTH; + DO_NEXT_OP(len); + } + goto atom_not_defined; + } + + /* Take the slow path if prop was not found in a native object. */ + if (!OBJ_IS_NATIVE(obj) || !OBJ_IS_NATIVE(obj2)) { + obj2->dropProperty(cx, prop); + if (!obj->getProperty(cx, id, &rval)) + goto error; + } else { + sprop = (JSScopeProperty *)prop; + do_native_get: + NATIVE_GET(cx, obj, obj2, sprop, JSGET_METHOD_BARRIER, &rval); + obj2->dropProperty(cx, (JSProperty *) sprop); + } + + do_push_rval: + PUSH_OPND(rval); + if (op == JSOP_CALLNAME) + PUSH_OPND(OBJECT_TO_JSVAL(obj)); +} +END_CASE(JSOP_NAME) + +BEGIN_CASE(JSOP_UINT16) + i = (jsint) GET_UINT16(regs.pc); + rval = INT_TO_JSVAL(i); + PUSH_OPND(rval); +END_CASE(JSOP_UINT16) + +BEGIN_CASE(JSOP_UINT24) + i = (jsint) GET_UINT24(regs.pc); + rval = INT_TO_JSVAL(i); + PUSH_OPND(rval); +END_CASE(JSOP_UINT24) + +BEGIN_CASE(JSOP_INT8) + i = GET_INT8(regs.pc); + rval = INT_TO_JSVAL(i); + PUSH_OPND(rval); +END_CASE(JSOP_INT8) + +BEGIN_CASE(JSOP_INT32) + i = GET_INT32(regs.pc); + rval = INT_TO_JSVAL(i); + PUSH_OPND(rval); +END_CASE(JSOP_INT32) + +BEGIN_CASE(JSOP_INDEXBASE) + /* + * Here atoms can exceed script->atomMap.length as we use atoms as a + * segment register for object literals as well. + */ + atoms += GET_INDEXBASE(regs.pc); +END_CASE(JSOP_INDEXBASE) + +BEGIN_CASE(JSOP_INDEXBASE1) +BEGIN_CASE(JSOP_INDEXBASE2) +BEGIN_CASE(JSOP_INDEXBASE3) + atoms += (op - JSOP_INDEXBASE1 + 1) << 16; +END_CASE(JSOP_INDEXBASE3) + +BEGIN_CASE(JSOP_RESETBASE0) +BEGIN_CASE(JSOP_RESETBASE) + atoms = script->atomMap.vector; +END_CASE(JSOP_RESETBASE) + +BEGIN_CASE(JSOP_DOUBLE) + JS_ASSERT(!fp->imacpc); + JS_ASSERT(size_t(atoms - script->atomMap.vector) < script->atomMap.length); + /* FALL THROUGH */ + +BEGIN_CASE(JSOP_STRING) + LOAD_ATOM(0); + PUSH_OPND(ATOM_KEY(atom)); +END_CASE(JSOP_DOUBLE) + +BEGIN_CASE(JSOP_OBJECT) + LOAD_OBJECT(0); + PUSH_OPND(OBJECT_TO_JSVAL(obj)); +END_CASE(JSOP_OBJECT) + +BEGIN_CASE(JSOP_REGEXP) +{ + JSObject *funobj; + + /* + * Push a regexp object for the atom mapped by the bytecode at pc, cloning + * the literal's regexp object if necessary, to simulate in the + * pre-compile/execute-later case what ECMA specifies for the + * compile-and-go case: that scanning each regexp literal creates a single + * corresponding RegExp object. + * + * To support pre-compilation transparently, we must handle the case where + * a regexp object literal is used in a different global at execution time + * from the global with which it was scanned at compile time. We do this + * by re-wrapping the JSRegExp private data struct with a cloned object + * having the right prototype and parent, and having its own lastIndex + * property value storage. + * + * Unlike JSOP_DEFFUN and other prolog bytecodes that may clone literal + * objects, we don't want to pay a script prolog execution price for all + * regexp literals in a script (many may not be used by a particular + * execution of that script, depending on control flow), so we initialize + * lazily here. + * + * XXX This code is specific to regular expression objects. If we need a + * similar op for other kinds of object literals, we should push cloning + * down under JSObjectOps and reuse code here. + */ + index = GET_FULL_INDEX(0); + JS_ASSERT(index < script->regexps()->length); + + slot = index; + if (fp->fun) { + /* + * We're in function code, not global or eval code (in eval code, + * JSOP_REGEXP is never emitted). The cloned funobj contains + * script->regexps()->length reserved slots for the cloned regexps; see + * fun_reserveSlots, jsfun.c. + */ + funobj = JSVAL_TO_OBJECT(fp->argv[-2]); + slot += JSCLASS_RESERVED_SLOTS(&js_FunctionClass); + if (script->upvarsOffset != 0) + slot += script->upvars()->length; + if (!JS_GetReservedSlot(cx, funobj, slot, &rval)) + goto error; + if (JSVAL_IS_VOID(rval)) + rval = JSVAL_NULL; + } else { + /* + * We're in global code. The code generator reserved a slot for the + * regexp among script->nfixed slots. All such slots are initialized to + * null, not void, for faster testing in JSOP_*GVAR cases. To simplify + * index calculations we count regexps in the reverse order down from + * script->nslots - 1. + */ + JS_ASSERT(slot < script->nfixed); + slot = script->nfixed - slot - 1; + rval = fp->slots[slot]; +#ifdef __GNUC__ + funobj = NULL; /* suppress bogus gcc warnings */ +#endif + } + + if (JSVAL_IS_NULL(rval)) { + /* Compute the current global object in obj2. */ + obj2 = fp->scopeChain; + while ((parent = OBJ_GET_PARENT(cx, obj2)) != NULL) + obj2 = parent; + + /* + * If obj's parent is not obj2, we must clone obj so that it has the + * right parent, and therefore, the right prototype. + * + * Yes, this means we assume that the correct RegExp.prototype to which + * regexp instances (including literals) delegate can be distinguished + * solely by the instance's parent, which was set to the parent of the + * RegExp constructor function object when the instance was created. + * In other words, + * + * (/x/.__parent__ == RegExp.__parent__) implies + * (/x/.__proto__ == RegExp.prototype) + * + * (unless you assign a different object to RegExp.prototype at + * runtime, in which case, ECMA doesn't specify operation, and you get + * what you deserve). + * + * This same coupling between instance parent and constructor parent + * turns up everywhere (see jsobj.c's FindClassObject, + * js_ConstructObject, and js_NewObject). It's fundamental to the + * design of the language when you consider multiple global objects and + * separate compilation and execution, even though it is not specified + * fully in ECMA. + */ + obj = script->getRegExp(index); + if (OBJ_GET_PARENT(cx, obj) != obj2) { + obj = js_CloneRegExpObject(cx, obj, obj2); + if (!obj) + goto error; + } + rval = OBJECT_TO_JSVAL(obj); + + /* Store the regexp object value in its cloneIndex slot. */ + if (fp->fun) { + if (!JS_SetReservedSlot(cx, funobj, slot, rval)) + goto error; + } else { + fp->slots[slot] = rval; + } + } + + PUSH_OPND(rval); +} +END_CASE(JSOP_REGEXP) + +BEGIN_CASE(JSOP_ZERO) + PUSH_OPND(JSVAL_ZERO); +END_CASE(JSOP_ZERO) + +BEGIN_CASE(JSOP_ONE) + PUSH_OPND(JSVAL_ONE); +END_CASE(JSOP_ONE) + +BEGIN_CASE(JSOP_NULL) + PUSH_OPND(JSVAL_NULL); +END_CASE(JSOP_NULL) + +BEGIN_CASE(JSOP_FALSE) + PUSH_OPND(JSVAL_FALSE); +END_CASE(JSOP_FALSE) + +BEGIN_CASE(JSOP_TRUE) + PUSH_OPND(JSVAL_TRUE); +END_CASE(JSOP_TRUE) + +BEGIN_CASE(JSOP_TABLESWITCH) + pc2 = regs.pc; + len = GET_JUMP_OFFSET(pc2); + + /* + * ECMAv2+ forbids conversion of discriminant, so we will skip to the + * default case if the discriminant isn't already an int jsval. (This + * opcode is emitted only for dense jsint-domain switches.) + */ + rval = POP_OPND(); + if (JSVAL_IS_INT(rval)) { + i = JSVAL_TO_INT(rval); + } else if (JSVAL_IS_DOUBLE(rval) && *JSVAL_TO_DOUBLE(rval) == 0) { + /* Treat -0 (double) as 0. */ + i = 0; + } else { + DO_NEXT_OP(len); + } + + pc2 += JUMP_OFFSET_LEN; + low = GET_JUMP_OFFSET(pc2); + pc2 += JUMP_OFFSET_LEN; + high = GET_JUMP_OFFSET(pc2); + + i -= low; + if ((jsuint)i < (jsuint)(high - low + 1)) { + pc2 += JUMP_OFFSET_LEN + JUMP_OFFSET_LEN * i; + off = (jsint) GET_JUMP_OFFSET(pc2); + if (off) + len = off; + } +END_VARLEN_CASE + +BEGIN_CASE(JSOP_TABLESWITCHX) + pc2 = regs.pc; + len = GET_JUMPX_OFFSET(pc2); + + /* + * ECMAv2+ forbids conversion of discriminant, so we will skip to the + * default case if the discriminant isn't already an int jsval. (This + * opcode is emitted only for dense jsint-domain switches.) + */ + rval = POP_OPND(); + if (JSVAL_IS_INT(rval)) { + i = JSVAL_TO_INT(rval); + } else if (JSVAL_IS_DOUBLE(rval) && *JSVAL_TO_DOUBLE(rval) == 0) { + /* Treat -0 (double) as 0. */ + i = 0; + } else { + DO_NEXT_OP(len); + } + + pc2 += JUMPX_OFFSET_LEN; + low = GET_JUMP_OFFSET(pc2); + pc2 += JUMP_OFFSET_LEN; + high = GET_JUMP_OFFSET(pc2); + + i -= low; + if ((jsuint)i < (jsuint)(high - low + 1)) { + pc2 += JUMP_OFFSET_LEN + JUMPX_OFFSET_LEN * i; + off = (jsint) GET_JUMPX_OFFSET(pc2); + if (off) + len = off; + } +END_VARLEN_CASE + +BEGIN_CASE(JSOP_LOOKUPSWITCHX) + off = JUMPX_OFFSET_LEN; + goto do_lookup_switch; + +BEGIN_CASE(JSOP_LOOKUPSWITCH) + off = JUMP_OFFSET_LEN; + + do_lookup_switch: + /* + * JSOP_LOOKUPSWITCH and JSOP_LOOKUPSWITCHX are never used if any atom + * index in it would exceed 64K limit. + */ + JS_ASSERT(!fp->imacpc); + JS_ASSERT(atoms == script->atomMap.vector); + pc2 = regs.pc; + lval = POP_OPND(); + + if (!JSVAL_IS_NUMBER(lval) && + !JSVAL_IS_STRING(lval) && + !JSVAL_IS_BOOLEAN(lval)) { + goto end_lookup_switch; + } + + pc2 += off; + npairs = (jsint) GET_UINT16(pc2); + pc2 += UINT16_LEN; + JS_ASSERT(npairs); /* empty switch uses JSOP_TABLESWITCH */ + +#define SEARCH_PAIRS(MATCH_CODE) \ + for (;;) { \ + JS_ASSERT(GET_INDEX(pc2) < script->atomMap.length); \ + atom = atoms[GET_INDEX(pc2)]; \ + rval = ATOM_KEY(atom); \ + MATCH_CODE \ + pc2 += INDEX_LEN; \ + if (match) \ + break; \ + pc2 += off; \ + if (--npairs == 0) { \ + pc2 = regs.pc; \ + break; \ + } \ + } + + if (JSVAL_IS_STRING(lval)) { + str = JSVAL_TO_STRING(lval); + SEARCH_PAIRS( + match = (JSVAL_IS_STRING(rval) && + ((str2 = JSVAL_TO_STRING(rval)) == str || + js_EqualStrings(str2, str))); + ) + } else if (JSVAL_IS_DOUBLE(lval)) { + d = *JSVAL_TO_DOUBLE(lval); + SEARCH_PAIRS( + match = (JSVAL_IS_DOUBLE(rval) && + *JSVAL_TO_DOUBLE(rval) == d); + ) + } else { + SEARCH_PAIRS( + match = (lval == rval); + ) + } +#undef SEARCH_PAIRS + + end_lookup_switch: + len = (op == JSOP_LOOKUPSWITCH) + ? GET_JUMP_OFFSET(pc2) + : GET_JUMPX_OFFSET(pc2); +END_VARLEN_CASE + +BEGIN_CASE(JSOP_TRAP) +{ + JSTrapStatus status; + + status = JS_HandleTrap(cx, script, regs.pc, &rval); + switch (status) { + case JSTRAP_ERROR: + goto error; + case JSTRAP_RETURN: + fp->rval = rval; + ok = JS_TRUE; + goto forced_return; + case JSTRAP_THROW: + cx->throwing = JS_TRUE; + cx->exception = rval; + goto error; + default: + break; + } + JS_ASSERT(status == JSTRAP_CONTINUE); + CHECK_INTERRUPT_HANDLER(); + JS_ASSERT(JSVAL_IS_INT(rval)); + op = (JSOp) JSVAL_TO_INT(rval); + JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT); + DO_OP(); +} + +BEGIN_CASE(JSOP_ARGUMENTS) + if (!js_GetArgsValue(cx, fp, &rval)) + goto error; + PUSH_OPND(rval); +END_CASE(JSOP_ARGUMENTS) + +BEGIN_CASE(JSOP_ARGSUB) + id = INT_TO_JSID(GET_ARGNO(regs.pc)); + if (!js_GetArgsProperty(cx, fp, id, &rval)) + goto error; + PUSH_OPND(rval); +END_CASE(JSOP_ARGSUB) + +BEGIN_CASE(JSOP_ARGCNT) + id = ATOM_TO_JSID(rt->atomState.lengthAtom); + if (!js_GetArgsProperty(cx, fp, id, &rval)) + goto error; + PUSH_OPND(rval); +END_CASE(JSOP_ARGCNT) + +BEGIN_CASE(JSOP_GETARG) +BEGIN_CASE(JSOP_CALLARG) + slot = GET_ARGNO(regs.pc); + JS_ASSERT(slot < fp->fun->nargs); + METER_SLOT_OP(op, slot); + PUSH_OPND(fp->argv[slot]); + if (op == JSOP_CALLARG) + PUSH_OPND(JSVAL_NULL); +END_CASE(JSOP_GETARG) + +BEGIN_CASE(JSOP_SETARG) + slot = GET_ARGNO(regs.pc); + JS_ASSERT(slot < fp->fun->nargs); + METER_SLOT_OP(op, slot); + vp = &fp->argv[slot]; + *vp = FETCH_OPND(-1); +END_SET_CASE(JSOP_SETARG) + +BEGIN_CASE(JSOP_GETLOCAL) + slot = GET_SLOTNO(regs.pc); + JS_ASSERT(slot < script->nslots); + PUSH_OPND(fp->slots[slot]); +END_CASE(JSOP_GETLOCAL) + +BEGIN_CASE(JSOP_CALLLOCAL) + slot = GET_SLOTNO(regs.pc); + JS_ASSERT(slot < script->nslots); + PUSH_OPND(fp->slots[slot]); + PUSH_OPND(JSVAL_NULL); +END_CASE(JSOP_CALLLOCAL) + +BEGIN_CASE(JSOP_SETLOCAL) + slot = GET_SLOTNO(regs.pc); + JS_ASSERT(slot < script->nslots); + vp = &fp->slots[slot]; + *vp = FETCH_OPND(-1); +END_SET_CASE(JSOP_SETLOCAL) + +BEGIN_CASE(JSOP_GETUPVAR) +BEGIN_CASE(JSOP_CALLUPVAR) +{ + JSUpvarArray *uva = script->upvars(); + + index = GET_UINT16(regs.pc); + JS_ASSERT(index < uva->length); + + rval = js_GetUpvar(cx, script->staticLevel, uva->vector[index]); + PUSH_OPND(rval); + + if (op == JSOP_CALLUPVAR) + PUSH_OPND(JSVAL_NULL); +} +END_CASE(JSOP_GETUPVAR) + +BEGIN_CASE(JSOP_GETUPVAR_DBG) +BEGIN_CASE(JSOP_CALLUPVAR_DBG) + fun = fp->fun; + JS_ASSERT(FUN_KIND(fun) == JSFUN_INTERPRETED); + JS_ASSERT(fun->u.i.wrapper); + + /* Scope for tempPool mark and local names allocation in it. */ + { + void *mark = JS_ARENA_MARK(&cx->tempPool); + jsuword *names = js_GetLocalNameArray(cx, fun, &cx->tempPool); + if (!names) + goto error; + + index = fun->countArgsAndVars() + GET_UINT16(regs.pc); + atom = JS_LOCAL_NAME_TO_ATOM(names[index]); + id = ATOM_TO_JSID(atom); + + ok = js_FindProperty(cx, id, &obj, &obj2, &prop); + JS_ARENA_RELEASE(&cx->tempPool, mark); + if (!ok) + goto error; + } + + if (!prop) + goto atom_not_defined; + + /* Minimize footprint with generic code instead of NATIVE_GET. */ + obj2->dropProperty(cx, prop); + vp = regs.sp; + PUSH_OPND(JSVAL_NULL); + if (!obj->getProperty(cx, id, vp)) + goto error; + + if (op == JSOP_CALLUPVAR_DBG) + PUSH_OPND(JSVAL_NULL); +END_CASE(JSOP_GETUPVAR_DBG) + +BEGIN_CASE(JSOP_GETDSLOT) +BEGIN_CASE(JSOP_CALLDSLOT) + JS_ASSERT(fp->argv); + obj = JSVAL_TO_OBJECT(fp->argv[-2]); + JS_ASSERT(obj); + JS_ASSERT(obj->dslots); + + index = GET_UINT16(regs.pc); + JS_ASSERT(JS_INITIAL_NSLOTS + index < jsatomid(obj->dslots[-1])); + JS_ASSERT_IF(OBJ_SCOPE(obj)->object == obj, + JS_INITIAL_NSLOTS + index < OBJ_SCOPE(obj)->freeslot); + + PUSH_OPND(obj->dslots[index]); + if (op == JSOP_CALLDSLOT) + PUSH_OPND(JSVAL_NULL); +END_CASE(JSOP_GETDSLOT) + +BEGIN_CASE(JSOP_GETGVAR) +BEGIN_CASE(JSOP_CALLGVAR) + slot = GET_SLOTNO(regs.pc); + JS_ASSERT(slot < GlobalVarCount(fp)); + METER_SLOT_OP(op, slot); + lval = fp->slots[slot]; + if (JSVAL_IS_NULL(lval)) { + op = (op == JSOP_GETGVAR) ? JSOP_NAME : JSOP_CALLNAME; + DO_OP(); + } + obj = fp->varobj; + slot = JSVAL_TO_INT(lval); + rval = OBJ_GET_SLOT(cx, obj, slot); + PUSH_OPND(rval); + if (op == JSOP_CALLGVAR) + PUSH_OPND(OBJECT_TO_JSVAL(obj)); +END_CASE(JSOP_GETGVAR) + +BEGIN_CASE(JSOP_SETGVAR) + slot = GET_SLOTNO(regs.pc); + JS_ASSERT(slot < GlobalVarCount(fp)); + METER_SLOT_OP(op, slot); + rval = FETCH_OPND(-1); + obj = fp->varobj; + lval = fp->slots[slot]; + if (JSVAL_IS_NULL(lval)) { + /* + * Inline-clone and deoptimize JSOP_SETNAME code here because + * JSOP_SETGVAR has arity 1: [rval], not arity 2: [obj, rval] + * as JSOP_SETNAME does, where [obj] is due to JSOP_BINDNAME. + */ +#ifdef JS_TRACER + if (TRACE_RECORDER(cx)) + js_AbortRecording(cx, "SETGVAR with NULL slot"); +#endif + LOAD_ATOM(0); + id = ATOM_TO_JSID(atom); + if (!obj->setProperty(cx, id, &rval)) + goto error; + } else { + slot = JSVAL_TO_INT(lval); + JS_LOCK_OBJ(cx, obj); + JSScope *scope = OBJ_SCOPE(obj); + if (!scope->methodWriteBarrier(cx, slot, rval)) { + JS_UNLOCK_SCOPE(cx, scope); + goto error; + } + LOCKED_OBJ_SET_SLOT(obj, slot, rval); + JS_UNLOCK_SCOPE(cx, scope); + } +END_SET_CASE(JSOP_SETGVAR) + +BEGIN_CASE(JSOP_DEFCONST) +BEGIN_CASE(JSOP_DEFVAR) + index = GET_INDEX(regs.pc); + atom = atoms[index]; + + /* + * index is relative to atoms at this point but for global var + * code below we need the absolute value. + */ + index += atoms - script->atomMap.vector; + obj = fp->varobj; + JS_ASSERT(obj->map->ops->defineProperty == js_DefineProperty); + attrs = JSPROP_ENUMERATE; + if (!(fp->flags & JSFRAME_EVAL)) + attrs |= JSPROP_PERMANENT; + if (op == JSOP_DEFCONST) + attrs |= JSPROP_READONLY; + + /* Lookup id in order to check for redeclaration problems. */ + id = ATOM_TO_JSID(atom); + prop = NULL; + if (!js_CheckRedeclaration(cx, obj, id, attrs, &obj2, &prop)) + goto error; + + /* Bind a variable only if it's not yet defined. */ + if (!prop) { + if (!js_DefineNativeProperty(cx, obj, id, JSVAL_VOID, JS_PropertyStub, JS_PropertyStub, + attrs, 0, 0, &prop)) { + goto error; + } + JS_ASSERT(prop); + obj2 = obj; + } + + /* + * Try to optimize a property we either just created, or found + * directly in the global object, that is permanent, has a slot, + * and has stub getter and setter, into a "fast global" accessed + * by the JSOP_*GVAR opcodes. + */ + if (!fp->fun && + index < GlobalVarCount(fp) && + obj2 == obj && + OBJ_IS_NATIVE(obj)) { + sprop = (JSScopeProperty *) prop; + if ((sprop->attrs & JSPROP_PERMANENT) && + SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj)) && + SPROP_HAS_STUB_GETTER_OR_IS_METHOD(sprop) && + SPROP_HAS_STUB_SETTER(sprop)) { + /* + * Fast globals use frame variables to map the global + * name's atom index to the permanent fp->varobj slot + * number, tagged as a jsval. The atom index for the + * global's name literal is identical to its variable + * index. + */ + fp->slots[index] = INT_TO_JSVAL(sprop->slot); + } + } + + obj2->dropProperty(cx, prop); +END_CASE(JSOP_DEFVAR) + +BEGIN_CASE(JSOP_DEFFUN) +{ + JSPropertyOp getter, setter; + bool doSet; + JSObject *pobj; + JSProperty *prop; + uint32 old; + + /* + * A top-level function defined in Global or Eval code (see ECMA-262 + * Ed. 3), or else a SpiderMonkey extension: a named function statement in + * a compound statement (not at the top statement level of global code, or + * at the top level of a function body). + */ + LOAD_FUNCTION(0); + obj = FUN_OBJECT(fun); + + if (FUN_NULL_CLOSURE(fun)) { + /* + * Even a null closure needs a parent for principals finding. + * FIXME: bug 476950, although debugger users may also demand some kind + * of scope link for debugger-assisted eval-in-frame. + */ + obj2 = fp->scopeChain; + } else { + JS_ASSERT(!FUN_FLAT_CLOSURE(fun)); + + /* + * Inline js_GetScopeChain a bit to optimize for the case of a + * top-level function. + */ + if (!fp->blockChain) { + obj2 = fp->scopeChain; + } else { + obj2 = js_GetScopeChain(cx, fp); + if (!obj2) + goto error; + } + } + + /* + * If static link is not current scope, clone fun's object to link to the + * current scope via parent. We do this to enable sharing of compiled + * functions among multiple equivalent scopes, amortizing the cost of + * compilation over a number of executions. Examples include XUL scripts + * and event handlers shared among Firefox or other Mozilla app chrome + * windows, and user-defined JS functions precompiled and then shared among + * requests in server-side JS. + */ + if (OBJ_GET_PARENT(cx, obj) != obj2) { + obj = js_CloneFunctionObject(cx, fun, obj2); + if (!obj) + goto error; + } + + /* + * Protect obj from any GC hiding below JSObject::setProperty or + * JSObject::defineProperty. All paths from here must flow through the + * fp->scopeChain code below the parent->defineProperty call. + */ + MUST_FLOW_THROUGH("restore_scope"); + fp->scopeChain = obj; + + rval = OBJECT_TO_JSVAL(obj); + + /* + * ECMA requires functions defined when entering Eval code to be + * impermanent. + */ + attrs = (fp->flags & JSFRAME_EVAL) + ? JSPROP_ENUMERATE + : JSPROP_ENUMERATE | JSPROP_PERMANENT; + + /* + * Load function flags that are also property attributes. Getters and + * setters do not need a slot, their value is stored elsewhere in the + * property itself, not in obj slots. + */ + getter = setter = JS_PropertyStub; + flags = JSFUN_GSFLAG2ATTR(fun->flags); + if (flags) { + /* Function cannot be both getter a setter. */ + JS_ASSERT(flags == JSPROP_GETTER || flags == JSPROP_SETTER); + attrs |= flags | JSPROP_SHARED; + rval = JSVAL_VOID; + if (flags == JSPROP_GETTER) + getter = js_CastAsPropertyOp(obj); + else + setter = js_CastAsPropertyOp(obj); + } + + /* + * We define the function as a property of the variable object and not the + * current scope chain even for the case of function expression statements + * and functions defined by eval inside let or with blocks. + */ + parent = fp->varobj; + JS_ASSERT(parent); + + /* + * Check for a const property of the same name -- or any kind of property + * if executing with the strict option. We check here at runtime as well + * as at compile-time, to handle eval as well as multiple HTML script tags. + */ + id = ATOM_TO_JSID(fun->atom); + prop = NULL; + ok = js_CheckRedeclaration(cx, parent, id, attrs, &pobj, &prop); + if (!ok) + goto restore_scope; + + /* + * We deviate from 10.1.2 in ECMA 262 v3 and under eval use for function + * declarations JSObject::setProperty, not JSObject::defineProperty, to + * preserve the JSOP_PERMANENT attribute of existing properties and make + * sure that such properties cannot be deleted. + * + * We also use JSObject::setProperty for the existing properties of Call + * objects with matching attributes to preserve the native getters and + * setters that store the value of the property in the interpreter frame, + * see bug 467495. + */ + doSet = (attrs == JSPROP_ENUMERATE); + JS_ASSERT_IF(doSet, fp->flags & JSFRAME_EVAL); + if (prop) { + if (parent == pobj && + OBJ_GET_CLASS(cx, parent) == &js_CallClass && + (old = ((JSScopeProperty *) prop)->attrs, + !(old & (JSPROP_GETTER|JSPROP_SETTER)) && + (old & (JSPROP_ENUMERATE|JSPROP_PERMANENT)) == attrs)) { + /* + * js_CheckRedeclaration must reject attempts to add a getter or + * setter to an existing property without a getter or setter. + */ + JS_ASSERT(!(attrs & ~(JSPROP_ENUMERATE|JSPROP_PERMANENT))); + JS_ASSERT(!(old & JSPROP_READONLY)); + doSet = JS_TRUE; + } + pobj->dropProperty(cx, prop); + } + ok = doSet + ? parent->setProperty(cx, id, &rval) + : parent->defineProperty(cx, id, rval, getter, setter, attrs); + + restore_scope: + /* Restore fp->scopeChain now that obj is defined in fp->varobj. */ + fp->scopeChain = obj2; + if (!ok) + goto error; +} +END_CASE(JSOP_DEFFUN) + +BEGIN_CASE(JSOP_DEFFUN_FC) +BEGIN_CASE(JSOP_DEFFUN_DBGFC) + LOAD_FUNCTION(0); + + obj = (op == JSOP_DEFFUN_FC) + ? js_NewFlatClosure(cx, fun) + : js_NewDebuggableFlatClosure(cx, fun); + if (!obj) + goto error; + rval = OBJECT_TO_JSVAL(obj); + + attrs = (fp->flags & JSFRAME_EVAL) + ? JSPROP_ENUMERATE + : JSPROP_ENUMERATE | JSPROP_PERMANENT; + + flags = JSFUN_GSFLAG2ATTR(fun->flags); + if (flags) { + attrs |= flags | JSPROP_SHARED; + rval = JSVAL_VOID; + } + + parent = fp->varobj; + JS_ASSERT(parent); + + id = ATOM_TO_JSID(fun->atom); + ok = js_CheckRedeclaration(cx, parent, id, attrs, NULL, NULL); + if (ok) { + if (attrs == JSPROP_ENUMERATE) { + JS_ASSERT(fp->flags & JSFRAME_EVAL); + ok = parent->setProperty(cx, id, &rval); + } else { + JS_ASSERT(attrs & JSPROP_PERMANENT); + + ok = parent->defineProperty(cx, id, rval, + (flags & JSPROP_GETTER) + ? js_CastAsPropertyOp(obj) + : JS_PropertyStub, + (flags & JSPROP_SETTER) + ? js_CastAsPropertyOp(obj) + : JS_PropertyStub, + attrs); + } + } + + if (!ok) + goto error; +END_CASE(JSOP_DEFFUN_FC) + +BEGIN_CASE(JSOP_DEFLOCALFUN) + /* + * Define a local function (i.e., one nested at the top level of another + * function), parented by the current scope chain, stored in a local + * variable slot that the compiler allocated. This is an optimization over + * JSOP_DEFFUN that avoids requiring a call object for the outer function's + * activation. + */ + LOAD_FUNCTION(SLOTNO_LEN); + JS_ASSERT(FUN_INTERPRETED(fun)); + JS_ASSERT(!FUN_FLAT_CLOSURE(fun)); + obj = FUN_OBJECT(fun); + + if (FUN_NULL_CLOSURE(fun)) { + obj = js_CloneFunctionObject(cx, fun, fp->scopeChain); + if (!obj) + goto error; + } else { + parent = js_GetScopeChain(cx, fp); + if (!parent) + goto error; + + if (OBJ_GET_PARENT(cx, obj) != parent) { +#ifdef JS_TRACER + if (TRACE_RECORDER(cx)) + js_AbortRecording(cx, "DEFLOCALFUN for closure"); +#endif + obj = js_CloneFunctionObject(cx, fun, parent); + if (!obj) + goto error; + } + } + + slot = GET_SLOTNO(regs.pc); + TRACE_2(DefLocalFunSetSlot, slot, obj); + + fp->slots[slot] = OBJECT_TO_JSVAL(obj); +END_CASE(JSOP_DEFLOCALFUN) + +BEGIN_CASE(JSOP_DEFLOCALFUN_FC) + LOAD_FUNCTION(SLOTNO_LEN); + + obj = js_NewFlatClosure(cx, fun); + if (!obj) + goto error; + + slot = GET_SLOTNO(regs.pc); + TRACE_2(DefLocalFunSetSlot, slot, obj); + + fp->slots[slot] = OBJECT_TO_JSVAL(obj); +END_CASE(JSOP_DEFLOCALFUN_FC) + +BEGIN_CASE(JSOP_DEFLOCALFUN_DBGFC) + LOAD_FUNCTION(SLOTNO_LEN); + + obj = js_NewDebuggableFlatClosure(cx, fun); + if (!obj) + goto error; + + slot = GET_SLOTNO(regs.pc); + fp->slots[slot] = OBJECT_TO_JSVAL(obj); +END_CASE(JSOP_DEFLOCALFUN_DBGFC) + +BEGIN_CASE(JSOP_LAMBDA) + /* Load the specified function object literal. */ + LOAD_FUNCTION(0); + obj = FUN_OBJECT(fun); + + /* do-while(0) so we can break instead of using a goto. */ + do { + if (FUN_NULL_CLOSURE(fun)) { + parent = fp->scopeChain; + + if (OBJ_GET_PARENT(cx, obj) == parent) { + op = JSOp(regs.pc[JSOP_LAMBDA_LENGTH]); + + /* + * Optimize ({method: function () { ... }, ...}) and + * this.method = function () { ... }; bytecode sequences. + */ + if (op == JSOP_SETMETHOD) { +#ifdef DEBUG + op2 = JSOp(regs.pc[JSOP_LAMBDA_LENGTH + JSOP_SETMETHOD_LENGTH]); + JS_ASSERT(op2 == JSOP_POP || op2 == JSOP_POPV); +#endif + + lval = FETCH_OPND(-1); + if (JSVAL_IS_OBJECT(lval) && + (obj2 = JSVAL_TO_OBJECT(lval)) && + OBJ_GET_CLASS(cx, obj2) == &js_ObjectClass) { + break; + } + } else if (op == JSOP_INITMETHOD) { + lval = FETCH_OPND(-1); + JS_ASSERT(!JSVAL_IS_PRIMITIVE(lval)); + obj2 = JSVAL_TO_OBJECT(lval); + JS_ASSERT(OBJ_GET_CLASS(cx, obj2) == &js_ObjectClass); + JS_ASSERT(OBJ_SCOPE(obj2)->object == obj2); + break; + } + } + } else { + parent = js_GetScopeChain(cx, fp); + if (!parent) + goto error; + } + + obj = js_CloneFunctionObject(cx, fun, parent); + if (!obj) + goto error; + } while (0); + + PUSH_OPND(OBJECT_TO_JSVAL(obj)); +END_CASE(JSOP_LAMBDA) + +BEGIN_CASE(JSOP_LAMBDA_FC) + LOAD_FUNCTION(0); + + obj = js_NewFlatClosure(cx, fun); + if (!obj) + goto error; + + PUSH_OPND(OBJECT_TO_JSVAL(obj)); +END_CASE(JSOP_LAMBDA_FC) + +BEGIN_CASE(JSOP_LAMBDA_DBGFC) + LOAD_FUNCTION(0); + + obj = js_NewDebuggableFlatClosure(cx, fun); + if (!obj) + goto error; + + PUSH_OPND(OBJECT_TO_JSVAL(obj)); +END_CASE(JSOP_LAMBDA_DBGFC) + +BEGIN_CASE(JSOP_CALLEE) + PUSH_OPND(fp->argv[-2]); +END_CASE(JSOP_CALLEE) + +#if JS_HAS_GETTER_SETTER +BEGIN_CASE(JSOP_GETTER) +BEGIN_CASE(JSOP_SETTER) + do_getter_setter: + op2 = (JSOp) *++regs.pc; + switch (op2) { + case JSOP_INDEXBASE: + atoms += GET_INDEXBASE(regs.pc); + regs.pc += JSOP_INDEXBASE_LENGTH - 1; + goto do_getter_setter; + case JSOP_INDEXBASE1: + case JSOP_INDEXBASE2: + case JSOP_INDEXBASE3: + atoms += (op2 - JSOP_INDEXBASE1 + 1) << 16; + goto do_getter_setter; + + case JSOP_SETNAME: + case JSOP_SETPROP: + LOAD_ATOM(0); + id = ATOM_TO_JSID(atom); + rval = FETCH_OPND(-1); + i = -1; + goto gs_pop_lval; + + case JSOP_SETELEM: + rval = FETCH_OPND(-1); + id = 0; + i = -2; + gs_pop_lval: + FETCH_OBJECT(cx, i - 1, lval, obj); + break; + + case JSOP_INITPROP: + JS_ASSERT(regs.sp - StackBase(fp) >= 2); + rval = FETCH_OPND(-1); + i = -1; + LOAD_ATOM(0); + id = ATOM_TO_JSID(atom); + goto gs_get_lval; + + default: + JS_ASSERT(op2 == JSOP_INITELEM); + + JS_ASSERT(regs.sp - StackBase(fp) >= 3); + rval = FETCH_OPND(-1); + id = 0; + i = -2; + gs_get_lval: + lval = FETCH_OPND(i-1); + JS_ASSERT(JSVAL_IS_OBJECT(lval)); + obj = JSVAL_TO_OBJECT(lval); + break; + } + + /* Ensure that id has a type suitable for use with obj. */ + if (id == 0) + FETCH_ELEMENT_ID(obj, i, id); + + if (JS_TypeOfValue(cx, rval) != JSTYPE_FUNCTION) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_GETTER_OR_SETTER, + (op == JSOP_GETTER) + ? js_getter_str + : js_setter_str); + goto error; + } + + /* + * Getters and setters are just like watchpoints from an access control + * point of view. + */ + if (!obj->checkAccess(cx, id, JSACC_WATCH, &rtmp, &attrs)) + goto error; + + if (op == JSOP_GETTER) { + getter = js_CastAsPropertyOp(JSVAL_TO_OBJECT(rval)); + setter = JS_PropertyStub; + attrs = JSPROP_GETTER; + } else { + getter = JS_PropertyStub; + setter = js_CastAsPropertyOp(JSVAL_TO_OBJECT(rval)); + attrs = JSPROP_SETTER; + } + attrs |= JSPROP_ENUMERATE | JSPROP_SHARED; + + /* Check for a readonly or permanent property of the same name. */ + if (!js_CheckRedeclaration(cx, obj, id, attrs, NULL, NULL)) + goto error; + + if (!obj->defineProperty(cx, id, JSVAL_VOID, getter, setter, attrs)) + goto error; + + regs.sp += i; + if (js_CodeSpec[op2].ndefs > js_CodeSpec[op2].nuses) { + JS_ASSERT(js_CodeSpec[op2].ndefs == js_CodeSpec[op2].nuses + 1); + STORE_OPND(-1, rval); + } + len = js_CodeSpec[op2].length; + DO_NEXT_OP(len); +#endif /* JS_HAS_GETTER_SETTER */ + +BEGIN_CASE(JSOP_HOLE) + PUSH_OPND(JSVAL_HOLE); +END_CASE(JSOP_HOLE) + +BEGIN_CASE(JSOP_NEWARRAY) + len = GET_UINT16(regs.pc); + cx->fp->assertValidStackDepth(len); + obj = js_NewArrayObject(cx, len, regs.sp - len, JS_TRUE); + if (!obj) + goto error; + regs.sp -= len - 1; + STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); +END_CASE(JSOP_NEWARRAY) + +BEGIN_CASE(JSOP_NEWINIT) + i = GET_INT8(regs.pc); + JS_ASSERT(i == JSProto_Array || i == JSProto_Object); + if (i == JSProto_Array) { + obj = js_NewArrayObject(cx, 0, NULL); + if (!obj) + goto error; + } else { + obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL); + if (!obj) + goto error; + + if (regs.pc[JSOP_NEWINIT_LENGTH] != JSOP_ENDINIT) { + JS_LOCK_OBJ(cx, obj); + JSScope *scope = js_GetMutableScope(cx, obj); + if (!scope) { + JS_UNLOCK_OBJ(cx, obj); + goto error; + } + JS_UNLOCK_SCOPE(cx, scope); + } + } + + PUSH_OPND(OBJECT_TO_JSVAL(obj)); + CHECK_INTERRUPT_HANDLER(); +END_CASE(JSOP_NEWINIT) + +BEGIN_CASE(JSOP_ENDINIT) + /* Re-set the newborn root to the top of this object tree. */ + JS_ASSERT(regs.sp - StackBase(fp) >= 1); + lval = FETCH_OPND(-1); + JS_ASSERT(JSVAL_IS_OBJECT(lval)); + cx->weakRoots.finalizableNewborns[FINALIZE_OBJECT] = JSVAL_TO_OBJECT(lval); +END_CASE(JSOP_ENDINIT) + +BEGIN_CASE(JSOP_INITPROP) +BEGIN_CASE(JSOP_INITMETHOD) + /* Load the property's initial value into rval. */ + JS_ASSERT(regs.sp - StackBase(fp) >= 2); + rval = FETCH_OPND(-1); + + /* Load the object being initialized into lval/obj. */ + lval = FETCH_OPND(-2); + obj = JSVAL_TO_OBJECT(lval); + JS_ASSERT(OBJ_IS_NATIVE(obj)); + JS_ASSERT(!OBJ_GET_CLASS(cx, obj)->reserveSlots); + JS_ASSERT(!(obj->getClass()->flags & JSCLASS_SHARE_ALL_PROPERTIES)); + + do { + JSScope *scope; + uint32 kshape; + JSPropertyCache *cache; + JSPropCacheEntry *entry; + + JS_LOCK_OBJ(cx, obj); + scope = OBJ_SCOPE(obj); + // FIXME: bug 513291 -- uncomment this assertion and remove the + // (!scope->owned()) => js_GetMutableScope code further + // below. + // JS_ASSERT(scope->object == obj); + JS_ASSERT(!scope->sealed()); + kshape = scope->shape; + cache = &JS_PROPERTY_CACHE(cx); + entry = &cache->table[PROPERTY_CACHE_HASH_PC(regs.pc, kshape)]; + PCMETER(cache->pctestentry = entry); + PCMETER(cache->tests++); + PCMETER(cache->initests++); + + if (entry->kpc == regs.pc && + entry->kshape == kshape && + PCVCAP_SHAPE(entry->vcap) == rt->protoHazardShape) { + JS_ASSERT(PCVCAP_TAG(entry->vcap) == 0); + + PCMETER(cache->pchits++); + PCMETER(cache->inipchits++); + + JS_ASSERT(PCVAL_IS_SPROP(entry->vword)); + sprop = PCVAL_TO_SPROP(entry->vword); + JS_ASSERT(!(sprop->attrs & JSPROP_READONLY)); + + /* + * If this property has a non-stub setter, it must be __proto__, + * __parent__, or another "shared prototype" built-in. Force a miss + * to save code size here and let the standard code path take care + * of business. + */ + if (!SPROP_HAS_STUB_SETTER(sprop)) + goto do_initprop_miss; + + if (!scope->owned()) { + scope = js_GetMutableScope(cx, obj); + if (!scope) { + JS_UNLOCK_OBJ(cx, obj); + goto error; + } + } + + /* + * Detect a repeated property name and force a miss to share the + * strict warning code and consolidate all the complexity managed + * by JSScope::addProperty. + */ + if (sprop->parent != scope->lastProperty()) + goto do_initprop_miss; + + /* + * Otherwise this entry must be for a direct property of obj, not a + * proto-property, and there cannot have been any deletions of + * prior properties. + */ + JS_ASSERT(!scope->inDictionaryMode()); + JS_ASSERT_IF(scope->table, !scope->hasProperty(sprop)); + + slot = sprop->slot; + JS_ASSERT(slot == scope->freeslot); + if (slot < STOBJ_NSLOTS(obj)) { + ++scope->freeslot; + } else { + if (!js_AllocSlot(cx, obj, &slot)) { + JS_UNLOCK_SCOPE(cx, scope); + goto error; + } + JS_ASSERT(slot == sprop->slot); + } + + JS_ASSERT(!scope->lastProperty() || + scope->shape == scope->lastProperty()->shape); + if (scope->table) { + JSScopeProperty *sprop2 = + scope->addDataProperty(cx, sprop->id, slot, sprop->attrs); + if (!sprop2) { + js_FreeSlot(cx, obj, slot); + JS_UNLOCK_SCOPE(cx, scope); + goto error; + } + JS_ASSERT(sprop2 == sprop); + } else { + JS_ASSERT(scope->owned()); + scope->extend(cx, sprop); + } + + /* + * No method change check here because here we are adding a new + * property, not updating an existing slot's value that might + * contain a method of a branded scope. + */ + TRACE_2(SetPropHit, entry, sprop); + LOCKED_OBJ_SET_SLOT(obj, slot, rval); + JS_UNLOCK_SCOPE(cx, scope); + break; + } + + do_initprop_miss: + PCMETER(cache->inipcmisses++); + JS_UNLOCK_SCOPE(cx, scope); + + /* Get the immediate property name into id. */ + LOAD_ATOM(0); + id = ATOM_TO_JSID(atom); + + /* Set the property named by obj[id] to rval. */ + if (!js_CheckRedeclaration(cx, obj, id, JSPROP_INITIALIZER, + NULL, NULL)) { + goto error; + } + + uintN defineHow = (op == JSOP_INITMETHOD) + ? JSDNP_CACHE_RESULT | JSDNP_SET_METHOD + : JSDNP_CACHE_RESULT; + if (!(JS_UNLIKELY(atom == cx->runtime->atomState.protoAtom) + ? js_SetPropertyHelper(cx, obj, id, defineHow, &rval) + : js_DefineNativeProperty(cx, obj, id, rval, NULL, NULL, + JSPROP_ENUMERATE, 0, 0, NULL, + defineHow))) { + goto error; + } + } while (0); + + /* Common tail for property cache hit and miss cases. */ + regs.sp--; +END_CASE(JSOP_INITPROP); + +BEGIN_CASE(JSOP_INITELEM) + /* Pop the element's value into rval. */ + JS_ASSERT(regs.sp - StackBase(fp) >= 3); + rval = FETCH_OPND(-1); + + /* Find the object being initialized at top of stack. */ + lval = FETCH_OPND(-3); + JS_ASSERT(!JSVAL_IS_PRIMITIVE(lval)); + obj = JSVAL_TO_OBJECT(lval); + + /* Fetch id now that we have obj. */ + FETCH_ELEMENT_ID(obj, -2, id); + + /* + * Check for property redeclaration strict warning (we may be in an object + * initialiser, not an array initialiser). + */ + if (!js_CheckRedeclaration(cx, obj, id, JSPROP_INITIALIZER, NULL, NULL)) + goto error; + + /* + * If rval is a hole, do not call JSObject::defineProperty. In this case, + * obj must be an array, so if the current op is the last element + * initialiser, set the array length to one greater than id. + */ + if (rval == JSVAL_HOLE) { + JS_ASSERT(OBJ_IS_ARRAY(cx, obj)); + JS_ASSERT(JSID_IS_INT(id)); + JS_ASSERT(jsuint(JSID_TO_INT(id)) < JS_ARGS_LENGTH_MAX); + if (js_GetOpcode(cx, script, regs.pc + JSOP_INITELEM_LENGTH) == JSOP_ENDINIT && + !js_SetLengthProperty(cx, obj, (jsuint) (JSID_TO_INT(id) + 1))) { + goto error; + } + } else { + if (!obj->defineProperty(cx, id, rval, NULL, NULL, JSPROP_ENUMERATE)) + goto error; + } + regs.sp -= 2; +END_CASE(JSOP_INITELEM) + +#if JS_HAS_SHARP_VARS + +BEGIN_CASE(JSOP_DEFSHARP) + slot = GET_UINT16(regs.pc); + JS_ASSERT(slot + 1 < fp->script->nfixed); + lval = fp->slots[slot]; + if (!JSVAL_IS_PRIMITIVE(lval)) { + obj = JSVAL_TO_OBJECT(lval); + } else { + JS_ASSERT(JSVAL_IS_VOID(lval)); + obj = js_NewArrayObject(cx, 0, NULL); + if (!obj) + goto error; + fp->slots[slot] = OBJECT_TO_JSVAL(obj); + } + i = (jsint) GET_UINT16(regs.pc + UINT16_LEN); + id = INT_TO_JSID(i); + rval = FETCH_OPND(-1); + if (JSVAL_IS_PRIMITIVE(rval)) { + char numBuf[12]; + JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_SHARP_DEF, numBuf); + goto error; + } + if (!obj->defineProperty(cx, id, rval, NULL, NULL, JSPROP_ENUMERATE)) + goto error; +END_CASE(JSOP_DEFSHARP) + +BEGIN_CASE(JSOP_USESHARP) + slot = GET_UINT16(regs.pc); + JS_ASSERT(slot + 1 < fp->script->nfixed); + lval = fp->slots[slot]; + i = (jsint) GET_UINT16(regs.pc + UINT16_LEN); + if (JSVAL_IS_VOID(lval)) { + rval = JSVAL_VOID; + } else { + obj = JSVAL_TO_OBJECT(fp->slots[slot]); + id = INT_TO_JSID(i); + if (!obj->getProperty(cx, id, &rval)) + goto error; + } + if (!JSVAL_IS_OBJECT(rval)) { + char numBuf[12]; + + JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_SHARP_USE, numBuf); + goto error; + } + PUSH_OPND(rval); +END_CASE(JSOP_USESHARP) + +BEGIN_CASE(JSOP_SHARPINIT) + slot = GET_UINT16(regs.pc); + JS_ASSERT(slot + 1 < fp->script->nfixed); + vp = &fp->slots[slot]; + rval = vp[1]; + + /* + * We peek ahead safely here because empty initialisers get zero + * JSOP_SHARPINIT ops, and non-empty ones get two: the first comes + * immediately after JSOP_NEWINIT followed by one or more property + * initialisers; and the second comes directly before JSOP_ENDINIT. + */ + if (regs.pc[JSOP_SHARPINIT_LENGTH] != JSOP_ENDINIT) { + rval = JSVAL_IS_VOID(rval) ? JSVAL_ONE : rval + 2; + } else { + JS_ASSERT(JSVAL_IS_INT(rval)); + rval -= 2; + if (rval == JSVAL_ZERO) + vp[0] = JSVAL_VOID; + } + vp[1] = rval; +END_CASE(JSOP_SHARPINIT) + +#endif /* JS_HAS_SHARP_VARS */ + +BEGIN_CASE(JSOP_GOSUB) + PUSH(JSVAL_FALSE); + i = (regs.pc - script->main) + JSOP_GOSUB_LENGTH; + PUSH(INT_TO_JSVAL(i)); + len = GET_JUMP_OFFSET(regs.pc); +END_VARLEN_CASE + +BEGIN_CASE(JSOP_GOSUBX) + PUSH(JSVAL_FALSE); + i = (regs.pc - script->main) + JSOP_GOSUBX_LENGTH; + len = GET_JUMPX_OFFSET(regs.pc); + PUSH(INT_TO_JSVAL(i)); +END_VARLEN_CASE + +BEGIN_CASE(JSOP_RETSUB) + /* Pop [exception or hole, retsub pc-index]. */ + rval = POP(); + lval = POP(); + JS_ASSERT(JSVAL_IS_BOOLEAN(lval)); + if (JSVAL_TO_BOOLEAN(lval)) { + /* + * Exception was pending during finally, throw it *before* we adjust + * pc, because pc indexes into script->trynotes. This turns out not to + * be necessary, but it seems clearer. And it points out a FIXME: + * 350509, due to Igor Bukanov. + */ + cx->throwing = JS_TRUE; + cx->exception = rval; + goto error; + } + JS_ASSERT(JSVAL_IS_INT(rval)); + len = JSVAL_TO_INT(rval); + regs.pc = script->main; +END_VARLEN_CASE + +BEGIN_CASE(JSOP_EXCEPTION) + JS_ASSERT(cx->throwing); + PUSH(cx->exception); + cx->throwing = JS_FALSE; + CHECK_BRANCH(); +END_CASE(JSOP_EXCEPTION) + +BEGIN_CASE(JSOP_FINALLY) + CHECK_BRANCH(); +END_CASE(JSOP_FINALLY) + +BEGIN_CASE(JSOP_THROWING) + JS_ASSERT(!cx->throwing); + cx->throwing = JS_TRUE; + cx->exception = POP_OPND(); +END_CASE(JSOP_THROWING) + +BEGIN_CASE(JSOP_THROW) + JS_ASSERT(!cx->throwing); + CHECK_BRANCH(); + cx->throwing = JS_TRUE; + cx->exception = POP_OPND(); + /* let the code at error try to catch the exception. */ + goto error; + +BEGIN_CASE(JSOP_SETLOCALPOP) + /* + * The stack must have a block with at least one local slot below the + * exception object. + */ + JS_ASSERT((size_t) (regs.sp - StackBase(fp)) >= 2); + slot = GET_UINT16(regs.pc); + JS_ASSERT(slot + 1 < script->nslots); + fp->slots[slot] = POP_OPND(); +END_CASE(JSOP_SETLOCALPOP) + +BEGIN_CASE(JSOP_IFPRIMTOP) + /* + * If the top of stack is of primitive type, jump to our target. Otherwise + * advance to the next opcode. + */ + JS_ASSERT(regs.sp > StackBase(fp)); + rval = FETCH_OPND(-1); + if (JSVAL_IS_PRIMITIVE(rval)) { + len = GET_JUMP_OFFSET(regs.pc); + BRANCH(len); + } +END_CASE(JSOP_IFPRIMTOP) + +BEGIN_CASE(JSOP_PRIMTOP) + JS_ASSERT(regs.sp > StackBase(fp)); + lval = FETCH_OPND(-1); + i = GET_INT8(regs.pc); + if (!JSVAL_IS_PRIMITIVE(lval)) { + lval = FETCH_OPND(-2); + js_ReportValueError2(cx, JSMSG_CANT_CONVERT_TO, -2, lval, NULL, + (i == JSTYPE_VOID) ? "primitive type" : JS_TYPE_STR(i)); + goto error; + } +END_CASE(JSOP_PRIMTOP) + +BEGIN_CASE(JSOP_OBJTOP) + lval = FETCH_OPND(-1); + if (JSVAL_IS_PRIMITIVE(lval)) { + js_ReportValueError(cx, GET_UINT16(regs.pc), -1, lval, NULL); + goto error; + } +END_CASE(JSOP_OBJTOP) + +BEGIN_CASE(JSOP_INSTANCEOF) + rval = FETCH_OPND(-1); + if (JSVAL_IS_PRIMITIVE(rval) || + !(obj = JSVAL_TO_OBJECT(rval))->map->ops->hasInstance) { + js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS, + -1, rval, NULL); + goto error; + } + lval = FETCH_OPND(-2); + cond = JS_FALSE; + if (!obj->map->ops->hasInstance(cx, obj, lval, &cond)) + goto error; + regs.sp--; + STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); +END_CASE(JSOP_INSTANCEOF) + +#if JS_HAS_DEBUGGER_KEYWORD +BEGIN_CASE(JSOP_DEBUGGER) +{ + JSTrapHandler handler = cx->debugHooks->debuggerHandler; + if (handler) { + switch (handler(cx, script, regs.pc, &rval, cx->debugHooks->debuggerHandlerData)) { + case JSTRAP_ERROR: + goto error; + case JSTRAP_CONTINUE: + break; + case JSTRAP_RETURN: + fp->rval = rval; + ok = JS_TRUE; + goto forced_return; + case JSTRAP_THROW: + cx->throwing = JS_TRUE; + cx->exception = rval; + goto error; + default:; + } + CHECK_INTERRUPT_HANDLER(); + } +} +END_CASE(JSOP_DEBUGGER) +#endif /* JS_HAS_DEBUGGER_KEYWORD */ + +#if JS_HAS_XML_SUPPORT +BEGIN_CASE(JSOP_DEFXMLNS) + rval = POP(); + if (!js_SetDefaultXMLNamespace(cx, rval)) + goto error; +END_CASE(JSOP_DEFXMLNS) + +BEGIN_CASE(JSOP_ANYNAME) + if (!js_GetAnyName(cx, &rval)) + goto error; + PUSH_OPND(rval); +END_CASE(JSOP_ANYNAME) + +BEGIN_CASE(JSOP_QNAMEPART) + LOAD_ATOM(0); + PUSH_OPND(ATOM_KEY(atom)); +END_CASE(JSOP_QNAMEPART) + +BEGIN_CASE(JSOP_QNAMECONST) + LOAD_ATOM(0); + rval = ATOM_KEY(atom); + lval = FETCH_OPND(-1); + obj = js_ConstructXMLQNameObject(cx, lval, rval); + if (!obj) + goto error; + STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); +END_CASE(JSOP_QNAMECONST) + +BEGIN_CASE(JSOP_QNAME) + rval = FETCH_OPND(-1); + lval = FETCH_OPND(-2); + obj = js_ConstructXMLQNameObject(cx, lval, rval); + if (!obj) + goto error; + regs.sp--; + STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); +END_CASE(JSOP_QNAME) + +BEGIN_CASE(JSOP_TOATTRNAME) + rval = FETCH_OPND(-1); + if (!js_ToAttributeName(cx, &rval)) + goto error; + STORE_OPND(-1, rval); +END_CASE(JSOP_TOATTRNAME) + +BEGIN_CASE(JSOP_TOATTRVAL) + rval = FETCH_OPND(-1); + JS_ASSERT(JSVAL_IS_STRING(rval)); + str = js_EscapeAttributeValue(cx, JSVAL_TO_STRING(rval), JS_FALSE); + if (!str) + goto error; + STORE_OPND(-1, STRING_TO_JSVAL(str)); +END_CASE(JSOP_TOATTRVAL) + +BEGIN_CASE(JSOP_ADDATTRNAME) +BEGIN_CASE(JSOP_ADDATTRVAL) + rval = FETCH_OPND(-1); + lval = FETCH_OPND(-2); + str = JSVAL_TO_STRING(lval); + str2 = JSVAL_TO_STRING(rval); + str = js_AddAttributePart(cx, op == JSOP_ADDATTRNAME, str, str2); + if (!str) + goto error; + regs.sp--; + STORE_OPND(-1, STRING_TO_JSVAL(str)); +END_CASE(JSOP_ADDATTRNAME) + +BEGIN_CASE(JSOP_BINDXMLNAME) + lval = FETCH_OPND(-1); + if (!js_FindXMLProperty(cx, lval, &obj, &id)) + goto error; + STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); + PUSH_OPND(ID_TO_VALUE(id)); +END_CASE(JSOP_BINDXMLNAME) + +BEGIN_CASE(JSOP_SETXMLNAME) + obj = JSVAL_TO_OBJECT(FETCH_OPND(-3)); + rval = FETCH_OPND(-1); + FETCH_ELEMENT_ID(obj, -2, id); + if (!obj->setProperty(cx, id, &rval)) + goto error; + rval = FETCH_OPND(-1); + regs.sp -= 2; + STORE_OPND(-1, rval); +END_CASE(JSOP_SETXMLNAME) + +BEGIN_CASE(JSOP_CALLXMLNAME) +BEGIN_CASE(JSOP_XMLNAME) + lval = FETCH_OPND(-1); + if (!js_FindXMLProperty(cx, lval, &obj, &id)) + goto error; + if (!obj->getProperty(cx, id, &rval)) + goto error; + STORE_OPND(-1, rval); + if (op == JSOP_CALLXMLNAME) + PUSH_OPND(OBJECT_TO_JSVAL(obj)); +END_CASE(JSOP_XMLNAME) + +BEGIN_CASE(JSOP_DESCENDANTS) +BEGIN_CASE(JSOP_DELDESC) + FETCH_OBJECT(cx, -2, lval, obj); + rval = FETCH_OPND(-1); + if (!js_GetXMLDescendants(cx, obj, rval, &rval)) + goto error; + + if (op == JSOP_DELDESC) { + regs.sp[-1] = rval; /* set local root */ + if (!js_DeleteXMLListElements(cx, JSVAL_TO_OBJECT(rval))) + goto error; + rval = JSVAL_TRUE; /* always succeed */ + } + + regs.sp--; + STORE_OPND(-1, rval); +END_CASE(JSOP_DESCENDANTS) + +BEGIN_CASE(JSOP_FILTER) + /* + * We push the hole value before jumping to [enditer] so we can detect the + * first iteration and direct js_StepXMLListFilter to initialize filter's + * state. + */ + PUSH_OPND(JSVAL_HOLE); + len = GET_JUMP_OFFSET(regs.pc); + JS_ASSERT(len > 0); +END_VARLEN_CASE + +BEGIN_CASE(JSOP_ENDFILTER) + cond = (regs.sp[-1] != JSVAL_HOLE); + if (cond) { + /* Exit the "with" block left from the previous iteration. */ + js_LeaveWith(cx); + } + if (!js_StepXMLListFilter(cx, cond)) + goto error; + if (regs.sp[-1] != JSVAL_NULL) { + /* + * Decrease sp after EnterWith returns as we use sp[-1] there to root + * temporaries. + */ + JS_ASSERT(VALUE_IS_XML(cx, regs.sp[-1])); + if (!js_EnterWith(cx, -2)) + goto error; + regs.sp--; + len = GET_JUMP_OFFSET(regs.pc); + JS_ASSERT(len < 0); + BRANCH(len); + } + regs.sp--; +END_CASE(JSOP_ENDFILTER); + +BEGIN_CASE(JSOP_TOXML) + rval = FETCH_OPND(-1); + obj = js_ValueToXMLObject(cx, rval); + if (!obj) + goto error; + STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); +END_CASE(JSOP_TOXML) + +BEGIN_CASE(JSOP_TOXMLLIST) + rval = FETCH_OPND(-1); + obj = js_ValueToXMLListObject(cx, rval); + if (!obj) + goto error; + STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); +END_CASE(JSOP_TOXMLLIST) + +BEGIN_CASE(JSOP_XMLTAGEXPR) + rval = FETCH_OPND(-1); + str = js_ValueToString(cx, rval); + if (!str) + goto error; + STORE_OPND(-1, STRING_TO_JSVAL(str)); +END_CASE(JSOP_XMLTAGEXPR) + +BEGIN_CASE(JSOP_XMLELTEXPR) + rval = FETCH_OPND(-1); + if (VALUE_IS_XML(cx, rval)) { + str = js_ValueToXMLString(cx, rval); + } else { + str = js_ValueToString(cx, rval); + if (str) + str = js_EscapeElementValue(cx, str); + } + if (!str) + goto error; + STORE_OPND(-1, STRING_TO_JSVAL(str)); +END_CASE(JSOP_XMLELTEXPR) + +BEGIN_CASE(JSOP_XMLOBJECT) + LOAD_OBJECT(0); + obj = js_CloneXMLObject(cx, obj); + if (!obj) + goto error; + PUSH_OPND(OBJECT_TO_JSVAL(obj)); +END_CASE(JSOP_XMLOBJECT) + +BEGIN_CASE(JSOP_XMLCDATA) + LOAD_ATOM(0); + str = ATOM_TO_STRING(atom); + obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_TEXT, NULL, str); + if (!obj) + goto error; + PUSH_OPND(OBJECT_TO_JSVAL(obj)); +END_CASE(JSOP_XMLCDATA) + +BEGIN_CASE(JSOP_XMLCOMMENT) + LOAD_ATOM(0); + str = ATOM_TO_STRING(atom); + obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_COMMENT, NULL, str); + if (!obj) + goto error; + PUSH_OPND(OBJECT_TO_JSVAL(obj)); +END_CASE(JSOP_XMLCOMMENT) + +BEGIN_CASE(JSOP_XMLPI) + LOAD_ATOM(0); + str = ATOM_TO_STRING(atom); + rval = FETCH_OPND(-1); + str2 = JSVAL_TO_STRING(rval); + obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_PROCESSING_INSTRUCTION, str, str2); + if (!obj) + goto error; + STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); +END_CASE(JSOP_XMLPI) + +BEGIN_CASE(JSOP_GETFUNNS) + if (!js_GetFunctionNamespace(cx, &rval)) + goto error; + PUSH_OPND(rval); +END_CASE(JSOP_GETFUNNS) +#endif /* JS_HAS_XML_SUPPORT */ + +BEGIN_CASE(JSOP_ENTERBLOCK) + LOAD_OBJECT(0); + JS_ASSERT(!OBJ_IS_CLONED_BLOCK(obj)); + JS_ASSERT(StackBase(fp) + OBJ_BLOCK_DEPTH(cx, obj) == regs.sp); + vp = regs.sp + OBJ_BLOCK_COUNT(cx, obj); + JS_ASSERT(regs.sp < vp); + JS_ASSERT(vp <= fp->slots + script->nslots); + while (regs.sp < vp) { + STORE_OPND(0, JSVAL_VOID); + regs.sp++; + } + +#ifdef DEBUG + JS_ASSERT(fp->blockChain == OBJ_GET_PARENT(cx, obj)); + + /* + * The young end of fp->scopeChain may omit blocks if we haven't closed + * over them, but if there are any closure blocks on fp->scopeChain, they'd + * better be (clones of) ancestors of the block we're entering now; + * anything else we should have popped off fp->scopeChain when we left its + * static scope. + */ + obj2 = fp->scopeChain; + while ((clasp = OBJ_GET_CLASS(cx, obj2)) == &js_WithClass) + obj2 = OBJ_GET_PARENT(cx, obj2); + if (clasp == &js_BlockClass && + obj2->getPrivate() == fp) { + JSObject *youngestProto = OBJ_GET_PROTO(cx, obj2); + JS_ASSERT(!OBJ_IS_CLONED_BLOCK(youngestProto)); + parent = obj; + while ((parent = OBJ_GET_PARENT(cx, parent)) != youngestProto) + JS_ASSERT(parent); + } +#endif + + fp->blockChain = obj; +END_CASE(JSOP_ENTERBLOCK) + +BEGIN_CASE(JSOP_LEAVEBLOCKEXPR) +BEGIN_CASE(JSOP_LEAVEBLOCK) +{ +#ifdef DEBUG + JS_ASSERT(OBJ_GET_CLASS(cx, fp->blockChain) == &js_BlockClass); + uintN blockDepth = OBJ_BLOCK_DEPTH(cx, fp->blockChain); + + JS_ASSERT(blockDepth <= StackDepth(script)); +#endif + /* + * If we're about to leave the dynamic scope of a block that has been + * cloned onto fp->scopeChain, clear its private data, move its locals from + * the stack into the clone, and pop it off the chain. + */ + obj = fp->scopeChain; + if (OBJ_GET_PROTO(cx, obj) == fp->blockChain) { + JS_ASSERT (OBJ_GET_CLASS(cx, obj) == &js_BlockClass); + if (!js_PutBlockObject(cx, JS_TRUE)) + goto error; + } + + /* Pop the block chain, too. */ + fp->blockChain = OBJ_GET_PARENT(cx, fp->blockChain); + + /* Move the result of the expression to the new topmost stack slot. */ + if (op == JSOP_LEAVEBLOCKEXPR) + rval = FETCH_OPND(-1); + regs.sp -= GET_UINT16(regs.pc); + if (op == JSOP_LEAVEBLOCKEXPR) { + JS_ASSERT(StackBase(fp) + blockDepth == regs.sp - 1); + STORE_OPND(-1, rval); + } else { + JS_ASSERT(StackBase(fp) + blockDepth == regs.sp); + } +} +END_CASE(JSOP_LEAVEBLOCK) + +BEGIN_CASE(JSOP_CALLBUILTIN) +#ifdef JS_TRACER + obj = js_GetBuiltinFunction(cx, GET_INDEX(regs.pc)); + if (!obj) + goto error; + rval = FETCH_OPND(-1); + PUSH_OPND(rval); + STORE_OPND(-2, OBJECT_TO_JSVAL(obj)); +#else + goto bad_opcode; /* This is an imacro-only opcode. */ +#endif +END_CASE(JSOP_CALLBUILTIN) + +#if JS_HAS_GENERATORS +BEGIN_CASE(JSOP_GENERATOR) + ASSERT_NOT_THROWING(cx); + regs.pc += JSOP_GENERATOR_LENGTH; + obj = js_NewGenerator(cx, fp); + if (!obj) + goto error; + JS_ASSERT(!fp->callobj && !fp->argsobj); + fp->rval = OBJECT_TO_JSVAL(obj); + ok = JS_TRUE; + if (inlineCallCount != 0) + goto inline_return; + goto exit; + +BEGIN_CASE(JSOP_YIELD) + ASSERT_NOT_THROWING(cx); + if (FRAME_TO_GENERATOR(fp)->state == JSGEN_CLOSING) { + js_ReportValueError(cx, JSMSG_BAD_GENERATOR_YIELD, + JSDVG_SEARCH_STACK, fp->argv[-2], NULL); + goto error; + } + fp->rval = FETCH_OPND(-1); + fp->flags |= JSFRAME_YIELDING; + regs.pc += JSOP_YIELD_LENGTH; + ok = JS_TRUE; + goto exit; + +BEGIN_CASE(JSOP_ARRAYPUSH) + slot = GET_UINT16(regs.pc); + JS_ASSERT(script->nfixed <= slot); + JS_ASSERT(slot < script->nslots); + lval = fp->slots[slot]; + obj = JSVAL_TO_OBJECT(lval); + rval = FETCH_OPND(-1); + if (!js_ArrayCompPush(cx, obj, rval)) + goto error; + regs.sp--; +END_CASE(JSOP_ARRAYPUSH) +#endif /* JS_HAS_GENERATORS */ + +#if JS_THREADED_INTERP + L_JSOP_BACKPATCH: + L_JSOP_BACKPATCH_POP: + +# if !JS_HAS_GENERATORS + L_JSOP_GENERATOR: + L_JSOP_YIELD: + L_JSOP_ARRAYPUSH: +# endif + +# if !JS_HAS_SHARP_VARS + L_JSOP_DEFSHARP: + L_JSOP_USESHARP: + L_JSOP_SHARPINIT: +# endif + +# if !JS_HAS_DESTRUCTURING + L_JSOP_ENUMCONSTELEM: +# endif + +# if !JS_HAS_XML_SUPPORT + L_JSOP_CALLXMLNAME: + L_JSOP_STARTXMLEXPR: + L_JSOP_STARTXML: + L_JSOP_DELDESC: + L_JSOP_GETFUNNS: + L_JSOP_XMLPI: + L_JSOP_XMLCOMMENT: + L_JSOP_XMLCDATA: + L_JSOP_XMLOBJECT: + L_JSOP_XMLELTEXPR: + L_JSOP_XMLTAGEXPR: + L_JSOP_TOXMLLIST: + L_JSOP_TOXML: + L_JSOP_ENDFILTER: + L_JSOP_FILTER: + L_JSOP_DESCENDANTS: + L_JSOP_XMLNAME: + L_JSOP_SETXMLNAME: + L_JSOP_BINDXMLNAME: + L_JSOP_ADDATTRVAL: + L_JSOP_ADDATTRNAME: + L_JSOP_TOATTRVAL: + L_JSOP_TOATTRNAME: + L_JSOP_QNAME: + L_JSOP_QNAMECONST: + L_JSOP_QNAMEPART: + L_JSOP_ANYNAME: + L_JSOP_DEFXMLNS: +# endif +#endif /* !JS_THREADED_INTERP */ diff --git a/ape-server/deps/js/src/jsotypes.h b/ape-server/deps/js/src/jsotypes.h new file mode 100755 index 0000000..b977cec --- /dev/null +++ b/ape-server/deps/js/src/jsotypes.h @@ -0,0 +1,198 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * This section typedefs the old 'native' types to the new PRs. + * These definitions are scheduled to be eliminated at the earliest + * possible time. The NSPR API is implemented and documented using + * the new definitions. + */ + +/* + * Note that we test for PROTYPES_H, not JSOTYPES_H. This is to avoid + * double-definitions of scalar types such as uint32, if NSPR's + * protypes.h is also included. + */ +#ifndef PROTYPES_H +#define PROTYPES_H + +#ifdef XP_BEOS +/* BeOS defines most int types in SupportDefs.h (int8, uint8, int16, + * uint16, int32, uint32, int64, uint64), so in the interest of + * not conflicting with other definitions elsewhere we have to skip the + * #ifdef jungle below, duplicate some definitions, and do our stuff. + */ +#include + +typedef JSUintn uintn; +#ifndef _XP_Core_ +typedef JSIntn intn; +#endif + +#else + +/* SVR4 typedef of uint is commonly found on UNIX machines. */ +#ifdef XP_UNIX +#include +#else +typedef JSUintn uint; +#endif + +typedef JSUintn uintn; +typedef JSUint64 uint64; +#if !defined(_WIN32) && !defined(XP_OS2) +typedef JSUint32 uint32; +#else +typedef unsigned long uint32; +#endif +typedef JSUint16 uint16; +typedef JSUint8 uint8; + +#ifndef _XP_Core_ +typedef JSIntn intn; +#endif + +/* + * On AIX 4.3, sys/inttypes.h (which is included by sys/types.h, a very + * common header file) defines the types int8, int16, int32, and int64. + * So we don't define these four types here to avoid conflicts in case + * the code also includes sys/types.h. + */ +#if defined(AIX) && defined(HAVE_SYS_INTTYPES_H) +#include +#else +typedef JSInt64 int64; + +/* /usr/include/model.h on HP-UX defines int8, int16, and int32 */ +#if !defined(_WIN32) && !defined(XP_OS2) +typedef JSInt32 int32; +#else +typedef long int32; +#endif +typedef JSInt16 int16; +typedef JSInt8 int8; +#endif /* AIX && HAVE_SYS_INTTYPES_H */ + +#endif /* XP_BEOS */ + +typedef JSFloat64 float64; + +/* Re: jsbit.h */ +#define TEST_BIT JS_TEST_BIT +#define SET_BIT JS_SET_BIT +#define CLEAR_BIT JS_CLEAR_BIT + +/* Re: prarena.h->plarena.h */ +#define PRArena PLArena +#define PRArenaPool PLArenaPool +#define PRArenaStats PLArenaStats +#define PR_ARENA_ALIGN PL_ARENA_ALIGN +#define PR_INIT_ARENA_POOL PL_INIT_ARENA_POOL +#define PR_ARENA_ALLOCATE PL_ARENA_ALLOCATE +#define PR_ARENA_GROW PL_ARENA_GROW +#define PR_ARENA_MARK PL_ARENA_MARK +#define PR_CLEAR_UNUSED PL_CLEAR_UNUSED +#define PR_CLEAR_ARENA PL_CLEAR_ARENA +#define PR_ARENA_RELEASE PL_ARENA_RELEASE +#define PR_COUNT_ARENA PL_COUNT_ARENA +#define PR_ARENA_DESTROY PL_ARENA_DESTROY +#define PR_InitArenaPool PL_InitArenaPool +#define PR_FreeArenaPool PL_FreeArenaPool +#define PR_FinishArenaPool PL_FinishArenaPool +#define PR_CompactArenaPool PL_CompactArenaPool +#define PR_ArenaFinish PL_ArenaFinish +#define PR_ArenaAllocate PL_ArenaAllocate +#define PR_ArenaGrow PL_ArenaGrow +#define PR_ArenaRelease PL_ArenaRelease +#define PR_ArenaCountAllocation PL_ArenaCountAllocation +#define PR_ArenaCountInplaceGrowth PL_ArenaCountInplaceGrowth +#define PR_ArenaCountGrowth PL_ArenaCountGrowth +#define PR_ArenaCountRelease PL_ArenaCountRelease +#define PR_ArenaCountRetract PL_ArenaCountRetract + +/* Re: prevent.h->plevent.h */ +#define PREvent PLEvent +#define PREventQueue PLEventQueue +#define PR_CreateEventQueue PL_CreateEventQueue +#define PR_DestroyEventQueue PL_DestroyEventQueue +#define PR_GetEventQueueMonitor PL_GetEventQueueMonitor +#define PR_ENTER_EVENT_QUEUE_MONITOR PL_ENTER_EVENT_QUEUE_MONITOR +#define PR_EXIT_EVENT_QUEUE_MONITOR PL_EXIT_EVENT_QUEUE_MONITOR +#define PR_PostEvent PL_PostEvent +#define PR_PostSynchronousEvent PL_PostSynchronousEvent +#define PR_GetEvent PL_GetEvent +#define PR_EventAvailable PL_EventAvailable +#define PREventFunProc PLEventFunProc +#define PR_MapEvents PL_MapEvents +#define PR_RevokeEvents PL_RevokeEvents +#define PR_ProcessPendingEvents PL_ProcessPendingEvents +#define PR_WaitForEvent PL_WaitForEvent +#define PR_EventLoop PL_EventLoop +#define PR_GetEventQueueSelectFD PL_GetEventQueueSelectFD +#define PRHandleEventProc PLHandleEventProc +#define PRDestroyEventProc PLDestroyEventProc +#define PR_InitEvent PL_InitEvent +#define PR_GetEventOwner PL_GetEventOwner +#define PR_HandleEvent PL_HandleEvent +#define PR_DestroyEvent PL_DestroyEvent +#define PR_DequeueEvent PL_DequeueEvent +#define PR_GetMainEventQueue PL_GetMainEventQueue + +/* Re: prhash.h->plhash.h */ +#define PRHashEntry PLHashEntry +#define PRHashTable PLHashTable +#define PRHashNumber PLHashNumber +#define PRHashFunction PLHashFunction +#define PRHashComparator PLHashComparator +#define PRHashEnumerator PLHashEnumerator +#define PRHashAllocOps PLHashAllocOps +#define PR_NewHashTable PL_NewHashTable +#define PR_HashTableDestroy PL_HashTableDestroy +#define PR_HashTableRawLookup PL_HashTableRawLookup +#define PR_HashTableRawAdd PL_HashTableRawAdd +#define PR_HashTableRawRemove PL_HashTableRawRemove +#define PR_HashTableAdd PL_HashTableAdd +#define PR_HashTableRemove PL_HashTableRemove +#define PR_HashTableEnumerateEntries PL_HashTableEnumerateEntries +#define PR_HashTableLookup PL_HashTableLookup +#define PR_HashTableDump PL_HashTableDump +#define PR_HashString PL_HashString +#define PR_CompareStrings PL_CompareStrings +#define PR_CompareValues PL_CompareValues + +#endif /* !defined(PROTYPES_H) */ diff --git a/ape-server/deps/js/src/jsparse.cpp b/ape-server/deps/js/src/jsparse.cpp new file mode 100755 index 0000000..0f27a02 --- /dev/null +++ b/ape-server/deps/js/src/jsparse.cpp @@ -0,0 +1,9435 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=99: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * JS parser. + * + * This is a recursive-descent parser for the JavaScript language specified by + * "The JavaScript 1.5 Language Specification". It uses lexical and semantic + * feedback to disambiguate non-LL(1) structures. It generates trees of nodes + * induced by the recursive parsing (not precise syntax trees, see jsparse.h). + * After tree construction, it rewrites trees to fold constants and evaluate + * compile-time expressions. Finally, it calls js_EmitTree (see jsemit.h) to + * generate bytecode. + * + * This parser attempts no error recovery. + */ +#include +#include +#include +#include "jstypes.h" +#include "jsstdint.h" +#include "jsarena.h" /* Added by JSIFY */ +#include "jsutil.h" /* Added by JSIFY */ +#include "jsapi.h" +#include "jsarray.h" +#include "jsatom.h" +#include "jscntxt.h" +#include "jsversion.h" +#include "jsemit.h" +#include "jsfun.h" +#include "jsinterp.h" +#include "jsiter.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsobj.h" +#include "jsopcode.h" +#include "jsparse.h" +#include "jsscan.h" +#include "jsscope.h" +#include "jsscript.h" +#include "jsstr.h" +#include "jsstaticcheck.h" +#include "jslibmath.h" +#include "jsvector.h" + +#if JS_HAS_XML_SUPPORT +#include "jsxml.h" +#endif + +#if JS_HAS_DESTRUCTURING +#include "jsdhash.h" +#endif + +/* + * Asserts to verify assumptions behind pn_ macros. + */ +#define pn_offsetof(m) offsetof(JSParseNode, m) + +JS_STATIC_ASSERT(pn_offsetof(pn_link) == pn_offsetof(dn_uses)); +JS_STATIC_ASSERT(pn_offsetof(pn_u.name.atom) == pn_offsetof(pn_u.apair.atom)); + +#undef pn_offsetof + +/* + * JS parsers, from lowest to highest precedence. + * + * Each parser takes a context, a token stream, and a tree context struct. + * Each returns a parse node tree or null on error. + */ + +typedef JSParseNode * +JSParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc); + +typedef JSParseNode * +JSVariablesParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, + bool inLetHead); + +typedef JSParseNode * +JSMemberParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, + JSBool allowCallSyntax); + +typedef JSParseNode * +JSPrimaryParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, + JSTokenType tt, JSBool afterDot); + +typedef JSParseNode * +JSParenParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, + JSParseNode *pn1, JSBool *genexp); + +static JSParser FunctionStmt; +static JSParser FunctionExpr; +static JSParser Statements; +static JSParser Statement; +static JSVariablesParser Variables; +static JSParser Expr; +static JSParser AssignExpr; +static JSParser CondExpr; +static JSParser OrExpr; +static JSParser AndExpr; +static JSParser BitOrExpr; +static JSParser BitXorExpr; +static JSParser BitAndExpr; +static JSParser EqExpr; +static JSParser RelExpr; +static JSParser ShiftExpr; +static JSParser AddExpr; +static JSParser MulExpr; +static JSParser UnaryExpr; +static JSMemberParser MemberExpr; +static JSPrimaryParser PrimaryExpr; +static JSParenParser ParenExpr; + +static bool RecognizeDirectivePrologue(JSContext *cx, JSTokenStream *ts, + JSTreeContext *tc, JSParseNode *pn); + +/* + * Insist that the next token be of type tt, or report errno and return null. + * NB: this macro uses cx and ts from its lexical environment. + */ +#define MUST_MATCH_TOKEN(tt, errno) \ + JS_BEGIN_MACRO \ + if (js_GetToken(cx, ts) != tt) { \ + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, errno); \ + return NULL; \ + } \ + JS_END_MACRO + +#ifdef METER_PARSENODES +static uint32 parsenodes = 0; +static uint32 maxparsenodes = 0; +static uint32 recyclednodes = 0; +#endif + +void +JSParseNode::become(JSParseNode *pn2) +{ + JS_ASSERT(!pn_defn); + JS_ASSERT(!pn2->pn_defn); + + JS_ASSERT(!pn_used); + if (pn2->pn_used) { + JSParseNode **pnup = &pn2->pn_lexdef->dn_uses; + while (*pnup != pn2) + pnup = &(*pnup)->pn_link; + *pnup = this; + pn_link = pn2->pn_link; + pn_used = true; + pn2->pn_link = NULL; + pn2->pn_used = false; + } + + /* If this is a function node fix up the pn_funbox->node back-pointer. */ + if (PN_TYPE(pn2) == TOK_FUNCTION && pn2->pn_arity == PN_FUNC) + pn2->pn_funbox->node = this; + + pn_type = pn2->pn_type; + pn_op = pn2->pn_op; + pn_arity = pn2->pn_arity; + pn_parens = pn2->pn_parens; + pn_u = pn2->pn_u; + pn2->clear(); +} + +void +JSParseNode::clear() +{ + pn_type = TOK_EOF; + pn_op = JSOP_NOP; + pn_used = pn_defn = false; + pn_arity = PN_NULLARY; + pn_parens = false; +} + +bool +JSCompiler::init(const jschar *base, size_t length, + FILE *fp, const char *filename, uintN lineno) +{ + JSContext *cx = context; + + tempPoolMark = JS_ARENA_MARK(&cx->tempPool); + if (!tokenStream.init(cx, base, length, fp, filename, lineno)) { + JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark); + return false; + } + + /* Root atoms and objects allocated for the parsed tree. */ + JS_KEEP_ATOMS(cx->runtime); + JS_PUSH_TEMP_ROOT_COMPILER(cx, this, &tempRoot); + return true; +} + +JSCompiler::~JSCompiler() +{ + JSContext *cx = context; + + if (principals) + JSPRINCIPALS_DROP(cx, principals); + JS_ASSERT(tempRoot.u.compiler == this); + JS_POP_TEMP_ROOT(cx, &tempRoot); + JS_UNKEEP_ATOMS(cx->runtime); + tokenStream.close(cx); + JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark); +} + +void +JSCompiler::setPrincipals(JSPrincipals *prin) +{ + JS_ASSERT(!principals); + if (prin) + JSPRINCIPALS_HOLD(context, prin); + principals = prin; +} + +JSObjectBox * +JSCompiler::newObjectBox(JSObject *obj) +{ + JS_ASSERT(obj); + + /* + * We use JSContext.tempPool to allocate parsed objects and place them on + * a list in JSTokenStream to ensure GC safety. Thus the tempPool arenas + * containing the entries must be alive until we are done with scanning, + * parsing and code generation for the whole script or top-level function. + */ + JSObjectBox *objbox; + JS_ARENA_ALLOCATE_TYPE(objbox, JSObjectBox, &context->tempPool); + if (!objbox) { + js_ReportOutOfScriptQuota(context); + return NULL; + } + objbox->traceLink = traceListHead; + traceListHead = objbox; + objbox->emitLink = NULL; + objbox->object = obj; + return objbox; +} + +JSFunctionBox * +JSCompiler::newFunctionBox(JSObject *obj, JSParseNode *fn, JSTreeContext *tc) +{ + JS_ASSERT(obj); + JS_ASSERT(HAS_FUNCTION_CLASS(obj)); + + /* + * We use JSContext.tempPool to allocate parsed objects and place them on + * a list in JSTokenStream to ensure GC safety. Thus the tempPool arenas + * containing the entries must be alive until we are done with scanning, + * parsing and code generation for the whole script or top-level function. + */ + JSFunctionBox *funbox; + JS_ARENA_ALLOCATE_TYPE(funbox, JSFunctionBox, &context->tempPool); + if (!funbox) { + js_ReportOutOfScriptQuota(context); + return NULL; + } + funbox->traceLink = traceListHead; + traceListHead = funbox; + funbox->emitLink = NULL; + funbox->object = obj; + funbox->node = fn; + funbox->siblings = tc->functionList; + tc->functionList = funbox; + ++tc->compiler->functionCount; + funbox->kids = NULL; + funbox->parent = tc->funbox; + funbox->queued = false; + funbox->inLoop = false; + for (JSStmtInfo *stmt = tc->topStmt; stmt; stmt = stmt->down) { + if (STMT_IS_LOOP(stmt)) { + funbox->inLoop = true; + break; + } + } + funbox->level = tc->staticLevel; + funbox->tcflags = (TCF_IN_FUNCTION | (tc->flags & (TCF_COMPILE_N_GO | TCF_STRICT_MODE_CODE))); + return funbox; +} + +void +JSCompiler::trace(JSTracer *trc) +{ + JSObjectBox *objbox; + + JS_ASSERT(tempRoot.u.compiler == this); + objbox = traceListHead; + while (objbox) { + JS_CALL_OBJECT_TRACER(trc, objbox->object, "parser.object"); + objbox = objbox->traceLink; + } +} + +static void +UnlinkFunctionBoxes(JSParseNode *pn, JSTreeContext *tc); + +static void +UnlinkFunctionBox(JSParseNode *pn, JSTreeContext *tc) +{ + JSFunctionBox *funbox = pn->pn_funbox; + if (funbox) { + JS_ASSERT(funbox->node == pn); + funbox->node = NULL; + + JSFunctionBox **funboxp = &tc->functionList; + while (*funboxp) { + if (*funboxp == funbox) { + *funboxp = funbox->siblings; + break; + } + funboxp = &(*funboxp)->siblings; + } + + uint32 oldflags = tc->flags; + JSFunctionBox *oldlist = tc->functionList; + + tc->flags = funbox->tcflags; + tc->functionList = funbox->kids; + UnlinkFunctionBoxes(pn->pn_body, tc); + funbox->kids = tc->functionList; + tc->flags = oldflags; + tc->functionList = oldlist; + + // FIXME: use a funbox freelist (consolidate aleFreeList and nodeList). + pn->pn_funbox = NULL; + } +} + +static void +UnlinkFunctionBoxes(JSParseNode *pn, JSTreeContext *tc) +{ + if (pn) { + switch (pn->pn_arity) { + case PN_NULLARY: + return; + case PN_UNARY: + UnlinkFunctionBoxes(pn->pn_kid, tc); + return; + case PN_BINARY: + UnlinkFunctionBoxes(pn->pn_left, tc); + UnlinkFunctionBoxes(pn->pn_right, tc); + return; + case PN_TERNARY: + UnlinkFunctionBoxes(pn->pn_kid1, tc); + UnlinkFunctionBoxes(pn->pn_kid2, tc); + UnlinkFunctionBoxes(pn->pn_kid3, tc); + return; + case PN_LIST: + for (JSParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) + UnlinkFunctionBoxes(pn2, tc); + return; + case PN_FUNC: + UnlinkFunctionBox(pn, tc); + return; + case PN_NAME: + UnlinkFunctionBoxes(pn->maybeExpr(), tc); + return; + case PN_NAMESET: + UnlinkFunctionBoxes(pn->pn_tree, tc); + } + } +} + +static void +RecycleFuncNameKids(JSParseNode *pn, JSTreeContext *tc); + +static JSParseNode * +RecycleTree(JSParseNode *pn, JSTreeContext *tc) +{ + JSParseNode *next, **head; + + if (!pn) + return NULL; + + /* Catch back-to-back dup recycles. */ + JS_ASSERT(pn != tc->compiler->nodeList); + next = pn->pn_next; + if (pn->pn_used || pn->pn_defn) { + /* + * JSAtomLists own definition nodes along with their used-node chains. + * Defer recycling such nodes until we unwind to top level to avoid + * linkage overhead or (alternatively) unlinking runtime complexity. + * Yes, this means dead code can contribute to static analysis results! + * + * Do recycle kids here, since they are no longer needed. + */ + pn->pn_next = NULL; + RecycleFuncNameKids(pn, tc); + } else { + UnlinkFunctionBoxes(pn, tc); + head = &tc->compiler->nodeList; + pn->pn_next = *head; + *head = pn; +#ifdef METER_PARSENODES + recyclednodes++; +#endif + } + return next; +} + +static void +RecycleFuncNameKids(JSParseNode *pn, JSTreeContext *tc) +{ + switch (pn->pn_arity) { + case PN_FUNC: + UnlinkFunctionBox(pn, tc); + /* FALL THROUGH */ + + case PN_NAME: + /* + * Only a definition node might have a non-null strong pn_expr link + * to recycle, but we test !pn_used to handle PN_FUNC fall through. + * Every node with the pn_used flag set has a non-null pn_lexdef + * weak reference to its definition node. + */ + if (!pn->pn_used && pn->pn_expr) { + RecycleTree(pn->pn_expr, tc); + pn->pn_expr = NULL; + } + break; + + default: + JS_ASSERT(PN_TYPE(pn) == TOK_FUNCTION); + } +} + +static JSParseNode * +NewOrRecycledNode(JSTreeContext *tc) +{ + JSParseNode *pn, *pn2; + + pn = tc->compiler->nodeList; + if (!pn) { + JSContext *cx = tc->compiler->context; + + JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool); + if (!pn) + js_ReportOutOfScriptQuota(cx); + } else { + tc->compiler->nodeList = pn->pn_next; + + /* Recycle immediate descendents only, to save work and working set. */ + switch (pn->pn_arity) { + case PN_FUNC: + RecycleTree(pn->pn_body, tc); + break; + case PN_LIST: + pn2 = pn->pn_head; + if (pn2) { + while (pn2 && !pn2->pn_used && !pn2->pn_defn) + pn2 = pn2->pn_next; + if (pn2) { + pn2 = pn->pn_head; + do { + pn2 = RecycleTree(pn2, tc); + } while (pn2); + } else { + *pn->pn_tail = tc->compiler->nodeList; + tc->compiler->nodeList = pn->pn_head; +#ifdef METER_PARSENODES + recyclednodes += pn->pn_count; +#endif + break; + } + } + break; + case PN_TERNARY: + RecycleTree(pn->pn_kid1, tc); + RecycleTree(pn->pn_kid2, tc); + RecycleTree(pn->pn_kid3, tc); + break; + case PN_BINARY: + if (pn->pn_left != pn->pn_right) + RecycleTree(pn->pn_left, tc); + RecycleTree(pn->pn_right, tc); + break; + case PN_UNARY: + RecycleTree(pn->pn_kid, tc); + break; + case PN_NAME: + if (!pn->pn_used) + RecycleTree(pn->pn_expr, tc); + break; + case PN_NULLARY: + break; + } + } + if (pn) { +#ifdef METER_PARSENODES + parsenodes++; + if (parsenodes - recyclednodes > maxparsenodes) + maxparsenodes = parsenodes - recyclednodes; +#endif + pn->pn_used = pn->pn_defn = false; + memset(&pn->pn_u, 0, sizeof pn->pn_u); + pn->pn_next = NULL; + } + return pn; +} + +static inline void +InitParseNode(JSParseNode *pn, JSTokenType type, JSOp op, JSParseNodeArity arity) +{ + pn->pn_type = type; + pn->pn_op = op; + pn->pn_arity = arity; + pn->pn_parens = false; + JS_ASSERT(!pn->pn_used); + JS_ASSERT(!pn->pn_defn); + pn->pn_next = pn->pn_link = NULL; +} + +/* + * Allocate a JSParseNode from tc's node freelist or, failing that, from cx's + * temporary arena. + */ +static JSParseNode * +NewParseNode(JSParseNodeArity arity, JSTreeContext *tc) +{ + JSParseNode *pn; + JSToken *tp; + + pn = NewOrRecycledNode(tc); + if (!pn) + return NULL; + tp = &CURRENT_TOKEN(&tc->compiler->tokenStream); + InitParseNode(pn, tp->type, JSOP_NOP, arity); + pn->pn_pos = tp->pos; + return pn; +} + +static inline void +InitNameNodeCommon(JSParseNode *pn, JSTreeContext *tc) +{ + pn->pn_expr = NULL; + pn->pn_cookie = FREE_UPVAR_COOKIE; + pn->pn_dflags = tc->atTopLevel() ? PND_TOPLEVEL : 0; + if (!tc->topStmt || tc->topStmt->type == STMT_BLOCK) + pn->pn_dflags |= PND_BLOCKCHILD; + pn->pn_blockid = tc->blockid(); +} + +static JSParseNode * +NewNameNode(JSContext *cx, JSAtom *atom, JSTreeContext *tc) +{ + JSParseNode *pn; + + pn = NewParseNode(PN_NAME, tc); + if (pn) { + pn->pn_atom = atom; + InitNameNodeCommon(pn, tc); + } + return pn; +} + +static JSParseNode * +NewBinary(JSTokenType tt, JSOp op, JSParseNode *left, JSParseNode *right, + JSTreeContext *tc) +{ + JSParseNode *pn, *pn1, *pn2; + + if (!left || !right) + return NULL; + + /* + * Flatten a left-associative (left-heavy) tree of a given operator into + * a list, to reduce js_FoldConstants and js_EmitTree recursion. + */ + if (PN_TYPE(left) == tt && + PN_OP(left) == op && + (js_CodeSpec[op].format & JOF_LEFTASSOC)) { + if (left->pn_arity != PN_LIST) { + pn1 = left->pn_left, pn2 = left->pn_right; + left->pn_arity = PN_LIST; + left->pn_parens = false; + left->initList(pn1); + left->append(pn2); + if (tt == TOK_PLUS) { + if (pn1->pn_type == TOK_STRING) + left->pn_xflags |= PNX_STRCAT; + else if (pn1->pn_type != TOK_NUMBER) + left->pn_xflags |= PNX_CANTFOLD; + if (pn2->pn_type == TOK_STRING) + left->pn_xflags |= PNX_STRCAT; + else if (pn2->pn_type != TOK_NUMBER) + left->pn_xflags |= PNX_CANTFOLD; + } + } + left->append(right); + left->pn_pos.end = right->pn_pos.end; + if (tt == TOK_PLUS) { + if (right->pn_type == TOK_STRING) + left->pn_xflags |= PNX_STRCAT; + else if (right->pn_type != TOK_NUMBER) + left->pn_xflags |= PNX_CANTFOLD; + } + return left; + } + + /* + * Fold constant addition immediately, to conserve node space and, what's + * more, so js_FoldConstants never sees mixed addition and concatenation + * operations with more than one leading non-string operand in a PN_LIST + * generated for expressions such as 1 + 2 + "pt" (which should evaluate + * to "3pt", not "12pt"). + */ + if (tt == TOK_PLUS && + left->pn_type == TOK_NUMBER && + right->pn_type == TOK_NUMBER) { + left->pn_dval += right->pn_dval; + left->pn_pos.end = right->pn_pos.end; + RecycleTree(right, tc); + return left; + } + + pn = NewOrRecycledNode(tc); + if (!pn) + return NULL; + InitParseNode(pn, tt, op, PN_BINARY); + pn->pn_pos.begin = left->pn_pos.begin; + pn->pn_pos.end = right->pn_pos.end; + pn->pn_left = left; + pn->pn_right = right; + return pn; +} + +#if JS_HAS_GETTER_SETTER +static JSTokenType +CheckGetterOrSetter(JSContext *cx, JSTokenStream *ts, JSTokenType tt) +{ + JSAtom *atom; + JSRuntime *rt; + JSOp op; + const char *name; + + JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_NAME); + atom = CURRENT_TOKEN(ts).t_atom; + rt = cx->runtime; + if (atom == rt->atomState.getterAtom) + op = JSOP_GETTER; + else if (atom == rt->atomState.setterAtom) + op = JSOP_SETTER; + else + return TOK_NAME; + if (js_PeekTokenSameLine(cx, ts) != tt) + return TOK_NAME; + (void) js_GetToken(cx, ts); + if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_BAD_GETTER_OR_SETTER, + (op == JSOP_GETTER) + ? js_getter_str + : js_setter_str); + return TOK_ERROR; + } + CURRENT_TOKEN(ts).t_op = op; + if (JS_HAS_STRICT_OPTION(cx)) { + name = js_AtomToPrintableString(cx, atom); + if (!name || + !js_ReportCompileErrorNumber(cx, ts, NULL, + JSREPORT_WARNING | JSREPORT_STRICT, + JSMSG_DEPRECATED_USAGE, + name)) { + return TOK_ERROR; + } + } + return tt; +} +#endif + +static bool +GenerateBlockId(JSTreeContext *tc, uint32& blockid) +{ + if (tc->blockidGen == JS_BIT(20)) { + JS_ReportErrorNumber(tc->compiler->context, js_GetErrorMessage, NULL, + JSMSG_NEED_DIET, "program"); + return false; + } + blockid = tc->blockidGen++; + return true; +} + +static bool +GenerateBlockIdForStmtNode(JSParseNode *pn, JSTreeContext *tc) +{ + JS_ASSERT(tc->topStmt); + JS_ASSERT(STMT_MAYBE_SCOPE(tc->topStmt)); + JS_ASSERT(pn->pn_type == TOK_LC || pn->pn_type == TOK_LEXICALSCOPE); + if (!GenerateBlockId(tc, tc->topStmt->blockid)) + return false; + pn->pn_blockid = tc->topStmt->blockid; + return true; +} + +/* + * Parse a top-level JS script. + */ +JSParseNode * +JSCompiler::parse(JSObject *chain) +{ + /* + * Protect atoms from being collected by a GC activation, which might + * - nest on this thread due to out of memory (the so-called "last ditch" + * GC attempted within js_NewGCThing), or + * - run for any reason on another thread if this thread is suspended on + * an object lock before it finishes generating bytecode into a script + * protected from the GC by a root or a stack frame reference. + */ + JSTreeContext tc(this); + tc.scopeChain = chain; + if (!GenerateBlockId(&tc, tc.bodyid)) + return NULL; + + JSParseNode *pn = Statements(context, TS(this), &tc); + if (pn) { + if (!js_MatchToken(context, TS(this), TOK_EOF)) { + js_ReportCompileErrorNumber(context, TS(this), NULL, JSREPORT_ERROR, + JSMSG_SYNTAX_ERROR); + pn = NULL; + } else { + if (!js_FoldConstants(context, pn, &tc)) + pn = NULL; + } + } + return pn; +} + +JS_STATIC_ASSERT(FREE_STATIC_LEVEL == JS_BITMASK(JSFB_LEVEL_BITS)); + +static inline bool +SetStaticLevel(JSTreeContext *tc, uintN staticLevel) +{ + /* + * Reserve FREE_STATIC_LEVEL (0xffff) in order to reserve FREE_UPVAR_COOKIE + * (0xffffffff) and other cookies with that level. + * + * This is a lot simpler than error-checking every MAKE_UPVAR_COOKIE, and + * practically speaking it leaves more than enough room for upvars. In fact + * we might want to split cookie fields giving fewer bits for skip and more + * for slot, but only based on evidence. + */ + if (staticLevel >= FREE_STATIC_LEVEL) { + JS_ReportErrorNumber(tc->compiler->context, js_GetErrorMessage, NULL, + JSMSG_TOO_DEEP, js_function_str); + return false; + } + tc->staticLevel = staticLevel; + return true; +} + +/* + * Compile a top-level script. + */ +JSScript * +JSCompiler::compileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *callerFrame, + JSPrincipals *principals, uint32 tcflags, + const jschar *chars, size_t length, + FILE *file, const char *filename, uintN lineno, + JSString *source /* = NULL */, + unsigned staticLevel /* = 0 */) +{ + JSCompiler jsc(cx, principals, callerFrame); + JSArenaPool codePool, notePool; + JSTokenType tt; + JSParseNode *pn; + uint32 scriptGlobals; + JSScript *script; + bool inDirectivePrologue; +#ifdef METER_PARSENODES + void *sbrk(ptrdiff_t), *before = sbrk(0); +#endif + + JS_ASSERT(!(tcflags & ~(TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL | TCF_NEED_MUTABLE_SCRIPT))); + + /* + * The scripted callerFrame can only be given for compile-and-go scripts + * and non-zero static level requires callerFrame. + */ + JS_ASSERT_IF(callerFrame, tcflags & TCF_COMPILE_N_GO); + JS_ASSERT_IF(staticLevel != 0, callerFrame); + + if (!jsc.init(chars, length, file, filename, lineno)) + return NULL; + + JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode), + &cx->scriptStackQuota); + JS_InitArenaPool(¬ePool, "note", 1024, sizeof(jssrcnote), + &cx->scriptStackQuota); + + JSCodeGenerator cg(&jsc, &codePool, ¬ePool, jsc.tokenStream.lineno); + + MUST_FLOW_THROUGH("out"); + + /* Null script early in case of error, to reduce our code footprint. */ + script = NULL; + + cg.flags |= tcflags; + cg.scopeChain = scopeChain; + if (!SetStaticLevel(&cg, staticLevel)) + goto out; + + /* If this is a direct call to eval, inherit the caller's strictness. */ + if (callerFrame && + callerFrame->script && + callerFrame->script->strictModeCode) { + cg.flags |= TCF_STRICT_MODE_CODE; + jsc.tokenStream.flags |= TSF_STRICT_MODE_CODE; + } + + /* + * If funbox is non-null after we create the new script, callerFrame->fun + * was saved in the 0th object table entry. + */ + JSObjectBox *funbox; + funbox = NULL; + + if (tcflags & TCF_COMPILE_N_GO) { + if (source) { + /* + * Save eval program source in script->atomMap.vector[0] for the + * eval cache (see obj_eval in jsobj.cpp). + */ + JSAtom *atom = js_AtomizeString(cx, source, 0); + if (!atom || !cg.atomList.add(&jsc, atom)) + goto out; + } + + if (callerFrame && callerFrame->fun) { + /* + * An eval script in a caller frame needs to have its enclosing + * function captured in case it refers to an upvar, and someone + * wishes to decompile it while it's running. + */ + funbox = jsc.newObjectBox(FUN_OBJECT(callerFrame->fun)); + if (!funbox) + goto out; + funbox->emitLink = cg.objectList.lastbox; + cg.objectList.lastbox = funbox; + cg.objectList.length++; + } + } + + /* + * Inline Statements to emit as we go to save AST space. We must generate + * our script-body blockid since we aren't calling Statements. + */ + uint32 bodyid; + if (!GenerateBlockId(&cg, bodyid)) + goto out; + cg.bodyid = bodyid; + +#if JS_HAS_XML_SUPPORT + pn = NULL; + bool onlyXML; + onlyXML = true; +#endif + + CG_SWITCH_TO_PROLOG(&cg); + if (js_Emit1(cx, &cg, JSOP_TRACE) < 0) + goto out; + CG_SWITCH_TO_MAIN(&cg); + + inDirectivePrologue = true; + for (;;) { + jsc.tokenStream.flags |= TSF_OPERAND; + tt = js_PeekToken(cx, &jsc.tokenStream); + jsc.tokenStream.flags &= ~TSF_OPERAND; + if (tt <= TOK_EOF) { + if (tt == TOK_EOF) + break; + JS_ASSERT(tt == TOK_ERROR); + goto out; + } + + pn = Statement(cx, &jsc.tokenStream, &cg); + if (!pn) + goto out; + JS_ASSERT(!cg.blockNode); + + if (inDirectivePrologue) + inDirectivePrologue = RecognizeDirectivePrologue(cx, &jsc.tokenStream, &cg, pn); + + if (!js_FoldConstants(cx, pn, &cg)) + goto out; + + if (cg.functionList) { + if (!jsc.analyzeFunctions(cg.functionList, cg.flags)) + goto out; + cg.functionList = NULL; + } + + if (!js_EmitTree(cx, &cg, pn)) + goto out; +#if JS_HAS_XML_SUPPORT + if (PN_TYPE(pn) != TOK_SEMI || + !pn->pn_kid || + !TREE_TYPE_IS_XML(PN_TYPE(pn->pn_kid))) { + onlyXML = false; + } +#endif + RecycleTree(pn, &cg); + } + +#if JS_HAS_XML_SUPPORT + /* + * Prevent XML data theft via + * + * It does not cope with malformed comment hiding hacks where --> is hidden + * by C-style comments, or on a dirty line. Such cases are already broken. + */ +#define TSF_IN_HTML_COMMENT 0x2000 + +/* Ignore keywords and return TOK_NAME instead to the parser. */ +#define TSF_KEYWORD_IS_NAME 0x4000 + +/* Tokenize as appropriate for strict mode code. */ +#define TSF_STRICT_MODE_CODE 0x8000 + +/* Unicode separators that are treated as line terminators, in addition to \n, \r */ +#define LINE_SEPARATOR 0x2028 +#define PARA_SEPARATOR 0x2029 + +extern void +js_CloseTokenStream(JSContext *cx, JSTokenStream *ts); + +extern JS_FRIEND_API(int) +js_fgets(char *buf, int size, FILE *file); + +/* + * If the given char array forms JavaScript keyword, return corresponding + * token. Otherwise return TOK_EOF. + */ +extern JSTokenType +js_CheckKeyword(const jschar *chars, size_t length); + +/* + * Friend-exported API entry point to call a mapping function on each reserved + * identifier in the scanner's keyword table. + */ +extern JS_FRIEND_API(void) +js_MapKeywords(void (*mapfun)(const char *)); + +/* + * Check that str forms a valid JS identifier name. The function does not + * check if str is a JS keyword. + */ +extern JSBool +js_IsIdentifier(JSString *str); + +/* + * Report a compile-time error by its number. Return true for a warning, false + * for an error. When pn is not null, use it to report error's location. + * Otherwise use ts, which must not be null. + */ +bool +js_ReportCompileErrorNumber(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, + uintN flags, uintN errorNumber, ...); + +/* + * Report a condition that should elicit a warning with JSOPTION_STRICT, + * or an error if ts or tc is handling strict mode code. This function + * defers to js_ReportCompileErrorNumber to do the real work. Either tc + * or ts may be NULL, if there is no tree context or token stream state + * whose strictness should affect the report. + * + * One could have js_ReportCompileErrorNumber recognize the + * JSREPORT_STRICT_MODE_ERROR flag instead of having a separate function + * like this one. However, the strict mode code flag we need to test is + * in the JSTreeContext structure for that code; we would have to change + * the ~120 js_ReportCompileErrorNumber calls to pass the additional + * argument, even though many of those sites would never use it. Using + * ts's TSF_STRICT_MODE_CODE flag instead of tc's would be brittle: at some + * points ts's flags don't correspond to those of the tc relevant to the + * error. + */ +bool +js_ReportStrictModeError(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, + JSParseNode *pn, uintN errorNumber, ...); + +/* + * Steal one JSREPORT_* bit (see jsapi.h) to tell that arguments to the error + * message have const jschar* type, not const char*. + */ +#define JSREPORT_UC 0x100 + +/* + * Look ahead one token and return its type. + */ +extern JSTokenType +js_PeekToken(JSContext *cx, JSTokenStream *ts); + +extern JSTokenType +js_PeekTokenSameLine(JSContext *cx, JSTokenStream *ts); + +/* + * Get the next token from ts. + */ +extern JSTokenType +js_GetToken(JSContext *cx, JSTokenStream *ts); + +/* + * Push back the last scanned token onto ts. + */ +extern void +js_UngetToken(JSTokenStream *ts); + +/* + * Get the next token from ts if its type is tt. + */ +extern JSBool +js_MatchToken(JSContext *cx, JSTokenStream *ts, JSTokenType tt); + +JS_END_EXTERN_C + +#endif /* jsscan_h___ */ diff --git a/ape-server/deps/js/src/jsscope.cpp b/ape-server/deps/js/src/jsscope.cpp new file mode 100755 index 0000000..659b3aa --- /dev/null +++ b/ape-server/deps/js/src/jsscope.cpp @@ -0,0 +1,2193 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * JS symbol tables. + */ +#include +#include +#include +#include "jstypes.h" +#include "jsstdint.h" +#include "jsarena.h" +#include "jsbit.h" +#include "jsclist.h" +#include "jsdhash.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jsapi.h" +#include "jsatom.h" +#include "jscntxt.h" +#include "jsdbgapi.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsscope.h" +#include "jsstr.h" +#include "jstracer.h" + +#include "jsscopeinlines.h" + +uint32 +js_GenerateShape(JSContext *cx, bool gcLocked) +{ + JSRuntime *rt; + uint32 shape; + + rt = cx->runtime; + shape = JS_ATOMIC_INCREMENT(&rt->shapeGen); + JS_ASSERT(shape != 0); + if (shape >= SHAPE_OVERFLOW_BIT) { + /* + * FIXME bug 440834: The shape id space has overflowed. Currently we + * cope badly with this and schedule the GC on the every call. But + * first we make sure that increments from other threads would not + * have a chance to wrap around shapeGen to zero. + */ + rt->shapeGen = SHAPE_OVERFLOW_BIT; + shape = SHAPE_OVERFLOW_BIT; + js_TriggerGC(cx, gcLocked); + } + return shape; +} + +JSScope * +js_GetMutableScope(JSContext *cx, JSObject *obj) +{ + JSScope *scope, *newscope; + JSClass *clasp; + uint32 freeslot; + + scope = OBJ_SCOPE(obj); + JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope)); + if (scope->owned()) + return scope; + + /* + * Compile-time block objects each have their own scope, created at + * birth, and runtime clone of a block objects are never mutated. + */ + JS_ASSERT(STOBJ_GET_CLASS(obj) != &js_BlockClass); + newscope = JSScope::create(cx, scope->ops, obj->getClass(), obj, scope->shape); + if (!newscope) + return NULL; + JS_LOCK_SCOPE(cx, newscope); + obj->map = newscope; + + JS_ASSERT(newscope->freeslot == JSSLOT_FREE(STOBJ_GET_CLASS(obj))); + clasp = STOBJ_GET_CLASS(obj); + if (clasp->reserveSlots) { + freeslot = JSSLOT_FREE(clasp) + clasp->reserveSlots(cx, obj); + if (freeslot > STOBJ_NSLOTS(obj)) + freeslot = STOBJ_NSLOTS(obj); + if (newscope->freeslot < freeslot) + newscope->freeslot = freeslot; + } + JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope); + JS_ATOMIC_DECREMENT(&scope->nrefs); + if (scope->nrefs == 0) + JSScope::destroy(cx, scope); + return newscope; +} + +/* + * JSScope uses multiplicative hashing, _a la_ jsdhash.[ch], but specialized + * to minimize footprint. But if a scope has fewer than SCOPE_HASH_THRESHOLD + * entries, we use linear search and avoid allocating scope->table. + */ +#define SCOPE_HASH_THRESHOLD 6 +#define MIN_SCOPE_SIZE_LOG2 4 +#define MIN_SCOPE_SIZE JS_BIT(MIN_SCOPE_SIZE_LOG2) +#define SCOPE_TABLE_NBYTES(n) ((n) * sizeof(JSScopeProperty *)) + +void +JSScope::initMinimal(JSContext *cx, uint32 newShape) +{ + shape = newShape; + emptyScope = NULL; + hashShift = JS_DHASH_BITS - MIN_SCOPE_SIZE_LOG2; + entryCount = removedCount = 0; + table = NULL; + lastProp = NULL; +} + +#ifdef DEBUG +JS_FRIEND_DATA(JSScopeStats) js_scope_stats = {0}; + +# define METER(x) JS_ATOMIC_INCREMENT(&js_scope_stats.x) +#else +# define METER(x) /* nothing */ +#endif + +bool +JSScope::createTable(JSContext *cx, bool report) +{ + int sizeLog2; + JSScopeProperty *sprop, **spp; + + JS_ASSERT(!table); + JS_ASSERT(lastProp); + + if (entryCount > SCOPE_HASH_THRESHOLD) { + /* + * Either we're creating a table for a large scope that was populated + * via property cache hit logic under JSOP_INITPROP, JSOP_SETNAME, or + * JSOP_SETPROP; or else calloc failed at least once already. In any + * event, let's try to grow, overallocating to hold at least twice the + * current population. + */ + sizeLog2 = JS_CeilingLog2(2 * entryCount); + hashShift = JS_DHASH_BITS - sizeLog2; + } else { + JS_ASSERT(hashShift == JS_DHASH_BITS - MIN_SCOPE_SIZE_LOG2); + sizeLog2 = MIN_SCOPE_SIZE_LOG2; + } + + table = (JSScopeProperty **) js_calloc(JS_BIT(sizeLog2) * sizeof(JSScopeProperty *)); + if (!table) { + if (report) + JS_ReportOutOfMemory(cx); + METER(tableAllocFails); + return false; + } + cx->updateMallocCounter(JS_BIT(sizeLog2) * sizeof(JSScopeProperty *)); + + hashShift = JS_DHASH_BITS - sizeLog2; + for (sprop = lastProp; sprop; sprop = sprop->parent) { + spp = search(sprop->id, true); + SPROP_STORE_PRESERVING_COLLISION(spp, sprop); + } + return true; +} + +JSScope * +JSScope::create(JSContext *cx, const JSObjectOps *ops, JSClass *clasp, + JSObject *obj, uint32 shape) +{ + JS_ASSERT(OPS_IS_NATIVE(ops)); + JS_ASSERT(obj); + + JSScope *scope = cx->create(ops, obj); + if (!scope) + return NULL; + + scope->nrefs = 1; + scope->freeslot = JSSLOT_FREE(clasp); + scope->flags = cx->runtime->gcRegenShapesScopeFlag; + scope->initMinimal(cx, shape); + +#ifdef JS_THREADSAFE + js_InitTitle(cx, &scope->title); +#endif + JS_RUNTIME_METER(cx->runtime, liveScopes); + JS_RUNTIME_METER(cx->runtime, totalScopes); + return scope; +} + +JSEmptyScope * +JSScope::createEmptyScope(JSContext *cx, JSClass *clasp) +{ + JS_ASSERT(!emptyScope); + + JSEmptyScope *scope = cx->create(ops, clasp); + if (!scope) + return NULL; + + /* + * This scope holds a reference to the new empty scope. Our only caller, + * getEmptyScope, also promises to incref on behalf of its caller. + */ + scope->nrefs = 2; + scope->freeslot = JSSLOT_FREE(clasp); + scope->flags = OWN_SHAPE | cx->runtime->gcRegenShapesScopeFlag; + scope->initMinimal(cx, js_GenerateShape(cx, false)); + +#ifdef JS_THREADSAFE + js_InitTitle(cx, &scope->title); +#endif + JS_RUNTIME_METER(cx->runtime, liveScopes); + JS_RUNTIME_METER(cx->runtime, totalScopes); + emptyScope = scope; + return scope; +} + +#ifdef DEBUG +# include "jsprf.h" +# define LIVE_SCOPE_METER(cx,expr) JS_LOCK_RUNTIME_VOID(cx->runtime,expr) +#else +# define LIVE_SCOPE_METER(cx,expr) /* nothing */ +#endif + +void +JSScope::destroy(JSContext *cx, JSScope *scope) +{ +#ifdef JS_THREADSAFE + js_FinishTitle(cx, &scope->title); +#endif + if (scope->table) + cx->free(scope->table); + if (scope->emptyScope) + scope->emptyScope->drop(cx, NULL); + + LIVE_SCOPE_METER(cx, cx->runtime->liveScopeProps -= scope->entryCount); + JS_RUNTIME_UNMETER(cx->runtime, liveScopes); + cx->free(scope); +} + +JS_STATIC_ASSERT(sizeof(JSHashNumber) == 4); +JS_STATIC_ASSERT(sizeof(jsid) == JS_BYTES_PER_WORD); + +#if JS_BYTES_PER_WORD == 4 +# define HASH_ID(id) ((JSHashNumber)(id)) +#elif JS_BYTES_PER_WORD == 8 +# define HASH_ID(id) ((JSHashNumber)(id) ^ (JSHashNumber)((id) >> 32)) +#else +# error "Unsupported configuration" +#endif + +/* + * Double hashing needs the second hash code to be relatively prime to table + * size, so we simply make hash2 odd. The inputs to multiplicative hash are + * the golden ratio, expressed as a fixed-point 32 bit fraction, and the id + * itself. + */ +#define SCOPE_HASH0(id) (HASH_ID(id) * JS_GOLDEN_RATIO) +#define SCOPE_HASH1(hash0,shift) ((hash0) >> (shift)) +#define SCOPE_HASH2(hash0,log2,shift) ((((hash0) << (log2)) >> (shift)) | 1) + +JSScopeProperty ** +JSScope::searchTable(jsid id, bool adding) +{ + JSHashNumber hash0, hash1, hash2; + int sizeLog2; + JSScopeProperty *stored, *sprop, **spp, **firstRemoved; + uint32 sizeMask; + + JS_ASSERT(table); + JS_ASSERT(!JSVAL_IS_NULL(id)); + + /* Compute the primary hash address. */ + METER(hashes); + hash0 = SCOPE_HASH0(id); + hash1 = SCOPE_HASH1(hash0, hashShift); + spp = table + hash1; + + /* Miss: return space for a new entry. */ + stored = *spp; + if (SPROP_IS_FREE(stored)) { + METER(misses); + return spp; + } + + /* Hit: return entry. */ + sprop = SPROP_CLEAR_COLLISION(stored); + if (sprop && sprop->id == id) { + METER(hits); + return spp; + } + + /* Collision: double hash. */ + sizeLog2 = JS_DHASH_BITS - hashShift; + hash2 = SCOPE_HASH2(hash0, sizeLog2, hashShift); + sizeMask = JS_BITMASK(sizeLog2); + + /* Save the first removed entry pointer so we can recycle it if adding. */ + if (SPROP_IS_REMOVED(stored)) { + firstRemoved = spp; + } else { + firstRemoved = NULL; + if (adding && !SPROP_HAD_COLLISION(stored)) + SPROP_FLAG_COLLISION(spp, sprop); + } + + for (;;) { + METER(steps); + hash1 -= hash2; + hash1 &= sizeMask; + spp = table + hash1; + + stored = *spp; + if (SPROP_IS_FREE(stored)) { + METER(stepMisses); + return (adding && firstRemoved) ? firstRemoved : spp; + } + + sprop = SPROP_CLEAR_COLLISION(stored); + if (sprop && sprop->id == id) { + METER(stepHits); + return spp; + } + + if (SPROP_IS_REMOVED(stored)) { + if (!firstRemoved) + firstRemoved = spp; + } else { + if (adding && !SPROP_HAD_COLLISION(stored)) + SPROP_FLAG_COLLISION(spp, sprop); + } + } + + /* NOTREACHED */ + return NULL; +} + +bool +JSScope::changeTable(JSContext *cx, int change) +{ + int oldlog2, newlog2; + uint32 oldsize, newsize, nbytes; + JSScopeProperty **newtable, **oldtable, **spp, **oldspp, *sprop; + + if (!table) + return createTable(cx, true); + + /* Grow, shrink, or compress by changing this->table. */ + oldlog2 = JS_DHASH_BITS - hashShift; + newlog2 = oldlog2 + change; + oldsize = JS_BIT(oldlog2); + newsize = JS_BIT(newlog2); + nbytes = SCOPE_TABLE_NBYTES(newsize); + newtable = (JSScopeProperty **) cx->calloc(nbytes); + if (!newtable) { + METER(tableAllocFails); + return false; + } + + /* Now that we have newtable allocated, update members. */ + hashShift = JS_DHASH_BITS - newlog2; + removedCount = 0; + oldtable = table; + table = newtable; + + /* Treat the above calloc as a JS_malloc, to match CreateScopeTable. */ + cx->updateMallocCounter(nbytes); + + /* Copy only live entries, leaving removed and free ones behind. */ + for (oldspp = oldtable; oldsize != 0; oldspp++) { + sprop = SPROP_FETCH(oldspp); + if (sprop) { + spp = search(sprop->id, true); + JS_ASSERT(SPROP_IS_FREE(*spp)); + *spp = sprop; + } + oldsize--; + } + + /* Finally, free the old table storage. */ + cx->free(oldtable); + return true; +} + +/* + * Take care to exclude the mark bits in case we're called from the GC. + */ +#define SPROP_FLAGS_NOT_MATCHED (SPROP_MARK | SPROP_FLAG_SHAPE_REGEN) + +static JSDHashNumber +js_HashScopeProperty(JSDHashTable *table, const void *key) +{ + const JSScopeProperty *sprop = (const JSScopeProperty *)key; + JSDHashNumber hash; + JSPropertyOp gsop; + + /* Accumulate from least to most random so the low bits are most random. */ + hash = 0; + JS_ASSERT_IF(sprop->isMethod(), + !sprop->setter || sprop->setter == js_watch_set); + gsop = sprop->getter; + if (gsop) + hash = JS_ROTATE_LEFT32(hash, 4) ^ jsword(gsop); + gsop = sprop->setter; + if (gsop) + hash = JS_ROTATE_LEFT32(hash, 4) ^ jsword(gsop); + + hash = JS_ROTATE_LEFT32(hash, 4) + ^ (sprop->flags & ~SPROP_FLAGS_NOT_MATCHED); + + hash = JS_ROTATE_LEFT32(hash, 4) ^ sprop->attrs; + hash = JS_ROTATE_LEFT32(hash, 4) ^ sprop->shortid; + hash = JS_ROTATE_LEFT32(hash, 4) ^ sprop->slot; + hash = JS_ROTATE_LEFT32(hash, 4) ^ sprop->id; + return hash; +} + +#define SPROP_MATCH(sprop, child) \ + SPROP_MATCH_PARAMS(sprop, (child)->id, (child)->getter, (child)->setter, \ + (child)->slot, (child)->attrs, (child)->flags, \ + (child)->shortid) + +#define SPROP_MATCH_PARAMS(sprop, aid, agetter, asetter, aslot, aattrs, \ + aflags, ashortid) \ + (JS_ASSERT(!JSVAL_IS_NULL((sprop)->id)), JS_ASSERT(!JSVAL_IS_NULL(aid)), \ + (sprop)->id == (aid) && \ + SPROP_MATCH_PARAMS_AFTER_ID(sprop, agetter, asetter, aslot, aattrs, \ + aflags, ashortid)) + +#define SPROP_MATCH_PARAMS_AFTER_ID(sprop, agetter, asetter, aslot, aattrs, \ + aflags, ashortid) \ + ((sprop)->getter == (agetter) && \ + (sprop)->setter == (asetter) && \ + (sprop)->slot == (aslot) && \ + (sprop)->attrs == (aattrs) && \ + (((sprop)->flags ^ (aflags)) & ~SPROP_FLAGS_NOT_MATCHED) == 0 && \ + (sprop)->shortid == (ashortid)) + +static JSBool +js_MatchScopeProperty(JSDHashTable *table, + const JSDHashEntryHdr *hdr, + const void *key) +{ + const JSPropertyTreeEntry *entry = (const JSPropertyTreeEntry *)hdr; + const JSScopeProperty *sprop = entry->child; + const JSScopeProperty *kprop = (const JSScopeProperty *)key; + + return SPROP_MATCH(sprop, kprop); +} + +static const JSDHashTableOps PropertyTreeHashOps = { + JS_DHashAllocTable, + JS_DHashFreeTable, + js_HashScopeProperty, + js_MatchScopeProperty, + JS_DHashMoveEntryStub, + JS_DHashClearEntryStub, + JS_DHashFinalizeStub, + NULL +}; + +/* + * A property tree node on rt->propertyFreeList overlays the following prefix + * struct on JSScopeProperty. + */ +typedef struct FreeNode { + jsid id; + JSScopeProperty *next; + JSScopeProperty **prevp; +} FreeNode; + +#define FREENODE(sprop) ((FreeNode *) (sprop)) + +#define FREENODE_INSERT(list, sprop) \ + JS_BEGIN_MACRO \ + FREENODE(sprop)->next = (list); \ + FREENODE(sprop)->prevp = &(list); \ + if (list) \ + FREENODE(list)->prevp = &FREENODE(sprop)->next; \ + (list) = (sprop); \ + JS_END_MACRO + +#define FREENODE_REMOVE(sprop) \ + JS_BEGIN_MACRO \ + *FREENODE(sprop)->prevp = FREENODE(sprop)->next; \ + if (FREENODE(sprop)->next) \ + FREENODE(FREENODE(sprop)->next)->prevp = FREENODE(sprop)->prevp; \ + JS_END_MACRO + +/* NB: Called with rt->gcLock held. */ +static JSScopeProperty * +NewScopeProperty(JSRuntime *rt) +{ + JSScopeProperty *sprop; + + sprop = rt->propertyFreeList; + if (sprop) { + FREENODE_REMOVE(sprop); + } else { + JS_ARENA_ALLOCATE_CAST(sprop, JSScopeProperty *, + &rt->propertyArenaPool, + sizeof(JSScopeProperty)); + if (!sprop) + return NULL; + } + + JS_RUNTIME_METER(rt, livePropTreeNodes); + JS_RUNTIME_METER(rt, totalPropTreeNodes); + return sprop; +} + +#define CHUNKY_KIDS_TAG ((jsuword)1) +#define KIDS_IS_CHUNKY(kids) ((jsuword)(kids) & CHUNKY_KIDS_TAG) +#define KIDS_TO_CHUNK(kids) ((PropTreeKidsChunk *) \ + ((jsuword)(kids) & ~CHUNKY_KIDS_TAG)) +#define CHUNK_TO_KIDS(chunk) ((JSScopeProperty *) \ + ((jsuword)(chunk) | CHUNKY_KIDS_TAG)) +#define MAX_KIDS_PER_CHUNK 10 +#define CHUNK_HASH_THRESHOLD 30 + +typedef struct PropTreeKidsChunk PropTreeKidsChunk; + +struct PropTreeKidsChunk { + JSScopeProperty *kids[MAX_KIDS_PER_CHUNK]; + JSDHashTable *table; + PropTreeKidsChunk *next; +}; + +static PropTreeKidsChunk * +NewPropTreeKidsChunk(JSRuntime *rt) +{ + PropTreeKidsChunk *chunk; + + chunk = (PropTreeKidsChunk *) js_calloc(sizeof *chunk); + if (!chunk) + return NULL; + JS_ASSERT(((jsuword)chunk & CHUNKY_KIDS_TAG) == 0); + JS_RUNTIME_METER(rt, propTreeKidsChunks); + return chunk; +} + +static void +DestroyPropTreeKidsChunk(JSRuntime *rt, PropTreeKidsChunk *chunk) +{ + JS_RUNTIME_UNMETER(rt, propTreeKidsChunks); + if (chunk->table) + JS_DHashTableDestroy(chunk->table); + js_free(chunk); +} + +/* NB: Called with rt->gcLock held. */ +static bool +InsertPropertyTreeChild(JSRuntime *rt, JSScopeProperty *parent, + JSScopeProperty *child, PropTreeKidsChunk *sweptChunk) +{ + JSDHashTable *table; + JSPropertyTreeEntry *entry; + JSScopeProperty **childp, *kids, *sprop; + PropTreeKidsChunk *chunk, **chunkp; + uintN i; + + JS_ASSERT(!parent || child->parent != parent); + JS_ASSERT(!JSVAL_IS_NULL(child->id)); + + if (!parent) { + table = &rt->propertyTreeHash; + entry = (JSPropertyTreeEntry *) + JS_DHashTableOperate(table, child, JS_DHASH_ADD); + if (!entry) + return false; + childp = &entry->child; + sprop = *childp; + if (!sprop) { + *childp = child; + } else { + /* + * A "Duplicate child" case. + * + * We can't do away with child, as at least one live scope entry + * still points at it. What's more, that scope's lastProp chains + * through an ancestor line to reach child, and js_Enumerate and + * others count on this linkage. We must leave child out of the + * hash table, and not require it to be there when we eventually + * GC it (see RemovePropertyTreeChild, below). + * + * It is necessary to leave the duplicate child out of the hash + * table to preserve entry uniqueness. It is safe to leave the + * child out of the hash table (unlike the duplicate child cases + * below), because the child's parent link will be null, which + * can't dangle. + */ + JS_ASSERT(sprop != child && SPROP_MATCH(sprop, child)); + JS_RUNTIME_METER(rt, duplicatePropTreeNodes); + } + } else { + JS_ASSERT(!JSVAL_IS_NULL(parent->id)); + childp = &parent->kids; + kids = *childp; + if (kids) { + if (KIDS_IS_CHUNKY(kids)) { + chunk = KIDS_TO_CHUNK(kids); + + table = chunk->table; + if (table) { + entry = (JSPropertyTreeEntry *) + JS_DHashTableOperate(table, child, JS_DHASH_ADD); + if (!entry) + return false; + if (!entry->child) { + entry->child = child; + while (chunk->next) + chunk = chunk->next; + for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { + childp = &chunk->kids[i]; + sprop = *childp; + if (!sprop) + goto insert; + } + chunkp = &chunk->next; + goto new_chunk; + } + } + + do { + for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { + childp = &chunk->kids[i]; + sprop = *childp; + if (!sprop) + goto insert; + + JS_ASSERT(sprop != child); + if (SPROP_MATCH(sprop, child)) { + /* + * Duplicate child, see comment above. In this + * case, we must let the duplicate be inserted at + * this level in the tree, so we keep iterating, + * looking for an empty slot in which to insert. + */ + JS_ASSERT(sprop != child); + JS_RUNTIME_METER(rt, duplicatePropTreeNodes); + } + } + chunkp = &chunk->next; + } while ((chunk = *chunkp) != NULL); + + new_chunk: + if (sweptChunk) { + chunk = sweptChunk; + } else { + chunk = NewPropTreeKidsChunk(rt); + if (!chunk) + return false; + } + *chunkp = chunk; + childp = &chunk->kids[0]; + } else { + sprop = kids; + JS_ASSERT(sprop != child); + if (SPROP_MATCH(sprop, child)) { + /* + * Duplicate child, see comment above. Once again, we + * must let duplicates created by deletion pile up in a + * kids-chunk-list, in order to find them when sweeping + * and thereby avoid dangling parent pointers. + */ + JS_RUNTIME_METER(rt, duplicatePropTreeNodes); + } + if (sweptChunk) { + chunk = sweptChunk; + } else { + chunk = NewPropTreeKidsChunk(rt); + if (!chunk) + return false; + } + parent->kids = CHUNK_TO_KIDS(chunk); + chunk->kids[0] = sprop; + childp = &chunk->kids[1]; + } + } + insert: + *childp = child; + } + + child->parent = parent; + return true; +} + +/* NB: Called with rt->gcLock held. */ +static PropTreeKidsChunk * +RemovePropertyTreeChild(JSRuntime *rt, JSScopeProperty *child) +{ + PropTreeKidsChunk *freeChunk; + JSScopeProperty *parent, *kids, *kid; + JSDHashTable *table; + PropTreeKidsChunk *list, *chunk, **chunkp, *lastChunk; + uintN i, j; + JSPropertyTreeEntry *entry; + + freeChunk = NULL; + parent = child->parent; + if (!parent) { + /* + * Don't remove child if it is not in rt->propertyTreeHash, but only + * matches a root child in the table that has compatible members. See + * the "Duplicate child" comments in InsertPropertyTreeChild, above. + */ + table = &rt->propertyTreeHash; + } else { + JS_ASSERT(!JSVAL_IS_NULL(parent->id)); + kids = parent->kids; + if (KIDS_IS_CHUNKY(kids)) { + list = chunk = KIDS_TO_CHUNK(kids); + chunkp = &list; + table = chunk->table; + + do { + for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { + if (chunk->kids[i] == child) { + lastChunk = chunk; + if (!lastChunk->next) { + j = i + 1; + } else { + j = 0; + do { + chunkp = &lastChunk->next; + lastChunk = *chunkp; + } while (lastChunk->next); + } + for (; j < MAX_KIDS_PER_CHUNK; j++) { + if (!lastChunk->kids[j]) + break; + } + --j; + if (chunk != lastChunk || j > i) + chunk->kids[i] = lastChunk->kids[j]; + lastChunk->kids[j] = NULL; + if (j == 0) { + *chunkp = NULL; + if (!list) + parent->kids = NULL; + freeChunk = lastChunk; + } + goto out; + } + } + + chunkp = &chunk->next; + } while ((chunk = *chunkp) != NULL); + } else { + table = NULL; + kid = kids; + if (kid == child) + parent->kids = NULL; + } + } + +out: + if (table) { + entry = (JSPropertyTreeEntry *) + JS_DHashTableOperate(table, child, JS_DHASH_LOOKUP); + + if (entry->child == child) + JS_DHashTableRawRemove(table, &entry->hdr); + } + return freeChunk; +} + +static JSDHashTable * +HashChunks(PropTreeKidsChunk *chunk, uintN n) +{ + JSDHashTable *table; + uintN i; + JSScopeProperty *sprop; + JSPropertyTreeEntry *entry; + + table = JS_NewDHashTable(&PropertyTreeHashOps, NULL, + sizeof(JSPropertyTreeEntry), + JS_DHASH_DEFAULT_CAPACITY(n + 1)); + if (!table) + return NULL; + do { + for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { + sprop = chunk->kids[i]; + if (!sprop) + break; + entry = (JSPropertyTreeEntry *) + JS_DHashTableOperate(table, sprop, JS_DHASH_ADD); + entry->child = sprop; + } + } while ((chunk = chunk->next) != NULL); + return table; +} + +/* + * Called without cx->runtime->gcLock held. This function acquires that lock + * only when inserting a new child. Thus there may be races to find or add a + * node that result in duplicates. We expect such races to be rare! + * + * We use rt->gcLock, not rt->rtLock, to avoid nesting the former inside the + * latter in js_GenerateShape below. + */ +static JSScopeProperty * +GetPropertyTreeChild(JSContext *cx, JSScopeProperty *parent, + const JSScopeProperty &child) +{ + JSRuntime *rt; + JSDHashTable *table; + JSPropertyTreeEntry *entry; + JSScopeProperty *sprop; + PropTreeKidsChunk *chunk; + uintN i, n; + + rt = cx->runtime; + if (!parent) { + JS_LOCK_GC(rt); + + table = &rt->propertyTreeHash; + entry = (JSPropertyTreeEntry *) + JS_DHashTableOperate(table, &child, JS_DHASH_ADD); + if (!entry) + goto out_of_memory; + + sprop = entry->child; + if (sprop) + goto out; + } else { + JS_ASSERT(!JSVAL_IS_NULL(parent->id)); + + /* + * Because chunks are appended at the end and never deleted except by + * the GC, we can search without taking the runtime's GC lock. We may + * miss a matching sprop added by another thread, and make a duplicate + * one, but that is an unlikely, therefore small, cost. The property + * tree has extremely low fan-out below its root in popular embeddings + * with real-world workloads. + * + * Patterns such as defining closures that capture a constructor's + * environment as getters or setters on the new object that is passed + * in as |this| can significantly increase fan-out below the property + * tree root -- see bug 335700 for details. + */ + entry = NULL; + sprop = parent->kids; + if (sprop) { + if (KIDS_IS_CHUNKY(sprop)) { + chunk = KIDS_TO_CHUNK(sprop); + + table = chunk->table; + if (table) { + JS_LOCK_GC(rt); + entry = (JSPropertyTreeEntry *) + JS_DHashTableOperate(table, &child, JS_DHASH_LOOKUP); + sprop = entry->child; + if (sprop) + goto out; + goto locked_not_found; + } + + n = 0; + do { + for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { + sprop = chunk->kids[i]; + if (!sprop) { + n += i; + if (n >= CHUNK_HASH_THRESHOLD) { + chunk = KIDS_TO_CHUNK(parent->kids); + if (!chunk->table) { + table = HashChunks(chunk, n); + JS_LOCK_GC(rt); + if (!table) + goto out_of_memory; + if (chunk->table) + JS_DHashTableDestroy(table); + else + chunk->table = table; + goto locked_not_found; + } + } + goto not_found; + } + + if (SPROP_MATCH(sprop, &child)) + return sprop; + } + n += MAX_KIDS_PER_CHUNK; + } while ((chunk = chunk->next) != NULL); + } else { + if (SPROP_MATCH(sprop, &child)) + return sprop; + } + } + + not_found: + JS_LOCK_GC(rt); + } + +locked_not_found: + sprop = NewScopeProperty(rt); + if (!sprop) + goto out_of_memory; + + sprop->id = child.id; + sprop->getter = child.getter; + sprop->setter = child.setter; + sprop->slot = child.slot; + sprop->attrs = child.attrs; + sprop->flags = child.flags; + sprop->shortid = child.shortid; + sprop->parent = sprop->kids = NULL; + sprop->shape = js_GenerateShape(cx, true); + + if (!parent) { + entry->child = sprop; + } else { + if (!InsertPropertyTreeChild(rt, parent, sprop, NULL)) + goto out_of_memory; + } + + out: + JS_UNLOCK_GC(rt); + return sprop; + + out_of_memory: + JS_UNLOCK_GC(rt); + JS_ReportOutOfMemory(cx); + return NULL; +} + +/* + * Get or create a property-tree or dictionary child property of parent, which + * must be lastProp if inDictionaryMode(), else parent must be one of lastProp + * or lastProp->parent. + */ +JSScopeProperty * +JSScope::getChildProperty(JSContext *cx, JSScopeProperty *parent, + JSScopeProperty &child) +{ + JS_ASSERT(!JSVAL_IS_NULL(child.id)); + + /* + * Aliases share another property's slot, passed in the |slot| parameter. + * Shared properties have no slot. Unshared properties that do not alias + * another property's slot allocate a slot here, but may lose it due to a + * JS_ClearScope call. + */ + if (!(child.flags & SPROP_IS_ALIAS)) { + if (child.attrs & JSPROP_SHARED) { + child.slot = SPROP_INVALID_SLOT; + } else { + /* + * We may have set slot from a nearly-matching sprop, above. + * If so, we're overwriting that nearly-matching sprop, so we + * can reuse its slot -- we don't need to allocate a new one. + * Similarly, we use a specific slot if provided by the caller. + */ + if (child.slot == SPROP_INVALID_SLOT && + !js_AllocSlot(cx, object, &child.slot)) { + return NULL; + } + } + } + + if (inDictionaryMode()) { + JS_ASSERT(parent == lastProp); + if (newDictionaryProperty(cx, child, &lastProp)) { + updateShape(cx); + return lastProp; + } + return NULL; + } + + JSScopeProperty *sprop = GetPropertyTreeChild(cx, parent, child); + if (sprop) { + JS_ASSERT(sprop->parent == parent); + if (parent == lastProp) { + extend(cx, sprop); + } else { + JS_ASSERT(parent == lastProp->parent); + setLastProperty(sprop); + updateShape(cx); + } + } + return sprop; +} + +#ifdef DEBUG_notbrendan +#define CHECK_ANCESTOR_LINE(scope, sparse) \ + JS_BEGIN_MACRO \ + if ((scope)->table) CheckAncestorLine(scope); \ + JS_END_MACRO + +static void +CheckAncestorLine(JSScope *scope) +{ + uint32 size; + JSScopeProperty **spp, **start, **end, *ancestorLine, *sprop, *aprop; + uint32 entryCount, ancestorCount; + + ancestorLine = scope->lastProperty(); + if (ancestorLine) + JS_ASSERT(scope->hasProperty(ancestorLine)); + + entryCount = 0; + size = SCOPE_CAPACITY(scope); + start = scope->table; + for (spp = start, end = start + size; spp < end; spp++) { + sprop = SPROP_FETCH(spp); + if (sprop) { + ++entryCount; + for (aprop = ancestorLine; aprop; aprop = aprop->parent) { + if (aprop == sprop) + break; + } + JS_ASSERT(aprop); + } + } + JS_ASSERT(entryCount == scope->entryCount); + + ancestorCount = 0; + for (sprop = ancestorLine; sprop; sprop = sprop->parent) + ancestorCount++; + JS_ASSERT(ancestorCount == scope->entryCount); +} +#else +#define CHECK_ANCESTOR_LINE(scope, sparse) /* nothing */ +#endif + +void +JSScope::reportReadOnlyScope(JSContext *cx) +{ + JSString *str; + const char *bytes; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(object)); + if (!str) + return; + bytes = js_GetStringBytes(cx, str); + if (!bytes) + return; + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_READ_ONLY, bytes); +} + +void +JSScope::generateOwnShape(JSContext *cx) +{ +#ifdef JS_TRACER + if (object) { + js_LeaveTraceIfGlobalObject(cx, object); + + /* + * The JIT must have arranged to re-guard after any unpredictable shape + * change, so if we are on trace here, we should already be prepared to + * bail off trace. + */ + JS_ASSERT_IF(JS_ON_TRACE(cx), cx->bailExit); + + /* + * If we are recording, here is where we forget already-guarded shapes. + * Any subsequent property operation upon object on the trace currently + * being recorded will re-guard (and re-memoize). + */ + JSTraceMonitor *tm = &JS_TRACE_MONITOR(cx); + if (TraceRecorder *tr = tm->recorder) + tr->forgetGuardedShapesForObject(object); + } +#endif + + shape = js_GenerateShape(cx, false); + setOwnShape(); +} + +JSScopeProperty * +JSScope::newDictionaryProperty(JSContext *cx, const JSScopeProperty &child, + JSScopeProperty **childp) +{ + JSScopeProperty *dprop = NewScopeProperty(cx->runtime); + if (!dprop) { + JS_ReportOutOfMemory(cx); + return NULL; + } + + dprop->id = child.id; + dprop->getter = child.getter; + dprop->setter = child.setter; + dprop->slot = child.slot; + dprop->attrs = child.attrs; + dprop->flags = child.flags | SPROP_IN_DICTIONARY; + dprop->shortid = child.shortid; + dprop->shape = js_GenerateShape(cx, false); + + dprop->childp = NULL; + insertDictionaryProperty(dprop, childp); + return dprop; +} + +bool +JSScope::toDictionaryMode(JSContext *cx, JSScopeProperty *&aprop) +{ + JS_ASSERT(!inDictionaryMode()); + + JSScopeProperty **oldTable = table; + uint32 saveRemovedCount = removedCount; + if (oldTable) { + int sizeLog2 = JS_DHASH_BITS - hashShift; + JSScopeProperty **newTable = (JSScopeProperty **) + js_calloc(JS_BIT(sizeLog2) * sizeof(JSScopeProperty *)); + + if (!newTable) { + JS_ReportOutOfMemory(cx); + METER(toDictFails); + return false; + } + table = newTable; + removedCount = 0; + } + + /* + * We are committed from here on. If we fail due to OOM in the loop below, + * we'll restore saveEntryCount, oldTable, oldLastProp. + */ + JSScopeProperty *oldLastProp = lastProp; + lastProp = NULL; + + /* + * Clear entryCount because JSScope::insertDictionaryProperty called from + * JSScope::newDictionaryProperty bumps it. + */ + uint32 saveEntryCount = entryCount; + entryCount = 0; + + for (JSScopeProperty *sprop = oldLastProp, **childp = &lastProp; sprop; sprop = sprop->parent) { + JSScopeProperty *dprop = newDictionaryProperty(cx, *sprop, childp); + if (!dprop) { + entryCount = saveEntryCount; + removedCount = saveRemovedCount; + if (table) + js_free(table); + table = oldTable; + lastProp = oldLastProp; + METER(toDictFails); + return false; + } + + if (table) { + JSScopeProperty **spp = search(dprop->id, true); + JS_ASSERT(!SPROP_FETCH(spp)); + SPROP_STORE_PRESERVING_COLLISION(spp, dprop); + } + + if (aprop == sprop) + aprop = dprop; + childp = &dprop->parent; + } + + if (oldTable) + js_free(oldTable); + setDictionaryMode(); + clearOwnShape(); + + if (lastProp) { + /* + * This scope may get OWN_SHAPE set again, but for now its shape must + * be the shape of its lastProp. If it is empty, its initial shape is + * still valid. See JSScope::updateShape's definition in jsscope.h. + */ + shape = lastProp->shape; + } + return true; +} + +JSScopeProperty * +JSScope::addProperty(JSContext *cx, jsid id, + JSPropertyOp getter, JSPropertyOp setter, + uint32 slot, uintN attrs, + uintN flags, intN shortid) +{ + JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, this)); + CHECK_ANCESTOR_LINE(this, true); + + JS_ASSERT(!JSVAL_IS_NULL(id)); + JS_ASSERT_IF(attrs & JSPROP_GETTER, getter); + JS_ASSERT_IF(attrs & JSPROP_SETTER, setter); + + JS_ASSERT_IF(!cx->runtime->gcRegenShapes, + hasRegenFlag(cx->runtime->gcRegenShapesScopeFlag)); + + /* + * You can't add properties to a sealed scope. But note well that you can + * change property attributes in a sealed scope, even though that replaces + * a JSScopeProperty * in the scope's hash table -- but no id is added, so + * the scope remains sealed. + */ + if (sealed()) { + reportReadOnlyScope(cx); + return NULL; + } + + /* Search for id with adding = true in order to claim its entry. */ + JSScopeProperty **spp = search(id, true); + JS_ASSERT(!SPROP_FETCH(spp)); + return addPropertyHelper(cx, id, getter, setter, slot, attrs, flags, shortid, spp); +} + +/* + * Normalize stub getter and setter values for faster is-stub testing in the + * SPROP_CALL_[GS]ETTER macros. + */ +static inline bool +NormalizeGetterAndSetter(JSContext *cx, JSScope *scope, + jsid id, uintN attrs, uintN flags, + JSPropertyOp &getter, + JSPropertyOp &setter) +{ + if (setter == JS_PropertyStub) + setter = NULL; + if (flags & SPROP_IS_METHOD) { + /* Here, getter is the method, a function object reference. */ + JS_ASSERT(getter); + JS_ASSERT(!setter || setter == js_watch_set); + JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER))); + } else { + if (getter == JS_PropertyStub) + getter = NULL; + } + + /* + * Check for a watchpoint on a deleted property; if one exists, change + * setter to js_watch_set or js_watch_set_wrapper. + * XXXbe this could get expensive with lots of watchpoints... + */ + if (!JS_CLIST_IS_EMPTY(&cx->runtime->watchPointList) && + js_FindWatchPoint(cx->runtime, scope, id)) { + setter = js_WrapWatchedSetter(cx, id, attrs, setter); + if (!setter) { + METER(wrapWatchFails); + return false; + } + } + return true; +} + +JSScopeProperty * +JSScope::addPropertyHelper(JSContext *cx, jsid id, + JSPropertyOp getter, JSPropertyOp setter, + uint32 slot, uintN attrs, + uintN flags, intN shortid, + JSScopeProperty **spp) +{ + NormalizeGetterAndSetter(cx, this, id, attrs, flags, getter, setter); + + /* Check whether we need to grow, if the load factor is >= .75. */ + uint32 size = SCOPE_CAPACITY(this); + if (entryCount + removedCount >= size - (size >> 2)) { + int change = removedCount < size >> 2; + if (!change) + METER(compresses); + else + METER(grows); + if (!changeTable(cx, change) && entryCount + removedCount == size - 1) + return NULL; + spp = search(id, true); + JS_ASSERT(!SPROP_FETCH(spp)); + } + + /* Find or create a property tree node labeled by our arguments. */ + JSScopeProperty *sprop; + { + JSScopeProperty child; + + child.id = id; + child.getter = getter; + child.setter = setter; + child.slot = slot; + child.attrs = attrs; + child.flags = flags; + child.shortid = shortid; + sprop = getChildProperty(cx, lastProp, child); + } + + if (sprop) { + /* Store the tree node pointer in the table entry for id. */ + if (table) + SPROP_STORE_PRESERVING_COLLISION(spp, sprop); + CHECK_ANCESTOR_LINE(this, false); +#ifdef DEBUG + LIVE_SCOPE_METER(cx, ++cx->runtime->liveScopeProps); + JS_RUNTIME_METER(cx->runtime, totalScopeProps); +#endif + + /* + * If we reach the hashing threshold, try to allocate this->table. + * If we can't (a rare event, preceded by swapping to death on most + * modern OSes), stick with linear search rather than whining about + * this little set-back. Therefore we must test !this->table and + * this->entryCount >= SCOPE_HASH_THRESHOLD, not merely whether the + * entry count just reached the threshold. + */ + if (!table && entryCount >= SCOPE_HASH_THRESHOLD) + (void) createTable(cx, false); + + METER(adds); + return sprop; + } + + METER(addFails); + return NULL; +} + +JSScopeProperty * +JSScope::putProperty(JSContext *cx, jsid id, + JSPropertyOp getter, JSPropertyOp setter, + uint32 slot, uintN attrs, + uintN flags, intN shortid) +{ + JSScopeProperty **spp, *sprop, *overwriting; + + JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, this)); + CHECK_ANCESTOR_LINE(this, true); + + JS_ASSERT(!JSVAL_IS_NULL(id)); + JS_ASSERT_IF(attrs & JSPROP_GETTER, getter); + JS_ASSERT_IF(attrs & JSPROP_SETTER, setter); + + JS_ASSERT_IF(!cx->runtime->gcRegenShapes, + hasRegenFlag(cx->runtime->gcRegenShapesScopeFlag)); + + if (sealed()) { + reportReadOnlyScope(cx); + return NULL; + } + + /* Search for id in order to claim its entry if table has been allocated. */ + spp = search(id, true); + sprop = SPROP_FETCH(spp); + if (!sprop) + return addPropertyHelper(cx, id, getter, setter, slot, attrs, flags, shortid, spp); + + /* Property exists: JSScope::search must have returned a valid *spp. */ + JS_ASSERT(!SPROP_IS_REMOVED(*spp)); + overwriting = sprop; + + NormalizeGetterAndSetter(cx, this, id, attrs, flags, getter, setter); + + /* + * If all property members match, this is a redundant add and we can + * return early. If the caller wants to allocate a slot, but doesn't + * care which slot, copy sprop->slot into slot so we can match sprop, + * if all other members match. + */ + if (!(attrs & JSPROP_SHARED) && + slot == SPROP_INVALID_SLOT && + SPROP_HAS_VALID_SLOT(sprop, this)) { + slot = sprop->slot; + } + if (SPROP_MATCH_PARAMS_AFTER_ID(sprop, getter, setter, slot, attrs, + flags, shortid)) { + METER(redundantPuts); + return sprop; + } + + /* + * If we are clearing sprop to force the existing property that it + * describes to be overwritten, then we have to unlink sprop from the + * ancestor line at this->lastProp. + * + * If sprop is not lastProp and this scope is not in dictionary mode, + * we must switch to dictionary mode so we can unlink the non-terminal + * sprop without breaking anyone sharing the property lineage via the + * runtime's property tree. + */ + if (sprop == lastProp && !inDictionaryMode()) { + removeLastProperty(); + } else { + if (!inDictionaryMode()) { + if (!toDictionaryMode(cx, sprop)) + return NULL; + spp = search(id, false); + } + removeDictionaryProperty(sprop); + } + + /* + * If we fail later on trying to find or create a new sprop, we will + * restore *spp from |overwriting|. Note that we don't bother to keep + * this->removedCount in sync, because we will fix up both *spp and + * this->entryCount shortly. + */ + if (table) + SPROP_STORE_PRESERVING_COLLISION(spp, NULL); + CHECK_ANCESTOR_LINE(this, true); + + { + JSScopeProperty child; + + /* Find or create a property tree node labeled by our arguments. */ + child.id = id; + child.getter = getter; + child.setter = setter; + child.slot = slot; + child.attrs = attrs; + child.flags = flags; + child.shortid = shortid; + sprop = getChildProperty(cx, lastProp, child); + } + + if (sprop) { + /* Store the tree node pointer in the table entry for id. */ + if (table) + SPROP_STORE_PRESERVING_COLLISION(spp, sprop); + CHECK_ANCESTOR_LINE(this, false); + + /* See comment in JSScope::addPropertyHelper about ignoring OOM here. */ + if (!table && entryCount >= SCOPE_HASH_THRESHOLD) + (void) createTable(cx, false); + + METER(puts); + return sprop; + } + + if (table) + SPROP_STORE_PRESERVING_COLLISION(spp, overwriting); + ++entryCount; + CHECK_ANCESTOR_LINE(this, true); + METER(putFails); + return NULL; +} + +JSScopeProperty * +JSScope::changeProperty(JSContext *cx, JSScopeProperty *sprop, + uintN attrs, uintN mask, + JSPropertyOp getter, JSPropertyOp setter) +{ + JSScopeProperty child, *newsprop; + + JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, this)); + CHECK_ANCESTOR_LINE(this, true); + + JS_ASSERT(!JSVAL_IS_NULL(sprop->id)); + JS_ASSERT(hasProperty(sprop)); + + attrs |= sprop->attrs & mask; + + /* Allow only shared (slot-less) => unshared (slot-full) transition. */ + JS_ASSERT(!((attrs ^ sprop->attrs) & JSPROP_SHARED) || + !(attrs & JSPROP_SHARED)); + + /* Don't allow method properties to be changed to accessor properties. */ + JS_ASSERT(!(sprop->flags & SPROP_IS_METHOD)); + + if (getter == JS_PropertyStub) + getter = NULL; + if (setter == JS_PropertyStub) + setter = NULL; + if (sprop->attrs == attrs && + sprop->getter == getter && + sprop->setter == setter) { + return sprop; + } + + child.id = sprop->id; + child.getter = getter; + child.setter = setter; + child.slot = sprop->slot; + child.attrs = attrs; + child.flags = sprop->flags; + child.shortid = sprop->shortid; + + if (inDictionaryMode()) { + removeDictionaryProperty(sprop); + newsprop = newDictionaryProperty(cx, child, &lastProp); + if (newsprop) { + if (table) { + JSScopeProperty **spp = search(sprop->id, false); + SPROP_STORE_PRESERVING_COLLISION(spp, newsprop); + } + updateShape(cx); + } + } else if (sprop == lastProp) { + newsprop = getChildProperty(cx, sprop->parent, child); + if (newsprop) { + if (table) { + JSScopeProperty **spp = search(sprop->id, false); + JS_ASSERT(SPROP_FETCH(spp) == sprop); + SPROP_STORE_PRESERVING_COLLISION(spp, newsprop); + } + CHECK_ANCESTOR_LINE(this, true); + } + } else { + /* + * Let JSScope::putProperty handle this |overwriting| case, including + * the conservation of sprop->slot (if it's valid). We must not call + * JSScope::remove here, because it will free a valid sprop->slot and + * JSScope::putProperty won't re-allocate it. + */ + newsprop = putProperty(cx, child.id, child.getter, child.setter, child.slot, + child.attrs, child.flags, child.shortid); + } + +#ifdef DEBUG + if (newsprop) + METER(changes); + else + METER(changeFails); +#endif + return newsprop; +} + +bool +JSScope::removeProperty(JSContext *cx, jsid id) +{ + JSScopeProperty **spp, *stored, *sprop; + uint32 size; + + JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, this)); + CHECK_ANCESTOR_LINE(this, true); + if (sealed()) { + reportReadOnlyScope(cx); + return false; + } + + spp = search(id, false); + stored = *spp; + sprop = SPROP_CLEAR_COLLISION(stored); + if (!sprop) { + METER(uselessRemoves); + return true; + } + + /* If sprop is not the last property added, switch to dictionary mode. */ + if (sprop != lastProp) { + if (!inDictionaryMode()) { + if (!toDictionaryMode(cx, sprop)) + return false; + spp = search(id, false); + } + JS_ASSERT(SPROP_FETCH(spp) == sprop); + } + + /* First, if sprop is unshared and not cleared, free its slot number. */ + if (SPROP_HAS_VALID_SLOT(sprop, this)) { + js_FreeSlot(cx, object, sprop->slot); + JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals); + } + + /* Next, remove id by setting its entry to a removed or free sentinel. */ + if (SPROP_HAD_COLLISION(stored)) { + JS_ASSERT(table); + *spp = SPROP_REMOVED; + ++removedCount; + } else { + METER(removeFrees); + if (table) + *spp = NULL; + } + LIVE_SCOPE_METER(cx, --cx->runtime->liveScopeProps); + + if (inDictionaryMode()) { + /* + * Remove sprop from its scope-owned doubly linked list, setting this + * scope's OWN_SHAPE flag first if sprop is lastProp so updateShape(cx) + * after this if-else will generate a fresh shape for this scope. + */ + if (sprop != lastProp) + setOwnShape(); + removeDictionaryProperty(sprop); + } else { + JS_ASSERT(sprop == lastProp); + removeLastProperty(); + } + updateShape(cx); + CHECK_ANCESTOR_LINE(this, true); + + /* Last, consider shrinking this->table if its load factor is <= .25. */ + size = SCOPE_CAPACITY(this); + if (size > MIN_SCOPE_SIZE && entryCount <= size >> 2) { + METER(shrinks); + (void) changeTable(cx, -1); + } + + METER(removes); + return true; +} + +void +JSScope::clear(JSContext *cx) +{ + CHECK_ANCESTOR_LINE(this, true); + LIVE_SCOPE_METER(cx, cx->runtime->liveScopeProps -= entryCount); + + if (table) + js_free(table); + clearDictionaryMode(); + clearOwnShape(); + js_LeaveTraceIfGlobalObject(cx, object); + + JSClass *clasp = object->getClass(); + JSObject *proto = object->getProto(); + JSEmptyScope *emptyScope; + uint32 newShape; + if (proto && + OBJ_IS_NATIVE(proto) && + (emptyScope = OBJ_SCOPE(proto)->emptyScope) && + emptyScope->clasp == clasp) { + newShape = emptyScope->shape; + } else { + newShape = js_GenerateShape(cx, false); + } + initMinimal(cx, newShape); + + JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals); +} + +void +JSScope::brandingShapeChange(JSContext *cx, uint32 slot, jsval v) +{ + generateOwnShape(cx); +} + +void +JSScope::deletingShapeChange(JSContext *cx, JSScopeProperty *sprop) +{ + JS_ASSERT(!JSVAL_IS_NULL(sprop->id)); + generateOwnShape(cx); +} + +bool +JSScope::methodShapeChange(JSContext *cx, JSScopeProperty *sprop, jsval toval) +{ + JS_ASSERT(!JSVAL_IS_NULL(sprop->id)); + if (sprop->isMethod()) { +#ifdef DEBUG + jsval prev = LOCKED_OBJ_GET_SLOT(object, sprop->slot); + JS_ASSERT(sprop->methodValue() == prev); + JS_ASSERT(hasMethodBarrier()); + JS_ASSERT(object->getClass() == &js_ObjectClass); + JS_ASSERT(!sprop->setter || sprop->setter == js_watch_set); +#endif + + /* + * Pass null to make a stub getter, but pass along sprop->setter to + * preserve watchpoints. Clear SPROP_IS_METHOD from flags as we are + * despecializing from a method memoized in the property tree to a + * plain old function-valued property. + */ + sprop = putProperty(cx, sprop->id, NULL, sprop->setter, sprop->slot, + sprop->attrs, sprop->flags & ~SPROP_IS_METHOD, + sprop->shortid); + if (!sprop) + return false; + } + + generateOwnShape(cx); + return true; +} + +bool +JSScope::methodShapeChange(JSContext *cx, uint32 slot, jsval toval) +{ + if (!hasMethodBarrier()) { + generateOwnShape(cx); + } else { + for (JSScopeProperty *sprop = lastProp; sprop; sprop = sprop->parent) { + JS_ASSERT(!JSVAL_IS_NULL(sprop->id)); + if (sprop->slot == slot) + return methodShapeChange(cx, sprop, toval); + } + } + return true; +} + +void +JSScope::protoShapeChange(JSContext *cx) +{ + generateOwnShape(cx); +} + +void +JSScope::sealingShapeChange(JSContext *cx) +{ + generateOwnShape(cx); +} + +void +JSScope::shadowingShapeChange(JSContext *cx, JSScopeProperty *sprop) +{ + JS_ASSERT(!JSVAL_IS_NULL(sprop->id)); + generateOwnShape(cx); +} + +void +js_TraceId(JSTracer *trc, jsid id) +{ + jsval v; + + v = ID_TO_VALUE(id); + JS_CALL_VALUE_TRACER(trc, v, "id"); +} + +#ifdef DEBUG +static void +PrintPropertyGetterOrSetter(JSTracer *trc, char *buf, size_t bufsize) +{ + JSScopeProperty *sprop; + jsid id; + size_t n; + const char *name; + + JS_ASSERT(trc->debugPrinter == PrintPropertyGetterOrSetter); + sprop = (JSScopeProperty *)trc->debugPrintArg; + id = sprop->id; + JS_ASSERT(!JSVAL_IS_NULL(id)); + name = trc->debugPrintIndex ? js_setter_str : js_getter_str; + + if (JSID_IS_ATOM(id)) { + n = js_PutEscapedString(buf, bufsize - 1, + ATOM_TO_STRING(JSID_TO_ATOM(id)), 0); + if (n < bufsize - 1) + JS_snprintf(buf + n, bufsize - n, " %s", name); + } else if (JSID_IS_INT(sprop->id)) { + JS_snprintf(buf, bufsize, "%d %s", JSID_TO_INT(id), name); + } else { + JS_snprintf(buf, bufsize, " %s", name); + } +} + +static void +PrintPropertyMethod(JSTracer *trc, char *buf, size_t bufsize) +{ + JSScopeProperty *sprop; + jsid id; + size_t n; + + JS_ASSERT(trc->debugPrinter == PrintPropertyMethod); + sprop = (JSScopeProperty *)trc->debugPrintArg; + id = sprop->id; + JS_ASSERT(!JSVAL_IS_NULL(id)); + + JS_ASSERT(JSID_IS_ATOM(id)); + n = js_PutEscapedString(buf, bufsize - 1, ATOM_TO_STRING(JSID_TO_ATOM(id)), 0); + if (n < bufsize - 1) + JS_snprintf(buf + n, bufsize - n, " method"); +} +#endif + +void +JSScopeProperty::trace(JSTracer *trc) +{ + if (IS_GC_MARKING_TRACER(trc)) + flags |= SPROP_MARK; + js_TraceId(trc, id); + +#if JS_HAS_GETTER_SETTER + if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) { + if (attrs & JSPROP_GETTER) { + JS_SET_TRACING_DETAILS(trc, PrintPropertyGetterOrSetter, this, 0); + JS_CallTracer(trc, getterObject(), JSTRACE_OBJECT); + } + if (attrs & JSPROP_SETTER) { + JS_SET_TRACING_DETAILS(trc, PrintPropertyGetterOrSetter, this, 1); + JS_CallTracer(trc, setterObject(), JSTRACE_OBJECT); + } + } +#endif /* JS_HAS_GETTER_SETTER */ + + if (isMethod()) { + JS_SET_TRACING_DETAILS(trc, PrintPropertyMethod, this, 0); + JS_CallTracer(trc, methodObject(), JSTRACE_OBJECT); + } +} + +#ifdef DEBUG + +#include + +static void +MeterKidCount(JSBasicStats *bs, uintN nkids) +{ + JS_BASIC_STATS_ACCUM(bs, nkids); + bs->hist[JS_MIN(nkids, 10)]++; +} + +static void +MeterPropertyTree(JSBasicStats *bs, JSScopeProperty *node) +{ + uintN i, nkids; + JSScopeProperty *kids, *kid; + PropTreeKidsChunk *chunk; + + nkids = 0; + kids = node->kids; + if (kids) { + if (KIDS_IS_CHUNKY(kids)) { + for (chunk = KIDS_TO_CHUNK(kids); chunk; chunk = chunk->next) { + for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { + kid = chunk->kids[i]; + if (!kid) + break; + MeterPropertyTree(bs, kid); + nkids++; + } + } + } else { + MeterPropertyTree(bs, kids); + nkids = 1; + } + } + + MeterKidCount(bs, nkids); +} + +static JSDHashOperator +js_MeterPropertyTree(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, + void *arg) +{ + JSPropertyTreeEntry *entry = (JSPropertyTreeEntry *)hdr; + JSBasicStats *bs = (JSBasicStats *)arg; + + MeterPropertyTree(bs, entry->child); + return JS_DHASH_NEXT; +} + +static void +DumpSubtree(JSContext *cx, JSScopeProperty *sprop, int level, FILE *fp) +{ + jsval v; + JSString *str; + JSScopeProperty *kids, *kid; + PropTreeKidsChunk *chunk; + uintN i; + + fprintf(fp, "%*sid ", level, ""); + v = ID_TO_VALUE(sprop->id); + JS_ASSERT(!JSVAL_IS_NULL(v)); + if (JSID_IS_INT(sprop->id)) { + fprintf(fp, "%d", JSVAL_TO_INT(v)); + } else { + if (JSID_IS_ATOM(sprop->id)) { + str = JSVAL_TO_STRING(v); + } else { + JS_ASSERT(JSID_IS_OBJECT(sprop->id)); + str = js_ValueToString(cx, v); + fputs("object ", fp); + } + if (!str) + fputs("", fp); + else + js_FileEscapedString(fp, str, '"'); + } + + fprintf(fp, " g/s %p/%p slot %u attrs %x flags %x shortid %d\n", + (void *) sprop->getter, (void *) sprop->setter, sprop->slot, + sprop->attrs, sprop->flags, sprop->shortid); + kids = sprop->kids; + if (kids) { + ++level; + if (KIDS_IS_CHUNKY(kids)) { + chunk = KIDS_TO_CHUNK(kids); + do { + for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { + kid = chunk->kids[i]; + if (!kid) + break; + JS_ASSERT(kid->parent == sprop); + DumpSubtree(cx, kid, level, fp); + } + } while ((chunk = chunk->next) != NULL); + } else { + kid = kids; + DumpSubtree(cx, kid, level, fp); + } + } +} + +#endif /* DEBUG */ + +void +js_SweepScopeProperties(JSContext *cx) +{ + JSRuntime *rt = cx->runtime; + JSArena **ap, *a; + JSScopeProperty *limit, *sprop, *parent, *kids, *kid; + uintN liveCount; + PropTreeKidsChunk *chunk, *nextChunk, *freeChunk; + uintN i; + +#ifdef DEBUG + JSBasicStats bs; + uint32 livePropCapacity = 0, totalLiveCount = 0; + static FILE *logfp; + if (!logfp) { + if (const char *filename = getenv("JS_PROPTREE_STATFILE")) + logfp = fopen(filename, "w"); + } + + if (logfp) { + JS_BASIC_STATS_INIT(&bs); + MeterKidCount(&bs, rt->propertyTreeHash.entryCount); + JS_DHashTableEnumerate(&rt->propertyTreeHash, js_MeterPropertyTree, &bs); + + double props, nodes, mean, sigma; + + props = rt->liveScopePropsPreSweep; + nodes = rt->livePropTreeNodes; + JS_ASSERT(nodes == bs.sum); + mean = JS_MeanAndStdDevBS(&bs, &sigma); + + fprintf(logfp, + "props %g nodes %g beta %g meankids %g sigma %g max %u\n", + props, nodes, nodes / props, mean, sigma, bs.max); + + JS_DumpHistogram(&bs, logfp); + } +#endif + + ap = &rt->propertyArenaPool.first.next; + while ((a = *ap) != NULL) { + limit = (JSScopeProperty *) a->avail; + liveCount = 0; + for (sprop = (JSScopeProperty *) a->base; sprop < limit; sprop++) { + /* If the id is null, sprop is already on the freelist. */ + if (sprop->id == JSVAL_NULL) + continue; + + /* + * If the mark bit is set, sprop is alive, so clear the mark bit + * and continue the while loop. + * + * Regenerate sprop->shape if it hasn't already been refreshed + * during the mark phase, when live scopes' lastProp members are + * followed to update both scope->shape and lastProp->shape. + */ + if (sprop->flags & SPROP_MARK) { + sprop->flags &= ~SPROP_MARK; + if (rt->gcRegenShapes) { + if (sprop->flags & SPROP_FLAG_SHAPE_REGEN) + sprop->flags &= ~SPROP_FLAG_SHAPE_REGEN; + else + sprop->shape = js_RegenerateShapeForGC(cx); + } + liveCount++; + continue; + } + + if (!(sprop->flags & SPROP_IN_DICTIONARY)) { + /* Ok, sprop is garbage to collect: unlink it from its parent. */ + freeChunk = RemovePropertyTreeChild(rt, sprop); + + /* + * Take care to reparent all sprop's kids to their grandparent. + * InsertPropertyTreeChild can potentially fail for two reasons: + * + * 1. If parent is null, insertion into the root property hash + * table may fail. We are forced to leave the kid out of the + * table (as can already happen with duplicates) but ensure + * that the kid's parent pointer is set to null. + * + * 2. If parent is non-null, allocation of a new KidsChunk can + * fail. To prevent this from happening, we allow sprops's own + * chunks to be reused by the grandparent, which removes the + * need for InsertPropertyTreeChild to malloc a new KidsChunk. + * + * If sprop does not have chunky kids, then we rely on the + * RemovePropertyTreeChild call above (which removed sprop from + * its parent) either leaving one free entry, or else returning + * the now-unused chunk to us so we can reuse it. + * + * We also require the grandparent to have either no kids or else + * chunky kids. A single non-chunky kid would force a new chunk to + * be malloced in some cases (if sprop had a single non-chunky + * kid, or a multiple of MAX_KIDS_PER_CHUNK kids). Note that + * RemovePropertyTreeChild never converts a single-entry chunky + * kid back to a non-chunky kid, so we are assured of correct + * behaviour. + */ + kids = sprop->kids; + if (kids) { + sprop->kids = NULL; + parent = sprop->parent; + + /* The grandparent must have either no kids or chunky kids. */ + JS_ASSERT(!parent || !parent->kids || + KIDS_IS_CHUNKY(parent->kids)); + if (KIDS_IS_CHUNKY(kids)) { + chunk = KIDS_TO_CHUNK(kids); + do { + nextChunk = chunk->next; + chunk->next = NULL; + for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { + kid = chunk->kids[i]; + if (!kid) + break; + JS_ASSERT(kid->parent == sprop); + + /* + * Clear a space in the kids array for possible + * re-use by InsertPropertyTreeChild. + */ + chunk->kids[i] = NULL; + if (!InsertPropertyTreeChild(rt, parent, kid, chunk)) { + /* + * This can happen only if we failed to add an + * entry to the root property hash table. + */ + JS_ASSERT(!parent); + kid->parent = NULL; + } + } + if (!chunk->kids[0]) { + /* The chunk wasn't reused, so we must free it. */ + DestroyPropTreeKidsChunk(rt, chunk); + } + } while ((chunk = nextChunk) != NULL); + } else { + kid = kids; + if (!InsertPropertyTreeChild(rt, parent, kid, freeChunk)) { + /* + * This can happen only if we failed to add an entry + * to the root property hash table. + */ + JS_ASSERT(!parent); + kid->parent = NULL; + } + } + } + + if (freeChunk && !freeChunk->kids[0]) { + /* The chunk wasn't reused, so we must free it. */ + DestroyPropTreeKidsChunk(rt, freeChunk); + } + } + + /* Clear id so we know (above) that sprop is on the freelist. */ + sprop->id = JSVAL_NULL; + FREENODE_INSERT(rt->propertyFreeList, sprop); + JS_RUNTIME_UNMETER(rt, livePropTreeNodes); + } + + /* If a contains no live properties, return it to the malloc heap. */ + if (liveCount == 0) { + for (sprop = (JSScopeProperty *) a->base; sprop < limit; sprop++) + FREENODE_REMOVE(sprop); + JS_ARENA_DESTROY(&rt->propertyArenaPool, a, ap); + } else { +#ifdef DEBUG + livePropCapacity += limit - (JSScopeProperty *) a->base; + totalLiveCount += liveCount; +#endif + ap = &a->next; + } + } + +#ifdef DEBUG + if (logfp) { + fprintf(logfp, + "\nProperty tree stats for gcNumber %lu\n", + (unsigned long) rt->gcNumber); + + fprintf(logfp, "arenautil %g%%\n", + (totalLiveCount && livePropCapacity) + ? (totalLiveCount * 100.0) / livePropCapacity + : 0.0); + +#define RATE(f1, f2) (((double)js_scope_stats.f1 / js_scope_stats.f2) * 100.0) + + fprintf(logfp, + "Scope search stats:\n" + " searches: %6u\n" + " hits: %6u %5.2f%% of searches\n" + " misses: %6u %5.2f%%\n" + " hashes: %6u %5.2f%%\n" + " steps: %6u %5.2f%% %5.2f%% of hashes\n" + " stepHits: %6u %5.2f%% %5.2f%%\n" + " stepMisses: %6u %5.2f%% %5.2f%%\n" + " tableAllocFails %6u\n" + " toDictFails %6u\n" + " wrapWatchFails %6u\n" + " adds: %6u\n" + " addFails: %6u\n" + " puts: %6u\n" + " redundantPuts: %6u\n" + " putFails: %6u\n" + " changes: %6u\n" + " changeFails: %6u\n" + " compresses: %6u\n" + " grows: %6u\n" + " removes: %6u\n" + " removeFrees: %6u\n" + " uselessRemoves: %6u\n" + " shrinks: %6u\n", + js_scope_stats.searches, + js_scope_stats.hits, RATE(hits, searches), + js_scope_stats.misses, RATE(misses, searches), + js_scope_stats.hashes, RATE(hashes, searches), + js_scope_stats.steps, RATE(steps, searches), RATE(steps, hashes), + js_scope_stats.stepHits, + RATE(stepHits, searches), RATE(stepHits, hashes), + js_scope_stats.stepMisses, + RATE(stepMisses, searches), RATE(stepMisses, hashes), + js_scope_stats.tableAllocFails, + js_scope_stats.toDictFails, + js_scope_stats.wrapWatchFails, + js_scope_stats.adds, + js_scope_stats.addFails, + js_scope_stats.puts, + js_scope_stats.redundantPuts, + js_scope_stats.putFails, + js_scope_stats.changes, + js_scope_stats.changeFails, + js_scope_stats.compresses, + js_scope_stats.grows, + js_scope_stats.removes, + js_scope_stats.removeFrees, + js_scope_stats.uselessRemoves, + js_scope_stats.shrinks); + +#undef RATE + + fflush(logfp); + } + + if (const char *filename = getenv("JS_PROPTREE_DUMPFILE")) { + char pathname[1024]; + JS_snprintf(pathname, sizeof pathname, "%s.%lu", filename, (unsigned long)rt->gcNumber); + FILE *dumpfp = fopen(pathname, "w"); + if (dumpfp) { + JSPropertyTreeEntry *pte, *end; + + pte = (JSPropertyTreeEntry *) rt->propertyTreeHash.entryStore; + end = pte + JS_DHASH_TABLE_SIZE(&rt->propertyTreeHash); + while (pte < end) { + if (pte->child) + DumpSubtree(cx, pte->child, 0, dumpfp); + pte++; + } + fclose(dumpfp); + } + } +#endif /* DEBUG */ +} + +bool +js_InitPropertyTree(JSRuntime *rt) +{ + if (!JS_DHashTableInit(&rt->propertyTreeHash, &PropertyTreeHashOps, NULL, + sizeof(JSPropertyTreeEntry), JS_DHASH_MIN_SIZE)) { + rt->propertyTreeHash.ops = NULL; + return false; + } + JS_InitArenaPool(&rt->propertyArenaPool, "properties", + 256 * sizeof(JSScopeProperty), sizeof(void *), NULL); + return true; +} + +void +js_FinishPropertyTree(JSRuntime *rt) +{ + if (rt->propertyTreeHash.ops) { + JS_DHashTableFinish(&rt->propertyTreeHash); + rt->propertyTreeHash.ops = NULL; + } + JS_FinishArenaPool(&rt->propertyArenaPool); +} diff --git a/ape-server/deps/js/src/jsscope.h b/ape-server/deps/js/src/jsscope.h new file mode 100755 index 0000000..9044c49 --- /dev/null +++ b/ape-server/deps/js/src/jsscope.h @@ -0,0 +1,892 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=99: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsscope_h___ +#define jsscope_h___ +/* + * JS symbol tables. + */ +#include "jstypes.h" +#include "jslock.h" +#include "jsobj.h" +#include "jsprvtd.h" +#include "jspubtd.h" + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4800) +#endif + +JS_BEGIN_EXTERN_C + +/* + * Given P independent, non-unique properties each of size S words mapped by + * all scopes in a runtime, construct a property tree of N nodes each of size + * S+L words (L for tree linkage). A nominal L value is 2 for leftmost-child + * and right-sibling links. We hope that the N < P by enough that the space + * overhead of L, and the overhead of scope entries pointing at property tree + * nodes, is worth it. + * + * The tree construction goes as follows. If any empty scope in the runtime + * has a property X added to it, find or create a node under the tree root + * labeled X, and set scope->lastProp to point at that node. If any non-empty + * scope whose most recently added property is labeled Y has another property + * labeled Z added, find or create a node for Z under the node that was added + * for Y, and set scope->lastProp to point at that node. + * + * A property is labeled by its members' values: id, getter, setter, slot, + * attributes, tiny or short id, and a field telling for..in order. Note that + * labels are not unique in the tree, but they are unique among a node's kids + * (barring rare and benign multi-threaded race condition outcomes, see below) + * and along any ancestor line from the tree root to a given leaf node (except + * for the hard case of duplicate formal parameters to a function). + * + * Thus the root of the tree represents all empty scopes, and the first ply + * of the tree represents all scopes containing one property, etc. Each node + * in the tree can stand for any number of scopes having the same ordered set + * of properties, where that node was the last added to the scope. (We need + * not store the root of the tree as a node, and do not -- all we need are + * links to its kids.) + * + * Sidebar on for..in loop order: ECMA requires no particular order, but this + * implementation has promised and delivered property definition order, and + * compatibility is king. We could use an order number per property, which + * would require a sort in js_Enumerate, and an entry order generation number + * per scope. An order number beats a list, which should be doubly-linked for + * O(1) delete. An even better scheme is to use a parent link in the property + * tree, so that the ancestor line can be iterated from scope->lastProp when + * filling in a JSIdArray from back to front. This parent link also helps the + * GC to sweep properties iteratively. + * + * What if a property Y is deleted from a scope? If Y is the last property in + * the scope, we simply adjust the scope's lastProp member after we remove the + * scope's hash-table entry pointing at that property node. The parent link + * mentioned in the for..in sidebar above makes this adjustment O(1). But if + * Y comes between X and Z in the scope, then we might have to "fork" the tree + * at X, leaving X->Y->Z in case other scopes have those properties added in + * that order; and to finish the fork, we'd add a node labeled Z with the path + * X->Z, if it doesn't exist. This could lead to lots of extra nodes, and to + * O(n^2) growth when deleting lots of properties. + * + * Rather, for O(1) growth all around, we should share the path X->Y->Z among + * scopes having those three properties added in that order, and among scopes + * having only X->Z where Y was deleted. All such scopes have a lastProp that + * points to the Z child of Y. But a scope in which Y was deleted does not + * have a table entry for Y, and when iterating that scope by traversing the + * ancestor line from Z, we will have to test for a table entry for each node, + * skipping nodes that lack entries. + * + * What if we add Y again? X->Y->Z->Y is wrong and we'll enumerate Y twice. + * Therefore we must fork in such a case if not earlier, or do something else. + * We used to fork on the theory that set after delete is rare, but the Web is + * a harsh mistress, and we now convert the scope to a "dictionary" on first + * delete, to avoid O(n^2) growth in the property tree. + * + * What about thread safety? If the property tree operations done by requests + * are find-node and insert-node, then the only hazard is duplicate insertion. + * This is harmless except for minor bloat. When all requests have ended or + * been suspended, the GC is free to sweep the tree after marking all nodes + * reachable from scopes, performing remove-node operations as needed. + * + * Is the property tree worth it compared to property storage in each table's + * entries? To decide, we must find the relation <> between the words used + * with a property tree and the words required without a tree. + * + * Model all scopes as one super-scope of capacity T entries (T a power of 2). + * Let alpha be the load factor of this double hash-table. With the property + * tree, each entry in the table is a word-sized pointer to a node that can be + * shared by many scopes. But all such pointers are overhead compared to the + * situation without the property tree, where the table stores property nodes + * directly, as entries each of size S words. With the property tree, we need + * L=2 extra words per node for siblings and kids pointers. Without the tree, + * (1-alpha)*S*T words are wasted on free or removed sentinel-entries required + * by double hashing. + * + * Therefore, + * + * (property tree) <> (no property tree) + * N*(S+L) + T <> S*T + * N*(S+L) + T <> P*S + (1-alpha)*S*T + * N*(S+L) + alpha*T + (1-alpha)*T <> P*S + (1-alpha)*S*T + * + * Note that P is alpha*T by definition, so + * + * N*(S+L) + P + (1-alpha)*T <> P*S + (1-alpha)*S*T + * N*(S+L) <> P*S - P + (1-alpha)*S*T - (1-alpha)*T + * N*(S+L) <> (P + (1-alpha)*T) * (S-1) + * N*(S+L) <> (P + (1-alpha)*P/alpha) * (S-1) + * N*(S+L) <> P * (1/alpha) * (S-1) + * + * Let N = P*beta for a compression ratio beta, beta <= 1: + * + * P*beta*(S+L) <> P * (1/alpha) * (S-1) + * beta*(S+L) <> (S-1)/alpha + * beta <> (S-1)/((S+L)*alpha) + * + * For S = 6 (32-bit architectures) and L = 2, the property tree wins iff + * + * beta < 5/(8*alpha) + * + * We ensure that alpha <= .75, so the property tree wins if beta < .83_. An + * average beta from recent Mozilla browser startups was around .6. + * + * Can we reduce L? Observe that the property tree degenerates into a list of + * lists if at most one property Y follows X in all scopes. In or near such a + * case, we waste a word on the right-sibling link outside of the root ply of + * the tree. Note also that the root ply tends to be large, so O(n^2) growth + * searching it is likely, indicating the need for hashing (but with increased + * thread safety costs). + * + * If only K out of N nodes in the property tree have more than one child, we + * could eliminate the sibling link and overlay a children list or hash-table + * pointer on the leftmost-child link (which would then be either null or an + * only-child link; the overlay could be tagged in the low bit of the pointer, + * or flagged elsewhere in the property tree node, although such a flag must + * not be considered when comparing node labels during tree search). + * + * For such a system, L = 1 + (K * averageChildrenTableSize) / N instead of 2. + * If K << N, L approaches 1 and the property tree wins if beta < .95. + * + * We observe that fan-out below the root ply of the property tree appears to + * have extremely low degree (see the MeterPropertyTree code that histograms + * child-counts in jsscope.c), so instead of a hash-table we use a linked list + * of child node pointer arrays ("kid chunks"). The details are isolated in + * jsscope.c; others must treat JSScopeProperty.kids as opaque. We leave it + * strongly typed for debug-ability of the common (null or one-kid) cases. + * + * One final twist (can you stand it?): the mean number of entries per scope + * in Mozilla is < 5, with a large standard deviation (~8). Instead of always + * allocating scope->table, we leave it null while initializing all the other + * scope members as if it were non-null and minimal-length. Until a property + * is added that crosses the threshold of 6 or more entries for hashing, we use + * linear search from scope->lastProp to find a given id, and save on the space + * overhead of a hash table. + */ + +struct JSEmptyScope; + +#define SPROP_INVALID_SLOT 0xffffffff + +struct JSScope : public JSObjectMap +{ +#ifdef JS_THREADSAFE + JSTitle title; /* lock state */ +#endif + JSObject *object; /* object that owns this scope */ + jsrefcount nrefs; /* count of all referencing objects */ + uint32 freeslot; /* index of next free slot in object */ + JSEmptyScope *emptyScope; /* cache for getEmptyScope below */ + uint8 flags; /* flags, see below */ + int8 hashShift; /* multiplicative hash shift */ + + uint16 spare; /* reserved */ + uint32 entryCount; /* number of entries in table */ + uint32 removedCount; /* removed entry sentinels in table */ + JSScopeProperty **table; /* table of ptrs to shared tree nodes */ + + /* + * A little information hiding for scope->lastProp, in case it ever becomes + * a tagged pointer again. + */ + inline JSScopeProperty *lastProperty() const; + + private: + JSScopeProperty *getChildProperty(JSContext *cx, JSScopeProperty *parent, + JSScopeProperty &child); + + JSScopeProperty *newDictionaryProperty(JSContext *cx, const JSScopeProperty &child, + JSScopeProperty **childp); + + bool toDictionaryMode(JSContext *cx, JSScopeProperty *&aprop); + + /* + * Private pointer to the last added property and methods to manipulate the + * list it links among properties in this scope. The {remove,insert} pair + * for DictionaryProperties assert that the scope is in dictionary mode and + * any reachable properties are flagged as dictionary properties. + * + * NB: these private methods do *not* update this scope's shape to track + * lastProp->shape after they finish updating the linked list in the case + * where lastProp is updated. It is up to calling code in jsscope.cpp to + * call updateShape(cx) after updating lastProp. + */ + JSScopeProperty *lastProp; + + /* These four inline methods are defined further below in this .h file. */ + inline void setLastProperty(JSScopeProperty *sprop); + inline void removeLastProperty(); + inline void removeDictionaryProperty(JSScopeProperty *sprop); + inline void insertDictionaryProperty(JSScopeProperty *sprop, JSScopeProperty **childp); + + /* Defined in jsscopeinlines.h to avoid including implementation dependencies here. */ + inline void updateShape(JSContext *cx); + + void initMinimal(JSContext *cx, uint32 newShape); + bool createTable(JSContext *cx, bool report); + bool changeTable(JSContext *cx, int change); + void reportReadOnlyScope(JSContext *cx); + void generateOwnShape(JSContext *cx); + JSScopeProperty **searchTable(jsid id, bool adding); + inline JSScopeProperty **search(jsid id, bool adding); + JSEmptyScope *createEmptyScope(JSContext *cx, JSClass *clasp); + + JSScopeProperty *addPropertyHelper(JSContext *cx, jsid id, + JSPropertyOp getter, JSPropertyOp setter, + uint32 slot, uintN attrs, + uintN flags, intN shortid, + JSScopeProperty **spp); + + public: + explicit JSScope(const JSObjectOps *ops, JSObject *obj = NULL) + : JSObjectMap(ops, 0), object(obj) {} + + /* Create a mutable, owned, empty scope. */ + static JSScope *create(JSContext *cx, const JSObjectOps *ops, JSClass *clasp, + JSObject *obj, uint32 shape); + + static void destroy(JSContext *cx, JSScope *scope); + + inline void hold(); + inline bool drop(JSContext *cx, JSObject *obj); + + /* + * Return an immutable, shareable, empty scope with the same ops as this + * and the same freeslot as this had when empty. + * + * If |this| is the scope of an object |proto|, the resulting scope can be + * used as the scope of a new object whose prototype is |proto|. + */ + inline JSEmptyScope *getEmptyScope(JSContext *cx, JSClass *clasp); + + inline bool canProvideEmptyScope(JSObjectOps *ops, JSClass *clasp); + + JSScopeProperty *lookup(jsid id); + + inline bool hasProperty(jsid id) { return lookup(id) != NULL; } + inline bool hasProperty(JSScopeProperty *sprop); + + /* Add a property whose id is not yet in this scope. */ + JSScopeProperty *addProperty(JSContext *cx, jsid id, + JSPropertyOp getter, JSPropertyOp setter, + uint32 slot, uintN attrs, + uintN flags, intN shortid); + + /* Add a data property whose id is not yet in this scope. */ + JSScopeProperty *addDataProperty(JSContext *cx, jsid id, uint32 slot, uintN attrs) { + JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER))); + return addProperty(cx, id, NULL, NULL, slot, attrs, 0, 0); + } + + /* Add or overwrite a property for id in this scope. */ + JSScopeProperty *putProperty(JSContext *cx, jsid id, + JSPropertyOp getter, JSPropertyOp setter, + uint32 slot, uintN attrs, + uintN flags, intN shortid); + + /* Change the given property into a sibling with the same id in this scope. */ + JSScopeProperty *changeProperty(JSContext *cx, JSScopeProperty *sprop, + uintN attrs, uintN mask, + JSPropertyOp getter, JSPropertyOp setter); + + /* Remove id from this scope. */ + bool removeProperty(JSContext *cx, jsid id); + + /* Clear the scope, making it empty. */ + void clear(JSContext *cx); + + /* Extend this scope to have sprop as its last-added property. */ + void extend(JSContext *cx, JSScopeProperty *sprop); + + /* + * Read barrier to clone a joined function object stored as a method. + * Defined in jsscopeinlines.h, but not declared inline per standard style + * in order to avoid gcc warnings. + */ + bool methodReadBarrier(JSContext *cx, JSScopeProperty *sprop, jsval *vp); + + /* + * Write barrier to check for a method value change. Defined inline below + * after methodReadBarrier. Two flavors to handle JSOP_*GVAR, which deals + * in slots not sprops, while not deoptimizing to map slot to sprop unless + * flags show this is necessary. The methodShapeChange overload (directly + * below) parallels this. + */ + bool methodWriteBarrier(JSContext *cx, JSScopeProperty *sprop, jsval v); + bool methodWriteBarrier(JSContext *cx, uint32 slot, jsval v); + + void trace(JSTracer *trc); + + void brandingShapeChange(JSContext *cx, uint32 slot, jsval v); + void deletingShapeChange(JSContext *cx, JSScopeProperty *sprop); + bool methodShapeChange(JSContext *cx, JSScopeProperty *sprop, jsval toval); + bool methodShapeChange(JSContext *cx, uint32 slot, jsval toval); + void protoShapeChange(JSContext *cx); + void sealingShapeChange(JSContext *cx); + void shadowingShapeChange(JSContext *cx, JSScopeProperty *sprop); + +/* By definition, hashShift = JS_DHASH_BITS - log2(capacity). */ +#define SCOPE_CAPACITY(scope) JS_BIT(JS_DHASH_BITS-(scope)->hashShift) + + enum { + DICTIONARY_MODE = 0x0001, + SEALED = 0x0002, + BRANDED = 0x0004, + INDEXED_PROPERTIES = 0x0008, + OWN_SHAPE = 0x0010, + METHOD_BARRIER = 0x0020, + + /* + * This flag toggles with each shape-regenerating GC cycle. + * See JSRuntime::gcRegenShapesScopeFlag. + */ + SHAPE_REGEN = 0x0040 + }; + + bool inDictionaryMode() { return flags & DICTIONARY_MODE; } + void setDictionaryMode() { flags |= DICTIONARY_MODE; } + void clearDictionaryMode() { flags &= ~DICTIONARY_MODE; } + + /* + * Don't define clearSealed, as it can't be done safely because JS_LOCK_OBJ + * will avoid taking the lock if the object owns its scope and the scope is + * sealed. + */ + bool sealed() { return flags & SEALED; } + void setSealed() { flags |= SEALED; } + + /* + * A branded scope's object contains plain old methods (function-valued + * properties without magic getters and setters), and its scope->shape + * evolves whenever a function value changes. + */ + bool branded() { return flags & BRANDED; } + void setBranded() { flags |= BRANDED; } + + bool hadIndexedProperties() { return flags & INDEXED_PROPERTIES; } + void setIndexedProperties() { flags |= INDEXED_PROPERTIES; } + + bool hasOwnShape() { return flags & OWN_SHAPE; } + void setOwnShape() { flags |= OWN_SHAPE; } + void clearOwnShape() { flags &= ~OWN_SHAPE; } + + bool hasRegenFlag(uint8 regenFlag) { return (flags & SHAPE_REGEN) == regenFlag; } + + /* + * A scope has a method barrier when some compiler-created "null closure" + * function objects (functions that do not use lexical bindings above their + * scope, only free variable names) that have a correct JSSLOT_PARENT value + * thanks to the COMPILE_N_GO optimization are stored as newly added direct + * property values of the scope's object. + * + * The de-facto standard JS language requires each evaluation of such a + * closure to result in a unique (according to === and observable effects) + * function object. ES3 tried to allow implementations to "join" such + * objects to a single compiler-created object, but this makes an overt + * mutation hazard, also an "identity hazard" against interoperation among + * implementations that join and do not join. + * + * To stay compatible with the de-facto standard, we store the compiler- + * created function object as the method value and set the METHOD_BARRIER + * flag. + * + * The method value is part of the method property tree node's identity, so + * it effectively brands the scope with a predictable shape corresponding + * to the method value, but without the overhead of setting the BRANDED + * flag, which requires assigning a new shape peculiar to each branded + * scope. Instead the shape is shared via the property tree among all the + * scopes referencing the method property tree node. + * + * Then when reading from a scope for which scope->hasMethodBarrier() is + * true, we count on the scope's qualified/guarded shape being unique and + * add a read barrier that clones the compiler-created function object on + * demand, reshaping the scope. + * + * This read barrier is bypassed when evaluating the callee sub-expression + * of a call expression (see the JOF_CALLOP opcodes in jsopcode.tbl), since + * such ops do not present an identity or mutation hazard. The compiler + * performs this optimization only for null closures that do not use their + * own name or equivalent built-in references (arguments.callee). + * + * The BRANDED write barrier, JSScope::methodWriteBarrer, must check for + * METHOD_BARRIER too, and regenerate this scope's shape if the method's + * value is in fact changing. + */ + bool hasMethodBarrier() { return flags & METHOD_BARRIER; } + void setMethodBarrier() { flags |= METHOD_BARRIER; } + + /* + * Test whether this scope may be branded due to method calls, which means + * any assignment to a function-valued property must regenerate shape; else + * test whether this scope has method properties, which require a method + * write barrier. + */ + bool + brandedOrHasMethodBarrier() { return flags & (BRANDED | METHOD_BARRIER); } + + bool owned() { return object != NULL; } +}; + +struct JSEmptyScope : public JSScope +{ + JSClass * const clasp; + + explicit JSEmptyScope(const JSObjectOps *ops, JSClass *clasp) + : JSScope(ops), clasp(clasp) {} +}; + +inline bool +JS_IS_SCOPE_LOCKED(JSContext *cx, JSScope *scope) +{ + return JS_IS_TITLE_LOCKED(cx, &scope->title); +} + +inline JSScope * +OBJ_SCOPE(JSObject *obj) +{ + JS_ASSERT(OBJ_IS_NATIVE(obj)); + return (JSScope *) obj->map; +} + +inline uint32 +OBJ_SHAPE(JSObject *obj) +{ + JS_ASSERT(obj->map->shape != JSObjectMap::SHAPELESS); + return obj->map->shape; +} + +/* + * Helpers for reinterpreting JSPropertyOp as JSObject* for scripted getters + * and setters. + */ +inline JSObject * +js_CastAsObject(JSPropertyOp op) +{ + return JS_FUNC_TO_DATA_PTR(JSObject *, op); +} + +inline jsval +js_CastAsObjectJSVal(JSPropertyOp op) +{ + return OBJECT_TO_JSVAL(JS_FUNC_TO_DATA_PTR(JSObject *, op)); +} + +inline JSPropertyOp +js_CastAsPropertyOp(JSObject *object) +{ + return JS_DATA_TO_FUNC_PTR(JSPropertyOp, object); +} + +struct JSScopeProperty { + jsid id; /* int-tagged jsval/untagged JSAtom* */ + JSPropertyOp getter; /* getter and setter hooks or objects */ + JSPropertyOp setter; /* getter is JSObject* and setter is 0 + if sprop->isMethod() */ + uint32 slot; /* abstract index in object slots */ + uint8 attrs; /* attributes, see jsapi.h JSPROP_* */ + uint8 flags; /* flags, see below for defines */ + int16 shortid; /* tinyid, or local arg/var index */ + JSScopeProperty *parent; /* parent node, reverse for..in order */ + union { + JSScopeProperty *kids; /* null, single child, or a tagged ptr + to many-kids data structure */ + JSScopeProperty **childp; /* dictionary list starting at lastProp + has a double-indirect back pointer, + either to sprop->parent if not last, + else to scope->lastProp */ + }; + uint32 shape; /* property cache shape identifier */ + +/* Bits stored in sprop->flags. */ +#define SPROP_MARK 0x01 +#define SPROP_IS_ALIAS 0x02 +#define SPROP_HAS_SHORTID 0x04 +#define SPROP_FLAG_SHAPE_REGEN 0x08 +#define SPROP_IS_METHOD 0x10 +#define SPROP_IN_DICTIONARY 0x20 + + bool isMethod() const { + return flags & SPROP_IS_METHOD; + } + JSObject *methodObject() const { + JS_ASSERT(isMethod()); + return js_CastAsObject(getter); + } + jsval methodValue() const { + JS_ASSERT(isMethod()); + return js_CastAsObjectJSVal(getter); + } + + bool hasGetterObject() const { + return attrs & JSPROP_GETTER; + } + JSObject *getterObject() const { + JS_ASSERT(hasGetterObject()); + return js_CastAsObject(getter); + } + jsval getterValue() const { + JS_ASSERT(hasGetterObject()); + return js_CastAsObjectJSVal(getter); + } + + bool hasSetterObject() const { + return attrs & JSPROP_SETTER; + } + JSObject *setterObject() const { + JS_ASSERT(hasSetterObject()); + return js_CastAsObject(setter); + } + jsval setterValue() const { + JS_ASSERT(hasSetterObject()); + return js_CastAsObjectJSVal(setter); + } + + bool get(JSContext* cx, JSObject* obj, JSObject *pobj, jsval* vp); + bool set(JSContext* cx, JSObject* obj, jsval* vp); + + void trace(JSTracer *trc); +}; + +/* JSScopeProperty pointer tag bit indicating a collision. */ +#define SPROP_COLLISION ((jsuword)1) +#define SPROP_REMOVED ((JSScopeProperty *) SPROP_COLLISION) + +/* Macros to get and set sprop pointer values and collision flags. */ +#define SPROP_IS_FREE(sprop) ((sprop) == NULL) +#define SPROP_IS_REMOVED(sprop) ((sprop) == SPROP_REMOVED) +#define SPROP_IS_LIVE(sprop) ((sprop) > SPROP_REMOVED) +#define SPROP_FLAG_COLLISION(spp,sprop) (*(spp) = (JSScopeProperty *) \ + ((jsuword)(sprop) | SPROP_COLLISION)) +#define SPROP_HAD_COLLISION(sprop) ((jsuword)(sprop) & SPROP_COLLISION) +#define SPROP_FETCH(spp) SPROP_CLEAR_COLLISION(*(spp)) + +#define SPROP_CLEAR_COLLISION(sprop) \ + ((JSScopeProperty *) ((jsuword)(sprop) & ~SPROP_COLLISION)) + +#define SPROP_STORE_PRESERVING_COLLISION(spp, sprop) \ + (*(spp) = (JSScopeProperty *) ((jsuword)(sprop) \ + | SPROP_HAD_COLLISION(*(spp)))) + +inline JSScopeProperty * +JSScope::lookup(jsid id) +{ + return SPROP_FETCH(search(id, false)); +} + +inline bool +JSScope::hasProperty(JSScopeProperty *sprop) +{ + return lookup(sprop->id) == sprop; +} + +inline JSScopeProperty * +JSScope::lastProperty() const +{ + JS_ASSERT_IF(lastProp, !JSVAL_IS_NULL(lastProp->id)); + return lastProp; +} + +/* + * Note that sprop must not be null, as emptying a scope requires extra work + * done only by methods in jsscope.cpp. + */ +inline void +JSScope::setLastProperty(JSScopeProperty *sprop) +{ + JS_ASSERT(!JSVAL_IS_NULL(sprop->id)); + JS_ASSERT_IF(lastProp, !JSVAL_IS_NULL(lastProp->id)); + + lastProp = sprop; +} + +inline void +JSScope::removeLastProperty() +{ + JS_ASSERT(!inDictionaryMode()); + JS_ASSERT_IF(lastProp->parent, !JSVAL_IS_NULL(lastProp->parent->id)); + + lastProp = lastProp->parent; + --entryCount; +} + +inline void +JSScope::removeDictionaryProperty(JSScopeProperty *sprop) +{ + JS_ASSERT(inDictionaryMode()); + JS_ASSERT(sprop->flags & SPROP_IN_DICTIONARY); + JS_ASSERT(sprop->childp); + JS_ASSERT(!JSVAL_IS_NULL(sprop->id)); + + JS_ASSERT(lastProp->flags & SPROP_IN_DICTIONARY); + JS_ASSERT(lastProp->childp == &lastProp); + JS_ASSERT_IF(lastProp != sprop, !JSVAL_IS_NULL(lastProp->id)); + JS_ASSERT_IF(lastProp->parent, !JSVAL_IS_NULL(lastProp->parent->id)); + + if (sprop->parent) + sprop->parent->childp = sprop->childp; + *sprop->childp = sprop->parent; + --entryCount; + sprop->childp = NULL; +} + +inline void +JSScope::insertDictionaryProperty(JSScopeProperty *sprop, JSScopeProperty **childp) +{ + /* + * Don't assert inDictionaryMode() here because we may be called from + * toDictionaryMode via newDictionaryProperty. + */ + JS_ASSERT(sprop->flags & SPROP_IN_DICTIONARY); + JS_ASSERT(!sprop->childp); + JS_ASSERT(!JSVAL_IS_NULL(sprop->id)); + + JS_ASSERT_IF(*childp, (*childp)->flags & SPROP_IN_DICTIONARY); + JS_ASSERT_IF(lastProp, lastProp->flags & SPROP_IN_DICTIONARY); + JS_ASSERT_IF(lastProp, lastProp->childp == &lastProp); + JS_ASSERT_IF(lastProp, !JSVAL_IS_NULL(lastProp->id)); + + sprop->parent = *childp; + *childp = sprop; + if (sprop->parent) + sprop->parent->childp = &sprop->parent; + sprop->childp = childp; + ++entryCount; +} + +/* + * If SPROP_HAS_SHORTID is set in sprop->flags, we use sprop->shortid rather + * than id when calling sprop's getter or setter. + */ +#define SPROP_USERID(sprop) \ + (((sprop)->flags & SPROP_HAS_SHORTID) ? INT_TO_JSVAL((sprop)->shortid) \ + : ID_TO_VALUE((sprop)->id)) + +#define SLOT_IN_SCOPE(slot,scope) ((slot) < (scope)->freeslot) +#define SPROP_HAS_VALID_SLOT(sprop,scope) SLOT_IN_SCOPE((sprop)->slot, scope) + +#define SPROP_HAS_STUB_GETTER(sprop) (!(sprop)->getter) +#define SPROP_HAS_STUB_SETTER(sprop) (!(sprop)->setter) + +#define SPROP_HAS_STUB_GETTER_OR_IS_METHOD(sprop) \ + (SPROP_HAS_STUB_GETTER(sprop) || (sprop)->isMethod()) + +#ifndef JS_THREADSAFE +# define js_GenerateShape(cx, gcLocked) js_GenerateShape (cx) +#endif + +extern uint32 +js_GenerateShape(JSContext *cx, bool gcLocked); + +#ifdef DEBUG +struct JSScopeStats { + jsrefcount searches; + jsrefcount hits; + jsrefcount misses; + jsrefcount hashes; + jsrefcount steps; + jsrefcount stepHits; + jsrefcount stepMisses; + jsrefcount tableAllocFails; + jsrefcount toDictFails; + jsrefcount wrapWatchFails; + jsrefcount adds; + jsrefcount addFails; + jsrefcount puts; + jsrefcount redundantPuts; + jsrefcount putFails; + jsrefcount changes; + jsrefcount changeFails; + jsrefcount compresses; + jsrefcount grows; + jsrefcount removes; + jsrefcount removeFrees; + jsrefcount uselessRemoves; + jsrefcount shrinks; +}; + +extern JS_FRIEND_DATA(JSScopeStats) js_scope_stats; + +# define METER(x) JS_ATOMIC_INCREMENT(&js_scope_stats.x) +#else +# define METER(x) /* nothing */ +#endif + +inline JSScopeProperty ** +JSScope::search(jsid id, bool adding) +{ + JSScopeProperty *sprop, **spp; + + METER(searches); + if (!table) { + /* Not enough properties to justify hashing: search from lastProp. */ + for (spp = &lastProp; (sprop = *spp); spp = &sprop->parent) { + if (sprop->id == id) { + METER(hits); + return spp; + } + } + METER(misses); + return spp; + } + return searchTable(id, adding); +} + +#undef METER + +inline bool +JSScope::canProvideEmptyScope(JSObjectOps *ops, JSClass *clasp) +{ + return this->ops == ops && (!emptyScope || emptyScope->clasp == clasp); +} + +inline JSEmptyScope * +JSScope::getEmptyScope(JSContext *cx, JSClass *clasp) +{ + if (emptyScope) { + JS_ASSERT(clasp == emptyScope->clasp); + emptyScope->hold(); + return emptyScope; + } + return createEmptyScope(cx, clasp); +} + +inline void +JSScope::hold() +{ + JS_ASSERT(nrefs >= 0); + JS_ATOMIC_INCREMENT(&nrefs); +} + +inline bool +JSScope::drop(JSContext *cx, JSObject *obj) +{ +#ifdef JS_THREADSAFE + /* We are called from only js_ShareWaitingTitles and js_FinalizeObject. */ + JS_ASSERT(!obj || CX_THREAD_IS_RUNNING_GC(cx)); +#endif + JS_ASSERT(nrefs > 0); + --nrefs; + + if (nrefs == 0) { + destroy(cx, this); + return false; + } + if (object == obj) + object = NULL; + return true; +} + +inline bool +JSScopeProperty::get(JSContext* cx, JSObject* obj, JSObject *pobj, jsval* vp) +{ + JS_ASSERT(!SPROP_HAS_STUB_GETTER(this)); + JS_ASSERT(!JSVAL_IS_NULL(this->id)); + + if (attrs & JSPROP_GETTER) { + JS_ASSERT(!isMethod()); + jsval fval = getterValue(); + return js_InternalGetOrSet(cx, obj, id, fval, JSACC_READ, 0, 0, vp); + } + + if (isMethod()) { + *vp = methodValue(); + + JSScope *scope = OBJ_SCOPE(pobj); + JS_ASSERT(scope->object == pobj); + return scope->methodReadBarrier(cx, this, vp); + } + + /* + * JSObjectOps is private, so we know there are only two implementations + * of the thisObject hook: with objects and XPConnect wrapped native + * objects. XPConnect objects don't expect the hook to be called here, + * but with objects do. + */ + if (STOBJ_GET_CLASS(obj) == &js_WithClass) + obj = obj->map->ops->thisObject(cx, obj); + return getter(cx, obj, SPROP_USERID(this), vp); +} + +inline bool +JSScopeProperty::set(JSContext* cx, JSObject* obj, jsval* vp) +{ + JS_ASSERT_IF(SPROP_HAS_STUB_SETTER(this), attrs & JSPROP_GETTER); + + if (attrs & JSPROP_SETTER) { + jsval fval = setterValue(); + return js_InternalGetOrSet(cx, obj, id, fval, JSACC_WRITE, 1, vp, vp); + } + + if (attrs & JSPROP_GETTER) { + js_ReportGetterOnlyAssignment(cx); + return false; + } + + /* See the comment in JSScopeProperty::get as to why we can check for With. */ + if (STOBJ_GET_CLASS(obj) == &js_WithClass) + obj = obj->map->ops->thisObject(cx, obj); + return setter(cx, obj, SPROP_USERID(this), vp); +} + +/* Macro for common expression to test for shared permanent attributes. */ +#define SPROP_IS_SHARED_PERMANENT(sprop) \ + ((~(sprop)->attrs & (JSPROP_SHARED | JSPROP_PERMANENT)) == 0) + +extern JSScope * +js_GetMutableScope(JSContext *cx, JSObject *obj); + +extern void +js_TraceId(JSTracer *trc, jsid id); + +extern void +js_SweepScopeProperties(JSContext *cx); + +extern bool +js_InitPropertyTree(JSRuntime *rt); + +extern void +js_FinishPropertyTree(JSRuntime *rt); + +JS_END_EXTERN_C + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif /* jsscope_h___ */ diff --git a/ape-server/deps/js/src/jsscopeinlines.h b/ape-server/deps/js/src/jsscopeinlines.h new file mode 100755 index 0000000..b572d33 --- /dev/null +++ b/ape-server/deps/js/src/jsscopeinlines.h @@ -0,0 +1,166 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsscopeinlines_h___ +#define jsscopeinlines_h___ + +#include "jscntxt.h" +#include "jsdbgapi.h" +#include "jsfun.h" +#include "jsobj.h" +#include "jsscope.h" + +inline void +JSScope::updateShape(JSContext *cx) +{ + JS_ASSERT(object); + js_LeaveTraceIfGlobalObject(cx, object); + + shape = (hasOwnShape() || !lastProp) ? js_GenerateShape(cx, false) : lastProp->shape; +} + +inline void +JSScope::extend(JSContext *cx, JSScopeProperty *sprop) +{ + ++entryCount; + setLastProperty(sprop); + updateShape(cx); + + jsuint index; + if (js_IdIsIndex(sprop->id, &index)) + setIndexedProperties(); + + if (sprop->isMethod()) + setMethodBarrier(); +} + +/* + * Property read barrier for deferred cloning of compiler-created function + * objects optimized as typically non-escaping, ad-hoc methods in obj. + */ +inline bool +JSScope::methodReadBarrier(JSContext *cx, JSScopeProperty *sprop, jsval *vp) +{ + JS_ASSERT(hasMethodBarrier()); + JS_ASSERT(hasProperty(sprop)); + JS_ASSERT(sprop->isMethod()); + JS_ASSERT(sprop->methodValue() == *vp); + JS_ASSERT(object->getClass() == &js_ObjectClass); + + JSObject *funobj = JSVAL_TO_OBJECT(*vp); + JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj); + JS_ASSERT(FUN_OBJECT(fun) == funobj && FUN_NULL_CLOSURE(fun)); + + funobj = js_CloneFunctionObject(cx, fun, OBJ_GET_PARENT(cx, funobj)); + if (!funobj) + return false; + *vp = OBJECT_TO_JSVAL(funobj); + return js_SetPropertyHelper(cx, object, sprop->id, 0, vp); +} + +inline bool +JSScope::methodWriteBarrier(JSContext *cx, JSScopeProperty *sprop, jsval v) +{ + if (flags & (BRANDED | METHOD_BARRIER)) { + jsval prev = LOCKED_OBJ_GET_SLOT(object, sprop->slot); + + if (prev != v && VALUE_IS_FUNCTION(cx, prev)) + return methodShapeChange(cx, sprop, v); + } + return true; +} + +inline bool +JSScope::methodWriteBarrier(JSContext *cx, uint32 slot, jsval v) +{ + if (flags & (BRANDED | METHOD_BARRIER)) { + jsval prev = LOCKED_OBJ_GET_SLOT(object, slot); + + if (prev != v && VALUE_IS_FUNCTION(cx, prev)) + return methodShapeChange(cx, slot, v); + } + return true; +} + +inline void +JSScope::trace(JSTracer *trc) +{ + JSContext *cx = trc->context; + JSScopeProperty *sprop = lastProp; + uint8 regenFlag = cx->runtime->gcRegenShapesScopeFlag; + if (IS_GC_MARKING_TRACER(trc) && cx->runtime->gcRegenShapes && !hasRegenFlag(regenFlag)) { + /* + * Either this scope has its own shape, which must be regenerated, or + * it must have the same shape as lastProp. + */ + uint32 newShape; + + if (sprop) { + if (!(sprop->flags & SPROP_FLAG_SHAPE_REGEN)) { + sprop->shape = js_RegenerateShapeForGC(cx); + sprop->flags |= SPROP_FLAG_SHAPE_REGEN; + } + newShape = sprop->shape; + } + if (!sprop || hasOwnShape()) { + newShape = js_RegenerateShapeForGC(cx); + JS_ASSERT_IF(sprop, newShape != sprop->shape); + } + shape = newShape; + flags ^= JSScope::SHAPE_REGEN; + + /* Also regenerate the shapes of empty scopes, in case they are not shared. */ + for (JSScope *empty = emptyScope; + empty && !empty->hasRegenFlag(regenFlag); + empty = empty->emptyScope) { + empty->shape = js_RegenerateShapeForGC(cx); + empty->flags ^= JSScope::SHAPE_REGEN; + } + } + if (sprop) { + JS_ASSERT(hasProperty(sprop)); + + /* Trace scope's property tree ancestor line. */ + do { + sprop->trace(trc); + } while ((sprop = sprop->parent) != NULL); + } +} + +#endif /* jsscopeinlines_h___ */ diff --git a/ape-server/deps/js/src/jsscript.cpp b/ape-server/deps/js/src/jsscript.cpp new file mode 100755 index 0000000..9b7d2ba --- /dev/null +++ b/ape-server/deps/js/src/jsscript.cpp @@ -0,0 +1,2038 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * JS script operations. + */ +#include +#include "jstypes.h" +#include "jsstdint.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jsprf.h" +#include "jsapi.h" +#include "jsatom.h" +#include "jscntxt.h" +#include "jsversion.h" +#include "jsdbgapi.h" +#include "jsemit.h" +#include "jsfun.h" +#include "jsinterp.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsopcode.h" +#include "jsparse.h" +#include "jsscope.h" +#include "jsscript.h" +#include "jstracer.h" +#if JS_HAS_XDR +#include "jsxdrapi.h" +#endif + +#include "jsscriptinlines.h" + +#if JS_HAS_SCRIPT_OBJECT + +static const char js_script_exec_str[] = "Script.prototype.exec"; +static const char js_script_compile_str[] = "Script.prototype.compile"; + +/* + * This routine requires that obj has been locked previously. + */ +static jsint +GetScriptExecDepth(JSContext *cx, JSObject *obj) +{ + jsval v; + + JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj)); + v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_START(&js_ScriptClass)); + return JSVAL_IS_VOID(v) ? 0 : JSVAL_TO_INT(v); +} + +static void +AdjustScriptExecDepth(JSContext *cx, JSObject *obj, jsint delta) +{ + jsint execDepth; + + JS_LOCK_OBJ(cx, obj); + execDepth = GetScriptExecDepth(cx, obj); + LOCKED_OBJ_SET_SLOT(obj, JSSLOT_START(&js_ScriptClass), + INT_TO_JSVAL(execDepth + delta)); + JS_UNLOCK_OBJ(cx, obj); +} + +#if JS_HAS_TOSOURCE +static JSBool +script_toSource(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *obj; + uint32 indent; + JSScript *script; + size_t i, j, k, n; + char buf[16]; + jschar *s, *t; + JSString *str; + + obj = JS_THIS_OBJECT(cx, vp); + if (!JS_InstanceOf(cx, obj, &js_ScriptClass, vp + 2)) + return JS_FALSE; + + indent = 0; + if (argc != 0) { + indent = js_ValueToECMAUint32(cx, &vp[2]); + if (JSVAL_IS_NULL(vp[2])) + return JS_FALSE; + } + + script = (JSScript *) obj->getPrivate(); + + /* Let n count the source string length, j the "front porch" length. */ + j = JS_snprintf(buf, sizeof buf, "(new %s(", js_ScriptClass.name); + n = j + 2; + if (!script) { + /* Let k count the constructor argument string length. */ + k = 0; + s = NULL; /* quell GCC overwarning */ + } else { + str = JS_DecompileScript(cx, script, "Script.prototype.toSource", + (uintN)indent); + if (!str) + return JS_FALSE; + str = js_QuoteString(cx, str, '\''); + if (!str) + return JS_FALSE; + const jschar *cs; + str->getCharsAndLength(cs, k); + s = const_cast(cs); + n += k; + } + + /* Allocate the source string and copy into it. */ + t = (jschar *) cx->malloc((n + 1) * sizeof(jschar)); + if (!t) + return JS_FALSE; + for (i = 0; i < j; i++) + t[i] = buf[i]; + for (j = 0; j < k; i++, j++) + t[i] = s[j]; + t[i++] = ')'; + t[i++] = ')'; + t[i] = 0; + + /* Create and return a JS string for t. */ + str = JS_NewUCString(cx, t, n); + if (!str) { + cx->free(t); + return JS_FALSE; + } + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; +} +#endif /* JS_HAS_TOSOURCE */ + +static JSBool +script_toString(JSContext *cx, uintN argc, jsval *vp) +{ + uint32 indent; + JSObject *obj; + JSScript *script; + JSString *str; + + indent = 0; + if (argc != 0) { + indent = js_ValueToECMAUint32(cx, &vp[2]); + if (JSVAL_IS_NULL(vp[2])) + return JS_FALSE; + } + + obj = JS_THIS_OBJECT(cx, vp); + if (!JS_InstanceOf(cx, obj, &js_ScriptClass, vp + 2)) + return JS_FALSE; + script = (JSScript *) obj->getPrivate(); + if (!script) { + *vp = STRING_TO_JSVAL(cx->runtime->emptyString); + return JS_TRUE; + } + + str = JS_DecompileScript(cx, script, "Script.prototype.toString", + (uintN)indent); + if (!str) + return JS_FALSE; + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +script_compile_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + JSObject *scopeobj; + JSScript *script, *oldscript; + JSStackFrame *caller; + const char *file; + uintN line; + JSPrincipals *principals; + jsint execDepth; + + /* Make sure obj is a Script object. */ + if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) + return JS_FALSE; + + /* If no args, leave private undefined and return early. */ + if (argc == 0) + goto out; + + /* Otherwise, the first arg is the script source to compile. */ + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); + + scopeobj = NULL; + if (argc >= 2) { + if (!js_ValueToObject(cx, argv[1], &scopeobj)) + return JS_FALSE; + argv[1] = OBJECT_TO_JSVAL(scopeobj); + } + + /* Compile using the caller's scope chain, which js_Invoke passes to fp. */ + caller = js_GetScriptedCaller(cx, NULL); + JS_ASSERT(!caller || cx->fp->scopeChain == caller->scopeChain); + + if (caller) { + if (!scopeobj) { + scopeobj = js_GetScopeChain(cx, caller); + if (!scopeobj) + return JS_FALSE; + } + + principals = JS_EvalFramePrincipals(cx, cx->fp, caller); + file = js_ComputeFilename(cx, caller, principals, &line); + } else { + file = NULL; + line = 0; + principals = NULL; + } + + /* Ensure we compile this script with the right (inner) principals. */ + scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_script_compile_str); + if (!scopeobj) + return JS_FALSE; + + /* + * Compile the new script using the caller's scope chain, a la eval(). + * Unlike jsobj.c:obj_eval, however, we do not pass TCF_COMPILE_N_GO in + * tcflags and use NULL for the callerFrame argument, because compilation + * is here separated from execution, and the run-time scope chain may not + * match the compile-time. TCF_COMPILE_N_GO is tested in jsemit.c and + * jsparse.c to optimize based on identity of run- and compile-time scope. + */ + script = JSCompiler::compileScript(cx, scopeobj, NULL, principals, + TCF_NEED_MUTABLE_SCRIPT, + str->chars(), str->length(), + NULL, file, line); + if (!script) + return JS_FALSE; + + JS_LOCK_OBJ(cx, obj); + execDepth = GetScriptExecDepth(cx, obj); + + /* + * execDepth must be 0 to allow compilation here, otherwise the JSScript + * struct can be released while running. + */ + if (execDepth > 0) { + JS_UNLOCK_OBJ(cx, obj); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_COMPILE_EXECED_SCRIPT); + return JS_FALSE; + } + + /* Swap script for obj's old script, if any. */ + oldscript = (JSScript*) obj->getPrivate(); + obj->setPrivate(script); + JS_UNLOCK_OBJ(cx, obj); + + if (oldscript) + js_DestroyScript(cx, oldscript); + + script->u.object = obj; + js_CallNewScriptHook(cx, script, NULL); + +out: + /* Return the object. */ + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static JSBool +script_compile(JSContext *cx, uintN argc, jsval *vp) +{ + return script_compile_sub(cx, JS_THIS_OBJECT(cx, vp), argc, vp + 2, vp); +} + +static JSBool +script_exec_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSObject *scopeobj; + JSStackFrame *caller; + JSPrincipals *principals; + JSScript *script; + JSBool ok; + + if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) + return JS_FALSE; + + scopeobj = NULL; + if (argc != 0) { + if (!js_ValueToObject(cx, argv[0], &scopeobj)) + return JS_FALSE; + argv[0] = OBJECT_TO_JSVAL(scopeobj); + } + + /* + * Emulate eval() by using caller's this, var object, sharp array, etc., + * all propagated by js_Execute via a non-null fourth (down) argument to + * js_Execute. If there is no scripted caller, js_Execute uses its second + * (chain) argument to set the exec frame's varobj, thisv, and scopeChain. + * + * Unlike eval, which the compiler detects, Script.prototype.exec may be + * called from a lightweight function, or even from native code (in which + * case fp->varobj and fp->scopeChain are null). If exec is called from + * a lightweight function, we will need to get a Call object representing + * its frame, to act as the var object and scope chain head. + */ + caller = js_GetScriptedCaller(cx, NULL); + if (caller && !caller->varobj) { + /* Called from a lightweight function. */ + JS_ASSERT(caller->fun && !JSFUN_HEAVYWEIGHT_TEST(caller->fun->flags)); + + /* Scope chain links from Call object to caller's scope chain. */ + if (!js_GetCallObject(cx, caller)) + return JS_FALSE; + } + + if (!scopeobj) { + /* No scope object passed in: try to use the caller's scope chain. */ + if (caller) { + /* + * Load caller->scopeChain after the conditional js_GetCallObject + * call above, which resets scopeChain as well as varobj. + */ + scopeobj = js_GetScopeChain(cx, caller); + if (!scopeobj) + return JS_FALSE; + } else { + /* + * Called from native code, so we don't know what scope object to + * use. We could use the caller's scope chain (see above), but Script.prototype.exec + * might be a shared/sealed "superglobal" method. A more general + * approach would use cx->globalObject, which will be the same as + * exec.__parent__ in the non-superglobal case. In the superglobal + * case it's the right object: the global, not the superglobal. + */ + scopeobj = cx->globalObject; + } + } + + scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_script_exec_str); + if (!scopeobj) + return JS_FALSE; + + /* Keep track of nesting depth for the script. */ + AdjustScriptExecDepth(cx, obj, 1); + + /* Must get to out label after this */ + script = (JSScript *) obj->getPrivate(); + if (!script) { + ok = JS_FALSE; + goto out; + } + + /* Belt-and-braces: check that this script object has access to scopeobj. */ + principals = script->principals; + ok = js_CheckPrincipalsAccess(cx, scopeobj, principals, + CLASS_ATOM(cx, Script)); + if (!ok) + goto out; + + ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval); + +out: + AdjustScriptExecDepth(cx, obj, -1); + return ok; +} + +static JSBool +script_exec(JSContext *cx, uintN argc, jsval *vp) +{ + return script_exec_sub(cx, JS_THIS_OBJECT(cx, vp), argc, vp + 2, vp); +} + +#endif /* JS_HAS_SCRIPT_OBJECT */ + +static const jsbytecode emptyScriptCode[] = {JSOP_STOP, SRC_NULL}; + +/* static */ const JSScript JSScript::emptyScriptConst = { + const_cast(emptyScriptCode), + 1, JSVERSION_DEFAULT, 0, 0, 0, 0, 0, true, false, false, false, + const_cast(emptyScriptCode), + {0, NULL}, NULL, 0, 0, 0, NULL +}; + +#if JS_HAS_XDR + +JSBool +js_XDRScript(JSXDRState *xdr, JSScript **scriptp, bool needMutableScript, + JSBool *hasMagic) +{ + JSContext *cx; + JSScript *script, *oldscript; + JSBool ok; + jsbytecode *code; + uint32 length, lineno, nslots, magic; + uint32 natoms, nsrcnotes, ntrynotes, nobjects, nupvars, nregexps, i; + uint32 prologLength, version; + JSTempValueRooter tvr; + JSPrincipals *principals; + uint32 encodeable; + JSBool filenameWasSaved; + jssrcnote *notes, *sn; + JSSecurityCallbacks *callbacks; + + cx = xdr->cx; + script = *scriptp; + nsrcnotes = ntrynotes = natoms = nobjects = nupvars = nregexps = 0; + filenameWasSaved = JS_FALSE; + notes = NULL; + + if (xdr->mode == JSXDR_ENCODE) + magic = JSXDR_MAGIC_SCRIPT_CURRENT; + if (!JS_XDRUint32(xdr, &magic)) + return JS_FALSE; + if (magic != JSXDR_MAGIC_SCRIPT_CURRENT) { + /* We do not provide binary compatibility with older scripts. */ + if (!hasMagic) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_SCRIPT_MAGIC); + return JS_FALSE; + } + *hasMagic = JS_FALSE; + return JS_TRUE; + } + if (hasMagic) + *hasMagic = JS_TRUE; + + /* + * Since the shortest possible script has JSOP_STOP as its only bytecode, + * encode only the length 0 for the emptyScript singleton, and return the + * emptyScript instead of a new script when decoding a script of length 0. + */ + if (xdr->mode == JSXDR_ENCODE) + length = (script == JSScript::emptyScript()) ? 0 : script->length; + if (!JS_XDRUint32(xdr, &length)) + return JS_FALSE; + if (length == 0) { + if (xdr->mode == JSXDR_ENCODE) { + JS_ASSERT(*scriptp == JSScript::emptyScript()); + return JS_TRUE; + } + + /* Decoding: check whether we need a mutable empty script. */ + if (cx->debugHooks->newScriptHook) + needMutableScript = true; + if (needMutableScript) { + /* + * We need a mutable empty script but the encoder serialized only + * the shorthand (0 length word) for us. Make a new mutable empty + * script here and return it immediately. + */ + script = js_NewScript(cx, 1, 1, 0, 0, 0, 0, 0); + if (!script) + return JS_FALSE; + + script->version = JSVERSION_DEFAULT; + script->noScriptRval = true; + script->code[0] = JSOP_STOP; + script->code[1] = SRC_NULL; + *scriptp = script; + return JS_TRUE; + } + + *scriptp = JSScript::emptyScript(); + return JS_TRUE; + } + + if (xdr->mode == JSXDR_ENCODE) { + prologLength = script->main - script->code; + JS_ASSERT((int16)script->version != JSVERSION_UNKNOWN); + version = (uint32)script->version | (script->nfixed << 16); + lineno = (uint32)script->lineno; + nslots = (uint32)script->nslots; + nslots = (uint32)((script->staticLevel << 16) | script->nslots); + natoms = (uint32)script->atomMap.length; + + /* Count the srcnotes, keeping notes pointing at the first one. */ + notes = script->notes(); + for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) + continue; + nsrcnotes = sn - notes; + nsrcnotes++; /* room for the terminator */ + + if (script->objectsOffset != 0) + nobjects = script->objects()->length; + if (script->upvarsOffset != 0) + nupvars = script->upvars()->length; + if (script->regexpsOffset != 0) + nregexps = script->regexps()->length; + if (script->trynotesOffset != 0) + ntrynotes = script->trynotes()->length; + } + + if (!JS_XDRUint32(xdr, &prologLength)) + return JS_FALSE; + if (!JS_XDRUint32(xdr, &version)) + return JS_FALSE; + + /* + * To fuse allocations, we need srcnote, atom, objects, upvar, regexp, + * and trynote counts early. + */ + if (!JS_XDRUint32(xdr, &natoms)) + return JS_FALSE; + if (!JS_XDRUint32(xdr, &nsrcnotes)) + return JS_FALSE; + if (!JS_XDRUint32(xdr, &ntrynotes)) + return JS_FALSE; + if (!JS_XDRUint32(xdr, &nobjects)) + return JS_FALSE; + if (!JS_XDRUint32(xdr, &nupvars)) + return JS_FALSE; + if (!JS_XDRUint32(xdr, &nregexps)) + return JS_FALSE; + + if (xdr->mode == JSXDR_DECODE) { + script = js_NewScript(cx, length, nsrcnotes, natoms, nobjects, nupvars, + nregexps, ntrynotes); + if (!script) + return JS_FALSE; + + script->main += prologLength; + script->version = JSVersion(version & 0xffff); + script->nfixed = uint16(version >> 16); + + /* If we know nsrcnotes, we allocated space for notes in script. */ + notes = script->notes(); + *scriptp = script; + JS_PUSH_TEMP_ROOT_SCRIPT(cx, script, &tvr); + } + + /* + * Control hereafter must goto error on failure, in order for the + * DECODE case to destroy script. + */ + oldscript = xdr->script; + code = script->code; + if (xdr->mode == JSXDR_ENCODE) { + code = js_UntrapScriptCode(cx, script); + if (!code) + goto error; + } + + xdr->script = script; + ok = JS_XDRBytes(xdr, (char *) code, length * sizeof(jsbytecode)); + + if (code != script->code) + cx->free(code); + + if (!ok) + goto error; + + if (!JS_XDRBytes(xdr, (char *)notes, nsrcnotes * sizeof(jssrcnote)) || + !JS_XDRCStringOrNull(xdr, (char **)&script->filename) || + !JS_XDRUint32(xdr, &lineno) || + !JS_XDRUint32(xdr, &nslots)) { + goto error; + } + + callbacks = JS_GetSecurityCallbacks(cx); + if (xdr->mode == JSXDR_ENCODE) { + principals = script->principals; + encodeable = callbacks && callbacks->principalsTranscoder; + if (!JS_XDRUint32(xdr, &encodeable)) + goto error; + if (encodeable && + !callbacks->principalsTranscoder(xdr, &principals)) { + goto error; + } + } else { + if (!JS_XDRUint32(xdr, &encodeable)) + goto error; + if (encodeable) { + if (!(callbacks && callbacks->principalsTranscoder)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_CANT_DECODE_PRINCIPALS); + goto error; + } + if (!callbacks->principalsTranscoder(xdr, &principals)) + goto error; + script->principals = principals; + } + } + + if (xdr->mode == JSXDR_DECODE) { + const char *filename = script->filename; + if (filename) { + filename = js_SaveScriptFilename(cx, filename); + if (!filename) + goto error; + cx->free((void *) script->filename); + script->filename = filename; + filenameWasSaved = JS_TRUE; + } + script->lineno = (uintN)lineno; + script->nslots = (uint16)nslots; + script->staticLevel = (uint16)(nslots >> 16); + } + + for (i = 0; i != natoms; ++i) { + if (!js_XDRAtom(xdr, &script->atomMap.vector[i])) + goto error; + } + + /* + * Here looping from 0-to-length to xdr objects is essential. It ensures + * that block objects from the script->objects array will be written and + * restored in the outer-to-inner order. js_XDRBlockObject relies on this + * to restore the parent chain. + */ + for (i = 0; i != nobjects; ++i) { + JSObject **objp = &script->objects()->vector[i]; + uint32 isBlock; + if (xdr->mode == JSXDR_ENCODE) { + JSClass *clasp = STOBJ_GET_CLASS(*objp); + JS_ASSERT(clasp == &js_FunctionClass || + clasp == &js_BlockClass); + isBlock = (clasp == &js_BlockClass) ? 1 : 0; + } + if (!JS_XDRUint32(xdr, &isBlock)) + goto error; + if (isBlock == 0) { + if (!js_XDRFunctionObject(xdr, objp)) + goto error; + } else { + JS_ASSERT(isBlock == 1); + if (!js_XDRBlockObject(xdr, objp)) + goto error; + } + } + for (i = 0; i != nupvars; ++i) { + if (!JS_XDRUint32(xdr, &script->upvars()->vector[i])) + goto error; + } + for (i = 0; i != nregexps; ++i) { + if (!js_XDRRegExpObject(xdr, &script->regexps()->vector[i])) + goto error; + } + + if (ntrynotes != 0) { + /* + * We combine tn->kind and tn->stackDepth when serializing as XDR is not + * efficient when serializing small integer types. + */ + JSTryNote *tn, *tnfirst; + uint32 kindAndDepth; + JS_STATIC_ASSERT(sizeof(tn->kind) == sizeof(uint8)); + JS_STATIC_ASSERT(sizeof(tn->stackDepth) == sizeof(uint16)); + + tnfirst = script->trynotes()->vector; + JS_ASSERT(script->trynotes()->length == ntrynotes); + tn = tnfirst + ntrynotes; + do { + --tn; + if (xdr->mode == JSXDR_ENCODE) { + kindAndDepth = ((uint32)tn->kind << 16) + | (uint32)tn->stackDepth; + } + if (!JS_XDRUint32(xdr, &kindAndDepth) || + !JS_XDRUint32(xdr, &tn->start) || + !JS_XDRUint32(xdr, &tn->length)) { + goto error; + } + if (xdr->mode == JSXDR_DECODE) { + tn->kind = (uint8)(kindAndDepth >> 16); + tn->stackDepth = (uint16)kindAndDepth; + } + } while (tn != tnfirst); + } + + xdr->script = oldscript; + if (xdr->mode == JSXDR_DECODE) + JS_POP_TEMP_ROOT(cx, &tvr); + return JS_TRUE; + + error: + if (xdr->mode == JSXDR_DECODE) { + JS_POP_TEMP_ROOT(cx, &tvr); + if (script->filename && !filenameWasSaved) { + cx->free((void *) script->filename); + script->filename = NULL; + } + js_DestroyScript(cx, script); + *scriptp = NULL; + } + xdr->script = oldscript; + return JS_FALSE; +} + +#if JS_HAS_SCRIPT_OBJECT && JS_HAS_XDR_FREEZE_THAW +/* + * These cannot be exposed to web content, and chrome does not need them, so + * we take them out of the Mozilla client altogether. Fortunately, there is + * no way to serialize a native function (see fun_xdrObject in jsfun.c). + */ + +static JSBool +script_freeze(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *obj; + JSXDRState *xdr; + JSScript *script; + JSBool ok, hasMagic; + uint32 len; + void *buf; + JSString *str; + + obj = JS_THIS_OBJECT(cx, vp); + if (!JS_InstanceOf(cx, obj, &js_ScriptClass, vp + 2)) + return JS_FALSE; + script = (JSScript *) obj->getPrivate(); + if (!script) + return JS_TRUE; + + /* create new XDR */ + xdr = JS_XDRNewMem(cx, JSXDR_ENCODE); + if (!xdr) + return JS_FALSE; + + /* write */ + ok = js_XDRScript(xdr, &script, false, &hasMagic); + if (!ok) + goto out; + if (!hasMagic) { + *vp = JSVAL_VOID; + goto out; + } + + buf = JS_XDRMemGetData(xdr, &len); + if (!buf) { + ok = JS_FALSE; + goto out; + } + + JS_ASSERT((jsword)buf % sizeof(jschar) == 0); + len /= sizeof(jschar); +#if IS_BIG_ENDIAN + { + jschar *chars; + uint32 i; + + /* Swap bytes in Unichars to keep frozen strings machine-independent. */ + chars = (jschar *)buf; + for (i = 0; i < len; i++) + chars[i] = JSXDR_SWAB16(chars[i]); + } +#endif + str = JS_NewUCStringCopyN(cx, (jschar *)buf, len); + if (!str) { + ok = JS_FALSE; + goto out; + } + + *vp = STRING_TO_JSVAL(str); + +out: + JS_XDRDestroy(xdr); + return ok; +} + +static JSBool +script_thaw(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *obj; + JSXDRState *xdr; + JSString *str; + void *buf; + size_t len; + JSScript *script, *oldscript; + JSBool ok, hasMagic; + jsint execDepth; + + obj = JS_THIS_OBJECT(cx, vp); + if (!JS_InstanceOf(cx, obj, &js_ScriptClass, vp + 2)) + return JS_FALSE; + + if (argc == 0) + return JS_TRUE; + str = js_ValueToString(cx, vp[2]); + if (!str) + return JS_FALSE; + vp[2] = STRING_TO_JSVAL(str); + + /* create new XDR */ + xdr = JS_XDRNewMem(cx, JSXDR_DECODE); + if (!xdr) + return JS_FALSE; + + const jschar *cs; + str->getCharsAndLength(cs, len); + buf = const_cast(cs); +#if IS_BIG_ENDIAN + { + jschar *from, *to; + uint32 i; + + /* Swap bytes in Unichars to keep frozen strings machine-independent. */ + from = (jschar *)buf; + to = (jschar *) cx->malloc(len * sizeof(jschar)); + if (!to) { + JS_XDRDestroy(xdr); + return JS_FALSE; + } + for (i = 0; i < len; i++) + to[i] = JSXDR_SWAB16(from[i]); + buf = (char *)to; + } +#endif + len *= sizeof(jschar); + JS_XDRMemSetData(xdr, buf, len); + + /* XXXbe should magic mismatch be error, or false return value? */ + ok = js_XDRScript(xdr, &script, true, &hasMagic); + if (!ok) + goto out; + if (!hasMagic) { + *vp = JSVAL_FALSE; + goto out; + } + + JS_LOCK_OBJ(cx, obj); + execDepth = GetScriptExecDepth(cx, obj); + + /* + * execDepth must be 0 to allow compilation here, otherwise the JSScript + * struct can be released while running. + */ + if (execDepth > 0) { + JS_UNLOCK_OBJ(cx, obj); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_COMPILE_EXECED_SCRIPT); + goto out; + } + + /* Swap script for obj's old script, if any. */ + oldscript = (JSScript *) obj->getPrivate(); + obj->setPrivate(script); + JS_UNLOCK_OBJ(cx, obj); + + if (oldscript) + js_DestroyScript(cx, oldscript); + + script->u.object = obj; + js_CallNewScriptHook(cx, script, NULL); + +out: + /* + * We reset the buffer to be NULL so that it doesn't free the chars + * memory owned by str (vp[2]). + */ + JS_XDRMemSetData(xdr, NULL, 0); + JS_XDRDestroy(xdr); +#if IS_BIG_ENDIAN + cx->free(buf); +#endif + *vp = JSVAL_TRUE; + return ok; +} + +static const char js_thaw_str[] = "thaw"; + +#endif /* JS_HAS_SCRIPT_OBJECT && JS_HAS_XDR_FREEZE_THAW */ +#endif /* JS_HAS_XDR */ + +#if JS_HAS_SCRIPT_OBJECT + +static JSFunctionSpec script_methods[] = { +#if JS_HAS_TOSOURCE + JS_FN(js_toSource_str, script_toSource, 0,0), +#endif + JS_FN(js_toString_str, script_toString, 0,0), + JS_FN("compile", script_compile, 2,0), + JS_FN("exec", script_exec, 1,0), +#if JS_HAS_XDR_FREEZE_THAW + JS_FN("freeze", script_freeze, 0,0), + JS_FN(js_thaw_str, script_thaw, 1,0), +#endif /* JS_HAS_XDR_FREEZE_THAW */ + JS_FS_END +}; + +#endif /* JS_HAS_SCRIPT_OBJECT */ + +static void +script_finalize(JSContext *cx, JSObject *obj) +{ + JSScript *script = (JSScript *) obj->getPrivate(); + if (script) + js_DestroyScript(cx, script); +} + +static JSBool +script_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ +#if JS_HAS_SCRIPT_OBJECT + return script_exec_sub(cx, JSVAL_TO_OBJECT(argv[-2]), argc, argv, rval); +#else + return JS_FALSE; +#endif +} + +static void +script_trace(JSTracer *trc, JSObject *obj) +{ + JSScript *script = (JSScript *) obj->getPrivate(); + if (script) + js_TraceScript(trc, script); +} + +#if !JS_HAS_SCRIPT_OBJECT +#define JSProto_Script JSProto_Object +#endif + +JS_FRIEND_DATA(JSClass) js_ScriptClass = { + js_Script_str, + JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | + JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Script), + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, script_finalize, + NULL, NULL, script_call, NULL,/*XXXbe xdr*/ + NULL, NULL, JS_CLASS_TRACE(script_trace), NULL +}; + +#if JS_HAS_SCRIPT_OBJECT + +static JSBool +Script(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + /* If not constructing, replace obj with a new Script object. */ + if (!JS_IsConstructing(cx)) { + obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL); + if (!obj) + return JS_FALSE; + + /* + * script_compile_sub does not use rval to root its temporaries so we + * can use it to root obj. + */ + *rval = OBJECT_TO_JSVAL(obj); + } + + if (!JS_SetReservedSlot(cx, obj, 0, INT_TO_JSVAL(0))) + return JS_FALSE; + + return script_compile_sub(cx, obj, argc, argv, rval); +} + +#if JS_HAS_SCRIPT_OBJECT && JS_HAS_XDR_FREEZE_THAW + +static JSBool +script_static_thaw(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *obj; + + obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL); + if (!obj) + return JS_FALSE; + vp[1] = OBJECT_TO_JSVAL(obj); + if (!script_thaw(cx, argc, vp)) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static JSFunctionSpec script_static_methods[] = { + JS_FN(js_thaw_str, script_static_thaw, 1,0), + JS_FS_END +}; + +#else /* !JS_HAS_SCRIPT_OBJECT || !JS_HAS_XDR_FREEZE_THAW */ + +#define script_static_methods NULL + +#endif /* !JS_HAS_SCRIPT_OBJECT || !JS_HAS_XDR_FREEZE_THAW */ + +JSObject * +js_InitScriptClass(JSContext *cx, JSObject *obj) +{ + return JS_InitClass(cx, obj, NULL, &js_ScriptClass, Script, 1, + NULL, script_methods, NULL, script_static_methods); +} + +#endif /* JS_HAS_SCRIPT_OBJECT */ + +/* + * Shared script filename management. + */ +static int +js_compare_strings(const void *k1, const void *k2) +{ + return strcmp((const char *) k1, (const char *) k2) == 0; +} + +/* NB: This struct overlays JSHashEntry -- see jshash.h, do not reorganize. */ +typedef struct ScriptFilenameEntry { + JSHashEntry *next; /* hash chain linkage */ + JSHashNumber keyHash; /* key hash function result */ + const void *key; /* ptr to filename, below */ + uint32 flags; /* user-defined filename prefix flags */ + JSPackedBool mark; /* GC mark flag */ + char filename[3]; /* two or more bytes, NUL-terminated */ +} ScriptFilenameEntry; + +static void * +js_alloc_table_space(void *priv, size_t size) +{ + return js_malloc(size); +} + +static void +js_free_table_space(void *priv, void *item, size_t size) +{ + js_free(item); +} + +static JSHashEntry * +js_alloc_sftbl_entry(void *priv, const void *key) +{ + size_t nbytes = offsetof(ScriptFilenameEntry, filename) + + strlen((const char *) key) + 1; + + return (JSHashEntry *) js_malloc(JS_MAX(nbytes, sizeof(JSHashEntry))); +} + +static void +js_free_sftbl_entry(void *priv, JSHashEntry *he, uintN flag) +{ + if (flag != HT_FREE_ENTRY) + return; + js_free(he); +} + +static JSHashAllocOps sftbl_alloc_ops = { + js_alloc_table_space, js_free_table_space, + js_alloc_sftbl_entry, js_free_sftbl_entry +}; + +static void +FinishRuntimeScriptState(JSRuntime *rt) +{ + if (rt->scriptFilenameTable) { + JS_HashTableDestroy(rt->scriptFilenameTable); + rt->scriptFilenameTable = NULL; + } +#ifdef JS_THREADSAFE + if (rt->scriptFilenameTableLock) { + JS_DESTROY_LOCK(rt->scriptFilenameTableLock); + rt->scriptFilenameTableLock = NULL; + } +#endif +} + +JSBool +js_InitRuntimeScriptState(JSRuntime *rt) +{ +#ifdef JS_THREADSAFE + JS_ASSERT(!rt->scriptFilenameTableLock); + rt->scriptFilenameTableLock = JS_NEW_LOCK(); + if (!rt->scriptFilenameTableLock) + return JS_FALSE; +#endif + JS_ASSERT(!rt->scriptFilenameTable); + rt->scriptFilenameTable = + JS_NewHashTable(16, JS_HashString, js_compare_strings, NULL, + &sftbl_alloc_ops, NULL); + if (!rt->scriptFilenameTable) { + FinishRuntimeScriptState(rt); /* free lock if threadsafe */ + return JS_FALSE; + } + JS_INIT_CLIST(&rt->scriptFilenamePrefixes); + return JS_TRUE; +} + +typedef struct ScriptFilenamePrefix { + JSCList links; /* circular list linkage for easy deletion */ + const char *name; /* pointer to pinned ScriptFilenameEntry string */ + size_t length; /* prefix string length, precomputed */ + uint32 flags; /* user-defined flags to inherit from this prefix */ +} ScriptFilenamePrefix; + +void +js_FreeRuntimeScriptState(JSRuntime *rt) +{ + if (!rt->scriptFilenameTable) + return; + + while (!JS_CLIST_IS_EMPTY(&rt->scriptFilenamePrefixes)) { + ScriptFilenamePrefix *sfp = (ScriptFilenamePrefix *) + rt->scriptFilenamePrefixes.next; + JS_REMOVE_LINK(&sfp->links); + js_free(sfp); + } + FinishRuntimeScriptState(rt); +} + +#ifdef DEBUG_brendan +#define DEBUG_SFTBL +#endif +#ifdef DEBUG_SFTBL +size_t sftbl_savings = 0; +#endif + +static ScriptFilenameEntry * +SaveScriptFilename(JSRuntime *rt, const char *filename, uint32 flags) +{ + JSHashTable *table; + JSHashNumber hash; + JSHashEntry **hep; + ScriptFilenameEntry *sfe; + size_t length; + JSCList *head, *link; + ScriptFilenamePrefix *sfp; + + table = rt->scriptFilenameTable; + hash = JS_HashString(filename); + hep = JS_HashTableRawLookup(table, hash, filename); + sfe = (ScriptFilenameEntry *) *hep; +#ifdef DEBUG_SFTBL + if (sfe) + sftbl_savings += strlen(sfe->filename); +#endif + + if (!sfe) { + sfe = (ScriptFilenameEntry *) + JS_HashTableRawAdd(table, hep, hash, filename, NULL); + if (!sfe) + return NULL; + sfe->key = strcpy(sfe->filename, filename); + sfe->flags = 0; + sfe->mark = JS_FALSE; + } + + /* If saving a prefix, add it to the set in rt->scriptFilenamePrefixes. */ + if (flags != 0) { + /* Search in case filename was saved already; we must be idempotent. */ + sfp = NULL; + length = strlen(filename); + for (head = link = &rt->scriptFilenamePrefixes; + link->next != head; + link = link->next) { + /* Lag link behind sfp to insert in non-increasing length order. */ + sfp = (ScriptFilenamePrefix *) link->next; + if (!strcmp(sfp->name, filename)) + break; + if (sfp->length <= length) { + sfp = NULL; + break; + } + sfp = NULL; + } + + if (!sfp) { + /* No such prefix: add one now. */ + sfp = (ScriptFilenamePrefix *) js_malloc(sizeof(ScriptFilenamePrefix)); + if (!sfp) + return NULL; + JS_INSERT_AFTER(&sfp->links, link); + sfp->name = sfe->filename; + sfp->length = length; + sfp->flags = 0; + } + + /* + * Accumulate flags in both sfe and sfp: sfe for later access from the + * JS_GetScriptedCallerFilenameFlags debug-API, and sfp so that longer + * filename entries can inherit by prefix. + */ + sfe->flags |= flags; + sfp->flags |= flags; + } + +#ifdef JS_FUNCTION_METERING + size_t len = strlen(sfe->filename); + if (len >= sizeof rt->lastScriptFilename) + len = sizeof rt->lastScriptFilename - 1; + memcpy(rt->lastScriptFilename, sfe->filename, len); + rt->lastScriptFilename[len] = '\0'; +#endif + + return sfe; +} + +const char * +js_SaveScriptFilename(JSContext *cx, const char *filename) +{ + JSRuntime *rt; + ScriptFilenameEntry *sfe; + JSCList *head, *link; + ScriptFilenamePrefix *sfp; + + rt = cx->runtime; + JS_ACQUIRE_LOCK(rt->scriptFilenameTableLock); + sfe = SaveScriptFilename(rt, filename, 0); + if (!sfe) { + JS_RELEASE_LOCK(rt->scriptFilenameTableLock); + JS_ReportOutOfMemory(cx); + return NULL; + } + + /* + * Try to inherit flags by prefix. We assume there won't be more than a + * few (dozen! ;-) prefixes, so linear search is tolerable. + * XXXbe every time I've assumed that in the JS engine, I've been wrong! + */ + for (head = &rt->scriptFilenamePrefixes, link = head->next; + link != head; + link = link->next) { + sfp = (ScriptFilenamePrefix *) link; + if (!strncmp(sfp->name, filename, sfp->length)) { + sfe->flags |= sfp->flags; + break; + } + } + JS_RELEASE_LOCK(rt->scriptFilenameTableLock); + return sfe->filename; +} + +const char * +js_SaveScriptFilenameRT(JSRuntime *rt, const char *filename, uint32 flags) +{ + ScriptFilenameEntry *sfe; + + /* This may be called very early, via the jsdbgapi.h entry point. */ + if (!rt->scriptFilenameTable && !js_InitRuntimeScriptState(rt)) + return NULL; + + JS_ACQUIRE_LOCK(rt->scriptFilenameTableLock); + sfe = SaveScriptFilename(rt, filename, flags); + JS_RELEASE_LOCK(rt->scriptFilenameTableLock); + if (!sfe) + return NULL; + + return sfe->filename; +} + +/* + * Back up from a saved filename by its offset within its hash table entry. + */ +#define FILENAME_TO_SFE(fn) \ + ((ScriptFilenameEntry *) ((fn) - offsetof(ScriptFilenameEntry, filename))) + +/* + * The sfe->key member, redundant given sfe->filename but required by the old + * jshash.c code, here gives us a useful sanity check. This assertion will + * very likely botch if someone tries to mark a string that wasn't allocated + * as an sfe->filename. + */ +#define ASSERT_VALID_SFE(sfe) JS_ASSERT((sfe)->key == (sfe)->filename) + +uint32 +js_GetScriptFilenameFlags(const char *filename) +{ + ScriptFilenameEntry *sfe; + + sfe = FILENAME_TO_SFE(filename); + ASSERT_VALID_SFE(sfe); + return sfe->flags; +} + +void +js_MarkScriptFilename(const char *filename) +{ + ScriptFilenameEntry *sfe; + + sfe = FILENAME_TO_SFE(filename); + ASSERT_VALID_SFE(sfe); + sfe->mark = JS_TRUE; +} + +static intN +js_script_filename_marker(JSHashEntry *he, intN i, void *arg) +{ + ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he; + + sfe->mark = JS_TRUE; + return HT_ENUMERATE_NEXT; +} + +void +js_MarkScriptFilenames(JSRuntime *rt, JSBool keepAtoms) +{ + JSCList *head, *link; + ScriptFilenamePrefix *sfp; + + if (!rt->scriptFilenameTable) + return; + + if (keepAtoms) { + JS_HashTableEnumerateEntries(rt->scriptFilenameTable, + js_script_filename_marker, + rt); + } + for (head = &rt->scriptFilenamePrefixes, link = head->next; + link != head; + link = link->next) { + sfp = (ScriptFilenamePrefix *) link; + js_MarkScriptFilename(sfp->name); + } +} + +static intN +js_script_filename_sweeper(JSHashEntry *he, intN i, void *arg) +{ + ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he; + + if (!sfe->mark) + return HT_ENUMERATE_REMOVE; + sfe->mark = JS_FALSE; + return HT_ENUMERATE_NEXT; +} + +void +js_SweepScriptFilenames(JSRuntime *rt) +{ + if (!rt->scriptFilenameTable) + return; + + /* + * JS_HashTableEnumerateEntries shrinks the table if many entries are + * removed preventing wasting memory on a too sparse table. + */ + JS_HashTableEnumerateEntries(rt->scriptFilenameTable, + js_script_filename_sweeper, + rt); +#ifdef DEBUG_notme +#ifdef DEBUG_SFTBL + printf("script filename table savings so far: %u\n", sftbl_savings); +#endif +#endif +} + +/* + * JSScript data structures memory alignment: + * + * JSScript + * JSObjectArray script objects' descriptor if JSScript.objectsOffset != 0, + * use script->objects() to access it. + * JSObjectArray script regexps' descriptor if JSScript.regexpsOffset != 0, + * use script->regexps() to access it. + * JSTryNoteArray script try notes' descriptor if JSScript.tryNotesOffset + * != 0, use script->trynotes() to access it. + * JSAtom *a[] array of JSScript.atomMap.length atoms pointed by + * JSScript.atomMap.vector if any. + * JSObject *o[] array of script->objects()->length objects if any + * pointed by script->objects()->vector. + * JSObject *r[] array of script->regexps()->length regexps if any + * pointed by script->regexps()->vector. + * JSTryNote t[] array of script->trynotes()->length try notes if any + * pointed by script->trynotes()->vector. + * jsbytecode b[] script bytecode pointed by JSScript.code. + * jssrcnote s[] script source notes, use script->notes() to access it + * + * The alignment avoids gaps between entries as alignment requirement for each + * subsequent structure or array is the same or divides the alignment + * requirement for the previous one. + * + * The followings asserts checks that assuming that the alignment requirement + * for JSObjectArray and JSTryNoteArray are sizeof(void *) and for JSTryNote + * it is sizeof(uint32) as the structure consists of 3 uint32 fields. + */ +JS_STATIC_ASSERT(sizeof(JSScript) % sizeof(void *) == 0); +JS_STATIC_ASSERT(sizeof(JSObjectArray) % sizeof(void *) == 0); +JS_STATIC_ASSERT(sizeof(JSTryNoteArray) == sizeof(JSObjectArray)); +JS_STATIC_ASSERT(sizeof(JSAtom *) == sizeof(JSObject *)); +JS_STATIC_ASSERT(sizeof(JSObject *) % sizeof(uint32) == 0); +JS_STATIC_ASSERT(sizeof(JSTryNote) == 3 * sizeof(uint32)); +JS_STATIC_ASSERT(sizeof(uint32) % sizeof(jsbytecode) == 0); +JS_STATIC_ASSERT(sizeof(jsbytecode) % sizeof(jssrcnote) == 0); + +/* + * Check that uint8 offset for object, upvar, regexp, and try note arrays is + * sufficient. + */ +JS_STATIC_ASSERT(sizeof(JSScript) + 2 * sizeof(JSObjectArray) + + sizeof(JSUpvarArray) < JS_BIT(8)); + +JSScript * +js_NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natoms, + uint32 nobjects, uint32 nupvars, uint32 nregexps, + uint32 ntrynotes) +{ + size_t size, vectorSize; + JSScript *script; + uint8 *cursor; + + size = sizeof(JSScript) + + sizeof(JSAtom *) * natoms + + length * sizeof(jsbytecode) + + nsrcnotes * sizeof(jssrcnote); + if (nobjects != 0) + size += sizeof(JSObjectArray) + nobjects * sizeof(JSObject *); + if (nupvars != 0) + size += sizeof(JSUpvarArray) + nupvars * sizeof(uint32); + if (nregexps != 0) + size += sizeof(JSObjectArray) + nregexps * sizeof(JSObject *); + if (ntrynotes != 0) + size += sizeof(JSTryNoteArray) + ntrynotes * sizeof(JSTryNote); + + script = (JSScript *) cx->malloc(size); + if (!script) + return NULL; + memset(script, 0, sizeof(JSScript)); + script->length = length; + script->version = cx->version; + + cursor = (uint8 *)script + sizeof(JSScript); + if (nobjects != 0) { + script->objectsOffset = (uint8)(cursor - (uint8 *)script); + cursor += sizeof(JSObjectArray); + } + if (nupvars != 0) { + script->upvarsOffset = (uint8)(cursor - (uint8 *)script); + cursor += sizeof(JSUpvarArray); + } + if (nregexps != 0) { + script->regexpsOffset = (uint8)(cursor - (uint8 *)script); + cursor += sizeof(JSObjectArray); + } + if (ntrynotes != 0) { + script->trynotesOffset = (uint8)(cursor - (uint8 *)script); + cursor += sizeof(JSTryNoteArray); + } + + if (natoms != 0) { + script->atomMap.length = natoms; + script->atomMap.vector = (JSAtom **)cursor; + vectorSize = natoms * sizeof(script->atomMap.vector[0]); + + /* + * Clear object map's vector so the GC tracing can run when not yet + * all atoms are copied to the array. + */ + memset(cursor, 0, vectorSize); + cursor += vectorSize; + } + + if (nobjects != 0) { + script->objects()->length = nobjects; + script->objects()->vector = (JSObject **)cursor; + vectorSize = nobjects * sizeof(script->objects()->vector[0]); + memset(cursor, 0, vectorSize); + cursor += vectorSize; + } + + if (nregexps != 0) { + script->regexps()->length = nregexps; + script->regexps()->vector = (JSObject **)cursor; + vectorSize = nregexps * sizeof(script->regexps()->vector[0]); + memset(cursor, 0, vectorSize); + cursor += vectorSize; + } + + if (ntrynotes != 0) { + script->trynotes()->length = ntrynotes; + script->trynotes()->vector = (JSTryNote *)cursor; + vectorSize = ntrynotes * sizeof(script->trynotes()->vector[0]); +#ifdef DEBUG + memset(cursor, 0, vectorSize); +#endif + cursor += vectorSize; + } + + /* + * NB: We allocate the vector of uint32 upvar cookies after all vectors of + * pointers, to avoid misalignment on 64-bit platforms. See bug 514645. + */ + if (nupvars != 0) { + script->upvars()->length = nupvars; + script->upvars()->vector = (uint32 *)cursor; + vectorSize = nupvars * sizeof(script->upvars()->vector[0]); + memset(cursor, 0, vectorSize); + cursor += vectorSize; + } + + script->code = script->main = (jsbytecode *)cursor; + JS_ASSERT(cursor + + length * sizeof(jsbytecode) + + nsrcnotes * sizeof(jssrcnote) == + (uint8 *)script + size); + +#ifdef CHECK_SCRIPT_OWNER + script->owner = cx->thread; +#endif + return script; +} + +JSScript * +js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg) +{ + uint32 mainLength, prologLength, nsrcnotes, nfixed; + JSScript *script; + const char *filename; + JSFunction *fun; + + /* The counts of indexed things must be checked during code generation. */ + JS_ASSERT(cg->atomList.count <= INDEX_LIMIT); + JS_ASSERT(cg->objectList.length <= INDEX_LIMIT); + JS_ASSERT(cg->regexpList.length <= INDEX_LIMIT); + + mainLength = CG_OFFSET(cg); + prologLength = CG_PROLOG_OFFSET(cg); + + if (prologLength + mainLength <= 3) { + /* + * Check very short scripts to see whether they are "empty" and return + * the const empty-script singleton if so. We are deliberately flexible + * about whether JSOP_TRACE is in the prolog. + */ + jsbytecode *pc = prologLength ? CG_PROLOG_BASE(cg) : CG_BASE(cg); + + if (JSOp(*pc) == JSOP_TRACE) { + ++pc; + if (pc == CG_PROLOG_BASE(cg) + prologLength) + pc = CG_BASE(cg); + } + if ((cg->flags & TCF_NO_SCRIPT_RVAL) && JSOp(*pc) == JSOP_FALSE) + ++pc; + + if (JSOp(*pc) == JSOP_STOP && + !cx->debugHooks->newScriptHook && + !(cg->flags & TCF_NEED_MUTABLE_SCRIPT)) + { + /* + * We can probably use the immutable empty script singleton, just + * one hard case (nupvars != 0) may stand in our way. + */ + JSScript *empty = JSScript::emptyScript(); + + if (cg->flags & TCF_IN_FUNCTION) { + fun = cg->fun; + JS_ASSERT(FUN_INTERPRETED(fun) && !FUN_SCRIPT(fun)); + if (fun->u.i.nupvars != 0) { + /* + * FIXME: upvar uses that were all optimized away may leave + * fun->u.i.nupvars non-zero, and since that count is added + * into fun->countLocalNames() in order to discriminate the + * fun->u.i.names union, we cannot force fun->u.i.nupvars + * to 0 to match JSScript::emptyScript()->upvars()->length. + * So we skip the empty script optimization. + * + * Fixing this requires the compiler to track upvar uses as + * it analyzes and optimizes closures, and subsequently as + * the emitter performs useless expression elimination. + */ + goto skip_empty; + } + js_FreezeLocalNames(cx, fun); + fun->u.i.script = empty; + } + + JS_RUNTIME_METER(cx->runtime, liveEmptyScripts); + JS_RUNTIME_METER(cx->runtime, totalEmptyScripts); + return empty; + } + } + + skip_empty: + CG_COUNT_FINAL_SRCNOTES(cg, nsrcnotes); + script = js_NewScript(cx, prologLength + mainLength, nsrcnotes, + cg->atomList.count, cg->objectList.length, + cg->upvarList.count, cg->regexpList.length, + cg->ntrynotes); + if (!script) + return NULL; + + /* Now that we have script, error control flow must go to label bad. */ + script->main += prologLength; + memcpy(script->code, CG_PROLOG_BASE(cg), prologLength * sizeof(jsbytecode)); + memcpy(script->main, CG_BASE(cg), mainLength * sizeof(jsbytecode)); + nfixed = (cg->flags & TCF_IN_FUNCTION) + ? cg->fun->u.i.nvars + : cg->ngvars + cg->regexpList.length + cg->sharpSlots(); + JS_ASSERT(nfixed < SLOTNO_LIMIT); + script->nfixed = (uint16) nfixed; + js_InitAtomMap(cx, &script->atomMap, &cg->atomList); + + filename = cg->compiler->tokenStream.filename; + if (filename) { + script->filename = js_SaveScriptFilename(cx, filename); + if (!script->filename) + goto bad; + } + script->lineno = cg->firstLine; + if (script->nfixed + cg->maxStackDepth >= JS_BIT(16)) { + js_ReportCompileErrorNumber(cx, CG_TS(cg), NULL, JSREPORT_ERROR, + JSMSG_NEED_DIET, "script"); + goto bad; + } + script->nslots = script->nfixed + cg->maxStackDepth; + script->staticLevel = cg->staticLevel; + script->principals = cg->compiler->principals; + if (script->principals) + JSPRINCIPALS_HOLD(cx, script->principals); + + if (!js_FinishTakingSrcNotes(cx, cg, script->notes())) + goto bad; + if (cg->ntrynotes != 0) + js_FinishTakingTryNotes(cg, script->trynotes()); + if (cg->objectList.length != 0) + cg->objectList.finish(script->objects()); + if (cg->regexpList.length != 0) + cg->regexpList.finish(script->regexps()); + if (cg->flags & TCF_NO_SCRIPT_RVAL) + script->noScriptRval = true; + if (cg->hasSharps()) + script->hasSharps = true; + if (cg->flags & TCF_STRICT_MODE_CODE) + script->strictModeCode = true; + + if (cg->upvarList.count != 0) { + JS_ASSERT(cg->upvarList.count <= cg->upvarMap.length); + memcpy(script->upvars()->vector, cg->upvarMap.vector, + cg->upvarList.count * sizeof(uint32)); + cg->upvarList.clear(); + cx->free(cg->upvarMap.vector); + cg->upvarMap.vector = NULL; + } + + /* + * We initialize fun->u.script to be the script constructed above + * so that the debugger has a valid FUN_SCRIPT(fun). + */ + fun = NULL; + if (cg->flags & TCF_IN_FUNCTION) { + fun = cg->fun; + JS_ASSERT(FUN_INTERPRETED(fun) && !FUN_SCRIPT(fun)); + if (script->upvarsOffset != 0) + JS_ASSERT(script->upvars()->length == fun->u.i.nupvars); + else + fun->u.i.nupvars = 0; + + js_FreezeLocalNames(cx, fun); + fun->u.i.script = script; +#ifdef CHECK_SCRIPT_OWNER + script->owner = NULL; +#endif + if (cg->flags & TCF_FUN_HEAVYWEIGHT) + fun->flags |= JSFUN_HEAVYWEIGHT; + } + + /* Tell the debugger about this compiled script. */ + js_CallNewScriptHook(cx, script, fun); + JS_RUNTIME_METER(cx->runtime, liveScripts); + JS_RUNTIME_METER(cx->runtime, totalScripts); + return script; + +bad: + js_DestroyScript(cx, script); + return NULL; +} + +JS_FRIEND_API(void) +js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun) +{ + JS_ASSERT(script != JSScript::emptyScript()); + + JSNewScriptHook hook; + + hook = cx->debugHooks->newScriptHook; + if (hook) { + JS_KEEP_ATOMS(cx->runtime); + hook(cx, script->filename, script->lineno, script, fun, + cx->debugHooks->newScriptHookData); + JS_UNKEEP_ATOMS(cx->runtime); + } +} + +JS_FRIEND_API(void) +js_CallDestroyScriptHook(JSContext *cx, JSScript *script) +{ + JS_ASSERT(script != JSScript::emptyScript()); + + JSDestroyScriptHook hook; + + hook = cx->debugHooks->destroyScriptHook; + if (hook) + hook(cx, script, cx->debugHooks->destroyScriptHookData); +} + +void +js_DestroyScript(JSContext *cx, JSScript *script) +{ + if (script == JSScript::emptyScript()) { + JS_RUNTIME_UNMETER(cx->runtime, liveEmptyScripts); + return; + } + + js_CallDestroyScriptHook(cx, script); + JS_ClearScriptTraps(cx, script); + + if (script->principals) + JSPRINCIPALS_DROP(cx, script->principals); + + if (JS_GSN_CACHE(cx).code == script->code) + JS_PURGE_GSN_CACHE(cx); + + /* + * Worry about purging the property cache and any compiled traces related + * to its bytecode if this script is being destroyed from JS_DestroyScript + * or equivalent according to a mandatory "New/Destroy" protocol. + * + * The GC purges all property caches when regenerating shapes upon shape + * generator overflow, so no need in that event to purge just the entries + * for this script. + * + * The GC purges trace-JITted code on every GC activation, not just when + * regenerating shapes, so we don't have to purge fragments if the GC is + * currently running. + * + * JS_THREADSAFE note: js_PurgePropertyCacheForScript purges only the + * current thread's property cache, so a script not owned by a function + * or object, which hands off lifetime management for that script to the + * GC, must be used by only one thread over its lifetime. + * + * This should be an API-compatible change, since a script is never safe + * against premature GC if shared among threads without a rooted object + * wrapping it to protect the script's mapped atoms against GC. We use + * script->owner to enforce this requirement via assertions. + */ +#ifdef CHECK_SCRIPT_OWNER + JS_ASSERT_IF(cx->runtime->gcRunning, !script->owner); +#endif + + /* FIXME: bug 506341; would like to do this only if regenerating shapes. */ + if (!cx->runtime->gcRunning) { + JSStackFrame *fp = js_GetTopStackFrame(cx); + + if (!(fp && (fp->flags & JSFRAME_EVAL))) { + js_PurgePropertyCacheForScript(cx, script); + +#ifdef CHECK_SCRIPT_OWNER + JS_ASSERT(script->owner == cx->thread); +#endif + } + } + +#ifdef JS_TRACER + js_PurgeScriptFragments(cx, script); +#endif + + cx->free(script); + + JS_RUNTIME_UNMETER(cx->runtime, liveScripts); +} + +void +js_TraceScript(JSTracer *trc, JSScript *script) +{ + JSAtomMap *map; + uintN i, length; + JSAtom **vector; + jsval v; + JSObjectArray *objarray; + + map = &script->atomMap; + length = map->length; + vector = map->vector; + for (i = 0; i < length; i++) { + v = ATOM_KEY(vector[i]); + if (JSVAL_IS_TRACEABLE(v)) { + JS_SET_TRACING_INDEX(trc, "atomMap", i); + JS_CallTracer(trc, JSVAL_TO_TRACEABLE(v), JSVAL_TRACE_KIND(v)); + } + } + + if (script->objectsOffset != 0) { + objarray = script->objects(); + i = objarray->length; + do { + --i; + if (objarray->vector[i]) { + JS_SET_TRACING_INDEX(trc, "objects", i); + JS_CallTracer(trc, objarray->vector[i], JSTRACE_OBJECT); + } + } while (i != 0); + } + + if (script->regexpsOffset != 0) { + objarray = script->regexps(); + i = objarray->length; + do { + --i; + if (objarray->vector[i]) { + JS_SET_TRACING_INDEX(trc, "regexps", i); + JS_CallTracer(trc, objarray->vector[i], JSTRACE_OBJECT); + } + } while (i != 0); + } + + if (script->u.object) { + JS_SET_TRACING_NAME(trc, "object"); + JS_CallTracer(trc, script->u.object, JSTRACE_OBJECT); + } + + if (IS_GC_MARKING_TRACER(trc) && script->filename) + js_MarkScriptFilename(script->filename); +} + +typedef struct GSNCacheEntry { + JSDHashEntryHdr hdr; + jsbytecode *pc; + jssrcnote *sn; +} GSNCacheEntry; + +#define GSN_CACHE_THRESHOLD 100 + +void +js_PurgeGSNCache(JSGSNCache *cache) +{ + cache->code = NULL; + if (cache->table.ops) { + JS_DHashTableFinish(&cache->table); + cache->table.ops = NULL; + } + GSN_CACHE_METER(cache, purges); +} + +jssrcnote * +js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc) +{ + ptrdiff_t target, offset; + GSNCacheEntry *entry; + jssrcnote *sn, *result; + uintN nsrcnotes; + + + target = pc - script->code; + if ((uint32)target >= script->length) + return NULL; + + if (JS_GSN_CACHE(cx).code == script->code) { + JS_METER_GSN_CACHE(cx, hits); + entry = (GSNCacheEntry *) + JS_DHashTableOperate(&JS_GSN_CACHE(cx).table, pc, + JS_DHASH_LOOKUP); + return entry->sn; + } + + JS_METER_GSN_CACHE(cx, misses); + offset = 0; + for (sn = script->notes(); ; sn = SN_NEXT(sn)) { + if (SN_IS_TERMINATOR(sn)) { + result = NULL; + break; + } + offset += SN_DELTA(sn); + if (offset == target && SN_IS_GETTABLE(sn)) { + result = sn; + break; + } + } + + if (JS_GSN_CACHE(cx).code != script->code && + script->length >= GSN_CACHE_THRESHOLD) { + JS_PURGE_GSN_CACHE(cx); + nsrcnotes = 0; + for (sn = script->notes(); !SN_IS_TERMINATOR(sn); + sn = SN_NEXT(sn)) { + if (SN_IS_GETTABLE(sn)) + ++nsrcnotes; + } + if (!JS_DHashTableInit(&JS_GSN_CACHE(cx).table, JS_DHashGetStubOps(), + NULL, sizeof(GSNCacheEntry), + JS_DHASH_DEFAULT_CAPACITY(nsrcnotes))) { + JS_GSN_CACHE(cx).table.ops = NULL; + } else { + pc = script->code; + for (sn = script->notes(); !SN_IS_TERMINATOR(sn); + sn = SN_NEXT(sn)) { + pc += SN_DELTA(sn); + if (SN_IS_GETTABLE(sn)) { + entry = (GSNCacheEntry *) + JS_DHashTableOperate(&JS_GSN_CACHE(cx).table, pc, + JS_DHASH_ADD); + entry->pc = pc; + entry->sn = sn; + } + } + JS_GSN_CACHE(cx).code = script->code; + JS_METER_GSN_CACHE(cx, fills); + } + } + + return result; +} + +uintN +js_FramePCToLineNumber(JSContext *cx, JSStackFrame *fp) +{ + return js_PCToLineNumber(cx, fp->script, fp->imacpc ? fp->imacpc : fp->regs->pc); +} + +uintN +js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc) +{ + JSOp op; + JSFunction *fun; + uintN lineno; + ptrdiff_t offset, target; + jssrcnote *sn; + JSSrcNoteType type; + + /* Cope with JSStackFrame.pc value prior to entering js_Interpret. */ + if (!pc) + return 0; + + /* + * Special case: function definition needs no line number note because + * the function's script contains its starting line number. + */ + op = js_GetOpcode(cx, script, pc); + if (js_CodeSpec[op].format & JOF_INDEXBASE) + pc += js_CodeSpec[op].length; + if (*pc == JSOP_DEFFUN) { + GET_FUNCTION_FROM_BYTECODE(script, pc, 0, fun); + return fun->u.i.script->lineno; + } + + /* + * General case: walk through source notes accumulating their deltas, + * keeping track of line-number notes, until we pass the note for pc's + * offset within script->code. + */ + lineno = script->lineno; + offset = 0; + target = pc - script->code; + for (sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { + offset += SN_DELTA(sn); + type = (JSSrcNoteType) SN_TYPE(sn); + if (type == SRC_SETLINE) { + if (offset <= target) + lineno = (uintN) js_GetSrcNoteOffset(sn, 0); + } else if (type == SRC_NEWLINE) { + if (offset <= target) + lineno++; + } + if (offset > target) + break; + } + return lineno; +} + +/* The line number limit is the same as the jssrcnote offset limit. */ +#define SN_LINE_LIMIT (SN_3BYTE_OFFSET_FLAG << 16) + +jsbytecode * +js_LineNumberToPC(JSScript *script, uintN target) +{ + ptrdiff_t offset, best; + uintN lineno, bestdiff, diff; + jssrcnote *sn; + JSSrcNoteType type; + + offset = 0; + best = -1; + lineno = script->lineno; + bestdiff = SN_LINE_LIMIT; + for (sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { + /* + * Exact-match only if offset is not in the prolog; otherwise use + * nearest greater-or-equal line number match. + */ + if (lineno == target && script->code + offset >= script->main) + goto out; + if (lineno >= target) { + diff = lineno - target; + if (diff < bestdiff) { + bestdiff = diff; + best = offset; + } + } + offset += SN_DELTA(sn); + type = (JSSrcNoteType) SN_TYPE(sn); + if (type == SRC_SETLINE) { + lineno = (uintN) js_GetSrcNoteOffset(sn, 0); + } else if (type == SRC_NEWLINE) { + lineno++; + } + } + if (best >= 0) + offset = best; +out: + return script->code + offset; +} + +JS_FRIEND_API(uintN) +js_GetScriptLineExtent(JSScript *script) +{ + uintN lineno; + jssrcnote *sn; + JSSrcNoteType type; + + lineno = script->lineno; + for (sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { + type = (JSSrcNoteType) SN_TYPE(sn); + if (type == SRC_SETLINE) { + lineno = (uintN) js_GetSrcNoteOffset(sn, 0); + } else if (type == SRC_NEWLINE) { + lineno++; + } + } + return 1 + lineno - script->lineno; +} diff --git a/ape-server/deps/js/src/jsscript.h b/ape-server/deps/js/src/jsscript.h new file mode 100755 index 0000000..4028ebb --- /dev/null +++ b/ape-server/deps/js/src/jsscript.h @@ -0,0 +1,370 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=79 ft=cpp: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsscript_h___ +#define jsscript_h___ +/* + * JS script descriptor. + */ +#include "jsatom.h" +#include "jsprvtd.h" +#include "jsdbgapi.h" + +JS_BEGIN_EXTERN_C + +/* + * Type of try note associated with each catch or finally block, and also with + * for-in loops. + */ +typedef enum JSTryNoteKind { + JSTRY_CATCH, + JSTRY_FINALLY, + JSTRY_ITER +} JSTryNoteKind; + +/* + * Exception handling record. + */ +struct JSTryNote { + uint8 kind; /* one of JSTryNoteKind */ + uint8 padding; /* explicit padding on uint16 boundary */ + uint16 stackDepth; /* stack depth upon exception handler entry */ + uint32 start; /* start of the try statement or for-in loop + relative to script->main */ + uint32 length; /* length of the try statement or for-in loop */ +}; + +typedef struct JSTryNoteArray { + JSTryNote *vector; /* array of indexed try notes */ + uint32 length; /* count of indexed try notes */ +} JSTryNoteArray; + +typedef struct JSObjectArray { + JSObject **vector; /* array of indexed objects */ + uint32 length; /* count of indexed objects */ +} JSObjectArray; + +typedef struct JSUpvarArray { + uint32 *vector; /* array of indexed upvar cookies */ + uint32 length; /* count of indexed upvar cookies */ +} JSUpvarArray; + +#define CALLEE_UPVAR_SLOT 0xffff +#define FREE_STATIC_LEVEL 0x3fff +#define FREE_UPVAR_COOKIE 0xffffffff +#define MAKE_UPVAR_COOKIE(skip,slot) ((skip) << 16 | (slot)) +#define UPVAR_FRAME_SKIP(cookie) ((uint32)(cookie) >> 16) +#define UPVAR_FRAME_SLOT(cookie) ((uint16)(cookie)) + +#define JS_OBJECT_ARRAY_SIZE(length) \ + (offsetof(JSObjectArray, vector) + sizeof(JSObject *) * (length)) + +#if defined DEBUG && defined JS_THREADSAFE +# define CHECK_SCRIPT_OWNER 1 +#endif + +struct JSScript { + jsbytecode *code; /* bytecodes and their immediate operands */ + uint32 length; /* length of code vector */ + uint16 version; /* JS version under which script was compiled */ + uint16 nfixed; /* number of slots besides stack operands in + slot array */ + uint8 objectsOffset; /* offset to the array of nested function, + block, scope, xml and one-time regexps + objects or 0 if none */ + uint8 upvarsOffset; /* offset of the array of display ("up") + closure vars or 0 if none */ + uint8 regexpsOffset; /* offset to the array of to-be-cloned + regexps or 0 if none. */ + uint8 trynotesOffset; /* offset to the array of try notes or + 0 if none */ + bool noScriptRval:1; /* no need for result value of last + expression statement */ + bool savedCallerFun:1; /* object 0 is caller function */ + bool hasSharps:1; /* script uses sharp variables */ + bool strictModeCode:1; /* code is in strict mode */ + + jsbytecode *main; /* main entry point, after predef'ing prolog */ + JSAtomMap atomMap; /* maps immediate index to literal struct */ + const char *filename; /* source filename or null */ + uint32 lineno; /* base line number of script */ + uint16 nslots; /* vars plus maximum stack depth */ + uint16 staticLevel;/* static level for display maintenance */ + JSPrincipals *principals;/* principals for this script */ + union { + JSObject *object; /* optional Script-class object wrapper */ + JSScript *nextToGC; /* next to GC in rt->scriptsToGC list */ + } u; +#ifdef CHECK_SCRIPT_OWNER + JSThread *owner; /* for thread-safe life-cycle assertions */ +#endif + + /* Script notes are allocated right after the code. */ + jssrcnote *notes() { return (jssrcnote *)(code + length); } + + JSObjectArray *objects() { + JS_ASSERT(objectsOffset != 0); + return (JSObjectArray *)((uint8 *) this + objectsOffset); + } + + JSUpvarArray *upvars() { + JS_ASSERT(upvarsOffset != 0); + return (JSUpvarArray *) ((uint8 *) this + upvarsOffset); + } + + JSObjectArray *regexps() { + JS_ASSERT(regexpsOffset != 0); + return (JSObjectArray *) ((uint8 *) this + regexpsOffset); + } + + JSTryNoteArray *trynotes() { + JS_ASSERT(trynotesOffset != 0); + return (JSTryNoteArray *) ((uint8 *) this + trynotesOffset); + } + + JSAtom *getAtom(size_t index) { + JS_ASSERT(index < atomMap.length); + return atomMap.vector[index]; + } + + JSObject *getObject(size_t index) { + JSObjectArray *arr = objects(); + JS_ASSERT(index < arr->length); + return arr->vector[index]; + } + + inline JSFunction *getFunction(size_t index); + + inline JSObject *getRegExp(size_t index); + + /* + * The isEmpty method tells whether this script has code that computes any + * result (not return value, result AKA normal completion value) other than + * JSVAL_VOID, or any other effects. It has a fast path for the case where + * |this| is the emptyScript singleton, but it also checks this->length and + * this->code, to handle debugger-generated mutable empty scripts. + */ + inline bool isEmpty() const; + + /* + * Accessor for the emptyScriptConst singleton, to consolidate const_cast. + * See the private member declaration. + */ + static JSScript *emptyScript() { + return const_cast(&emptyScriptConst); + } + + private: + /* + * Use const to put this in read-only memory if possible. We are stuck with + * non-const JSScript * and jsbytecode * by legacy code (back in the 1990s, + * const wasn't supported correctly on all target platforms). The debugger + * does mutate bytecode, and script->u.object may be set after construction + * in some cases, so making JSScript pointers const will be "hard". + */ + static const JSScript emptyScriptConst; +}; + +#define SHARP_NSLOTS 2 /* [#array, #depth] slots if the script + uses sharp variables */ + +static JS_INLINE uintN +StackDepth(JSScript *script) +{ + return script->nslots - script->nfixed; +} + +/* + * If pc_ does not point within script_'s bytecode, then it must point into an + * imacro body, so we use cx->runtime common atoms instead of script_'s atoms. + * This macro uses cx from its callers' environments in the pc-in-imacro case. + */ +#define JS_GET_SCRIPT_ATOM(script_, pc_, index, atom) \ + JS_BEGIN_MACRO \ + if ((pc_) < (script_)->code || \ + (script_)->code + (script_)->length <= (pc_)) { \ + JS_ASSERT((size_t)(index) < js_common_atom_count); \ + (atom) = COMMON_ATOMS_START(&cx->runtime->atomState)[index]; \ + } else { \ + (atom) = script_->getAtom(index); \ + } \ + JS_END_MACRO + +extern JS_FRIEND_DATA(JSClass) js_ScriptClass; + +extern JSObject * +js_InitScriptClass(JSContext *cx, JSObject *obj); + +/* + * On first new context in rt, initialize script runtime state, specifically + * the script filename table and its lock. + */ +extern JSBool +js_InitRuntimeScriptState(JSRuntime *rt); + +/* + * On JS_DestroyRuntime(rt), forcibly free script filename prefixes and any + * script filename table entries that have not been GC'd. + * + * This allows script filename prefixes to outlive any context in rt. + */ +extern void +js_FreeRuntimeScriptState(JSRuntime *rt); + +extern const char * +js_SaveScriptFilename(JSContext *cx, const char *filename); + +extern const char * +js_SaveScriptFilenameRT(JSRuntime *rt, const char *filename, uint32 flags); + +extern uint32 +js_GetScriptFilenameFlags(const char *filename); + +extern void +js_MarkScriptFilename(const char *filename); + +extern void +js_MarkScriptFilenames(JSRuntime *rt, JSBool keepAtoms); + +extern void +js_SweepScriptFilenames(JSRuntime *rt); + +/* + * Two successively less primitive ways to make a new JSScript. The first + * does *not* call a non-null cx->runtime->newScriptHook -- only the second, + * js_NewScriptFromCG, calls this optional debugger hook. + * + * The js_NewScript function can't know whether the script it creates belongs + * to a function, or is top-level or eval code, but the debugger wants access + * to the newly made script's function, if any -- so callers of js_NewScript + * are responsible for notifying the debugger after successfully creating any + * kind (function or other) of new JSScript. + * + * NB: js_NewScript always creates a new script; it never returns the empty + * script singleton (JSScript::emptyScript()). Callers who know they can use + * that read-only singleton are responsible for choosing it instead of calling + * js_NewScript with length and nsrcnotes equal to 1 and other parameters save + * cx all zero. + */ +extern JSScript * +js_NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natoms, + uint32 nobjects, uint32 nupvars, uint32 nregexps, + uint32 ntrynotes); + +extern JSScript * +js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg); + +/* + * New-script-hook calling is factored from js_NewScriptFromCG so that it + * and callers of js_XDRScript can share this code. In the case of callers + * of js_XDRScript, the hook should be invoked only after successful decode + * of any owning function (the fun parameter) or script object (null fun). + */ +extern JS_FRIEND_API(void) +js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun); + +extern JS_FRIEND_API(void) +js_CallDestroyScriptHook(JSContext *cx, JSScript *script); + +extern void +js_DestroyScript(JSContext *cx, JSScript *script); + +extern void +js_TraceScript(JSTracer *trc, JSScript *script); + +/* + * To perturb as little code as possible, we introduce a js_GetSrcNote lookup + * cache without adding an explicit cx parameter. Thus js_GetSrcNote becomes + * a macro that uses cx from its calls' lexical environments. + */ +#define js_GetSrcNote(script,pc) js_GetSrcNoteCached(cx, script, pc) + +extern jssrcnote * +js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc); + +/* + * NOTE: use js_FramePCToLineNumber(cx, fp) when you have an active fp, in + * preference to js_PCToLineNumber (cx, fp->script fp->regs->pc), because + * fp->imacpc may be non-null, indicating an active imacro. + */ +extern uintN +js_FramePCToLineNumber(JSContext *cx, JSStackFrame *fp); + +extern uintN +js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc); + +extern jsbytecode * +js_LineNumberToPC(JSScript *script, uintN lineno); + +extern JS_FRIEND_API(uintN) +js_GetScriptLineExtent(JSScript *script); + +static JS_INLINE JSOp +js_GetOpcode(JSContext *cx, JSScript *script, jsbytecode *pc) +{ + JSOp op = (JSOp) *pc; + if (op == JSOP_TRAP) + op = JS_GetTrapOpcode(cx, script, pc); + return op; +} + +/* + * If magic is non-null, js_XDRScript succeeds on magic number mismatch but + * returns false in *magic; it reflects a match via a true *magic out param. + * If magic is null, js_XDRScript returns false on bad magic number errors, + * which it reports. + * + * NB: after a successful JSXDR_DECODE, and provided that *scriptp is not the + * JSScript::emptyScript() immutable singleton, js_XDRScript callers must do + * any required subsequent set-up of owning function or script object and then + * call js_CallNewScriptHook. + * + * If the caller requires a mutable empty script (for debugging or u.object + * ownership setting), pass true for needMutableScript. Otherwise pass false. + * Call js_CallNewScriptHook only with a mutable script, i.e. never with the + * JSScript::emptyScript() singleton. + */ +extern JSBool +js_XDRScript(JSXDRState *xdr, JSScript **scriptp, bool needMutableScript, + JSBool *hasMagic); + +JS_END_EXTERN_C + +#endif /* jsscript_h___ */ diff --git a/ape-server/deps/js/src/jsscriptinlines.h b/ape-server/deps/js/src/jsscriptinlines.h new file mode 100755 index 0000000..fdd3bf5 --- /dev/null +++ b/ape-server/deps/js/src/jsscriptinlines.h @@ -0,0 +1,89 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=79 ft=cpp: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is SpiderMonkey JavaScript engine. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Jason Orendorff + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsscriptinlines_h___ +#define jsscriptinlines_h___ + +#include "jsfun.h" +#include "jsopcode.h" +#include "jsregexp.h" +#include "jsscript.h" + +inline JSFunction * +JSScript::getFunction(size_t index) +{ + JSObject *funobj = getObject(index); + JS_ASSERT(HAS_FUNCTION_CLASS(funobj)); + JS_ASSERT(funobj == (JSObject *) funobj->getPrivate()); + JSFunction *fun = (JSFunction *) funobj; + JS_ASSERT(FUN_INTERPRETED(fun)); + return fun; +} + +inline JSObject * +JSScript::getRegExp(size_t index) +{ + JSObjectArray *arr = regexps(); + JS_ASSERT((uint32) index < arr->length); + JSObject *obj = arr->vector[index]; + JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_RegExpClass); + return obj; +} + +inline bool +JSScript::isEmpty() const +{ + if (this == emptyScript()) + return true; + + if (length <= 3) { + jsbytecode *pc = code; + + if (JSOp(*pc) == JSOP_TRACE) + ++pc; + if (noScriptRval && JSOp(*pc) == JSOP_FALSE) + ++pc; + if (JSOp(*pc) == JSOP_STOP) + return true; + } + return false; +} + +#endif /* jsscriptinlines_h___ */ diff --git a/ape-server/deps/js/src/jsshell.msg b/ape-server/deps/js/src/jsshell.msg new file mode 100755 index 0000000..e746cc1 --- /dev/null +++ b/ape-server/deps/js/src/jsshell.msg @@ -0,0 +1,56 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + Error messages for JSShell. See js.msg for format. +*/ + +MSG_DEF(JSSMSG_NOT_AN_ERROR, 0, 0, JSEXN_NONE, "") +MSG_DEF(JSSMSG_CANT_OPEN, 1, 2, JSEXN_NONE, "can't open {0}: {1}") +MSG_DEF(JSSMSG_TRAP_USAGE, 2, 0, JSEXN_NONE, "usage: trap [fun] [pc] expr") +MSG_DEF(JSSMSG_LINE2PC_USAGE, 3, 0, JSEXN_NONE, "usage: line2pc [fun] line") +MSG_DEF(JSSMSG_FILE_SCRIPTS_ONLY, 4, 0, JSEXN_NONE, "only works on JS scripts read from files") +MSG_DEF(JSSMSG_UNEXPECTED_EOF, 5, 1, JSEXN_NONE, "unexpected EOF in {0}") +MSG_DEF(JSSMSG_DOEXP_USAGE, 6, 0, JSEXN_NONE, "usage: doexp obj id") +MSG_DEF(JSSMSG_SCRIPTS_ONLY, 7, 0, JSEXN_NONE, "only works on scripts") +MSG_DEF(JSSMSG_NOT_ENOUGH_ARGS, 8, 1, JSEXN_NONE, "{0}: not enough arguments") +MSG_DEF(JSSMSG_TOO_MANY_ARGS, 9, 1, JSEXN_NONE, "{0}: too many arguments") +MSG_DEF(JSSMSG_ASSERT_EQ_FAILED, 10, 2, JSEXN_NONE, "Assertion failed: got {0}, expected {1}") +MSG_DEF(JSSMSG_ASSERT_EQ_FAILED_MSG, 11, 3, JSEXN_NONE, "Assertion failed: got {0}, expected {1}: {2}") +MSG_DEF(JSSMSG_INVALID_ARGS, 12, 1, JSEXN_NONE, "{0}: invalid arguments") diff --git a/ape-server/deps/js/src/jsstack.js b/ape-server/deps/js/src/jsstack.js new file mode 100755 index 0000000..1824495 --- /dev/null +++ b/ape-server/deps/js/src/jsstack.js @@ -0,0 +1,373 @@ +/* + * Check that only JS_REQUIRES_STACK/JS_FORCES_STACK functions, and functions + * that have called a JS_FORCES_STACK function, access cx->fp directly or + * indirectly. + */ + +require({ after_gcc_pass: 'cfg' }); +include('gcc_util.js'); +include('unstable/adts.js'); +include('unstable/analysis.js'); +include('unstable/lazy_types.js'); +include('unstable/esp.js'); + +var Zero_NonZero = {}; +include('unstable/zero_nonzero.js', Zero_NonZero); + +// Tell MapFactory we don't need multimaps (a speed optimization). +MapFactory.use_injective = true; + +/* + * There are two regions in the program: RED and GREEN. Functions and member + * variables may be declared RED in the C++ source. GREEN is the default. + * + * RED signals danger. A GREEN part of a function must not call a RED function + * or access a RED member. + * + * The body of a RED function is all red. The body of a GREEN function is all + * GREEN by default, but parts dominated by a call to a TURN_RED function are + * red. This way GREEN functions can safely access RED stuff by calling a + * TURN_RED function as preparation. + * + * The analysis does not attempt to prove anything about the body of a TURN_RED + * function. (Both annotations are trusted; only unannotated code is checked + * for errors.) + */ +const RED = 'JS_REQUIRES_STACK'; +const TURN_RED = 'JS_FORCES_STACK'; +const IGNORE_ERRORS = 'JS_IGNORE_STACK'; + +function attrs(tree) { + let a = DECL_P(tree) ? DECL_ATTRIBUTES(tree) : TYPE_ATTRIBUTES(tree); + return translate_attributes(a); +} + +function hasUserAttribute(tree, attrname) { + let attributes = attrs(tree); + if (attributes) { + for (let i = 0; i < attributes.length; i++) { + let attr = attributes[i]; + if (attr.name == 'user' && attr.value.length == 1 && attr.value[0] == attrname) + return true; + } + } + return false; +} + +function process_tree_type(d) +{ + let t = dehydra_convert(d); + if (t.isFunction) + return; + + if (t.typedef !== undefined) + if (isRed(TYPE_NAME(d))) + error("Typedef declaration is annotated JS_REQUIRES_STACK: the annotation should be on the type itself", t.loc); + + if (hasAttribute(t, RED)) { + error("Non-function is annotated JS_REQUIRES_STACK", t.loc); + return; + } + + for (let st = t; st !== undefined && st.isPointer; st = st.type) { + if (hasAttribute(st, RED)) { + error("Non-function is annotated JS_REQUIRES_STACK", t.loc); + return; + } + + if (st.parameters) + return; + } +} + +function process_tree_decl(d) +{ + // For VAR_DECLs, walk the DECL_INITIAL looking for bad assignments + if (TREE_CODE(d) != VAR_DECL) + return; + + let i = DECL_INITIAL(d); + if (!i) + return; + + assignCheck(i, TREE_TYPE(d), function() { return location_of(d); }); + + functionPointerWalk(i, d); +} + +/* + * x is an expression or decl. These functions assume that + */ +function isRed(x) { return hasUserAttribute(x, RED); } +function isTurnRed(x) { return hasUserAttribute(x, TURN_RED); } + +function process_tree(fndecl) +{ + if (hasUserAttribute(fndecl, IGNORE_ERRORS)) + return; + + if (!(isRed(fndecl) || isTurnRed(fndecl))) { + // Ordinarily a user of ESP runs the analysis, then generates output based + // on the results. But in our case (a) we need sub-basic-block resolution, + // which ESP doesn't keep; (b) it so happens that even though ESP can + // iterate over blocks multiple times, in our case that won't cause + // spurious output. (It could cause us to the same error message each time + // through--but that's easily avoided.) Therefore we generate the output + // while the ESP analysis is running. + let a = new RedGreenCheck(fndecl, 0); + if (a.hasRed) + a.run(); + } + + functionPointerCheck(fndecl); +} + +function RedGreenCheck(fndecl, trace) { + //print("RedGreenCheck: " + fndecl.toCString()); + this._fndecl = fndecl; + + // Tell ESP that fndecl is a "property variable". This makes ESP track it in + // a flow-sensitive way. The variable will be 1 in RED regions and "don't + // know" in GREEN regions. (We are technically lying to ESP about fndecl + // being a variable--what we really want is a synthetic variable indicating + // RED/GREEN state, but ESP operates on GCC decl nodes.) + this._state_var_decl = fndecl; + let state_var = new ESP.PropVarSpec(this._state_var_decl, true, undefined); + + // Call base class constructor. + let cfg = function_decl_cfg(fndecl); + ESP.Analysis.apply(this, [cfg, [state_var], Zero_NonZero.meet, trace]); + this.join = Zero_NonZero.join; + + // Preprocess all instructions in the cfg to determine whether this analysis + // is necessary and gather some information we'll use later. + // + // Each isn may include a function call, an assignment, and/or some reads. + // Using walk_tree to walk the isns is a little crazy but robust. + // + this.hasRed = false; + let self = this; // Allow our 'this' to be accessed inside closure + for (let bb in cfg_bb_iterator(cfg)) { + for (let isn in bb_isn_iterator(bb)) { + // Treehydra objects don't support reading never-defined properties + // as undefined, so we have to explicitly initialize anything we want + // to check for later. + isn.redInfo = undefined; + walk_tree(isn, function(t, stack) { + function getLocation(skiptop) { + if (!skiptop) { + let loc = location_of(t); + if (loc !== undefined) + return loc; + } + + for (let i = stack.length - 1; i >= 0; --i) { + let loc = location_of(stack[i]); + if (loc !== undefined) + return loc; + } + return location_of(DECL_SAVED_TREE(fndecl)); + } + + switch (TREE_CODE(t)) { + case FIELD_DECL: + if (isRed(t)) { + let varName = dehydra_convert(t).name; + // location_of(t) is the location of the declaration. + isn.redInfo = ["cannot access JS_REQUIRES_STACK variable " + varName, + getLocation(true)]; + self.hasRed = true; + } + break; + case CALL_EXPR: + { + let callee = call_function_decl(t); + if (callee) { + if (isRed(callee)) { + let calleeName = dehydra_convert(callee).name; + isn.redInfo = ["cannot call JS_REQUIRES_STACK function " + calleeName, + getLocation(false)]; + self.hasRed = true; + } else if (isTurnRed(callee)) { + isn.turnRed = true; + } + } + else { + let fntype = TREE_CHECK( + TREE_TYPE( // the function type + TREE_TYPE( // the function pointer type + CALL_EXPR_FN(t) + ) + ), + FUNCTION_TYPE, METHOD_TYPE); + if (isRed(fntype)) { + isn.redInfo = ["cannot call JS_REQUIRES_STACK function pointer", + getLocation(false)]; + self.hasRed = true; + } + else if (isTurnRed(fntype)) { + isn.turnRed = true; + } + } + } + break; + } + }); + } + } + + // Initialize mixin for infeasible-path elimination. + this._zeroNonzero = new Zero_NonZero.Zero_NonZero(); +} + +RedGreenCheck.prototype = new ESP.Analysis; + +RedGreenCheck.prototype.flowStateCond = function(isn, truth, state) { + // forward event to mixin + this._zeroNonzero.flowStateCond(isn, truth, state); +}; + +RedGreenCheck.prototype.flowState = function(isn, state) { + // forward event to mixin + //try { // The try/catch here is a workaround for some baffling bug in zero_nonzero. + this._zeroNonzero.flowState(isn, state); + //} catch (exc) { + // warning(exc, location_of(isn)); + // warning("(Remove the workaround in jsstack.js and recompile to get a JS stack trace.)", + // location_of(isn)); + //} + let stackState = state.get(this._state_var_decl); + let green = stackState != 1 && stackState != ESP.NOT_REACHED; + let redInfo = isn.redInfo; + if (green && redInfo) { + error(redInfo[0], redInfo[1]); + isn.redInfo = undefined; // avoid duplicate messages about this instruction + } + + // If we call a TURNS_RED function, it doesn't take effect until after the + // whole isn finishes executing (the most conservative rule). + if (isn.turnRed) + state.assignValue(this._state_var_decl, 1, isn); +}; + +function followTypedefs(type) +{ + while (type.typedef !== undefined) + type = type.typedef; + return type; +} + +function assignCheck(source, destType, locfunc) +{ + if (TREE_CODE(destType) != POINTER_TYPE) + return; + + let destCode = TREE_CODE(TREE_TYPE(destType)); + if (destCode != FUNCTION_TYPE && destCode != METHOD_TYPE) + return; + + if (isRed(TREE_TYPE(destType))) + return; + + while (TREE_CODE(source) == NOP_EXPR) + source = source.operands()[0]; + + // The destination is a green function pointer + + if (TREE_CODE(source) == ADDR_EXPR) { + let sourcefn = source.operands()[0]; + + // oddly enough, SpiderMonkey assigns the address of something that's not + // a function to a function pointer as part of the API! See JS_TN + if (TREE_CODE(sourcefn) != FUNCTION_DECL) + return; + + if (isRed(sourcefn)) + error("Assigning non-JS_REQUIRES_STACK function pointer from JS_REQUIRES_STACK function " + dehydra_convert(sourcefn).name, locfunc()); + } + else { + let sourceType = TREE_TYPE(TREE_TYPE(source).tree_check(POINTER_TYPE)); + switch (TREE_CODE(sourceType)) { + case FUNCTION_TYPE: + case METHOD_TYPE: + if (isRed(sourceType)) + error("Assigning non-JS_REQUIRES_STACK function pointer from JS_REQUIRES_STACK function pointer", locfunc()); + break; + } + } +} + +/** + * A type checker which verifies that a red function pointer is never converted + * to a green function pointer. + */ + +function functionPointerWalk(t, baseloc) +{ + walk_tree(t, function(t, stack) { + function getLocation(skiptop) { + if (!skiptop) { + let loc = location_of(t); + if (loc !== undefined) + return loc; + } + + for (let i = stack.length - 1; i >= 0; --i) { + let loc = location_of(stack[i]); + if (loc !== undefined) + return loc; + } + return location_of(baseloc); + } + + switch (TREE_CODE(t)) { + case GIMPLE_MODIFY_STMT: { + let [dest, source] = t.operands(); + assignCheck(source, TREE_TYPE(dest), getLocation); + break; + } + case CONSTRUCTOR: { + let ttype = TREE_TYPE(t); + switch (TREE_CODE(ttype)) { + case RECORD_TYPE: + case UNION_TYPE: { + for each (let ce in VEC_iterate(CONSTRUCTOR_ELTS(t))) + assignCheck(ce.value, TREE_TYPE(ce.index), getLocation); + break; + } + case ARRAY_TYPE: { + let eltype = TREE_TYPE(ttype); + for each (let ce in VEC_iterate(CONSTRUCTOR_ELTS(t))) + assignCheck(ce.value, eltype, getLocation); + break; + } + default: + warning("Unexpected type in initializer: " + TREE_CODE(TREE_TYPE(t)), getLocation()); + } + break; + } + case CALL_EXPR: { + // Check that the arguments to a function and the declared types + // of those arguments are compatible. + let ops = t.operands(); + let funcType = TREE_TYPE( // function type + TREE_TYPE(ops[1])); // function pointer type + let argTypes = [t for (t in function_type_args(funcType))]; + for (let i = argTypes.length - 1; i >= 0; --i) { + let destType = argTypes[i]; + let source = ops[i + 3]; + assignCheck(source, destType, getLocation); + } + break; + } + } + }); +} + +function functionPointerCheck(fndecl) +{ + let cfg = function_decl_cfg(fndecl); + for (let bb in cfg_bb_iterator(cfg)) + for (let isn in bb_isn_iterator(bb)) + functionPointerWalk(isn, DECL_SAVED_TREE(fndecl)); +} diff --git a/ape-server/deps/js/src/jsstaticcheck.h b/ape-server/deps/js/src/jsstaticcheck.h new file mode 100755 index 0000000..db38ebf --- /dev/null +++ b/ape-server/deps/js/src/jsstaticcheck.h @@ -0,0 +1,69 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsstaticcheck_h___ +#define jsstaticcheck_h___ + +#ifdef NS_STATIC_CHECKING +/* + * Trigger a control flow check to make sure that code flows through label + */ +inline __attribute__ ((unused)) void MUST_FLOW_THROUGH(const char *label) { +} + +/* avoid unused goto-label warnings */ +#define MUST_FLOW_LABEL(label) goto label; label: + +inline JS_FORCES_STACK void VOUCH_DOES_NOT_REQUIRE_STACK() {} + +inline JS_FORCES_STACK void +JS_ASSERT_NOT_ON_TRACE(JSContext *cx) +{ + JS_ASSERT(!JS_ON_TRACE(cx)); +} + +#else +#define MUST_FLOW_THROUGH(label) ((void) 0) +#define MUST_FLOW_LABEL(label) +#define VOUCH_DOES_NOT_REQUIRE_STACK() ((void) 0) +#define JS_ASSERT_NOT_ON_TRACE(cx) JS_ASSERT(!JS_ON_TRACE(cx)) +#endif +#define VOUCH_HAVE_STACK VOUCH_DOES_NOT_REQUIRE_STACK + +#endif /* jsstaticcheck_h___ */ diff --git a/ape-server/deps/js/src/jsstdint.h b/ape-server/deps/js/src/jsstdint.h new file mode 100755 index 0000000..4ced93e --- /dev/null +++ b/ape-server/deps/js/src/jsstdint.h @@ -0,0 +1,121 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Jim Blandy + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * This header provides definitions for the types we use, + * even on systems that lack . + * + * NOTE: This header should only be included in private SpiderMonkey + * code; public headers should use only the JS{Int,Uint}N types; see + * the comment for them in "jsinttypes.h". + * + * At the moment, these types are not widely used within SpiderMonkey; + * this file is meant to make existing uses portable, and to allow us + * to transition portably to using them more, if desired. + */ + +#ifndef jsstdint_h___ +#define jsstdint_h___ + +#include "jsinttypes.h" + +/* If we have a working stdint.h, then jsinttypes.h has already + defined the standard integer types. Otherwise, define the standard + names in terms of the 'JS' types. */ +#if ! defined(JS_HAVE_STDINT_H) + +typedef JSInt8 int8_t; +typedef JSInt16 int16_t; +typedef JSInt32 int32_t; +typedef JSInt64 int64_t; + +typedef JSUint8 uint8_t; +typedef JSUint16 uint16_t; +typedef JSUint32 uint32_t; +typedef JSUint64 uint64_t; + +/* Suppress other, conflicting attempts to define stdint-bits. */ +#define _STDINT_H + +/* If JS_STDDEF_H_HAS_INTPTR_T or JS_CRTDEFS_H_HAS_INTPTR_T are + defined, then jsinttypes.h included the given header, which + introduced definitions for intptr_t and uintptr_t. Otherwise, + define the standard names in terms of the 'JS' types. */ +#if !defined(JS_STDDEF_H_HAS_INTPTR_T) && !defined(JS_CRTDEFS_H_HAS_INTPTR_T) +typedef JSIntPtr intptr_t; +typedef JSUintPtr uintptr_t; +#endif + +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) + +#define INT8_MAX 127 +#define INT8_MIN (-INT8_MAX - 1) +#define INT16_MAX 32767 +#define INT16_MIN (-INT16_MAX - 1) +#define INT32_MAX 2147483647 +#define INT32_MIN (-INT32_MAX - 1) +#define INT64_MAX 9223372036854775807LL +#define INT64_MIN (-INT64_MAX - 1) + +#define UINT8_MAX 255 +#define UINT16_MAX 65535 +#define UINT32_MAX 4294967295U +#define UINT64_MAX 18446744073709551615ULL + +/* + * These are technically wrong as they can't be used in the preprocessor, but + * we would require compiler assistance, and at the moment we don't need + * preprocessor-correctness. + */ +#ifdef _MSC_VER +#undef SIZE_MAX +#endif + +#define INTPTR_MAX ((intptr_t) (UINTPTR_MAX >> 1)) +#define INTPTR_MIN (intptr_t(uintptr_t(INTPTR_MAX) + uintptr_t(1))) +#define UINTPTR_MAX ((uintptr_t) -1) +#define SIZE_MAX UINTPTR_MAX +#define PTRDIFF_MAX INTPTR_MAX +#define PTRDIFF_MIN INTPTR_MIN + +#endif /* !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) */ + +#endif /* JS_HAVE_STDINT_H */ + +#endif /* jsstdint_h___ */ diff --git a/ape-server/deps/js/src/jsstr.cpp b/ape-server/deps/js/src/jsstr.cpp new file mode 100755 index 0000000..08c9f3f --- /dev/null +++ b/ape-server/deps/js/src/jsstr.cpp @@ -0,0 +1,5595 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=99: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * JS string type implementation. + * + * In order to avoid unnecessary js_LockGCThing/js_UnlockGCThing calls, these + * native methods store strings (possibly newborn) converted from their 'this' + * parameter and arguments on the stack: 'this' conversions at argv[-1], arg + * conversions at their index (argv[0], argv[1]). This is a legitimate method + * of rooting things that might lose their newborn root due to subsequent GC + * allocations in the same native method. + */ +#define __STDC_LIMIT_MACROS + +#include +#include +#include "jstypes.h" +#include "jsstdint.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jshash.h" /* Added by JSIFY */ +#include "jsprf.h" +#include "jsapi.h" +#include "jsarray.h" +#include "jsatom.h" +#include "jsbool.h" +#include "jsbuiltins.h" +#include "jscntxt.h" +#include "jsversion.h" +#include "jsgc.h" +#include "jsinterp.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsobj.h" +#include "jsopcode.h" +#include "jsregexp.h" +#include "jsscope.h" +#include "jsstaticcheck.h" +#include "jsstr.h" +#include "jsbit.h" +#include "jsvector.h" +#include "jsstrinlines.h" + +#define JSSTRDEP_RECURSION_LIMIT 100 + +JS_STATIC_ASSERT(size_t(JSString::MAX_LENGTH) <= size_t(JSVAL_INT_MAX)); +JS_STATIC_ASSERT(INT_FITS_IN_JSVAL(JSString::MAX_LENGTH)); + +static size_t +MinimizeDependentStrings(JSString *str, int level, JSString **basep) +{ + JSString *base; + size_t start, length; + + JS_ASSERT(str->isDependent()); + base = str->dependentBase(); + start = str->dependentStart(); + if (base->isDependent()) { + if (level < JSSTRDEP_RECURSION_LIMIT) { + start += MinimizeDependentStrings(base, level + 1, &base); + } else { + do { + start += base->dependentStart(); + base = base->dependentBase(); + } while (base->isDependent()); + } + if (start == 0) { + JS_ASSERT(str->dependentIsPrefix()); + str->prefixSetBase(base); + } else if (start <= JSString::MAX_DEPENDENT_START) { + length = str->dependentLength(); + str->reinitDependent(base, start, length); + } + } + *basep = base; + return start; +} + +jschar * +js_GetDependentStringChars(JSString *str) +{ + size_t start; + JSString *base; + + start = MinimizeDependentStrings(str, 0, &base); + JS_ASSERT(start < base->flatLength()); + return base->flatChars() + start; +} + +const jschar * +js_GetStringChars(JSContext *cx, JSString *str) +{ + if (!js_MakeStringImmutable(cx, str)) + return NULL; + return str->flatChars(); +} + +JSString * JS_FASTCALL +js_ConcatStrings(JSContext *cx, JSString *left, JSString *right) +{ + size_t rn, ln, lrdist, n; + jschar *ls, *s; + const jschar *rs; + JSString *ldep; /* non-null if left should become dependent */ + JSString *str; + + right->getCharsAndLength(rs, rn); + if (rn == 0) + return left; + + left->getCharsAndLength(const_cast(ls), ln); + if (ln == 0) + return right; + + if (!left->isMutable()) { + /* We must copy if left does not own a buffer to realloc. */ + s = (jschar *) cx->malloc((ln + rn + 1) * sizeof(jschar)); + if (!s) + return NULL; + js_strncpy(s, ls, ln); + ldep = NULL; + } else { + /* We can realloc left's space and make it depend on our result. */ + JS_ASSERT(left->isFlat()); + s = (jschar *) cx->realloc(ls, (ln + rn + 1) * sizeof(jschar)); + if (!s) + return NULL; + + /* Take care: right could depend on left! */ + lrdist = (size_t)(rs - ls); + if (lrdist < ln) + rs = s + lrdist; + left->mChars = ls = s; + ldep = left; + } + + js_strncpy(s + ln, rs, rn); + n = ln + rn; + s[n] = 0; + + str = js_NewString(cx, s, n); + if (!str) { + /* Out of memory: clean up any space we (re-)allocated. */ + if (!ldep) { + cx->free(s); + } else { + s = (jschar *) cx->realloc(ls, (ln + 1) * sizeof(jschar)); + if (s) + left->mChars = s; + } + } else { + str->flatSetMutable(); + + /* Morph left into a dependent prefix if we realloc'd its buffer. */ + if (ldep) { + ldep->reinitPrefix(str, ln); +#ifdef DEBUG + { + JSRuntime *rt = cx->runtime; + JS_RUNTIME_METER(rt, liveDependentStrings); + JS_RUNTIME_METER(rt, totalDependentStrings); + JS_LOCK_RUNTIME_VOID(rt, + (rt->strdepLengthSum += (double)ln, + rt->strdepLengthSquaredSum += (double)ln * (double)ln)); + } +#endif + } + } + + return str; +} + +const jschar * +js_UndependString(JSContext *cx, JSString *str) +{ + size_t n, size; + jschar *s; + + if (str->isDependent()) { + n = str->dependentLength(); + size = (n + 1) * sizeof(jschar); + s = (jschar *) cx->malloc(size); + if (!s) + return NULL; + + js_strncpy(s, str->dependentChars(), n); + s[n] = 0; + str->reinitFlat(s, n); + +#ifdef DEBUG + { + JSRuntime *rt = cx->runtime; + JS_RUNTIME_UNMETER(rt, liveDependentStrings); + JS_RUNTIME_UNMETER(rt, totalDependentStrings); + JS_LOCK_RUNTIME_VOID(rt, + (rt->strdepLengthSum -= (double)n, + rt->strdepLengthSquaredSum -= (double)n * (double)n)); + } +#endif + } + + return str->flatChars(); +} + +JSBool +js_MakeStringImmutable(JSContext *cx, JSString *str) +{ + if (str->isDependent() && !js_UndependString(cx, str)) { + JS_RUNTIME_METER(cx->runtime, badUndependStrings); + return JS_FALSE; + } + str->flatClearMutable(); + return JS_TRUE; +} + +static JSString * +ArgToRootedString(JSContext *cx, uintN argc, jsval *vp, uintN arg) +{ + if (arg >= argc) + return ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]); + vp += 2 + arg; + + if (!JSVAL_IS_PRIMITIVE(*vp) && !JSVAL_TO_OBJECT(*vp)->defaultValue(cx, JSTYPE_STRING, vp)) + return NULL; + + JSString *str; + if (JSVAL_IS_STRING(*vp)) { + str = JSVAL_TO_STRING(*vp); + } else if (JSVAL_IS_BOOLEAN(*vp)) { + str = ATOM_TO_STRING(cx->runtime->atomState.booleanAtoms[ + JSVAL_TO_BOOLEAN(*vp)? 1 : 0]); + } else if (JSVAL_IS_NULL(*vp)) { + str = ATOM_TO_STRING(cx->runtime->atomState.nullAtom); + } else if (JSVAL_IS_VOID(*vp)) { + str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]); + } + else { + if (JSVAL_IS_INT(*vp)) { + str = js_NumberToString(cx, JSVAL_TO_INT(*vp)); + } else { + JS_ASSERT(JSVAL_IS_DOUBLE(*vp)); + str = js_NumberToString(cx, *JSVAL_TO_DOUBLE(*vp)); + } + if (str) + *vp = STRING_TO_JSVAL(str); + } + return str; +} + +/* + * Forward declarations for URI encode/decode and helper routines + */ +static JSBool +str_decodeURI(JSContext *cx, uintN argc, jsval *vp); + +static JSBool +str_decodeURI_Component(JSContext *cx, uintN argc, jsval *vp); + +static JSBool +str_encodeURI(JSContext *cx, uintN argc, jsval *vp); + +static JSBool +str_encodeURI_Component(JSContext *cx, uintN argc, jsval *vp); + +static const uint32 OVERLONG_UTF8 = UINT32_MAX; + +static uint32 +Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length); + +/* + * Contributions from the String class to the set of methods defined for the + * global object. escape and unescape used to be defined in the Mocha library, + * but as ECMA decided to spec them, they've been moved to the core engine + * and made ECMA-compliant. (Incomplete escapes are interpreted as literal + * characters by unescape.) + */ + +/* + * Stuff to emulate the old libmocha escape, which took a second argument + * giving the type of escape to perform. Retained for compatibility, and + * copied here to avoid reliance on net.h, mkparse.c/NET_EscapeBytes. + */ + +#define URL_XALPHAS ((uint8) 1) +#define URL_XPALPHAS ((uint8) 2) +#define URL_PATH ((uint8) 4) + +static const uint8 urlCharType[256] = +/* Bit 0 xalpha -- the alphas + * Bit 1 xpalpha -- as xalpha but + * converts spaces to plus and plus to %20 + * Bit 2 ... path -- as xalphas but doesn't escape '/' + */ + /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1x */ + 0,0,0,0,0,0,0,0,0,0,7,4,0,7,7,4, /* 2x !"#$%&'()*+,-./ */ + 7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0, /* 3x 0123456789:;<=>? */ + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 4x @ABCDEFGHIJKLMNO */ + 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,7, /* 5X PQRSTUVWXYZ[\]^_ */ + 0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 6x `abcdefghijklmno */ + 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0, /* 7X pqrstuvwxyz{\}~ DEL */ + 0, }; + +/* This matches the ECMA escape set when mask is 7 (default.) */ + +#define IS_OK(C, mask) (urlCharType[((uint8) (C))] & (mask)) + +/* See ECMA-262 Edition 3 B.2.1 */ +JSBool +js_str_escape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + size_t i, ni, length, newlength; + const jschar *chars; + jschar *newchars; + jschar ch; + jsint mask; + jsdouble d; + const char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + + mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH; + if (argc > 1) { + d = js_ValueToNumber(cx, &argv[1]); + if (JSVAL_IS_NULL(argv[1])) + return JS_FALSE; + if (!JSDOUBLE_IS_FINITE(d) || + (mask = (jsint)d) != d || + mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH)) + { + char numBuf[12]; + JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) mask); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_STRING_MASK, numBuf); + return JS_FALSE; + } + } + + str = ArgToRootedString(cx, argc, argv - 2, 0); + if (!str) + return JS_FALSE; + + str->getCharsAndLength(chars, length); + newlength = length; + + /* Take a first pass and see how big the result string will need to be. */ + for (i = 0; i < length; i++) { + if ((ch = chars[i]) < 128 && IS_OK(ch, mask)) + continue; + if (ch < 256) { + if (mask == URL_XPALPHAS && ch == ' ') + continue; /* The character will be encoded as '+' */ + newlength += 2; /* The character will be encoded as %XX */ + } else { + newlength += 5; /* The character will be encoded as %uXXXX */ + } + + /* + * This overflow test works because newlength is incremented by at + * most 5 on each iteration. + */ + if (newlength < length) { + js_ReportAllocationOverflow(cx); + return JS_FALSE; + } + } + + if (newlength >= ~(size_t)0 / sizeof(jschar)) { + js_ReportAllocationOverflow(cx); + return JS_FALSE; + } + + newchars = (jschar *) cx->malloc((newlength + 1) * sizeof(jschar)); + if (!newchars) + return JS_FALSE; + for (i = 0, ni = 0; i < length; i++) { + if ((ch = chars[i]) < 128 && IS_OK(ch, mask)) { + newchars[ni++] = ch; + } else if (ch < 256) { + if (mask == URL_XPALPHAS && ch == ' ') { + newchars[ni++] = '+'; /* convert spaces to pluses */ + } else { + newchars[ni++] = '%'; + newchars[ni++] = digits[ch >> 4]; + newchars[ni++] = digits[ch & 0xF]; + } + } else { + newchars[ni++] = '%'; + newchars[ni++] = 'u'; + newchars[ni++] = digits[ch >> 12]; + newchars[ni++] = digits[(ch & 0xF00) >> 8]; + newchars[ni++] = digits[(ch & 0xF0) >> 4]; + newchars[ni++] = digits[ch & 0xF]; + } + } + JS_ASSERT(ni == newlength); + newchars[newlength] = 0; + + str = js_NewString(cx, newchars, newlength); + if (!str) { + cx->free(newchars); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} +#undef IS_OK + +static JSBool +str_escape(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *obj; + + obj = JS_THIS_OBJECT(cx, vp); + return obj && js_str_escape(cx, obj, argc, vp + 2, vp); +} + +/* See ECMA-262 Edition 3 B.2.2 */ +static JSBool +str_unescape(JSContext *cx, uintN argc, jsval *vp) +{ + JSString *str; + size_t i, ni, length; + const jschar *chars; + jschar *newchars; + jschar ch; + + str = ArgToRootedString(cx, argc, vp, 0); + if (!str) + return JS_FALSE; + + str->getCharsAndLength(chars, length); + + /* Don't bother allocating less space for the new string. */ + newchars = (jschar *) cx->malloc((length + 1) * sizeof(jschar)); + if (!newchars) + return JS_FALSE; + ni = i = 0; + while (i < length) { + ch = chars[i++]; + if (ch == '%') { + if (i + 1 < length && + JS7_ISHEX(chars[i]) && JS7_ISHEX(chars[i + 1])) + { + ch = JS7_UNHEX(chars[i]) * 16 + JS7_UNHEX(chars[i + 1]); + i += 2; + } else if (i + 4 < length && chars[i] == 'u' && + JS7_ISHEX(chars[i + 1]) && JS7_ISHEX(chars[i + 2]) && + JS7_ISHEX(chars[i + 3]) && JS7_ISHEX(chars[i + 4])) + { + ch = (((((JS7_UNHEX(chars[i + 1]) << 4) + + JS7_UNHEX(chars[i + 2])) << 4) + + JS7_UNHEX(chars[i + 3])) << 4) + + JS7_UNHEX(chars[i + 4]); + i += 5; + } + } + newchars[ni++] = ch; + } + newchars[ni] = 0; + + str = js_NewString(cx, newchars, ni); + if (!str) { + cx->free(newchars); + return JS_FALSE; + } + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +#if JS_HAS_UNEVAL +static JSBool +str_uneval(JSContext *cx, uintN argc, jsval *vp) +{ + JSString *str; + + str = js_ValueToSource(cx, argc != 0 ? vp[2] : JSVAL_VOID); + if (!str) + return JS_FALSE; + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; +} +#endif + +const char js_escape_str[] = "escape"; +const char js_unescape_str[] = "unescape"; +#if JS_HAS_UNEVAL +const char js_uneval_str[] = "uneval"; +#endif +const char js_decodeURI_str[] = "decodeURI"; +const char js_encodeURI_str[] = "encodeURI"; +const char js_decodeURIComponent_str[] = "decodeURIComponent"; +const char js_encodeURIComponent_str[] = "encodeURIComponent"; + +static JSFunctionSpec string_functions[] = { + JS_FN(js_escape_str, str_escape, 1,0), + JS_FN(js_unescape_str, str_unescape, 1,0), +#if JS_HAS_UNEVAL + JS_FN(js_uneval_str, str_uneval, 1,0), +#endif + JS_FN(js_decodeURI_str, str_decodeURI, 1,0), + JS_FN(js_encodeURI_str, str_encodeURI, 1,0), + JS_FN(js_decodeURIComponent_str, str_decodeURI_Component, 1,0), + JS_FN(js_encodeURIComponent_str, str_encodeURI_Component, 1,0), + + JS_FS_END +}; + +jschar js_empty_ucstr[] = {0}; +JSSubString js_EmptySubString = {0, js_empty_ucstr}; + +static JSBool +str_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + jsval v; + JSString *str; + + if (id == ATOM_KEY(cx->runtime->atomState.lengthAtom)) { + if (OBJ_GET_CLASS(cx, obj) == &js_StringClass) { + /* Follow ECMA-262 by fetching intrinsic length of our string. */ + v = obj->fslots[JSSLOT_PRIMITIVE_THIS]; + JS_ASSERT(JSVAL_IS_STRING(v)); + str = JSVAL_TO_STRING(v); + } else { + /* Preserve compatibility: convert obj to a string primitive. */ + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + } + + *vp = INT_TO_JSVAL((jsint) str->length()); + } + + return JS_TRUE; +} + +#define STRING_ELEMENT_ATTRS (JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT) + +static JSBool +str_enumerate(JSContext *cx, JSObject *obj) +{ + jsval v; + JSString *str, *str1; + size_t i, length; + + v = obj->fslots[JSSLOT_PRIMITIVE_THIS]; + JS_ASSERT(JSVAL_IS_STRING(v)); + str = JSVAL_TO_STRING(v); + + length = str->length(); + for (i = 0; i < length; i++) { + str1 = js_NewDependentString(cx, str, i, 1); + if (!str1) + return JS_FALSE; + if (!obj->defineProperty(cx, INT_TO_JSID(i), STRING_TO_JSVAL(str1), NULL, NULL, + STRING_ELEMENT_ATTRS)) { + return JS_FALSE; + } + } + return JS_TRUE; +} + +static JSBool +str_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, + JSObject **objp) +{ + jsval v; + JSString *str, *str1; + jsint slot; + + if (!JSVAL_IS_INT(id) || (flags & JSRESOLVE_ASSIGNING)) + return JS_TRUE; + + v = obj->fslots[JSSLOT_PRIMITIVE_THIS]; + JS_ASSERT(JSVAL_IS_STRING(v)); + str = JSVAL_TO_STRING(v); + + slot = JSVAL_TO_INT(id); + if ((size_t)slot < str->length()) { + str1 = JSString::getUnitString(cx, str, size_t(slot)); + if (!str1) + return JS_FALSE; + if (!obj->defineProperty(cx, INT_TO_JSID(slot), STRING_TO_JSVAL(str1), NULL, NULL, + STRING_ELEMENT_ATTRS)) { + return JS_FALSE; + } + *objp = obj; + } + return JS_TRUE; +} + +JSClass js_StringClass = { + js_String_str, + JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_NEW_RESOLVE | + JSCLASS_HAS_CACHED_PROTO(JSProto_String), + JS_PropertyStub, JS_PropertyStub, str_getProperty, JS_PropertyStub, + str_enumerate, (JSResolveOp)str_resolve, JS_ConvertStub, NULL, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +#define NORMALIZE_THIS(cx,vp,str) \ + JS_BEGIN_MACRO \ + if (JSVAL_IS_STRING(vp[1])) { \ + str = JSVAL_TO_STRING(vp[1]); \ + } else { \ + str = NormalizeThis(cx, vp); \ + if (!str) \ + return JS_FALSE; \ + } \ + JS_END_MACRO + +static JSString * +NormalizeThis(JSContext *cx, jsval *vp) +{ + JSString *str; + + if (JSVAL_IS_NULL(vp[1]) && JSVAL_IS_NULL(JS_THIS(cx, vp))) + return NULL; + str = js_ValueToString(cx, vp[1]); + if (!str) + return NULL; + vp[1] = STRING_TO_JSVAL(str); + return str; +} + +#if JS_HAS_TOSOURCE + +/* + * String.prototype.quote is generic (as are most string methods), unlike + * toSource, toString, and valueOf. + */ +static JSBool +str_quote(JSContext *cx, uintN argc, jsval *vp) +{ + JSString *str; + + NORMALIZE_THIS(cx, vp, str); + str = js_QuoteString(cx, str, '"'); + if (!str) + return JS_FALSE; + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +str_toSource(JSContext *cx, uintN argc, jsval *vp) +{ + jsval v; + JSString *str; + size_t i, j, k, n; + char buf[16]; + const jschar *s; + jschar *t; + + if (!js_GetPrimitiveThis(cx, vp, &js_StringClass, &v)) + return JS_FALSE; + JS_ASSERT(JSVAL_IS_STRING(v)); + str = js_QuoteString(cx, JSVAL_TO_STRING(v), '"'); + if (!str) + return JS_FALSE; + j = JS_snprintf(buf, sizeof buf, "(new %s(", js_StringClass.name); + str->getCharsAndLength(s, k); + n = j + k + 2; + t = (jschar *) cx->malloc((n + 1) * sizeof(jschar)); + if (!t) + return JS_FALSE; + for (i = 0; i < j; i++) + t[i] = buf[i]; + for (j = 0; j < k; i++, j++) + t[i] = s[j]; + t[i++] = ')'; + t[i++] = ')'; + t[i] = 0; + str = js_NewString(cx, t, n); + if (!str) { + cx->free(t); + return JS_FALSE; + } + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +#endif /* JS_HAS_TOSOURCE */ + +JSBool +js_str_toString(JSContext *cx, uintN argc, jsval *vp) +{ + return js_GetPrimitiveThis(cx, vp, &js_StringClass, vp); +} + +/* + * Java-like string native methods. + */ + +static JSString * +SubstringTail(JSContext *cx, JSString *str, jsdouble length, jsdouble begin, jsdouble end) +{ + if (begin < 0) + begin = 0; + else if (begin > length) + begin = length; + + if (end < 0) + end = 0; + else if (end > length) + end = length; + if (end < begin) { + /* ECMA emulates old JDK1.0 java.lang.String.substring. */ + jsdouble tmp = begin; + begin = end; + end = tmp; + } + + return js_NewDependentString(cx, str, (size_t)begin, (size_t)(end - begin)); +} + +static JSBool +str_substring(JSContext *cx, uintN argc, jsval *vp) +{ + JSString *str; + jsdouble d; + jsdouble length, begin, end; + + NORMALIZE_THIS(cx, vp, str); + if (argc != 0) { + d = js_ValueToNumber(cx, &vp[2]); + if (JSVAL_IS_NULL(vp[2])) + return JS_FALSE; + length = str->length(); + begin = js_DoubleToInteger(d); + if (argc == 1) { + end = length; + } else { + d = js_ValueToNumber(cx, &vp[3]); + if (JSVAL_IS_NULL(vp[3])) + return JS_FALSE; + end = js_DoubleToInteger(d); + } + + str = SubstringTail(cx, str, length, begin, end); + if (!str) + return JS_FALSE; + } + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +#ifdef JS_TRACER +static JSString* FASTCALL +String_p_toString(JSContext* cx, JSObject* obj) +{ + if (!JS_InstanceOf(cx, obj, &js_StringClass, NULL)) + return NULL; + jsval v = obj->fslots[JSSLOT_PRIMITIVE_THIS]; + JS_ASSERT(JSVAL_IS_STRING(v)); + return JSVAL_TO_STRING(v); +} +#endif + +JSString* JS_FASTCALL +js_toLowerCase(JSContext *cx, JSString *str) +{ + size_t i, n; + const jschar *s; + jschar *news; + + str->getCharsAndLength(s, n); + news = (jschar *) cx->malloc((n + 1) * sizeof(jschar)); + if (!news) + return NULL; + for (i = 0; i < n; i++) + news[i] = JS_TOLOWER(s[i]); + news[n] = 0; + str = js_NewString(cx, news, n); + if (!str) { + cx->free(news); + return NULL; + } + return str; +} + +static JSBool +str_toLowerCase(JSContext *cx, uintN argc, jsval *vp) +{ + JSString *str; + + NORMALIZE_THIS(cx, vp, str); + str = js_toLowerCase(cx, str); + if (!str) + return JS_FALSE; + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +str_toLocaleLowerCase(JSContext *cx, uintN argc, jsval *vp) +{ + JSString *str; + + /* + * Forcefully ignore the first (or any) argument and return toLowerCase(), + * ECMA has reserved that argument, presumably for defining the locale. + */ + if (cx->localeCallbacks && cx->localeCallbacks->localeToLowerCase) { + NORMALIZE_THIS(cx, vp, str); + return cx->localeCallbacks->localeToLowerCase(cx, str, vp); + } + return str_toLowerCase(cx, 0, vp); +} + +JSString* JS_FASTCALL +js_toUpperCase(JSContext *cx, JSString *str) +{ + size_t i, n; + const jschar *s; + jschar *news; + + str->getCharsAndLength(s, n); + news = (jschar *) cx->malloc((n + 1) * sizeof(jschar)); + if (!news) + return NULL; + for (i = 0; i < n; i++) + news[i] = JS_TOUPPER(s[i]); + news[n] = 0; + str = js_NewString(cx, news, n); + if (!str) { + cx->free(news); + return NULL; + } + return str; +} + +static JSBool +str_toUpperCase(JSContext *cx, uintN argc, jsval *vp) +{ + JSString *str; + + NORMALIZE_THIS(cx, vp, str); + str = js_toUpperCase(cx, str); + if (!str) + return JS_FALSE; + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +str_toLocaleUpperCase(JSContext *cx, uintN argc, jsval *vp) +{ + JSString *str; + + /* + * Forcefully ignore the first (or any) argument and return toUpperCase(), + * ECMA has reserved that argument, presumably for defining the locale. + */ + if (cx->localeCallbacks && cx->localeCallbacks->localeToUpperCase) { + NORMALIZE_THIS(cx, vp, str); + return cx->localeCallbacks->localeToUpperCase(cx, str, vp); + } + return str_toUpperCase(cx, 0, vp); +} + +static JSBool +str_localeCompare(JSContext *cx, uintN argc, jsval *vp) +{ + JSString *str, *thatStr; + + NORMALIZE_THIS(cx, vp, str); + if (argc == 0) { + *vp = JSVAL_ZERO; + } else { + thatStr = js_ValueToString(cx, vp[2]); + if (!thatStr) + return JS_FALSE; + if (cx->localeCallbacks && cx->localeCallbacks->localeCompare) { + vp[2] = STRING_TO_JSVAL(thatStr); + return cx->localeCallbacks->localeCompare(cx, str, thatStr, vp); + } + *vp = INT_TO_JSVAL(js_CompareStrings(str, thatStr)); + } + return JS_TRUE; +} + +static JSBool +str_charAt(JSContext *cx, uintN argc, jsval *vp) +{ + jsval t; + JSString *str; + jsint i; + jsdouble d; + + t = vp[1]; + if (JSVAL_IS_STRING(t) && argc != 0 && JSVAL_IS_INT(vp[2])) { + str = JSVAL_TO_STRING(t); + i = JSVAL_TO_INT(vp[2]); + if ((size_t)i >= str->length()) + goto out_of_range; + } else { + str = NormalizeThis(cx, vp); + if (!str) + return JS_FALSE; + + if (argc == 0) { + d = 0.0; + } else { + d = js_ValueToNumber(cx, &vp[2]); + if (JSVAL_IS_NULL(vp[2])) + return JS_FALSE; + d = js_DoubleToInteger(d); + } + + if (d < 0 || str->length() <= d) + goto out_of_range; + i = (jsint) d; + } + + str = JSString::getUnitString(cx, str, size_t(i)); + if (!str) + return JS_FALSE; + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; + +out_of_range: + *vp = JS_GetEmptyStringValue(cx); + return JS_TRUE; +} + +static JSBool +str_charCodeAt(JSContext *cx, uintN argc, jsval *vp) +{ + jsval t; + JSString *str; + jsint i; + jsdouble d; + + t = vp[1]; + if (JSVAL_IS_STRING(t) && argc != 0 && JSVAL_IS_INT(vp[2])) { + str = JSVAL_TO_STRING(t); + i = JSVAL_TO_INT(vp[2]); + if ((size_t)i >= str->length()) + goto out_of_range; + } else { + str = NormalizeThis(cx, vp); + if (!str) + return JS_FALSE; + + if (argc == 0) { + d = 0.0; + } else { + d = js_ValueToNumber(cx, &vp[2]); + if (JSVAL_IS_NULL(vp[2])) + return JS_FALSE; + d = js_DoubleToInteger(d); + } + + if (d < 0 || str->length() <= d) + goto out_of_range; + i = (jsint) d; + } + + *vp = INT_TO_JSVAL(str->chars()[i]); + return JS_TRUE; + +out_of_range: + *vp = JS_GetNaNValue(cx); + return JS_TRUE; +} + +#ifdef JS_TRACER + +jsdouble FASTCALL +js_String_p_charCodeAt(JSString* str, jsdouble d) +{ + d = js_DoubleToInteger(d); + if (d < 0 || (int32)str->length() <= d) + return js_NaN; + return jsdouble(str->chars()[jsuint(d)]); +} + +int32 FASTCALL +js_String_p_charCodeAt_int(JSString* str, jsint i) +{ + if (i < 0 || (int32)str->length() <= i) + return 0; + return str->chars()[i]; +} +JS_DEFINE_CALLINFO_2(extern, INT32, js_String_p_charCodeAt_int, STRING, INT32, 1, 1) + +jsdouble FASTCALL +js_String_p_charCodeAt0(JSString* str) +{ + if ((int32)str->length() == 0) + return js_NaN; + return jsdouble(str->chars()[0]); +} + +/* + * The FuncFilter replaces the generic double version of charCodeAt with the + * integer fast path if appropriate. + */ +int32 FASTCALL +js_String_p_charCodeAt0_int(JSString* str) +{ + if ((int32)str->length() == 0) + return 0; + return str->chars()[0]; +} +JS_DEFINE_CALLINFO_1(extern, INT32, js_String_p_charCodeAt0_int, STRING, 1, 1) +#endif + +jsint +js_BoyerMooreHorspool(const jschar *text, jsuint textlen, + const jschar *pat, jsuint patlen) +{ + uint8 skip[sBMHCharSetSize]; + + JS_ASSERT(0 < patlen && patlen <= sBMHPatLenMax); + for (jsuint i = 0; i < sBMHCharSetSize; i++) + skip[i] = (uint8)patlen; + jsuint m = patlen - 1; + for (jsuint i = 0; i < m; i++) { + jschar c = pat[i]; + if (c >= sBMHCharSetSize) + return sBMHBadPattern; + skip[c] = (uint8)(m - i); + } + jschar c; + for (jsuint k = m; + k < textlen; + k += ((c = text[k]) >= sBMHCharSetSize) ? patlen : skip[c]) { + for (jsuint i = k, j = m; ; i--, j--) { + if (text[i] != pat[j]) + break; + if (j == 0) + return static_cast(i); /* safe: max string size */ + } + } + return -1; +} + +static JS_ALWAYS_INLINE jsint +StringMatch(const jschar *text, jsuint textlen, + const jschar *pat, jsuint patlen) +{ + if (patlen == 0) + return 0; + if (textlen < patlen) + return -1; + +#if __i386__ + /* + * Given enough registers, the unrolled loop below is faster than the + * following loop. 32-bit x86 does not have enough registers. + */ + if (patlen == 1) { + const jschar p0 = *pat; + for (const jschar *c = text, *end = text + textlen; c != end; ++c) { + if (*c == p0) + return c - text; + } + return -1; + } +#endif + + /* + * If the text or pattern string is short, BMH will be more expensive than + * the basic linear scan due to initialization cost and a more complex loop + * body. While the correct threshold is input-dependent, we can make a few + * conservative observations: + * - When |textlen| is "big enough", the initialization time will be + * proportionally small, so the worst-case slowdown is minimized. + * - When |patlen| is "too small", even the best case for BMH will be + * slower than a simple scan for large |textlen| due to the more complex + * loop body of BMH. + * From this, the values for "big enough" and "too small" are determined + * empirically. See bug 526348. + */ + if (textlen >= 512 && patlen >= 11 && patlen <= sBMHPatLenMax) { + jsint index = js_BoyerMooreHorspool(text, textlen, pat, patlen); + if (index != sBMHBadPattern) + return index; + } + + const jschar *textend = text + textlen - (patlen - 1); + const jschar *patend = pat + patlen; + const jschar p0 = *pat; + const jschar *patNext = pat + 1; + uint8 fixup; + +#if __APPLE__ && __GNUC__ && __i386__ + /* + * It is critical that |t| is kept in a register. The version of gcc we use + * to build on 32-bit Mac does not realize this. See bug 526173. + */ + register const jschar *t asm("esi") = text; +#else + const jschar *t = text; +#endif + + /* Credit: Duff */ + switch ((textend - text) & 7) { + do { + case 0: if (*t++ == p0) { fixup = 8; goto match; } + case 7: if (*t++ == p0) { fixup = 7; goto match; } + case 6: if (*t++ == p0) { fixup = 6; goto match; } + case 5: if (*t++ == p0) { fixup = 5; goto match; } + case 4: if (*t++ == p0) { fixup = 4; goto match; } + case 3: if (*t++ == p0) { fixup = 3; goto match; } + case 2: if (*t++ == p0) { fixup = 2; goto match; } + case 1: if (*t++ == p0) { fixup = 1; goto match; } + continue; + do { + if (*t++ == p0) { + match: + for (const jschar *p1 = patNext, *t1 = t; + p1 != patend; + ++p1, ++t1) { + if (*p1 != *t1) + goto failed_match; + } + return t - text - 1; + } + failed_match:; + } while (--fixup > 0); + } while(t != textend); + } + return -1; +} + +static JSBool +str_indexOf(JSContext *cx, uintN argc, jsval *vp) +{ + + JSString *str; + NORMALIZE_THIS(cx, vp, str); + + JSString *patstr = ArgToRootedString(cx, argc, vp, 0); + if (!patstr) + return JS_FALSE; + + const jschar *text = str->chars(); + jsuint textlen = str->length(); + const jschar *pat = patstr->chars(); + jsuint patlen = patstr->length(); + + jsuint start; + if (argc > 1) { + jsval indexVal = vp[3]; + if (JSVAL_IS_INT(indexVal)) { + jsint i = JSVAL_TO_INT(indexVal); + if (i <= 0) { + start = 0; + } else if (jsuint(i) > textlen) { + start = 0; + textlen = 0; + } else { + start = i; + text += start; + textlen -= start; + } + } else { + jsdouble d = js_ValueToNumber(cx, &vp[3]); + if (JSVAL_IS_NULL(vp[3])) + return JS_FALSE; + d = js_DoubleToInteger(d); + if (d <= 0) { + start = 0; + } else if (d > textlen) { + start = 0; + textlen = 0; + } else { + start = (jsint)d; + text += start; + textlen -= start; + } + } + } else { + start = 0; + } + + jsint match = StringMatch(text, textlen, pat, patlen); + *vp = INT_TO_JSVAL((match == -1) ? -1 : start + match); + return true; +} + +static JSBool +str_lastIndexOf(JSContext *cx, uintN argc, jsval *vp) +{ + JSString *str, *str2; + const jschar *text, *pat; + jsint i, j, textlen, patlen; + jsdouble d; + + NORMALIZE_THIS(cx, vp, str); + text = str->chars(); + textlen = (jsint) str->length(); + + if (argc != 0 && JSVAL_IS_STRING(vp[2])) { + str2 = JSVAL_TO_STRING(vp[2]); + } else { + str2 = ArgToRootedString(cx, argc, vp, 0); + if (!str2) + return JS_FALSE; + } + pat = str2->chars(); + patlen = (jsint) str2->length(); + + i = textlen - patlen; // Start searching here + if (i < 0) { + *vp = INT_TO_JSVAL(-1); + return JS_TRUE; + } + + if (argc > 1) { + if (JSVAL_IS_INT(vp[3])) { + j = JSVAL_TO_INT(vp[3]); + if (j <= 0) + i = 0; + else if (j < i) + i = j; + } else { + d = js_ValueToNumber(cx, &vp[3]); + if (JSVAL_IS_NULL(vp[3])) + return JS_FALSE; + if (!JSDOUBLE_IS_NaN(d)) { + d = js_DoubleToInteger(d); + if (d <= 0) + i = 0; + else if (d < i) + i = (jsint)d; + } + } + } + + if (patlen == 0) { + *vp = INT_TO_JSVAL(i); + return JS_TRUE; + } + + const jschar *t = text + i; + const jschar *textend = text - 1; + const jschar p0 = *pat; + const jschar *patNext = pat + 1; + const jschar *patEnd = pat + patlen; + + for (; t != textend; --t) { + if (*t == p0) { + const jschar *t1 = t + 1; + for (const jschar *p1 = patNext; p1 != patEnd; ++p1, ++t1) { + if (*t1 != *p1) + goto break_continue; + } + *vp = INT_TO_JSVAL(t - text); + return JS_TRUE; + } + break_continue:; + } + + *vp = INT_TO_JSVAL(-1); + return JS_TRUE; +} + +static JSBool +js_TrimString(JSContext *cx, jsval *vp, JSBool trimLeft, JSBool trimRight) +{ + JSString *str; + const jschar *chars; + size_t length, begin, end; + + NORMALIZE_THIS(cx, vp, str); + str->getCharsAndLength(chars, length); + begin = 0; + end = length; + + if (trimLeft) { + while (begin < length && JS_ISSPACE(chars[begin])) + ++begin; + } + + if (trimRight) { + while (end > begin && JS_ISSPACE(chars[end-1])) + --end; + } + + str = js_NewDependentString(cx, str, begin, end - begin); + if (!str) + return JS_FALSE; + + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +str_trim(JSContext *cx, uintN argc, jsval *vp) +{ + return js_TrimString(cx, vp, JS_TRUE, JS_TRUE); +} + +static JSBool +str_trimLeft(JSContext *cx, uintN argc, jsval *vp) +{ + return js_TrimString(cx, vp, JS_TRUE, JS_FALSE); +} + +static JSBool +str_trimRight(JSContext *cx, uintN argc, jsval *vp) +{ + return js_TrimString(cx, vp, JS_FALSE, JS_TRUE); +} + +/* + * Perl-inspired string functions. + */ + +/* + * RegExpGuard factors logic out of String regexp operations. After each + * operation completes, RegExpGuard data members become available, according to + * the comments below. + * + * Notes on parameters to RegExpGuard member functions: + * - 'optarg' indicates in which argument position RegExp flags will be found, + * if present. This is a Mozilla extension and not part of any ECMA spec. + * - 'flat' indicates that the given pattern string will not be interpreted as + * a regular expression, hence regexp meta-characters are ignored. + */ +class RegExpGuard +{ + RegExpGuard(const RegExpGuard &); + void operator=(const RegExpGuard &); + + JSContext *mCx; + JSObject *mReobj; + JSRegExp *mRe; + + public: + RegExpGuard(JSContext *cx) : mCx(cx), mRe(NULL) {} + + ~RegExpGuard() { + if (mRe) + DROP_REGEXP(mCx, mRe); + } + + JSContext* cx() const { return mCx; } + + /* init must succeed in order to call tryFlatMatch or normalizeRegExp. */ + bool + init(uintN argc, jsval *vp) + { + jsval patval = vp[2]; + if (argc != 0 && VALUE_IS_REGEXP(mCx, patval)) { + mReobj = JSVAL_TO_OBJECT(patval); + mRe = (JSRegExp *) mReobj->getPrivate(); + HOLD_REGEXP(mCx, mRe); + } else { + patstr = ArgToRootedString(mCx, argc, vp, 0); + if (!patstr) + return false; + } + return true; + } + + /* + * Upper bound on the number of characters we are willing to potentially + * waste on searching for RegExp meta-characters. + */ + static const size_t sMaxFlatPatLen = 256; + + /* + * Attempt to match |patstr| with |textstr|. Return false if flat matching + * could not be used. + */ + bool + tryFlatMatch(JSString *textstr, bool flat, uintN optarg, uintN argc) + { + if (mRe) + return false; + patstr->getCharsAndLength(pat, patlen); + if (optarg < argc || + (!flat && + (patlen > sMaxFlatPatLen || js_ContainsRegExpMetaChars(pat, patlen)))) { + return false; + } + textstr->getCharsAndLength(text, textlen); + match = StringMatch(text, textlen, pat, patlen); + return true; + } + + /* Data available on successful return from |tryFlatMatch|. */ + JSString *patstr; + const jschar *pat; + size_t patlen; + const jschar *text; + size_t textlen; + jsint match; + + /* If the pattern is not already a regular expression, make it so. */ + bool + normalizeRegExp(bool flat, uintN optarg, uintN argc, jsval *vp) + { + /* If we don't have a RegExp, build RegExp from pattern string. */ + if (mRe) + return true; + JSString *opt; + if (optarg < argc) { + opt = js_ValueToString(mCx, vp[2 + optarg]); + if (!opt) + return false; + } else { + opt = NULL; + } + mRe = js_NewRegExpOpt(mCx, patstr, opt, flat); + if (!mRe) + return false; + mReobj = NULL; + return true; + } + + /* Data available on successful return from |normalizeRegExp|. */ + JSObject *reobj() const { return mReobj; } /* nullable */ + JSRegExp *re() const { return mRe; } /* non-null */ +}; + +/* js_ExecuteRegExp indicates success in two ways, based on the 'test' flag. */ +static JS_ALWAYS_INLINE bool +Matched(bool test, jsval v) +{ + return test ? (v == JSVAL_TRUE) : !JSVAL_IS_NULL(v); +} + +typedef bool (*DoMatchCallback)(JSContext *cx, size_t count, void *data); + +/* + * BitOR-ing these flags allows the DoMatch caller to control when how the + * RegExp engine is called and when callbacks are fired. + */ +enum MatchControlFlags { + TEST_GLOBAL_BIT = 0x1, /* use RegExp.test for global regexps */ + TEST_SINGLE_BIT = 0x2, /* use RegExp.test for non-global regexps */ + CALLBACK_ON_SINGLE_BIT = 0x4, /* fire callback on non-global match */ + + MATCH_ARGS = TEST_GLOBAL_BIT, + MATCHALL_ARGS = CALLBACK_ON_SINGLE_BIT, + REPLACE_ARGS = TEST_GLOBAL_BIT | TEST_SINGLE_BIT | CALLBACK_ON_SINGLE_BIT +}; + +/* Factor out looping and matching logic. */ +static bool +DoMatch(JSContext *cx, jsval *vp, JSString *str, const RegExpGuard &g, + DoMatchCallback callback, void *data, MatchControlFlags flags) +{ + if (g.re()->flags & JSREG_GLOB) { + /* global matching ('g') */ + bool testGlobal = flags & TEST_GLOBAL_BIT; + if (g.reobj()) + js_ClearRegExpLastIndex(g.reobj()); + for (size_t count = 0, i = 0, length = str->length(); i <= length; ++count) { + if (!js_ExecuteRegExp(cx, g.re(), str, &i, testGlobal, vp)) + return false; + if (!Matched(testGlobal, *vp)) + break; + if (!callback(cx, count, data)) + return false; + if (cx->regExpStatics.lastMatch.length == 0) + ++i; + } + } else { + /* single match */ + bool testSingle = !!(flags & TEST_SINGLE_BIT), + callbackOnSingle = !!(flags & CALLBACK_ON_SINGLE_BIT); + size_t i = 0; + if (!js_ExecuteRegExp(cx, g.re(), str, &i, testSingle, vp)) + return false; + if (callbackOnSingle && Matched(testSingle, *vp) && + !callback(cx, 0, data)) { + return false; + } + } + return true; +} + +/* + * DoMatch will only callback on global matches, hence this function builds + * only the "array of matches" returned by match on global regexps. + */ +static bool +MatchCallback(JSContext *cx, size_t count, void *p) +{ + JS_ASSERT(count <= JSVAL_INT_MAX); /* by max string length */ + + jsval &arrayval = *static_cast(p); + JSObject *arrayobj = JSVAL_TO_OBJECT(arrayval); + if (!arrayobj) { + arrayobj = js_NewArrayObject(cx, 0, NULL); + if (!arrayobj) + return false; + arrayval = OBJECT_TO_JSVAL(arrayobj); + } + + JSString *str = cx->regExpStatics.input; + JSSubString &match = cx->regExpStatics.lastMatch; + ptrdiff_t off = match.chars - str->chars(); + JS_ASSERT(off >= 0 && size_t(off) <= str->length()); + JSString *matchstr = js_NewDependentString(cx, str, off, match.length); + if (!matchstr) + return false; + + jsval v = STRING_TO_JSVAL(matchstr); + + JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING); + return !!arrayobj->setProperty(cx, INT_TO_JSID(count), &v); +} + +static bool +BuildFlatMatchArray(JSContext *cx, JSString *textstr, const RegExpGuard &g, + jsval *vp) +{ + if (g.match < 0) { + *vp = JSVAL_NULL; + return true; + } + + /* For this non-global match, produce a RegExp.exec-style array. */ + JSObject *obj = js_NewSlowArrayObject(cx); + if (!obj) + return false; + *vp = OBJECT_TO_JSVAL(obj); + + return obj->defineProperty(cx, INT_TO_JSID(0), STRING_TO_JSVAL(g.patstr)) && + obj->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.indexAtom), + INT_TO_JSVAL(g.match)) && + obj->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.inputAtom), + STRING_TO_JSVAL(textstr)); +} + +static JSBool +str_match(JSContext *cx, uintN argc, jsval *vp) +{ + JSString *str; + NORMALIZE_THIS(cx, vp, str); + + RegExpGuard g(cx); + if (!g.init(argc, vp)) + return false; + if (g.tryFlatMatch(str, false, 1, argc)) + return BuildFlatMatchArray(cx, str, g, vp); + if (!g.normalizeRegExp(false, 1, argc, vp)) + return false; + + JSAutoTempValueRooter array(cx, JSVAL_NULL); + if (!DoMatch(cx, vp, str, g, MatchCallback, array.addr(), MATCH_ARGS)) + return false; + + /* When not global, DoMatch will leave |RegEx.exec()| in *vp. */ + if (g.re()->flags & JSREG_GLOB) + *vp = array.value(); + return true; +} + +static JSBool +str_search(JSContext *cx, uintN argc, jsval *vp) +{ + JSString *str; + NORMALIZE_THIS(cx, vp, str); + + RegExpGuard g(cx); + if (!g.init(argc, vp)) + return false; + if (g.tryFlatMatch(str, false, 1, argc)) { + *vp = INT_TO_JSVAL(g.match); + return true; + } + if (!g.normalizeRegExp(false, 1, argc, vp)) + return false; + + size_t i = 0; + if (!js_ExecuteRegExp(cx, g.re(), str, &i, true, vp)) + return false; + + if (*vp == JSVAL_TRUE) + *vp = INT_TO_JSVAL(cx->regExpStatics.leftContext.length); + else + *vp = INT_TO_JSVAL(-1); + return true; +} + +struct ReplaceData +{ + ReplaceData(JSContext *cx) + : g(cx), invokevp(NULL), cb(cx) + {} + + ~ReplaceData() { + if (invokevp) { + /* If we set invokevp, we already left trace. */ + VOUCH_HAVE_STACK(); + js_FreeStack(g.cx(), invokevpMark); + } + } + + JSString *str; /* 'this' parameter object as a string */ + RegExpGuard g; /* regexp parameter object and private data */ + JSObject *lambda; /* replacement function object or null */ + JSString *repstr; /* replacement string */ + jschar *dollar; /* null or pointer to first $ in repstr */ + jschar *dollarEnd; /* limit pointer for js_strchr_limit */ + jsint index; /* index in result of next replacement */ + jsint leftIndex; /* left context index in str->chars */ + JSSubString dollarStr; /* for "$$" InterpretDollar result */ + bool calledBack; /* record whether callback has been called */ + jsval *invokevp; /* reusable allocation from js_AllocStack */ + void *invokevpMark; /* the mark to return */ + JSCharBuffer cb; /* buffer built during DoMatch */ +}; + +static JSSubString * +InterpretDollar(JSContext *cx, jschar *dp, jschar *ep, ReplaceData &rdata, + size_t *skip) +{ + JSRegExpStatics *res; + jschar dc, *cp; + uintN num, tmp; + + JS_ASSERT(*dp == '$'); + + /* If there is only a dollar, bail now */ + if (dp + 1 >= ep) + return NULL; + + /* Interpret all Perl match-induced dollar variables. */ + res = &cx->regExpStatics; + dc = dp[1]; + if (JS7_ISDEC(dc)) { + /* ECMA-262 Edition 3: 1-9 or 01-99 */ + num = JS7_UNDEC(dc); + if (num > res->parenCount) + return NULL; + + cp = dp + 2; + if (cp < ep && (dc = *cp, JS7_ISDEC(dc))) { + tmp = 10 * num + JS7_UNDEC(dc); + if (tmp <= res->parenCount) { + cp++; + num = tmp; + } + } + if (num == 0) + return NULL; + + /* Adjust num from 1 $n-origin to 0 array-index-origin. */ + num--; + *skip = cp - dp; + return REGEXP_PAREN_SUBSTRING(res, num); + } + + *skip = 2; + switch (dc) { + case '$': + rdata.dollarStr.chars = dp; + rdata.dollarStr.length = 1; + return &rdata.dollarStr; + case '&': + return &res->lastMatch; + case '+': + return &res->lastParen; + case '`': + return &res->leftContext; + case '\'': + return &res->rightContext; + } + return NULL; +} + +static JS_ALWAYS_INLINE bool +PushRegExpSubstr(JSContext *cx, const JSSubString &sub, jsval *&sp) +{ + JSString *whole = cx->regExpStatics.input; + size_t off = sub.chars - whole->chars(); + JSString *str = js_NewDependentString(cx, whole, off, sub.length); + if (!str) + return false; + *sp++ = STRING_TO_JSVAL(str); + return true; +} + +static bool +FindReplaceLength(JSContext *cx, ReplaceData &rdata, size_t *sizep) +{ + JSString *repstr; + size_t replen, skip; + jschar *dp, *ep; + JSSubString *sub; + JSObject *lambda; + + lambda = rdata.lambda; + if (lambda) { + uintN i, m, n; + + js_LeaveTrace(cx); + + /* + * In the lambda case, not only do we find the replacement string's + * length, we compute repstr and return it via rdata for use within + * DoReplace. The lambda is called with arguments ($&, $1, $2, ..., + * index, input), i.e., all the properties of a regexp match array. + * For $&, etc., we must create string jsvals from cx->regExpStatics. + * We grab up stack space to keep the newborn strings GC-rooted. + */ + uintN p = rdata.g.re()->parenCount; + uintN argc = 1 + p + 2; + + if (!rdata.invokevp) { + rdata.invokevp = js_AllocStack(cx, 2 + argc, &rdata.invokevpMark); + if (!rdata.invokevp) + return false; + } + jsval* invokevp = rdata.invokevp; + + MUST_FLOW_THROUGH("lambda_out"); + bool ok = false; + bool freeMoreParens = false; + + /* + * Save the regExpStatics from the current regexp, since they may be + * clobbered by a RegExp usage in the lambda function. Note that all + * members of JSRegExpStatics are JSSubStrings, so not GC roots, save + * input, which is rooted otherwise via vp[1] in str_replace. + */ + JSRegExpStatics save = cx->regExpStatics; + + /* Push lambda and its 'this' parameter. */ + jsval *sp = invokevp; + *sp++ = OBJECT_TO_JSVAL(lambda); + *sp++ = OBJECT_TO_JSVAL(OBJ_GET_PARENT(cx, lambda)); + + /* Push $&, $1, $2, ... */ + if (!PushRegExpSubstr(cx, cx->regExpStatics.lastMatch, sp)) + goto lambda_out; + + i = 0; + m = cx->regExpStatics.parenCount; + n = JS_MIN(m, 9); + for (uintN j = 0; i < n; i++, j++) { + if (!PushRegExpSubstr(cx, cx->regExpStatics.parens[j], sp)) + goto lambda_out; + } + for (uintN j = 0; i < m; i++, j++) { + if (!PushRegExpSubstr(cx, cx->regExpStatics.moreParens[j], sp)) + goto lambda_out; + } + + /* + * We need to clear moreParens in the top-of-stack cx->regExpStatics + * so it won't be possibly realloc'ed, leaving the bottom-of-stack + * moreParens pointing to freed memory. + */ + cx->regExpStatics.moreParens = NULL; + freeMoreParens = true; + + /* Make sure to push undefined for any unmatched parens. */ + for (; i < p; i++) + *sp++ = JSVAL_VOID; + + /* Push match index and input string. */ + *sp++ = INT_TO_JSVAL((jsint)cx->regExpStatics.leftContext.length); + *sp++ = STRING_TO_JSVAL(rdata.str); + + if (!js_Invoke(cx, argc, invokevp, 0)) + goto lambda_out; + + /* + * NB: we count on the newborn string root to hold any string + * created by this js_ValueToString that would otherwise be GC- + * able, until we use rdata.repstr in DoReplace. + */ + repstr = js_ValueToString(cx, *invokevp); + if (!repstr) + goto lambda_out; + + rdata.repstr = repstr; + *sizep = repstr->length(); + + ok = true; + + lambda_out: + if (freeMoreParens) + cx->free(cx->regExpStatics.moreParens); + cx->regExpStatics = save; + return ok; + } + + repstr = rdata.repstr; + replen = repstr->length(); + for (dp = rdata.dollar, ep = rdata.dollarEnd; dp; + dp = js_strchr_limit(dp, '$', ep)) { + sub = InterpretDollar(cx, dp, ep, rdata, &skip); + if (sub) { + replen += sub->length - skip; + dp += skip; + } + else + dp++; + } + *sizep = replen; + return true; +} + +static void +DoReplace(JSContext *cx, ReplaceData &rdata, jschar *chars) +{ + JSString *repstr; + jschar *bp, *cp, *dp, *ep; + size_t len, skip; + JSSubString *sub; + + repstr = rdata.repstr; + bp = cp = repstr->chars(); + for (dp = rdata.dollar, ep = rdata.dollarEnd; dp; + dp = js_strchr_limit(dp, '$', ep)) { + len = dp - cp; + js_strncpy(chars, cp, len); + chars += len; + cp = dp; + sub = InterpretDollar(cx, dp, ep, rdata, &skip); + if (sub) { + len = sub->length; + js_strncpy(chars, sub->chars, len); + chars += len; + cp += skip; + dp += skip; + } else { + dp++; + } + } + js_strncpy(chars, cp, repstr->length() - (cp - bp)); +} + +static bool +ReplaceCallback(JSContext *cx, size_t count, void *p) +{ + ReplaceData &rdata = *static_cast(p); + + rdata.calledBack = true; + JSString *str = rdata.str; + size_t leftoff = rdata.leftIndex; + const jschar *left = str->chars() + leftoff; + size_t leftlen = cx->regExpStatics.lastMatch.chars - left; + rdata.leftIndex = cx->regExpStatics.lastMatch.chars - str->chars(); + rdata.leftIndex += cx->regExpStatics.lastMatch.length; + + size_t replen = 0; /* silence 'unused' warning */ + if (!FindReplaceLength(cx, rdata, &replen)) + return false; + + size_t growth = leftlen + replen; + if (!rdata.cb.growBy(growth)) + return false; + + jschar *chars = rdata.cb.begin() + rdata.index; + rdata.index += growth; + js_strncpy(chars, left, leftlen); + chars += leftlen; + DoReplace(cx, rdata, chars); + return true; +} + +static bool +BuildFlatReplacement(JSContext *cx, JSString *textstr, JSString *repstr, + const RegExpGuard &g, jsval *vp) +{ + if (g.match == -1) { + *vp = STRING_TO_JSVAL(textstr); + return true; + } + + const jschar *rep; + size_t replen; + repstr->getCharsAndLength(rep, replen); + + JSCharBuffer cb(cx); + if (!cb.reserve(g.textlen - g.patlen + replen) || + !cb.append(g.text, static_cast(g.match)) || + !cb.append(rep, replen) || + !cb.append(g.text + g.match + g.patlen, g.text + g.textlen)) { + return false; + } + + JSString *str = js_NewStringFromCharBuffer(cx, cb); + if (!str) + return false; + *vp = STRING_TO_JSVAL(str); + return true; +} + +static JSBool +str_replace(JSContext *cx, uintN argc, jsval *vp) +{ + ReplaceData rdata(cx); + NORMALIZE_THIS(cx, vp, rdata.str); + + /* Extract replacement string/function. */ + if (argc >= 2 && JS_TypeOfValue(cx, vp[3]) == JSTYPE_FUNCTION) { + rdata.lambda = JSVAL_TO_OBJECT(vp[3]); + rdata.repstr = NULL; + rdata.dollar = rdata.dollarEnd = NULL; + } else { + rdata.lambda = NULL; + rdata.repstr = ArgToRootedString(cx, argc, vp, 1); + if (!rdata.repstr) + return false; + + /* We're about to store pointers into the middle of our string. */ + if (!js_MakeStringImmutable(cx, rdata.repstr)) + return false; + rdata.dollarEnd = rdata.repstr->chars() + rdata.repstr->length(); + rdata.dollar = js_strchr_limit(rdata.repstr->chars(), '$', + rdata.dollarEnd); + } + + if (!rdata.g.init(argc, vp)) + return false; + if (!rdata.dollar && !rdata.lambda && + rdata.g.tryFlatMatch(rdata.str, true, 2, argc)) { + return BuildFlatReplacement(cx, rdata.str, rdata.repstr, rdata.g, vp); + } + if (!rdata.g.normalizeRegExp(true, 2, argc, vp)) + return false; + + rdata.index = 0; + rdata.leftIndex = 0; + rdata.calledBack = false; + + if (!DoMatch(cx, vp, rdata.str, rdata.g, ReplaceCallback, &rdata, REPLACE_ARGS)) + return false; + + if (!rdata.calledBack) { + /* Didn't match, so the string is unmodified. */ + *vp = STRING_TO_JSVAL(rdata.str); + return true; + } + + JSSubString *sub = &cx->regExpStatics.rightContext; + if (!rdata.cb.append(sub->chars, sub->length)) + return false; + + JSString *retstr = js_NewStringFromCharBuffer(cx, rdata.cb); + if (!retstr) + return false; + + *vp = STRING_TO_JSVAL(retstr); + return true; +} + +/* + * Subroutine used by str_split to find the next split point in str, starting + * at offset *ip and looking either for the separator substring given by sep, or + * for the next re match. In the re case, return the matched separator in *sep, + * and the possibly updated offset in *ip. + * + * Return -2 on error, -1 on end of string, >= 0 for a valid index of the next + * separator occurrence if found, or str->length if no separator is found. + */ +static jsint +find_split(JSContext *cx, JSString *str, JSRegExp *re, jsint *ip, + JSSubString *sep) +{ + jsint i, j, k; + size_t length; + jschar *chars; + + /* + * Stop if past end of string. If at end of string, we will compare the + * null char stored there (by js_NewString*) to sep->chars[j] in the while + * loop at the end of this function, so that + * + * "ab,".split(',') => ["ab", ""] + * + * and the resulting array converts back to the string "ab," for symmetry. + * However, we ape Perl and do this only if there is a sufficiently large + * limit argument (see str_split). + */ + i = *ip; + length = str->length(); + if ((size_t)i > length) + return -1; + + chars = str->chars(); + + /* + * Match a regular expression against the separator at or above index i. + * Call js_ExecuteRegExp with true for the test argument. On successful + * match, get the separator from cx->regExpStatics.lastMatch. + */ + if (re) { + size_t index; + jsval rval; + + again: + /* JS1.2 deviated from Perl by never matching at end of string. */ + index = (size_t)i; + if (!js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, &rval)) + return -2; + if (rval != JSVAL_TRUE) { + /* Mismatch: ensure our caller advances i past end of string. */ + sep->length = 1; + return length; + } + i = (jsint)index; + *sep = cx->regExpStatics.lastMatch; + if (sep->length == 0) { + /* + * Empty string match: never split on an empty match at the start + * of a find_split cycle. Same rule as for an empty global match + * in DoMatch. + */ + if (i == *ip) { + /* + * "Bump-along" to avoid sticking at an empty match, but don't + * bump past end of string -- our caller must do that by adding + * sep->length to our return value. + */ + if ((size_t)i == length) + return -1; + i++; + goto again; + } + if ((size_t)i == length) { + /* + * If there was a trivial zero-length match at the end of the + * split, then we shouldn't output the matched string at the end + * of the split array. See ECMA-262 Ed. 3, 15.5.4.14, Step 15. + */ + sep->chars = NULL; + } + } + JS_ASSERT((size_t)i >= sep->length); + return i - sep->length; + } + + /* + * Special case: if sep is the empty string, split str into one character + * substrings. Let our caller worry about whether to split once at end of + * string into an empty substring. + */ + if (sep->length == 0) + return ((size_t)i == length) ? -1 : i + 1; + + /* + * Now that we know sep is non-empty, search starting at i in str for an + * occurrence of all of sep's chars. If we find them, return the index of + * the first separator char. Otherwise, return length. + */ + j = 0; + while ((size_t)(k = i + j) < length) { + if (chars[k] == sep->chars[j]) { + if ((size_t)++j == sep->length) + return i; + } else { + i++; + j = 0; + } + } + return k; +} + +static JSBool +str_split(JSContext *cx, uintN argc, jsval *vp) +{ + JSString *str, *sub; + JSObject *arrayobj; + jsval v; + JSBool ok, limited; + JSRegExp *re; + JSSubString *sep, tmp; + jsdouble d; + jsint i, j; + uint32 len, limit; + + NORMALIZE_THIS(cx, vp, str); + + arrayobj = js_NewArrayObject(cx, 0, NULL); + if (!arrayobj) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(arrayobj); + + if (argc == 0) { + v = STRING_TO_JSVAL(str); + ok = arrayobj->setProperty(cx, INT_TO_JSID(0), &v); + } else { + if (VALUE_IS_REGEXP(cx, vp[2])) { + re = (JSRegExp *) JSVAL_TO_OBJECT(vp[2])->getPrivate(); + sep = &tmp; + + /* Set a magic value so we can detect a successful re match. */ + sep->chars = NULL; + sep->length = 0; + } else { + JSString *str2 = js_ValueToString(cx, vp[2]); + if (!str2) + return JS_FALSE; + vp[2] = STRING_TO_JSVAL(str2); + + /* + * Point sep at a local copy of str2's header because find_split + * will modify sep->length. + */ + str2->getCharsAndLength(tmp.chars, tmp.length); + sep = &tmp; + re = NULL; + } + + /* Use the second argument as the split limit, if given. */ + limited = (argc > 1) && !JSVAL_IS_VOID(vp[3]); + limit = 0; /* Avoid warning. */ + if (limited) { + d = js_ValueToNumber(cx, &vp[3]); + if (JSVAL_IS_NULL(vp[3])) + return JS_FALSE; + + /* Clamp limit between 0 and 1 + string length. */ + limit = js_DoubleToECMAUint32(d); + if (limit > str->length()) + limit = 1 + str->length(); + } + + len = i = 0; + while ((j = find_split(cx, str, re, &i, sep)) >= 0) { + if (limited && len >= limit) + break; + sub = js_NewDependentString(cx, str, i, (size_t)(j - i)); + if (!sub) + return JS_FALSE; + v = STRING_TO_JSVAL(sub); + if (!JS_SetElement(cx, arrayobj, len, &v)) + return JS_FALSE; + len++; + + /* + * Imitate perl's feature of including parenthesized substrings + * that matched part of the delimiter in the new array, after the + * split substring that was delimited. + */ + if (re && sep->chars) { + uintN num; + JSSubString *parsub; + + for (num = 0; num < cx->regExpStatics.parenCount; num++) { + if (limited && len >= limit) + break; + parsub = REGEXP_PAREN_SUBSTRING(&cx->regExpStatics, num); + sub = js_NewStringCopyN(cx, parsub->chars, parsub->length); + if (!sub) + return JS_FALSE; + v = STRING_TO_JSVAL(sub); + if (!JS_SetElement(cx, arrayobj, len, &v)) + return JS_FALSE; + len++; + } + sep->chars = NULL; + } + i = j + sep->length; + } + ok = (j != -2); + } + return ok; +} + +#if JS_HAS_PERL_SUBSTR +static JSBool +str_substr(JSContext *cx, uintN argc, jsval *vp) +{ + JSString *str; + jsdouble d; + jsdouble length, begin, end; + + NORMALIZE_THIS(cx, vp, str); + if (argc != 0) { + d = js_ValueToNumber(cx, &vp[2]); + if (JSVAL_IS_NULL(vp[2])) + return JS_FALSE; + length = str->length(); + begin = js_DoubleToInteger(d); + if (begin < 0) { + begin += length; + if (begin < 0) + begin = 0; + } else if (begin > length) { + begin = length; + } + + if (argc == 1) { + end = length; + } else { + d = js_ValueToNumber(cx, &vp[3]); + if (JSVAL_IS_NULL(vp[3])) + return JS_FALSE; + end = js_DoubleToInteger(d); + if (end < 0) + end = 0; + end += begin; + if (end > length) + end = length; + } + + str = js_NewDependentString(cx, str, + (size_t)begin, + (size_t)(end - begin)); + if (!str) + return JS_FALSE; + } + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; +} +#endif /* JS_HAS_PERL_SUBSTR */ + +/* + * Python-esque sequence operations. + */ +static JSBool +str_concat(JSContext *cx, uintN argc, jsval *vp) +{ + JSString *str, *str2; + jsval *argv; + uintN i; + + NORMALIZE_THIS(cx, vp, str); + + /* Set vp (aka rval) early to handle the argc == 0 case. */ + *vp = STRING_TO_JSVAL(str); + + for (i = 0, argv = vp + 2; i < argc; i++) { + str2 = js_ValueToString(cx, argv[i]); + if (!str2) + return JS_FALSE; + argv[i] = STRING_TO_JSVAL(str2); + + str = js_ConcatStrings(cx, str, str2); + if (!str) + return JS_FALSE; + *vp = STRING_TO_JSVAL(str); + } + + return JS_TRUE; +} + +static JSBool +str_slice(JSContext *cx, uintN argc, jsval *vp) +{ + jsval t, v; + JSString *str; + + t = vp[1]; + v = vp[2]; + if (argc == 1 && JSVAL_IS_STRING(t) && JSVAL_IS_INT(v)) { + size_t begin, end, length; + + str = JSVAL_TO_STRING(t); + begin = JSVAL_TO_INT(v); + end = str->length(); + if (begin <= end) { + length = end - begin; + if (length == 0) { + str = cx->runtime->emptyString; + } else { + str = (length == 1) + ? JSString::getUnitString(cx, str, begin) + : js_NewDependentString(cx, str, begin, length); + if (!str) + return JS_FALSE; + } + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; + } + } + + NORMALIZE_THIS(cx, vp, str); + + if (argc != 0) { + double begin, end, length; + + begin = js_ValueToNumber(cx, &vp[2]); + if (JSVAL_IS_NULL(vp[2])) + return JS_FALSE; + begin = js_DoubleToInteger(begin); + length = str->length(); + if (begin < 0) { + begin += length; + if (begin < 0) + begin = 0; + } else if (begin > length) { + begin = length; + } + + if (argc == 1) { + end = length; + } else { + end = js_ValueToNumber(cx, &vp[3]); + if (JSVAL_IS_NULL(vp[3])) + return JS_FALSE; + end = js_DoubleToInteger(end); + if (end < 0) { + end += length; + if (end < 0) + end = 0; + } else if (end > length) { + end = length; + } + if (end < begin) + end = begin; + } + + str = js_NewDependentString(cx, str, + (size_t)begin, + (size_t)(end - begin)); + if (!str) + return JS_FALSE; + } + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +#if JS_HAS_STR_HTML_HELPERS +/* + * HTML composition aids. + */ +static JSBool +tagify(JSContext *cx, const char *begin, JSString *param, const char *end, + jsval *vp) +{ + JSString *str; + jschar *tagbuf; + size_t beglen, endlen, parlen, taglen; + size_t i, j; + + NORMALIZE_THIS(cx, vp, str); + + if (!end) + end = begin; + + beglen = strlen(begin); + taglen = 1 + beglen + 1; /* '' */ + parlen = 0; /* Avoid warning. */ + if (param) { + parlen = param->length(); + taglen += 2 + parlen + 1; /* '="param"' */ + } + endlen = strlen(end); + taglen += str->length() + 2 + endlen + 1; /* 'str' */ + + if (taglen >= ~(size_t)0 / sizeof(jschar)) { + js_ReportAllocationOverflow(cx); + return JS_FALSE; + } + + tagbuf = (jschar *) cx->malloc((taglen + 1) * sizeof(jschar)); + if (!tagbuf) + return JS_FALSE; + + j = 0; + tagbuf[j++] = '<'; + for (i = 0; i < beglen; i++) + tagbuf[j++] = (jschar)begin[i]; + if (param) { + tagbuf[j++] = '='; + tagbuf[j++] = '"'; + js_strncpy(&tagbuf[j], param->chars(), parlen); + j += parlen; + tagbuf[j++] = '"'; + } + tagbuf[j++] = '>'; + js_strncpy(&tagbuf[j], str->chars(), str->length()); + j += str->length(); + tagbuf[j++] = '<'; + tagbuf[j++] = '/'; + for (i = 0; i < endlen; i++) + tagbuf[j++] = (jschar)end[i]; + tagbuf[j++] = '>'; + JS_ASSERT(j == taglen); + tagbuf[j] = 0; + + str = js_NewString(cx, tagbuf, taglen); + if (!str) { + js_free((char *)tagbuf); + return JS_FALSE; + } + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +tagify_value(JSContext *cx, uintN argc, jsval *vp, + const char *begin, const char *end) +{ + JSString *param; + + param = ArgToRootedString(cx, argc, vp, 0); + if (!param) + return JS_FALSE; + return tagify(cx, begin, param, end, vp); +} + +static JSBool +str_bold(JSContext *cx, uintN argc, jsval *vp) +{ + return tagify(cx, "b", NULL, NULL, vp); +} + +static JSBool +str_italics(JSContext *cx, uintN argc, jsval *vp) +{ + return tagify(cx, "i", NULL, NULL, vp); +} + +static JSBool +str_fixed(JSContext *cx, uintN argc, jsval *vp) +{ + return tagify(cx, "tt", NULL, NULL, vp); +} + +static JSBool +str_fontsize(JSContext *cx, uintN argc, jsval *vp) +{ + return tagify_value(cx, argc, vp, "font size", "font"); +} + +static JSBool +str_fontcolor(JSContext *cx, uintN argc, jsval *vp) +{ + return tagify_value(cx, argc, vp, "font color", "font"); +} + +static JSBool +str_link(JSContext *cx, uintN argc, jsval *vp) +{ + return tagify_value(cx, argc, vp, "a href", "a"); +} + +static JSBool +str_anchor(JSContext *cx, uintN argc, jsval *vp) +{ + return tagify_value(cx, argc, vp, "a name", "a"); +} + +static JSBool +str_strike(JSContext *cx, uintN argc, jsval *vp) +{ + return tagify(cx, "strike", NULL, NULL, vp); +} + +static JSBool +str_small(JSContext *cx, uintN argc, jsval *vp) +{ + return tagify(cx, "small", NULL, NULL, vp); +} + +static JSBool +str_big(JSContext *cx, uintN argc, jsval *vp) +{ + return tagify(cx, "big", NULL, NULL, vp); +} + +static JSBool +str_blink(JSContext *cx, uintN argc, jsval *vp) +{ + return tagify(cx, "blink", NULL, NULL, vp); +} + +static JSBool +str_sup(JSContext *cx, uintN argc, jsval *vp) +{ + return tagify(cx, "sup", NULL, NULL, vp); +} + +static JSBool +str_sub(JSContext *cx, uintN argc, jsval *vp) +{ + return tagify(cx, "sub", NULL, NULL, vp); +} +#endif /* JS_HAS_STR_HTML_HELPERS */ + +#ifdef JS_TRACER +JSString* FASTCALL +js_String_getelem(JSContext* cx, JSString* str, int32 i) +{ + if ((size_t)i >= str->length()) + return NULL; + return JSString::getUnitString(cx, str, size_t(i)); +} +#endif + +JS_DEFINE_TRCINFO_1(js_str_toString, + (2, (extern, STRING_RETRY, String_p_toString, CONTEXT, THIS, 1, 1))) +JS_DEFINE_TRCINFO_1(str_charAt, + (3, (extern, STRING_RETRY, js_String_getelem, CONTEXT, THIS_STRING, INT32, 1, 1))) +JS_DEFINE_TRCINFO_2(str_charCodeAt, + (1, (extern, DOUBLE, js_String_p_charCodeAt0, THIS_STRING, 1, 1)), + (2, (extern, DOUBLE, js_String_p_charCodeAt, THIS_STRING, DOUBLE, 1, 1))) +JS_DEFINE_TRCINFO_1(str_concat, + (3, (extern, STRING_RETRY, js_ConcatStrings, CONTEXT, THIS_STRING, STRING, 1, 1))) + +#define GENERIC JSFUN_GENERIC_NATIVE +#define PRIMITIVE JSFUN_THISP_PRIMITIVE +#define GENERIC_PRIMITIVE (GENERIC | PRIMITIVE) + +static JSFunctionSpec string_methods[] = { +#if JS_HAS_TOSOURCE + JS_FN("quote", str_quote, 0,GENERIC_PRIMITIVE), + JS_FN(js_toSource_str, str_toSource, 0,JSFUN_THISP_STRING), +#endif + + /* Java-like methods. */ + JS_TN(js_toString_str, js_str_toString, 0,JSFUN_THISP_STRING, &js_str_toString_trcinfo), + JS_FN(js_valueOf_str, js_str_toString, 0,JSFUN_THISP_STRING), + JS_FN(js_toJSON_str, js_str_toString, 0,JSFUN_THISP_STRING), + JS_FN("substring", str_substring, 2,GENERIC_PRIMITIVE), + JS_FN("toLowerCase", str_toLowerCase, 0,GENERIC_PRIMITIVE), + JS_FN("toUpperCase", str_toUpperCase, 0,GENERIC_PRIMITIVE), + JS_TN("charAt", str_charAt, 1,GENERIC_PRIMITIVE, &str_charAt_trcinfo), + JS_TN("charCodeAt", str_charCodeAt, 1,GENERIC_PRIMITIVE, &str_charCodeAt_trcinfo), + JS_FN("indexOf", str_indexOf, 1,GENERIC_PRIMITIVE), + JS_FN("lastIndexOf", str_lastIndexOf, 1,GENERIC_PRIMITIVE), + JS_FN("trim", str_trim, 0,GENERIC_PRIMITIVE), + JS_FN("trimLeft", str_trimLeft, 0,GENERIC_PRIMITIVE), + JS_FN("trimRight", str_trimRight, 0,GENERIC_PRIMITIVE), + JS_FN("toLocaleLowerCase", str_toLocaleLowerCase, 0,GENERIC_PRIMITIVE), + JS_FN("toLocaleUpperCase", str_toLocaleUpperCase, 0,GENERIC_PRIMITIVE), + JS_FN("localeCompare", str_localeCompare, 1,GENERIC_PRIMITIVE), + + /* Perl-ish methods (search is actually Python-esque). */ + JS_FN("match", str_match, 1,GENERIC_PRIMITIVE), + JS_FN("search", str_search, 1,GENERIC_PRIMITIVE), + JS_FN("replace", str_replace, 2,GENERIC_PRIMITIVE), + JS_FN("split", str_split, 2,GENERIC_PRIMITIVE), +#if JS_HAS_PERL_SUBSTR + JS_FN("substr", str_substr, 2,GENERIC_PRIMITIVE), +#endif + + /* Python-esque sequence methods. */ + JS_TN("concat", str_concat, 1,GENERIC_PRIMITIVE, &str_concat_trcinfo), + JS_FN("slice", str_slice, 2,GENERIC_PRIMITIVE), + + /* HTML string methods. */ +#if JS_HAS_STR_HTML_HELPERS + JS_FN("bold", str_bold, 0,PRIMITIVE), + JS_FN("italics", str_italics, 0,PRIMITIVE), + JS_FN("fixed", str_fixed, 0,PRIMITIVE), + JS_FN("fontsize", str_fontsize, 1,PRIMITIVE), + JS_FN("fontcolor", str_fontcolor, 1,PRIMITIVE), + JS_FN("link", str_link, 1,PRIMITIVE), + JS_FN("anchor", str_anchor, 1,PRIMITIVE), + JS_FN("strike", str_strike, 0,PRIMITIVE), + JS_FN("small", str_small, 0,PRIMITIVE), + JS_FN("big", str_big, 0,PRIMITIVE), + JS_FN("blink", str_blink, 0,PRIMITIVE), + JS_FN("sup", str_sup, 0,PRIMITIVE), + JS_FN("sub", str_sub, 0,PRIMITIVE), +#endif + + JS_FS_END +}; + +#define C(c) c, 0x00 + +/* + * String data for all unit strings (including zero-char backstop required for independent strings), + * packed into single array. + */ +static const jschar UnitStringData[] = { + C(0x00), C(0x01), C(0x02), C(0x03), C(0x04), C(0x05), C(0x06), C(0x07), + C(0x08), C(0x09), C(0x0a), C(0x0b), C(0x0c), C(0x0d), C(0x0e), C(0x0f), + C(0x10), C(0x11), C(0x12), C(0x13), C(0x14), C(0x15), C(0x16), C(0x17), + C(0x18), C(0x19), C(0x1a), C(0x1b), C(0x1c), C(0x1d), C(0x1e), C(0x1f), + C(0x20), C(0x21), C(0x22), C(0x23), C(0x24), C(0x25), C(0x26), C(0x27), + C(0x28), C(0x29), C(0x2a), C(0x2b), C(0x2c), C(0x2d), C(0x2e), C(0x2f), + C(0x30), C(0x31), C(0x32), C(0x33), C(0x34), C(0x35), C(0x36), C(0x37), + C(0x38), C(0x39), C(0x3a), C(0x3b), C(0x3c), C(0x3d), C(0x3e), C(0x3f), + C(0x40), C(0x41), C(0x42), C(0x43), C(0x44), C(0x45), C(0x46), C(0x47), + C(0x48), C(0x49), C(0x4a), C(0x4b), C(0x4c), C(0x4d), C(0x4e), C(0x4f), + C(0x50), C(0x51), C(0x52), C(0x53), C(0x54), C(0x55), C(0x56), C(0x57), + C(0x58), C(0x59), C(0x5a), C(0x5b), C(0x5c), C(0x5d), C(0x5e), C(0x5f), + C(0x60), C(0x61), C(0x62), C(0x63), C(0x64), C(0x65), C(0x66), C(0x67), + C(0x68), C(0x69), C(0x6a), C(0x6b), C(0x6c), C(0x6d), C(0x6e), C(0x6f), + C(0x70), C(0x71), C(0x72), C(0x73), C(0x74), C(0x75), C(0x76), C(0x77), + C(0x78), C(0x79), C(0x7a), C(0x7b), C(0x7c), C(0x7d), C(0x7e), C(0x7f), + C(0x80), C(0x81), C(0x82), C(0x83), C(0x84), C(0x85), C(0x86), C(0x87), + C(0x88), C(0x89), C(0x8a), C(0x8b), C(0x8c), C(0x8d), C(0x8e), C(0x8f), + C(0x90), C(0x91), C(0x92), C(0x93), C(0x94), C(0x95), C(0x96), C(0x97), + C(0x98), C(0x99), C(0x9a), C(0x9b), C(0x9c), C(0x9d), C(0x9e), C(0x9f), + C(0xa0), C(0xa1), C(0xa2), C(0xa3), C(0xa4), C(0xa5), C(0xa6), C(0xa7), + C(0xa8), C(0xa9), C(0xaa), C(0xab), C(0xac), C(0xad), C(0xae), C(0xaf), + C(0xb0), C(0xb1), C(0xb2), C(0xb3), C(0xb4), C(0xb5), C(0xb6), C(0xb7), + C(0xb8), C(0xb9), C(0xba), C(0xbb), C(0xbc), C(0xbd), C(0xbe), C(0xbf), + C(0xc0), C(0xc1), C(0xc2), C(0xc3), C(0xc4), C(0xc5), C(0xc6), C(0xc7), + C(0xc8), C(0xc9), C(0xca), C(0xcb), C(0xcc), C(0xcd), C(0xce), C(0xcf), + C(0xd0), C(0xd1), C(0xd2), C(0xd3), C(0xd4), C(0xd5), C(0xd6), C(0xd7), + C(0xd8), C(0xd9), C(0xda), C(0xdb), C(0xdc), C(0xdd), C(0xde), C(0xdf), + C(0xe0), C(0xe1), C(0xe2), C(0xe3), C(0xe4), C(0xe5), C(0xe6), C(0xe7), + C(0xe8), C(0xe9), C(0xea), C(0xeb), C(0xec), C(0xed), C(0xee), C(0xef), + C(0xf0), C(0xf1), C(0xf2), C(0xf3), C(0xf4), C(0xf5), C(0xf6), C(0xf7), + C(0xf8), C(0xf9), C(0xfa), C(0xfb), C(0xfc), C(0xfd), C(0xfe), C(0xff) +}; + +#define U(c) { 1 | JSString::ATOMIZED, {(jschar *)UnitStringData + (c) * 2} } + +#ifdef __SUNPRO_CC +#pragma pack(8) +#else +#pragma pack(push, 8) +#endif + +JSString JSString::unitStringTable[] +#ifdef __GNUC__ +__attribute__ ((aligned (8))) +#endif += { + U(0x00), U(0x01), U(0x02), U(0x03), U(0x04), U(0x05), U(0x06), U(0x07), + U(0x08), U(0x09), U(0x0a), U(0x0b), U(0x0c), U(0x0d), U(0x0e), U(0x0f), + U(0x10), U(0x11), U(0x12), U(0x13), U(0x14), U(0x15), U(0x16), U(0x17), + U(0x18), U(0x19), U(0x1a), U(0x1b), U(0x1c), U(0x1d), U(0x1e), U(0x1f), + U(0x20), U(0x21), U(0x22), U(0x23), U(0x24), U(0x25), U(0x26), U(0x27), + U(0x28), U(0x29), U(0x2a), U(0x2b), U(0x2c), U(0x2d), U(0x2e), U(0x2f), + U(0x30), U(0x31), U(0x32), U(0x33), U(0x34), U(0x35), U(0x36), U(0x37), + U(0x38), U(0x39), U(0x3a), U(0x3b), U(0x3c), U(0x3d), U(0x3e), U(0x3f), + U(0x40), U(0x41), U(0x42), U(0x43), U(0x44), U(0x45), U(0x46), U(0x47), + U(0x48), U(0x49), U(0x4a), U(0x4b), U(0x4c), U(0x4d), U(0x4e), U(0x4f), + U(0x50), U(0x51), U(0x52), U(0x53), U(0x54), U(0x55), U(0x56), U(0x57), + U(0x58), U(0x59), U(0x5a), U(0x5b), U(0x5c), U(0x5d), U(0x5e), U(0x5f), + U(0x60), U(0x61), U(0x62), U(0x63), U(0x64), U(0x65), U(0x66), U(0x67), + U(0x68), U(0x69), U(0x6a), U(0x6b), U(0x6c), U(0x6d), U(0x6e), U(0x6f), + U(0x70), U(0x71), U(0x72), U(0x73), U(0x74), U(0x75), U(0x76), U(0x77), + U(0x78), U(0x79), U(0x7a), U(0x7b), U(0x7c), U(0x7d), U(0x7e), U(0x7f), + U(0x80), U(0x81), U(0x82), U(0x83), U(0x84), U(0x85), U(0x86), U(0x87), + U(0x88), U(0x89), U(0x8a), U(0x8b), U(0x8c), U(0x8d), U(0x8e), U(0x8f), + U(0x90), U(0x91), U(0x92), U(0x93), U(0x94), U(0x95), U(0x96), U(0x97), + U(0x98), U(0x99), U(0x9a), U(0x9b), U(0x9c), U(0x9d), U(0x9e), U(0x9f), + U(0xa0), U(0xa1), U(0xa2), U(0xa3), U(0xa4), U(0xa5), U(0xa6), U(0xa7), + U(0xa8), U(0xa9), U(0xaa), U(0xab), U(0xac), U(0xad), U(0xae), U(0xaf), + U(0xb0), U(0xb1), U(0xb2), U(0xb3), U(0xb4), U(0xb5), U(0xb6), U(0xb7), + U(0xb8), U(0xb9), U(0xba), U(0xbb), U(0xbc), U(0xbd), U(0xbe), U(0xbf), + U(0xc0), U(0xc1), U(0xc2), U(0xc3), U(0xc4), U(0xc5), U(0xc6), U(0xc7), + U(0xc8), U(0xc9), U(0xca), U(0xcb), U(0xcc), U(0xcd), U(0xce), U(0xcf), + U(0xd0), U(0xd1), U(0xd2), U(0xd3), U(0xd4), U(0xd5), U(0xd6), U(0xd7), + U(0xd8), U(0xd9), U(0xda), U(0xdb), U(0xdc), U(0xdd), U(0xde), U(0xdf), + U(0xe0), U(0xe1), U(0xe2), U(0xe3), U(0xe4), U(0xe5), U(0xe6), U(0xe7), + U(0xe8), U(0xe9), U(0xea), U(0xeb), U(0xec), U(0xed), U(0xee), U(0xef), + U(0xf0), U(0xf1), U(0xf2), U(0xf3), U(0xf4), U(0xf5), U(0xf6), U(0xf7), + U(0xf8), U(0xf9), U(0xfa), U(0xfb), U(0xfc), U(0xfd), U(0xfe), U(0xff) +}; + +#ifdef __SUNPRO_CC +#pragma pack(0) +#else +#pragma pack(pop) +#endif + +#undef U + +#define O0(c) 0x30, C(c) +#define O1(c) 0x31, C(c) /* template for 10 .. 19 */ +#define O2(c) 0x32, C(c) /* template for 20 .. 29 */ +#define O3(c) 0x33, C(c) +#define O4(c) 0x34, C(c) +#define O5(c) 0x35, C(c) +#define O6(c) 0x36, C(c) +#define O7(c) 0x37, C(c) +#define O8(c) 0x38, C(c) +#define O9(c) 0x39, C(c) + +#define O10(c) 0x31, O0(c) /* template for 100 .. 109 */ +#define O11(c) 0x31, O1(c) /* template for 110 .. 119 */ +#define O12(c) 0x31, O2(c) +#define O13(c) 0x31, O3(c) +#define O14(c) 0x31, O4(c) +#define O15(c) 0x31, O5(c) +#define O16(c) 0x31, O6(c) +#define O17(c) 0x31, O7(c) +#define O18(c) 0x31, O8(c) +#define O19(c) 0x31, O9(c) +#define O20(c) 0x32, O0(c) +#define O21(c) 0x32, O1(c) +#define O22(c) 0x32, O2(c) +#define O23(c) 0x32, O3(c) +#define O24(c) 0x32, O4(c) +#define O25(c) 0x32, O5(c) + +/* + * Array starts with 100, 101, 102... (0x31 0x30 0x30 0x00 for 100\0) + * 100, 101, 102 also share the pointers to 0, 1, 2 ... + * 110, 111, 112 also share the pointers to 10, 11, 12... + */ +static const jschar Hundreds[] = { + O10(0x30), O10(0x31), O10(0x32), O10(0x33), O10(0x34), O10(0x35), O10(0x36), O10(0x37), O10(0x38), O10(0x39), + O11(0x30), O11(0x31), O11(0x32), O11(0x33), O11(0x34), O11(0x35), O11(0x36), O11(0x37), O11(0x38), O11(0x39), + O12(0x30), O12(0x31), O12(0x32), O12(0x33), O12(0x34), O12(0x35), O12(0x36), O12(0x37), O12(0x38), O12(0x39), + O13(0x30), O13(0x31), O13(0x32), O13(0x33), O13(0x34), O13(0x35), O13(0x36), O13(0x37), O13(0x38), O13(0x39), + O14(0x30), O14(0x31), O14(0x32), O14(0x33), O14(0x34), O14(0x35), O14(0x36), O14(0x37), O14(0x38), O14(0x39), + O15(0x30), O15(0x31), O15(0x32), O15(0x33), O15(0x34), O15(0x35), O15(0x36), O15(0x37), O15(0x38), O15(0x39), + O16(0x30), O16(0x31), O16(0x32), O16(0x33), O16(0x34), O16(0x35), O16(0x36), O16(0x37), O16(0x38), O16(0x39), + O17(0x30), O17(0x31), O17(0x32), O17(0x33), O17(0x34), O17(0x35), O17(0x36), O17(0x37), O17(0x38), O17(0x39), + O18(0x30), O18(0x31), O18(0x32), O18(0x33), O18(0x34), O18(0x35), O18(0x36), O18(0x37), O18(0x38), O18(0x39), + O19(0x30), O19(0x31), O19(0x32), O19(0x33), O19(0x34), O19(0x35), O19(0x36), O19(0x37), O19(0x38), O19(0x39), + O20(0x30), O20(0x31), O20(0x32), O20(0x33), O20(0x34), O20(0x35), O20(0x36), O20(0x37), O20(0x38), O20(0x39), + O21(0x30), O21(0x31), O21(0x32), O21(0x33), O21(0x34), O21(0x35), O21(0x36), O21(0x37), O21(0x38), O21(0x39), + O22(0x30), O22(0x31), O22(0x32), O22(0x33), O22(0x34), O22(0x35), O22(0x36), O22(0x37), O22(0x38), O22(0x39), + O23(0x30), O23(0x31), O23(0x32), O23(0x33), O23(0x34), O23(0x35), O23(0x36), O23(0x37), O23(0x38), O23(0x39), + O24(0x30), O24(0x31), O24(0x32), O24(0x33), O24(0x34), O24(0x35), O24(0x36), O24(0x37), O24(0x38), O24(0x39), + O25(0x30), O25(0x31), O25(0x32), O25(0x33), O25(0x34), O25(0x35) +}; + +#define L1(c) { 1 | JSString::ATOMIZED, {(jschar *)Hundreds + 2 + (c) * 4} } /* length 1: 0..9 */ +#define L2(c) { 2 | JSString::ATOMIZED, {(jschar *)Hundreds + 41 + (c - 10) * 4} } /* length 2: 10..99 */ +#define L3(c) { 3 | JSString::ATOMIZED, {(jschar *)Hundreds + (c - 100) * 4} } /* length 3: 100..255 */ + +#ifdef __SUNPRO_CC +#pragma pack(8) +#else +#pragma pack(push, 8) +#endif + +JSString JSString::intStringTable[] +#ifdef __GNUC__ +__attribute__ ((aligned (8))) +#endif += { + L1(0x00), L1(0x01), L1(0x02), L1(0x03), L1(0x04), L1(0x05), L1(0x06), L1(0x07), + L1(0x08), L1(0x09), L2(0x0a), L2(0x0b), L2(0x0c), L2(0x0d), L2(0x0e), L2(0x0f), + L2(0x10), L2(0x11), L2(0x12), L2(0x13), L2(0x14), L2(0x15), L2(0x16), L2(0x17), + L2(0x18), L2(0x19), L2(0x1a), L2(0x1b), L2(0x1c), L2(0x1d), L2(0x1e), L2(0x1f), + L2(0x20), L2(0x21), L2(0x22), L2(0x23), L2(0x24), L2(0x25), L2(0x26), L2(0x27), + L2(0x28), L2(0x29), L2(0x2a), L2(0x2b), L2(0x2c), L2(0x2d), L2(0x2e), L2(0x2f), + L2(0x30), L2(0x31), L2(0x32), L2(0x33), L2(0x34), L2(0x35), L2(0x36), L2(0x37), + L2(0x38), L2(0x39), L2(0x3a), L2(0x3b), L2(0x3c), L2(0x3d), L2(0x3e), L2(0x3f), + L2(0x40), L2(0x41), L2(0x42), L2(0x43), L2(0x44), L2(0x45), L2(0x46), L2(0x47), + L2(0x48), L2(0x49), L2(0x4a), L2(0x4b), L2(0x4c), L2(0x4d), L2(0x4e), L2(0x4f), + L2(0x50), L2(0x51), L2(0x52), L2(0x53), L2(0x54), L2(0x55), L2(0x56), L2(0x57), + L2(0x58), L2(0x59), L2(0x5a), L2(0x5b), L2(0x5c), L2(0x5d), L2(0x5e), L2(0x5f), + L2(0x60), L2(0x61), L2(0x62), L2(0x63), L3(0x64), L3(0x65), L3(0x66), L3(0x67), + L3(0x68), L3(0x69), L3(0x6a), L3(0x6b), L3(0x6c), L3(0x6d), L3(0x6e), L3(0x6f), + L3(0x70), L3(0x71), L3(0x72), L3(0x73), L3(0x74), L3(0x75), L3(0x76), L3(0x77), + L3(0x78), L3(0x79), L3(0x7a), L3(0x7b), L3(0x7c), L3(0x7d), L3(0x7e), L3(0x7f), + L3(0x80), L3(0x81), L3(0x82), L3(0x83), L3(0x84), L3(0x85), L3(0x86), L3(0x87), + L3(0x88), L3(0x89), L3(0x8a), L3(0x8b), L3(0x8c), L3(0x8d), L3(0x8e), L3(0x8f), + L3(0x90), L3(0x91), L3(0x92), L3(0x93), L3(0x94), L3(0x95), L3(0x96), L3(0x97), + L3(0x98), L3(0x99), L3(0x9a), L3(0x9b), L3(0x9c), L3(0x9d), L3(0x9e), L3(0x9f), + L3(0xa0), L3(0xa1), L3(0xa2), L3(0xa3), L3(0xa4), L3(0xa5), L3(0xa6), L3(0xa7), + L3(0xa8), L3(0xa9), L3(0xaa), L3(0xab), L3(0xac), L3(0xad), L3(0xae), L3(0xaf), + L3(0xb0), L3(0xb1), L3(0xb2), L3(0xb3), L3(0xb4), L3(0xb5), L3(0xb6), L3(0xb7), + L3(0xb8), L3(0xb9), L3(0xba), L3(0xbb), L3(0xbc), L3(0xbd), L3(0xbe), L3(0xbf), + L3(0xc0), L3(0xc1), L3(0xc2), L3(0xc3), L3(0xc4), L3(0xc5), L3(0xc6), L3(0xc7), + L3(0xc8), L3(0xc9), L3(0xca), L3(0xcb), L3(0xcc), L3(0xcd), L3(0xce), L3(0xcf), + L3(0xd0), L3(0xd1), L3(0xd2), L3(0xd3), L3(0xd4), L3(0xd5), L3(0xd6), L3(0xd7), + L3(0xd8), L3(0xd9), L3(0xda), L3(0xdb), L3(0xdc), L3(0xdd), L3(0xde), L3(0xdf), + L3(0xe0), L3(0xe1), L3(0xe2), L3(0xe3), L3(0xe4), L3(0xe5), L3(0xe6), L3(0xe7), + L3(0xe8), L3(0xe9), L3(0xea), L3(0xeb), L3(0xec), L3(0xed), L3(0xee), L3(0xef), + L3(0xf0), L3(0xf1), L3(0xf2), L3(0xf3), L3(0xf4), L3(0xf5), L3(0xf6), L3(0xf7), + L3(0xf8), L3(0xf9), L3(0xfa), L3(0xfb), L3(0xfc), L3(0xfd), L3(0xfe), L3(0xff) +}; + +#ifdef __SUNPRO_CC +#pragma pack(0) +#else +#pragma pack(pop) +#endif + +#undef L1 +#undef L2 +#undef L3 + +static const char AsciiHundreds[] = { + O10(0x30), O10(0x31), O10(0x32), O10(0x33), O10(0x34), O10(0x35), O10(0x36), O10(0x37), O10(0x38), O10(0x39), + O11(0x30), O11(0x31), O11(0x32), O11(0x33), O11(0x34), O11(0x35), O11(0x36), O11(0x37), O11(0x38), O11(0x39), + O12(0x30), O12(0x31), O12(0x32), O12(0x33), O12(0x34), O12(0x35), O12(0x36), O12(0x37), O12(0x38), O12(0x39), + O13(0x30), O13(0x31), O13(0x32), O13(0x33), O13(0x34), O13(0x35), O13(0x36), O13(0x37), O13(0x38), O13(0x39), + O14(0x30), O14(0x31), O14(0x32), O14(0x33), O14(0x34), O14(0x35), O14(0x36), O14(0x37), O14(0x38), O14(0x39), + O15(0x30), O15(0x31), O15(0x32), O15(0x33), O15(0x34), O15(0x35), O15(0x36), O15(0x37), O15(0x38), O15(0x39), + O16(0x30), O16(0x31), O16(0x32), O16(0x33), O16(0x34), O16(0x35), O16(0x36), O16(0x37), O16(0x38), O16(0x39), + O17(0x30), O17(0x31), O17(0x32), O17(0x33), O17(0x34), O17(0x35), O17(0x36), O17(0x37), O17(0x38), O17(0x39), + O18(0x30), O18(0x31), O18(0x32), O18(0x33), O18(0x34), O18(0x35), O18(0x36), O18(0x37), O18(0x38), O18(0x39), + O19(0x30), O19(0x31), O19(0x32), O19(0x33), O19(0x34), O19(0x35), O19(0x36), O19(0x37), O19(0x38), O19(0x39), + O20(0x30), O20(0x31), O20(0x32), O20(0x33), O20(0x34), O20(0x35), O20(0x36), O20(0x37), O20(0x38), O20(0x39), + O21(0x30), O21(0x31), O21(0x32), O21(0x33), O21(0x34), O21(0x35), O21(0x36), O21(0x37), O21(0x38), O21(0x39), + O22(0x30), O22(0x31), O22(0x32), O22(0x33), O22(0x34), O22(0x35), O22(0x36), O22(0x37), O22(0x38), O22(0x39), + O23(0x30), O23(0x31), O23(0x32), O23(0x33), O23(0x34), O23(0x35), O23(0x36), O23(0x37), O23(0x38), O23(0x39), + O24(0x30), O24(0x31), O24(0x32), O24(0x33), O24(0x34), O24(0x35), O24(0x36), O24(0x37), O24(0x38), O24(0x39), + O25(0x30), O25(0x31), O25(0x32), O25(0x33), O25(0x34), O25(0x35) +}; + +#define L1(c) (AsciiHundreds + 2 + (c) * 4) /* length 1: 0..9 */ +#define L2(c) (AsciiHundreds + 41 + (c - 10) * 4) /* length 2: 10..99 */ +#define L3(c) (AsciiHundreds + (c - 100) * 4) /* length 3: 100..255 */ + +const char *JSString::deflatedIntStringTable[] = { + L1(0x00), L1(0x01), L1(0x02), L1(0x03), L1(0x04), L1(0x05), L1(0x06), L1(0x07), + L1(0x08), L1(0x09), L2(0x0a), L2(0x0b), L2(0x0c), L2(0x0d), L2(0x0e), L2(0x0f), + L2(0x10), L2(0x11), L2(0x12), L2(0x13), L2(0x14), L2(0x15), L2(0x16), L2(0x17), + L2(0x18), L2(0x19), L2(0x1a), L2(0x1b), L2(0x1c), L2(0x1d), L2(0x1e), L2(0x1f), + L2(0x20), L2(0x21), L2(0x22), L2(0x23), L2(0x24), L2(0x25), L2(0x26), L2(0x27), + L2(0x28), L2(0x29), L2(0x2a), L2(0x2b), L2(0x2c), L2(0x2d), L2(0x2e), L2(0x2f), + L2(0x30), L2(0x31), L2(0x32), L2(0x33), L2(0x34), L2(0x35), L2(0x36), L2(0x37), + L2(0x38), L2(0x39), L2(0x3a), L2(0x3b), L2(0x3c), L2(0x3d), L2(0x3e), L2(0x3f), + L2(0x40), L2(0x41), L2(0x42), L2(0x43), L2(0x44), L2(0x45), L2(0x46), L2(0x47), + L2(0x48), L2(0x49), L2(0x4a), L2(0x4b), L2(0x4c), L2(0x4d), L2(0x4e), L2(0x4f), + L2(0x50), L2(0x51), L2(0x52), L2(0x53), L2(0x54), L2(0x55), L2(0x56), L2(0x57), + L2(0x58), L2(0x59), L2(0x5a), L2(0x5b), L2(0x5c), L2(0x5d), L2(0x5e), L2(0x5f), + L2(0x60), L2(0x61), L2(0x62), L2(0x63), L3(0x64), L3(0x65), L3(0x66), L3(0x67), + L3(0x68), L3(0x69), L3(0x6a), L3(0x6b), L3(0x6c), L3(0x6d), L3(0x6e), L3(0x6f), + L3(0x70), L3(0x71), L3(0x72), L3(0x73), L3(0x74), L3(0x75), L3(0x76), L3(0x77), + L3(0x78), L3(0x79), L3(0x7a), L3(0x7b), L3(0x7c), L3(0x7d), L3(0x7e), L3(0x7f), + L3(0x80), L3(0x81), L3(0x82), L3(0x83), L3(0x84), L3(0x85), L3(0x86), L3(0x87), + L3(0x88), L3(0x89), L3(0x8a), L3(0x8b), L3(0x8c), L3(0x8d), L3(0x8e), L3(0x8f), + L3(0x90), L3(0x91), L3(0x92), L3(0x93), L3(0x94), L3(0x95), L3(0x96), L3(0x97), + L3(0x98), L3(0x99), L3(0x9a), L3(0x9b), L3(0x9c), L3(0x9d), L3(0x9e), L3(0x9f), + L3(0xa0), L3(0xa1), L3(0xa2), L3(0xa3), L3(0xa4), L3(0xa5), L3(0xa6), L3(0xa7), + L3(0xa8), L3(0xa9), L3(0xaa), L3(0xab), L3(0xac), L3(0xad), L3(0xae), L3(0xaf), + L3(0xb0), L3(0xb1), L3(0xb2), L3(0xb3), L3(0xb4), L3(0xb5), L3(0xb6), L3(0xb7), + L3(0xb8), L3(0xb9), L3(0xba), L3(0xbb), L3(0xbc), L3(0xbd), L3(0xbe), L3(0xbf), + L3(0xc0), L3(0xc1), L3(0xc2), L3(0xc3), L3(0xc4), L3(0xc5), L3(0xc6), L3(0xc7), + L3(0xc8), L3(0xc9), L3(0xca), L3(0xcb), L3(0xcc), L3(0xcd), L3(0xce), L3(0xcf), + L3(0xd0), L3(0xd1), L3(0xd2), L3(0xd3), L3(0xd4), L3(0xd5), L3(0xd6), L3(0xd7), + L3(0xd8), L3(0xd9), L3(0xda), L3(0xdb), L3(0xdc), L3(0xdd), L3(0xde), L3(0xdf), + L3(0xe0), L3(0xe1), L3(0xe2), L3(0xe3), L3(0xe4), L3(0xe5), L3(0xe6), L3(0xe7), + L3(0xe8), L3(0xe9), L3(0xea), L3(0xeb), L3(0xec), L3(0xed), L3(0xee), L3(0xef), + L3(0xf0), L3(0xf1), L3(0xf2), L3(0xf3), L3(0xf4), L3(0xf5), L3(0xf6), L3(0xf7), + L3(0xf8), L3(0xf9), L3(0xfa), L3(0xfb), L3(0xfc), L3(0xfd), L3(0xfe), L3(0xff) +}; + +#undef L1 +#undef L2 +#undef L3 + +#undef C + +#undef O0 +#undef O1 +#undef O2 +#undef O3 +#undef O4 +#undef O5 +#undef O6 +#undef O7 +#undef O8 +#undef O9 +#undef O10 +#undef O11 +#undef O12 +#undef O13 +#undef O14 +#undef O15 +#undef O16 +#undef O17 +#undef O18 +#undef O19 +#undef O20 +#undef O21 +#undef O22 +#undef O23 +#undef O24 +#undef O25 + +JSBool +js_String(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + + if (argc > 0) { + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); + } else { + str = cx->runtime->emptyString; + } + if (!JS_IsConstructing(cx)) { + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; + } + obj->fslots[JSSLOT_PRIMITIVE_THIS] = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +#ifdef JS_TRACER + +JSObject* FASTCALL +js_String_tn(JSContext* cx, JSObject* proto, JSString* str) +{ + JS_ASSERT(JS_ON_TRACE(cx)); + return js_NewObjectWithClassProto(cx, &js_StringClass, proto, STRING_TO_JSVAL(str)); +} +JS_DEFINE_CALLINFO_3(extern, OBJECT, js_String_tn, CONTEXT, CALLEE_PROTOTYPE, STRING, 0, 0) + +#endif /* !JS_TRACER */ + +static JSBool +str_fromCharCode(JSContext *cx, uintN argc, jsval *vp) +{ + jsval *argv; + uintN i; + uint16 code; + jschar *chars; + JSString *str; + + argv = vp + 2; + JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX); + if (argc == 1 && + (code = js_ValueToUint16(cx, &argv[0])) < UNIT_STRING_LIMIT) { + str = JSString::unitString(code); + if (!str) + return JS_FALSE; + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; + } + chars = (jschar *) cx->malloc((argc + 1) * sizeof(jschar)); + if (!chars) + return JS_FALSE; + for (i = 0; i < argc; i++) { + code = js_ValueToUint16(cx, &argv[i]); + if (JSVAL_IS_NULL(argv[i])) { + cx->free(chars); + return JS_FALSE; + } + chars[i] = (jschar)code; + } + chars[i] = 0; + str = js_NewString(cx, chars, argc); + if (!str) { + cx->free(chars); + return JS_FALSE; + } + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +#ifdef JS_TRACER +static JSString* FASTCALL +String_fromCharCode(JSContext* cx, int32 i) +{ + JS_ASSERT(JS_ON_TRACE(cx)); + jschar c = (jschar)i; + if (c < UNIT_STRING_LIMIT) + return JSString::unitString(c); + return js_NewStringCopyN(cx, &c, 1); +} +#endif + +JS_DEFINE_TRCINFO_1(str_fromCharCode, + (2, (static, STRING_RETRY, String_fromCharCode, CONTEXT, INT32, 1, 1))) + +static JSFunctionSpec string_static_methods[] = { + JS_TN("fromCharCode", str_fromCharCode, 1, 0, &str_fromCharCode_trcinfo), + JS_FS_END +}; + +static JSHashNumber +js_hash_string_pointer(const void *key) +{ + return (JSHashNumber)JS_PTR_TO_UINT32(key) >> JSVAL_TAGBITS; +} + +JSBool +js_InitRuntimeStringState(JSContext *cx) +{ + JSRuntime *rt; + + rt = cx->runtime; + rt->emptyString = ATOM_TO_STRING(rt->atomState.emptyAtom); + return JS_TRUE; +} + +JSBool +js_InitDeflatedStringCache(JSRuntime *rt) +{ + JSHashTable *cache; + + /* Initialize string cache */ + JS_ASSERT(!rt->deflatedStringCache); + cache = JS_NewHashTable(8, js_hash_string_pointer, + JS_CompareValues, JS_CompareValues, + NULL, NULL); + if (!cache) + return JS_FALSE; + rt->deflatedStringCache = cache; + +#ifdef JS_THREADSAFE + JS_ASSERT(!rt->deflatedStringCacheLock); + rt->deflatedStringCacheLock = JS_NEW_LOCK(); + if (!rt->deflatedStringCacheLock) + return JS_FALSE; +#endif + return JS_TRUE; +} + +void +js_FinishRuntimeStringState(JSContext *cx) +{ + cx->runtime->emptyString = NULL; +} + +void +js_FinishDeflatedStringCache(JSRuntime *rt) +{ + if (rt->deflatedStringCache) { + JS_HashTableDestroy(rt->deflatedStringCache); + rt->deflatedStringCache = NULL; + } +#ifdef JS_THREADSAFE + if (rt->deflatedStringCacheLock) { + JS_DESTROY_LOCK(rt->deflatedStringCacheLock); + rt->deflatedStringCacheLock = NULL; + } +#endif +} + +JSObject * +js_InitStringClass(JSContext *cx, JSObject *obj) +{ + JSObject *proto; + + /* Define the escape, unescape functions in the global object. */ + if (!JS_DefineFunctions(cx, obj, string_functions)) + return NULL; + + proto = JS_InitClass(cx, obj, NULL, &js_StringClass, js_String, 1, + NULL, string_methods, + NULL, string_static_methods); + if (!proto) + return NULL; + proto->fslots[JSSLOT_PRIMITIVE_THIS] = STRING_TO_JSVAL(cx->runtime->emptyString); + if (!js_DefineNativeProperty(cx, proto, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), + JSVAL_VOID, NULL, NULL, + JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0, + NULL)) { + return JS_FALSE; + } + + return proto; +} + +JSString * +js_NewString(JSContext *cx, jschar *chars, size_t length) +{ + JSString *str; + + if (length > JSString::MAX_LENGTH) { + if (JS_ON_TRACE(cx)) { + /* + * If we can't leave the trace, signal OOM condition, otherwise + * exit from trace before throwing. + */ + if (!js_CanLeaveTrace(cx)) + return NULL; + + js_LeaveTrace(cx); + } + js_ReportAllocationOverflow(cx); + return NULL; + } + + str = js_NewGCString(cx); + if (!str) + return NULL; + str->initFlat(chars, length); +#ifdef DEBUG + { + JSRuntime *rt = cx->runtime; + JS_RUNTIME_METER(rt, liveStrings); + JS_RUNTIME_METER(rt, totalStrings); + JS_LOCK_RUNTIME_VOID(rt, + (rt->lengthSum += (double)length, + rt->lengthSquaredSum += (double)length * (double)length)); + } +#endif + return str; +} + +static const size_t sMinWasteSize = 16; + +JSString * +js_NewStringFromCharBuffer(JSContext *cx, JSCharBuffer &cb) +{ + if (cb.empty()) + return ATOM_TO_STRING(cx->runtime->atomState.emptyAtom); + + size_t length = cb.length(); + if (!cb.append('\0')) + return NULL; + + size_t capacity = cb.capacity(); + + jschar *buf = cb.extractRawBuffer(); + if (!buf) + return NULL; + + /* For medium/big buffers, avoid wasting more than 1/4 of the memory. */ + JS_ASSERT(capacity >= length); + if (capacity > sMinWasteSize && capacity - length > (length >> 2)) { + size_t bytes = sizeof(jschar) * (length + 1); + jschar *tmp = (jschar *)cx->realloc(buf, bytes); + if (!tmp) { + cx->free(buf); + return NULL; + } + buf = tmp; + } + + JSString *str = js_NewString(cx, buf, length); + if (!str) + cx->free(buf); + return str; +} + +JSString * +js_NewDependentString(JSContext *cx, JSString *base, size_t start, + size_t length) +{ + JSString *ds; + + if (length == 0) + return cx->runtime->emptyString; + + if (start == 0 && length == base->length()) + return base; + + if (start > JSString::MAX_DEPENDENT_START || + (start != 0 && length > JSString::MAX_DEPENDENT_LENGTH)) { + return js_NewStringCopyN(cx, base->chars() + start, length); + } + + ds = js_NewGCString(cx); + if (!ds) + return NULL; + if (start == 0) + ds->initPrefix(base, length); + else + ds->initDependent(base, start, length); +#ifdef DEBUG + { + JSRuntime *rt = cx->runtime; + JS_RUNTIME_METER(rt, liveDependentStrings); + JS_RUNTIME_METER(rt, totalDependentStrings); + JS_RUNTIME_METER(rt, liveStrings); + JS_RUNTIME_METER(rt, totalStrings); + JS_LOCK_RUNTIME_VOID(rt, + (rt->strdepLengthSum += (double)length, + rt->strdepLengthSquaredSum += (double)length * (double)length)); + JS_LOCK_RUNTIME_VOID(rt, + (rt->lengthSum += (double)length, + rt->lengthSquaredSum += (double)length * (double)length)); + } +#endif + return ds; +} + +#ifdef DEBUG +#include + +void printJSStringStats(JSRuntime *rt) +{ + double mean, sigma; + + mean = JS_MeanAndStdDev(rt->totalStrings, rt->lengthSum, + rt->lengthSquaredSum, &sigma); + + fprintf(stderr, "%lu total strings, mean length %g (sigma %g)\n", + (unsigned long)rt->totalStrings, mean, sigma); + + mean = JS_MeanAndStdDev(rt->totalDependentStrings, rt->strdepLengthSum, + rt->strdepLengthSquaredSum, &sigma); + + fprintf(stderr, "%lu total dependent strings, mean length %g (sigma %g)\n", + (unsigned long)rt->totalDependentStrings, mean, sigma); +} +#endif + +JSString * +js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n) +{ + jschar *news; + JSString *str; + + news = (jschar *) cx->malloc((n + 1) * sizeof(jschar)); + if (!news) + return NULL; + js_strncpy(news, s, n); + news[n] = 0; + str = js_NewString(cx, news, n); + if (!str) + cx->free(news); + return str; +} + +JSString * +js_NewStringCopyZ(JSContext *cx, const jschar *s) +{ + size_t n, m; + jschar *news; + JSString *str; + + n = js_strlen(s); + m = (n + 1) * sizeof(jschar); + news = (jschar *) cx->malloc(m); + if (!news) + return NULL; + memcpy(news, s, m); + str = js_NewString(cx, news, n); + if (!str) + cx->free(news); + return str; +} + +void +js_PurgeDeflatedStringCache(JSRuntime *rt, JSString *str) +{ + JSHashNumber hash; + JSHashEntry *he, **hep; + + hash = js_hash_string_pointer(str); + JS_ACQUIRE_LOCK(rt->deflatedStringCacheLock); + hep = JS_HashTableRawLookup(rt->deflatedStringCache, hash, str); + he = *hep; + if (he) { +#ifdef DEBUG + rt->deflatedStringCacheBytes -= str->length(); +#endif + js_free(he->value); + JS_HashTableRawRemove(rt->deflatedStringCache, hep, he); + } + JS_RELEASE_LOCK(rt->deflatedStringCacheLock); +} + +JS_FRIEND_API(const char *) +js_ValueToPrintable(JSContext *cx, jsval v, JSValueToStringFun v2sfun) +{ + JSString *str; + + str = v2sfun(cx, v); + if (!str) + return NULL; + str = js_QuoteString(cx, str, 0); + if (!str) + return NULL; + return js_GetStringBytes(cx, str); +} + +JS_FRIEND_API(JSString *) +js_ValueToString(JSContext *cx, jsval v) +{ + JSString *str; + + if (!JSVAL_IS_PRIMITIVE(v) && !JSVAL_TO_OBJECT(v)->defaultValue(cx, JSTYPE_STRING, &v)) + return NULL; + + if (JSVAL_IS_STRING(v)) { + str = JSVAL_TO_STRING(v); + } else if (JSVAL_IS_INT(v)) { + str = js_NumberToString(cx, JSVAL_TO_INT(v)); + } else if (JSVAL_IS_DOUBLE(v)) { + str = js_NumberToString(cx, *JSVAL_TO_DOUBLE(v)); + } else if (JSVAL_IS_BOOLEAN(v)) { + str = js_BooleanToString(cx, JSVAL_TO_BOOLEAN(v)); + } else if (JSVAL_IS_NULL(v)) { + str = ATOM_TO_STRING(cx->runtime->atomState.nullAtom); + } else { + str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]); + } + return str; +} + +static inline JSBool +pushAtom(JSAtom *atom, JSCharBuffer &cb) +{ + JSString *str = ATOM_TO_STRING(atom); + const jschar *chars; + size_t length; + str->getCharsAndLength(chars, length); + return cb.append(chars, length); +} + +/* This function implements E-262-3 section 9.8, toString. */ +JS_FRIEND_API(JSBool) +js_ValueToCharBuffer(JSContext *cx, jsval v, JSCharBuffer &cb) +{ + if (!JSVAL_IS_PRIMITIVE(v) && !JSVAL_TO_OBJECT(v)->defaultValue(cx, JSTYPE_STRING, &v)) + return JS_FALSE; + + if (JSVAL_IS_STRING(v)) { + JSString *str = JSVAL_TO_STRING(v); + const jschar *chars; + size_t length; + str->getCharsAndLength(chars, length); + return cb.append(chars, length); + } + if (JSVAL_IS_NUMBER(v)) + return js_NumberValueToCharBuffer(cx, v, cb); + if (JSVAL_IS_BOOLEAN(v)) + return js_BooleanToCharBuffer(cx, JSVAL_TO_BOOLEAN(v), cb); + if (JSVAL_IS_NULL(v)) + return pushAtom(cx->runtime->atomState.nullAtom, cb); + JS_ASSERT(JSVAL_IS_VOID(v)); + return pushAtom(cx->runtime->atomState.typeAtoms[JSTYPE_VOID], cb); +} + +JS_FRIEND_API(JSString *) +js_ValueToSource(JSContext *cx, jsval v) +{ + if (JSVAL_IS_VOID(v)) + return ATOM_TO_STRING(cx->runtime->atomState.void0Atom); + if (JSVAL_IS_STRING(v)) + return js_QuoteString(cx, JSVAL_TO_STRING(v), '"'); + if (JSVAL_IS_PRIMITIVE(v)) { + /* Special case to preserve negative zero, _contra_ toString. */ + if (JSVAL_IS_DOUBLE(v) && JSDOUBLE_IS_NEGZERO(*JSVAL_TO_DOUBLE(v))) { + /* NB: _ucNstr rather than _ucstr to indicate non-terminated. */ + static const jschar js_negzero_ucNstr[] = {'-', '0'}; + + return js_NewStringCopyN(cx, js_negzero_ucNstr, 2); + } + return js_ValueToString(cx, v); + } + + JSAtom *atom = cx->runtime->atomState.toSourceAtom; + JSAutoTempValueRooter tvr(cx, JSVAL_NULL); + if (!js_TryMethod(cx, JSVAL_TO_OBJECT(v), atom, 0, NULL, tvr.addr())) + return NULL; + return js_ValueToString(cx, tvr.value()); +} + +/* + * str is not necessarily a GC thing here. + */ +uint32 +js_HashString(JSString *str) +{ + const jschar *s; + size_t n; + uint32 h; + + str->getCharsAndLength(s, n); + for (h = 0; n; s++, n--) + h = JS_ROTATE_LEFT32(h, 4) ^ *s; + return h; +} + +/* + * str is not necessarily a GC thing here. + */ +JSBool JS_FASTCALL +js_EqualStrings(JSString *str1, JSString *str2) +{ + size_t n; + const jschar *s1, *s2; + + JS_ASSERT(str1); + JS_ASSERT(str2); + + /* Fast case: pointer equality could be a quick win. */ + if (str1 == str2) + return JS_TRUE; + + n = str1->length(); + if (n != str2->length()) + return JS_FALSE; + + if (n == 0) + return JS_TRUE; + + s1 = str1->chars(), s2 = str2->chars(); + do { + if (*s1 != *s2) + return JS_FALSE; + ++s1, ++s2; + } while (--n != 0); + + return JS_TRUE; +} +JS_DEFINE_CALLINFO_2(extern, BOOL, js_EqualStrings, STRING, STRING, 1, 1) + +int32 JS_FASTCALL +js_CompareStrings(JSString *str1, JSString *str2) +{ + size_t l1, l2, n, i; + const jschar *s1, *s2; + intN cmp; + + JS_ASSERT(str1); + JS_ASSERT(str2); + + /* Fast case: pointer equality could be a quick win. */ + if (str1 == str2) + return 0; + + str1->getCharsAndLength(s1, l1); + str2->getCharsAndLength(s2, l2); + n = JS_MIN(l1, l2); + for (i = 0; i < n; i++) { + cmp = s1[i] - s2[i]; + if (cmp != 0) + return cmp; + } + return (intN)(l1 - l2); +} +JS_DEFINE_CALLINFO_2(extern, INT32, js_CompareStrings, STRING, STRING, 1, 1) + +size_t +js_strlen(const jschar *s) +{ + const jschar *t; + + for (t = s; *t != 0; t++) + continue; + return (size_t)(t - s); +} + +jschar * +js_strchr(const jschar *s, jschar c) +{ + while (*s != 0) { + if (*s == c) + return (jschar *)s; + s++; + } + return NULL; +} + +jschar * +js_strchr_limit(const jschar *s, jschar c, const jschar *limit) +{ + while (s < limit) { + if (*s == c) + return (jschar *)s; + s++; + } + return NULL; +} + +jschar * +js_InflateString(JSContext *cx, const char *bytes, size_t *lengthp) +{ + size_t nbytes, nchars, i; + jschar *chars; +#ifdef DEBUG + JSBool ok; +#endif + + nbytes = *lengthp; + if (js_CStringsAreUTF8) { + if (!js_InflateStringToBuffer(cx, bytes, nbytes, NULL, &nchars)) + goto bad; + chars = (jschar *) cx->malloc((nchars + 1) * sizeof (jschar)); + if (!chars) + goto bad; +#ifdef DEBUG + ok = +#endif + js_InflateStringToBuffer(cx, bytes, nbytes, chars, &nchars); + JS_ASSERT(ok); + } else { + nchars = nbytes; + chars = (jschar *) cx->malloc((nchars + 1) * sizeof(jschar)); + if (!chars) + goto bad; + for (i = 0; i < nchars; i++) + chars[i] = (unsigned char) bytes[i]; + } + *lengthp = nchars; + chars[nchars] = 0; + return chars; + + bad: + /* + * For compatibility with callers of JS_DecodeBytes we must zero lengthp + * on errors. + */ + *lengthp = 0; + return NULL; +} + +/* + * May be called with null cx by js_GetStringBytes, see below. + */ +char * +js_DeflateString(JSContext *cx, const jschar *chars, size_t nchars) +{ + size_t nbytes, i; + char *bytes; +#ifdef DEBUG + JSBool ok; +#endif + + if (js_CStringsAreUTF8) { + nbytes = js_GetDeflatedStringLength(cx, chars, nchars); + if (nbytes == (size_t) -1) + return NULL; + bytes = (char *) (cx ? cx->malloc(nbytes + 1) : js_malloc(nbytes + 1)); + if (!bytes) + return NULL; +#ifdef DEBUG + ok = +#endif + js_DeflateStringToBuffer(cx, chars, nchars, bytes, &nbytes); + JS_ASSERT(ok); + } else { + nbytes = nchars; + bytes = (char *) (cx ? cx->malloc(nbytes + 1) : js_malloc(nbytes + 1)); + if (!bytes) + return NULL; + for (i = 0; i < nbytes; i++) + bytes[i] = (char) chars[i]; + } + bytes[nbytes] = 0; + return bytes; +} + +/* + * May be called with null cx through js_GetStringBytes, see below. + */ +size_t +js_GetDeflatedStringLength(JSContext *cx, const jschar *chars, size_t nchars) +{ + size_t nbytes; + const jschar *end; + uintN c, c2; + char buffer[10]; + + if (!js_CStringsAreUTF8) + return nchars; + + nbytes = nchars; + for (end = chars + nchars; chars != end; chars++) { + c = *chars; + if (c < 0x80) + continue; + if (0xD800 <= c && c <= 0xDFFF) { + /* Surrogate pair. */ + chars++; + if (c >= 0xDC00 || chars == end) + goto bad_surrogate; + c2 = *chars; + if (c2 < 0xDC00 || c2 > 0xDFFF) + goto bad_surrogate; + c = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000; + } + c >>= 11; + nbytes++; + while (c) { + c >>= 5; + nbytes++; + } + } + return nbytes; + + bad_surrogate: + if (cx) { + JS_snprintf(buffer, 10, "0x%x", c); + JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, + NULL, JSMSG_BAD_SURROGATE_CHAR, buffer); + } + return (size_t) -1; +} + +JSBool +js_DeflateStringToBuffer(JSContext *cx, const jschar *src, size_t srclen, + char *dst, size_t *dstlenp) +{ + size_t dstlen, i, origDstlen, utf8Len; + jschar c, c2; + uint32 v; + uint8 utf8buf[6]; + + dstlen = *dstlenp; + if (!js_CStringsAreUTF8) { + if (srclen > dstlen) { + for (i = 0; i < dstlen; i++) + dst[i] = (char) src[i]; + if (cx) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BUFFER_TOO_SMALL); + } + return JS_FALSE; + } + for (i = 0; i < srclen; i++) + dst[i] = (char) src[i]; + *dstlenp = srclen; + return JS_TRUE; + } + + origDstlen = dstlen; + while (srclen) { + c = *src++; + srclen--; + if ((c >= 0xDC00) && (c <= 0xDFFF)) + goto badSurrogate; + if (c < 0xD800 || c > 0xDBFF) { + v = c; + } else { + if (srclen < 1) + goto badSurrogate; + c2 = *src; + if ((c2 < 0xDC00) || (c2 > 0xDFFF)) + goto badSurrogate; + src++; + srclen--; + v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000; + } + if (v < 0x0080) { + /* no encoding necessary - performance hack */ + if (dstlen == 0) + goto bufferTooSmall; + *dst++ = (char) v; + utf8Len = 1; + } else { + utf8Len = js_OneUcs4ToUtf8Char(utf8buf, v); + if (utf8Len > dstlen) + goto bufferTooSmall; + for (i = 0; i < utf8Len; i++) + *dst++ = (char) utf8buf[i]; + } + dstlen -= utf8Len; + } + *dstlenp = (origDstlen - dstlen); + return JS_TRUE; + +badSurrogate: + *dstlenp = (origDstlen - dstlen); + /* Delegate error reporting to the measurement function. */ + if (cx) + js_GetDeflatedStringLength(cx, src - 1, srclen + 1); + return JS_FALSE; + +bufferTooSmall: + *dstlenp = (origDstlen - dstlen); + if (cx) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BUFFER_TOO_SMALL); + } + return JS_FALSE; +} + +JSBool +js_InflateStringToBuffer(JSContext *cx, const char *src, size_t srclen, + jschar *dst, size_t *dstlenp) +{ + size_t dstlen, i, origDstlen, offset, j, n; + uint32 v; + + if (!js_CStringsAreUTF8) { + if (dst) { + dstlen = *dstlenp; + if (srclen > dstlen) { + for (i = 0; i < dstlen; i++) + dst[i] = (unsigned char) src[i]; + if (cx) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BUFFER_TOO_SMALL); + } + return JS_FALSE; + } + for (i = 0; i < srclen; i++) + dst[i] = (unsigned char) src[i]; + } + *dstlenp = srclen; + return JS_TRUE; + } + + dstlen = dst ? *dstlenp : (size_t) -1; + origDstlen = dstlen; + offset = 0; + + while (srclen) { + v = (uint8) *src; + n = 1; + if (v & 0x80) { + while (v & (0x80 >> n)) + n++; + if (n > srclen) + goto bufferTooSmall; + if (n == 1 || n > 4) + goto badCharacter; + for (j = 1; j < n; j++) { + if ((src[j] & 0xC0) != 0x80) + goto badCharacter; + } + v = Utf8ToOneUcs4Char((uint8 *)src, n); + if (v >= 0x10000) { + v -= 0x10000; + if (v > 0xFFFFF || dstlen < 2) { + *dstlenp = (origDstlen - dstlen); + if (cx) { + char buffer[10]; + JS_snprintf(buffer, 10, "0x%x", v + 0x10000); + JS_ReportErrorFlagsAndNumber(cx, + JSREPORT_ERROR, + js_GetErrorMessage, NULL, + JSMSG_UTF8_CHAR_TOO_LARGE, + buffer); + } + return JS_FALSE; + } + if (dstlen < 2) + goto bufferTooSmall; + if (dst) { + *dst++ = (jschar)((v >> 10) + 0xD800); + v = (jschar)((v & 0x3FF) + 0xDC00); + } + dstlen--; + } + } + if (!dstlen) + goto bufferTooSmall; + if (dst) + *dst++ = (jschar) v; + dstlen--; + offset += n; + src += n; + srclen -= n; + } + *dstlenp = (origDstlen - dstlen); + return JS_TRUE; + +badCharacter: + *dstlenp = (origDstlen - dstlen); + if (cx) { + char buffer[10]; + JS_snprintf(buffer, 10, "%d", offset); + JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, + js_GetErrorMessage, NULL, + JSMSG_MALFORMED_UTF8_CHAR, + buffer); + } + return JS_FALSE; + +bufferTooSmall: + *dstlenp = (origDstlen - dstlen); + if (cx) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BUFFER_TOO_SMALL); + } + return JS_FALSE; +} + +JSBool +js_SetStringBytes(JSContext *cx, JSString *str, char *bytes, size_t length) +{ + JSRuntime *rt; + JSHashTable *cache; + JSBool ok; + JSHashNumber hash; + JSHashEntry **hep; + + rt = cx->runtime; + JS_ACQUIRE_LOCK(rt->deflatedStringCacheLock); + + cache = rt->deflatedStringCache; + hash = js_hash_string_pointer(str); + hep = JS_HashTableRawLookup(cache, hash, str); + JS_ASSERT(*hep == NULL); + ok = JS_HashTableRawAdd(cache, hep, hash, str, bytes) != NULL; + if (ok) { + str->setDeflated(); +#ifdef DEBUG + rt->deflatedStringCacheBytes += length; +#endif + } + + JS_RELEASE_LOCK(rt->deflatedStringCacheLock); + return ok; +} + +const char * +js_GetStringBytes(JSContext *cx, JSString *str) +{ + JSRuntime *rt; + JSHashTable *cache; + char *bytes; + JSHashNumber hash; + JSHashEntry *he, **hep; + + if (JSString::isUnitString(str)) { +#ifdef IS_LITTLE_ENDIAN + /* Unit string data is {c, 0, 0, 0} so we can just cast. */ + return (char *)str->chars(); +#else + /* Unit string data is {0, c, 0, 0} so we can point into the middle. */ + return (char *)str->chars() + 1; +#endif + } + + if (JSString::isIntString(str)) { + /* + * We must burn some space on deflated int strings to preserve static + * allocation (which is to say, JSRuntime independence). + */ + return JSString::deflatedIntStringTable[str - JSString::intStringTable]; + } + + if (cx) { + rt = cx->runtime; + } else { + /* JS_GetStringBytes calls us with null cx. */ + rt = js_GetGCStringRuntime(str); + } + +#ifdef JS_THREADSAFE + if (!rt->deflatedStringCacheLock) { + /* + * Called from last GC (see js_DestroyContext), after runtime string + * state has been finalized. We have no choice but to leak here. + */ + return js_DeflateString(NULL, str->chars(), str->length()); + } +#endif + + JS_ACQUIRE_LOCK(rt->deflatedStringCacheLock); + + cache = rt->deflatedStringCache; + hash = js_hash_string_pointer(str); + hep = JS_HashTableRawLookup(cache, hash, str); + he = *hep; + if (he) { + bytes = (char *) he->value; + + /* Try to catch failure to JS_ShutDown between runtime epochs. */ + if (!js_CStringsAreUTF8) { + JS_ASSERT_IF(*bytes != (char) str->chars()[0], + *bytes == '\0' && str->empty()); + } + } else { + bytes = js_DeflateString(cx, str->chars(), str->length()); + if (bytes) { + if (JS_HashTableRawAdd(cache, hep, hash, str, bytes)) { +#ifdef DEBUG + rt->deflatedStringCacheBytes += str->length(); +#endif + str->setDeflated(); + } else { + if (cx) + cx->free(bytes); + else + js_free(bytes); + bytes = NULL; + } + } + } + + JS_RELEASE_LOCK(rt->deflatedStringCacheLock); + return bytes; +} + +/* + * From java.lang.Character.java: + * + * The character properties are currently encoded into 32 bits in the + * following manner: + * + * 10 bits signed offset used for converting case + * 1 bit if 1, adding the signed offset converts the character to + * lowercase + * 1 bit if 1, subtracting the signed offset converts the character to + * uppercase + * 1 bit if 1, character has a titlecase equivalent (possibly itself) + * 3 bits 0 may not be part of an identifier + * 1 ignorable control; may continue a Unicode identifier or JS + * identifier + * 2 may continue a JS identifier but not a Unicode identifier + * (unused) + * 3 may continue a Unicode identifier or JS identifier + * 4 is a JS whitespace character + * 5 may start or continue a JS identifier; + * may continue but not start a Unicode identifier (_) + * 6 may start or continue a JS identifier but not a Unicode + * identifier ($) + * 7 may start or continue a Unicode identifier or JS identifier + * Thus: + * 5, 6, 7 may start a JS identifier + * 1, 2, 3, 5, 6, 7 may continue a JS identifier + * 7 may start a Unicode identifier + * 1, 3, 5, 7 may continue a Unicode identifier + * 1 is ignorable within an identifier + * 4 is JS whitespace + * 2 bits 0 this character has no numeric property + * 1 adding the digit offset to the character code and then + * masking with 0x1F will produce the desired numeric value + * 2 this character has a "strange" numeric value + * 3 a JS supradecimal digit: adding the digit offset to the + * character code, then masking with 0x1F, then adding 10 + * will produce the desired numeric value + * 5 bits digit offset + * 1 bit XML 1.0 name start character + * 1 bit XML 1.0 name character + * 2 bits reserved for future use + * 5 bits character type + */ + +/* The X table has 1024 entries for a total of 1024 bytes. */ + +const uint8 js_X[] = { + 0, 1, 2, 3, 4, 5, 6, 7, /* 0x0000 */ + 8, 9, 10, 11, 12, 13, 14, 15, /* 0x0200 */ + 16, 17, 18, 19, 20, 21, 22, 23, /* 0x0400 */ + 24, 25, 26, 27, 28, 28, 28, 28, /* 0x0600 */ + 28, 28, 28, 28, 29, 30, 31, 32, /* 0x0800 */ + 33, 34, 35, 36, 37, 38, 39, 40, /* 0x0A00 */ + 41, 42, 43, 44, 45, 46, 28, 28, /* 0x0C00 */ + 47, 48, 49, 50, 51, 52, 53, 28, /* 0x0E00 */ + 28, 28, 54, 55, 56, 57, 58, 59, /* 0x1000 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1200 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1400 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1600 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1800 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1A00 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1C00 */ + 60, 60, 61, 62, 63, 64, 65, 66, /* 0x1E00 */ + 67, 68, 69, 70, 71, 72, 73, 74, /* 0x2000 */ + 75, 75, 75, 76, 77, 78, 28, 28, /* 0x2200 */ + 79, 80, 81, 82, 83, 83, 84, 85, /* 0x2400 */ + 86, 85, 28, 28, 87, 88, 89, 28, /* 0x2600 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2800 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2A00 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2C00 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2E00 */ + 90, 91, 92, 93, 94, 56, 95, 28, /* 0x3000 */ + 96, 97, 98, 99, 83, 100, 83, 101, /* 0x3200 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3400 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3600 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3800 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3A00 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3C00 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3E00 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4000 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4200 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4400 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4600 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4800 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4A00 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4C00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x4E00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5000 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5200 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5400 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5600 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5800 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5A00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5C00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5E00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6000 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6200 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6400 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6600 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6800 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6A00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6C00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6E00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7000 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7200 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7400 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7600 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7800 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7A00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7C00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7E00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8000 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8200 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8400 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8600 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8800 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8A00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8C00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8E00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9000 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9200 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9400 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9600 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9800 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9A00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9C00 */ + 56, 56, 56, 56, 56, 56, 102, 28, /* 0x9E00 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA000 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA200 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA400 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA600 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA800 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0xAA00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xAC00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xAE00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB000 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB200 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB400 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB600 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB800 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xBA00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xBC00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xBE00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC000 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC200 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC400 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC600 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC800 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xCA00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xCC00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xCE00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xD000 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xD200 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xD400 */ + 56, 56, 56, 56, 56, 56, 103, 28, /* 0xD600 */ +104, 104, 104, 104, 104, 104, 104, 104, /* 0xD800 */ +104, 104, 104, 104, 104, 104, 104, 104, /* 0xDA00 */ +104, 104, 104, 104, 104, 104, 104, 104, /* 0xDC00 */ +104, 104, 104, 104, 104, 104, 104, 104, /* 0xDE00 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xE000 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xE200 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xE400 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xE600 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xE800 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xEA00 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xEC00 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xEE00 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xF000 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xF200 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xF400 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xF600 */ +105, 105, 105, 105, 56, 56, 56, 56, /* 0xF800 */ +106, 28, 28, 28, 107, 108, 109, 110, /* 0xFA00 */ + 56, 56, 56, 56, 111, 112, 113, 114, /* 0xFC00 */ +115, 116, 56, 117, 118, 119, 120, 121 /* 0xFE00 */ +}; + +/* The Y table has 7808 entries for a total of 7808 bytes. */ + +const uint8 js_Y[] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */ + 0, 1, 1, 1, 1, 1, 0, 0, /* 0 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */ + 2, 3, 3, 3, 4, 3, 3, 3, /* 0 */ + 5, 6, 3, 7, 3, 8, 3, 3, /* 0 */ + 9, 9, 9, 9, 9, 9, 9, 9, /* 0 */ + 9, 9, 3, 3, 7, 7, 7, 3, /* 0 */ + 3, 10, 10, 10, 10, 10, 10, 10, /* 1 */ + 10, 10, 10, 10, 10, 10, 10, 10, /* 1 */ + 10, 10, 10, 10, 10, 10, 10, 10, /* 1 */ + 10, 10, 10, 5, 3, 6, 11, 12, /* 1 */ + 11, 13, 13, 13, 13, 13, 13, 13, /* 1 */ + 13, 13, 13, 13, 13, 13, 13, 13, /* 1 */ + 13, 13, 13, 13, 13, 13, 13, 13, /* 1 */ + 13, 13, 13, 5, 7, 6, 7, 0, /* 1 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ + 2, 3, 4, 4, 4, 4, 15, 15, /* 2 */ + 11, 15, 16, 5, 7, 8, 15, 11, /* 2 */ + 15, 7, 17, 17, 11, 16, 15, 3, /* 2 */ + 11, 18, 16, 6, 19, 19, 19, 3, /* 2 */ + 20, 20, 20, 20, 20, 20, 20, 20, /* 3 */ + 20, 20, 20, 20, 20, 20, 20, 20, /* 3 */ + 20, 20, 20, 20, 20, 20, 20, 7, /* 3 */ + 20, 20, 20, 20, 20, 20, 20, 16, /* 3 */ + 21, 21, 21, 21, 21, 21, 21, 21, /* 3 */ + 21, 21, 21, 21, 21, 21, 21, 21, /* 3 */ + 21, 21, 21, 21, 21, 21, 21, 7, /* 3 */ + 21, 21, 21, 21, 21, 21, 21, 22, /* 3 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ + 25, 26, 23, 24, 23, 24, 23, 24, /* 4 */ + 16, 23, 24, 23, 24, 23, 24, 23, /* 4 */ + 24, 23, 24, 23, 24, 23, 24, 23, /* 5 */ + 24, 16, 23, 24, 23, 24, 23, 24, /* 5 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ + 27, 23, 24, 23, 24, 23, 24, 28, /* 5 */ + 16, 29, 23, 24, 23, 24, 30, 23, /* 6 */ + 24, 31, 31, 23, 24, 16, 32, 32, /* 6 */ + 33, 23, 24, 31, 34, 16, 35, 36, /* 6 */ + 23, 24, 16, 16, 35, 37, 16, 38, /* 6 */ + 23, 24, 23, 24, 23, 24, 38, 23, /* 6 */ + 24, 39, 40, 16, 23, 24, 39, 23, /* 6 */ + 24, 41, 41, 23, 24, 23, 24, 42, /* 6 */ + 23, 24, 16, 40, 23, 24, 40, 40, /* 6 */ + 40, 40, 40, 40, 43, 44, 45, 43, /* 7 */ + 44, 45, 43, 44, 45, 23, 24, 23, /* 7 */ + 24, 23, 24, 23, 24, 23, 24, 23, /* 7 */ + 24, 23, 24, 23, 24, 16, 23, 24, /* 7 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 7 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 7 */ + 16, 43, 44, 45, 23, 24, 46, 46, /* 7 */ + 46, 46, 23, 24, 23, 24, 23, 24, /* 7 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 8 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 8 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 8 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 9 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 9 */ + 16, 16, 16, 47, 48, 16, 49, 49, /* 9 */ + 50, 50, 16, 51, 16, 16, 16, 16, /* 9 */ + 49, 16, 16, 52, 16, 16, 16, 16, /* 9 */ + 53, 54, 16, 16, 16, 16, 16, 54, /* 9 */ + 16, 16, 55, 16, 16, 16, 16, 16, /* 9 */ + 16, 16, 16, 16, 16, 16, 16, 16, /* 9 */ + 16, 16, 16, 56, 16, 16, 16, 16, /* 10 */ + 56, 16, 57, 57, 16, 16, 16, 16, /* 10 */ + 16, 16, 58, 16, 16, 16, 16, 16, /* 10 */ + 16, 16, 16, 16, 16, 16, 16, 16, /* 10 */ + 16, 16, 16, 16, 16, 16, 16, 16, /* 10 */ + 16, 46, 46, 46, 46, 46, 46, 46, /* 10 */ + 59, 59, 59, 59, 59, 59, 59, 59, /* 10 */ + 59, 11, 11, 59, 59, 59, 59, 59, /* 10 */ + 59, 59, 11, 11, 11, 11, 11, 11, /* 11 */ + 11, 11, 11, 11, 11, 11, 11, 11, /* 11 */ + 59, 59, 11, 11, 11, 11, 11, 11, /* 11 */ + 11, 11, 11, 11, 11, 11, 11, 46, /* 11 */ + 59, 59, 59, 59, 59, 11, 11, 11, /* 11 */ + 11, 11, 46, 46, 46, 46, 46, 46, /* 11 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 11 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 11 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ + 60, 60, 60, 60, 60, 60, 46, 46, /* 13 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */ + 60, 60, 46, 46, 46, 46, 46, 46, /* 13 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */ + 46, 46, 46, 46, 3, 3, 46, 46, /* 13 */ + 46, 46, 59, 46, 46, 46, 3, 46, /* 13 */ + 46, 46, 46, 46, 11, 11, 61, 3, /* 14 */ + 62, 62, 62, 46, 63, 46, 64, 64, /* 14 */ + 16, 20, 20, 20, 20, 20, 20, 20, /* 14 */ + 20, 20, 20, 20, 20, 20, 20, 20, /* 14 */ + 20, 20, 46, 20, 20, 20, 20, 20, /* 14 */ + 20, 20, 20, 20, 65, 66, 66, 66, /* 14 */ + 16, 21, 21, 21, 21, 21, 21, 21, /* 14 */ + 21, 21, 21, 21, 21, 21, 21, 21, /* 14 */ + 21, 21, 16, 21, 21, 21, 21, 21, /* 15 */ + 21, 21, 21, 21, 67, 68, 68, 46, /* 15 */ + 69, 70, 38, 38, 38, 71, 72, 46, /* 15 */ + 46, 46, 38, 46, 38, 46, 38, 46, /* 15 */ + 38, 46, 23, 24, 23, 24, 23, 24, /* 15 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 15 */ + 73, 74, 16, 40, 46, 46, 46, 46, /* 15 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 15 */ + 46, 75, 75, 75, 75, 75, 75, 75, /* 16 */ + 75, 75, 75, 75, 75, 46, 75, 75, /* 16 */ + 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */ + 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */ + 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */ + 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */ + 21, 21, 21, 21, 21, 21, 21, 21, /* 16 */ + 21, 21, 21, 21, 21, 21, 21, 21, /* 16 */ + 21, 21, 21, 21, 21, 21, 21, 21, /* 17 */ + 21, 21, 21, 21, 21, 21, 21, 21, /* 17 */ + 46, 74, 74, 74, 74, 74, 74, 74, /* 17 */ + 74, 74, 74, 74, 74, 46, 74, 74, /* 17 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */ + 23, 24, 15, 60, 60, 60, 60, 46, /* 18 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 18 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ + 40, 23, 24, 23, 24, 46, 46, 23, /* 19 */ + 24, 46, 46, 23, 24, 46, 46, 46, /* 19 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 19 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 19 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 19 */ + 23, 24, 23, 24, 46, 46, 23, 24, /* 19 */ + 23, 24, 23, 24, 23, 24, 46, 46, /* 19 */ + 23, 24, 46, 46, 46, 46, 46, 46, /* 19 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ + 46, 76, 76, 76, 76, 76, 76, 76, /* 20 */ + 76, 76, 76, 76, 76, 76, 76, 76, /* 20 */ + 76, 76, 76, 76, 76, 76, 76, 76, /* 21 */ + 76, 76, 76, 76, 76, 76, 76, 76, /* 21 */ + 76, 76, 76, 76, 76, 76, 76, 46, /* 21 */ + 46, 59, 3, 3, 3, 3, 3, 3, /* 21 */ + 46, 77, 77, 77, 77, 77, 77, 77, /* 21 */ + 77, 77, 77, 77, 77, 77, 77, 77, /* 21 */ + 77, 77, 77, 77, 77, 77, 77, 77, /* 21 */ + 77, 77, 77, 77, 77, 77, 77, 77, /* 21 */ + 77, 77, 77, 77, 77, 77, 77, 16, /* 22 */ + 46, 3, 46, 46, 46, 46, 46, 46, /* 22 */ + 46, 60, 60, 60, 60, 60, 60, 60, /* 22 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 22 */ + 60, 60, 46, 60, 60, 60, 60, 60, /* 22 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 22 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 22 */ + 60, 60, 46, 60, 60, 60, 3, 60, /* 22 */ + 3, 60, 60, 3, 60, 46, 46, 46, /* 23 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 23 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 23 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 23 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 23 */ + 40, 40, 40, 46, 46, 46, 46, 46, /* 23 */ + 40, 40, 40, 3, 3, 46, 46, 46, /* 23 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 23 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 24 */ + 46, 46, 46, 46, 3, 46, 46, 46, /* 24 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 24 */ + 46, 46, 46, 3, 46, 46, 46, 3, /* 24 */ + 46, 40, 40, 40, 40, 40, 40, 40, /* 24 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 24 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 24 */ + 40, 40, 40, 46, 46, 46, 46, 46, /* 24 */ + 59, 40, 40, 40, 40, 40, 40, 40, /* 25 */ + 40, 40, 40, 60, 60, 60, 60, 60, /* 25 */ + 60, 60, 60, 46, 46, 46, 46, 46, /* 25 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 25 */ + 78, 78, 78, 78, 78, 78, 78, 78, /* 25 */ + 78, 78, 3, 3, 3, 3, 46, 46, /* 25 */ + 60, 40, 40, 40, 40, 40, 40, 40, /* 25 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 25 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ + 46, 46, 40, 40, 40, 40, 40, 46, /* 26 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 27 */ + 40, 40, 40, 40, 40, 40, 40, 46, /* 27 */ + 40, 40, 40, 40, 3, 40, 60, 60, /* 27 */ + 60, 60, 60, 60, 60, 79, 79, 60, /* 27 */ + 60, 60, 60, 60, 60, 59, 59, 60, /* 27 */ + 60, 15, 60, 60, 60, 60, 46, 46, /* 27 */ + 9, 9, 9, 9, 9, 9, 9, 9, /* 27 */ + 9, 9, 46, 46, 46, 46, 46, 46, /* 27 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ + 46, 60, 60, 80, 46, 40, 40, 40, /* 29 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ + 40, 40, 46, 46, 60, 40, 80, 80, /* 29 */ + 80, 60, 60, 60, 60, 60, 60, 60, /* 30 */ + 60, 80, 80, 80, 80, 60, 46, 46, /* 30 */ + 15, 60, 60, 60, 60, 46, 46, 46, /* 30 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 30 */ + 40, 40, 60, 60, 3, 3, 81, 81, /* 30 */ + 81, 81, 81, 81, 81, 81, 81, 81, /* 30 */ + 3, 46, 46, 46, 46, 46, 46, 46, /* 30 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 30 */ + 46, 60, 80, 80, 46, 40, 40, 40, /* 31 */ + 40, 40, 40, 40, 40, 46, 46, 40, /* 31 */ + 40, 46, 46, 40, 40, 40, 40, 40, /* 31 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 31 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 31 */ + 40, 46, 40, 40, 40, 40, 40, 40, /* 31 */ + 40, 46, 40, 46, 46, 46, 40, 40, /* 31 */ + 40, 40, 46, 46, 60, 46, 80, 80, /* 31 */ + 80, 60, 60, 60, 60, 46, 46, 80, /* 32 */ + 80, 46, 46, 80, 80, 60, 46, 46, /* 32 */ + 46, 46, 46, 46, 46, 46, 46, 80, /* 32 */ + 46, 46, 46, 46, 40, 40, 46, 40, /* 32 */ + 40, 40, 60, 60, 46, 46, 81, 81, /* 32 */ + 81, 81, 81, 81, 81, 81, 81, 81, /* 32 */ + 40, 40, 4, 4, 82, 82, 82, 82, /* 32 */ + 19, 83, 15, 46, 46, 46, 46, 46, /* 32 */ + 46, 46, 60, 46, 46, 40, 40, 40, /* 33 */ + 40, 40, 40, 46, 46, 46, 46, 40, /* 33 */ + 40, 46, 46, 40, 40, 40, 40, 40, /* 33 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 33 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 33 */ + 40, 46, 40, 40, 40, 40, 40, 40, /* 33 */ + 40, 46, 40, 40, 46, 40, 40, 46, /* 33 */ + 40, 40, 46, 46, 60, 46, 80, 80, /* 33 */ + 80, 60, 60, 46, 46, 46, 46, 60, /* 34 */ + 60, 46, 46, 60, 60, 60, 46, 46, /* 34 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 34 */ + 46, 40, 40, 40, 40, 46, 40, 46, /* 34 */ + 46, 46, 46, 46, 46, 46, 81, 81, /* 34 */ + 81, 81, 81, 81, 81, 81, 81, 81, /* 34 */ + 60, 60, 40, 40, 40, 46, 46, 46, /* 34 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 34 */ + 46, 60, 60, 80, 46, 40, 40, 40, /* 35 */ + 40, 40, 40, 40, 46, 40, 46, 40, /* 35 */ + 40, 40, 46, 40, 40, 40, 40, 40, /* 35 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 35 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 35 */ + 40, 46, 40, 40, 40, 40, 40, 40, /* 35 */ + 40, 46, 40, 40, 46, 40, 40, 40, /* 35 */ + 40, 40, 46, 46, 60, 40, 80, 80, /* 35 */ + 80, 60, 60, 60, 60, 60, 46, 60, /* 36 */ + 60, 80, 46, 80, 80, 60, 46, 46, /* 36 */ + 15, 46, 46, 46, 46, 46, 46, 46, /* 36 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 36 */ + 40, 46, 46, 46, 46, 46, 81, 81, /* 36 */ + 81, 81, 81, 81, 81, 81, 81, 81, /* 36 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 36 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 36 */ + 46, 60, 80, 80, 46, 40, 40, 40, /* 37 */ + 40, 40, 40, 40, 40, 46, 46, 40, /* 37 */ + 40, 46, 46, 40, 40, 40, 40, 40, /* 37 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 37 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 37 */ + 40, 46, 40, 40, 40, 40, 40, 40, /* 37 */ + 40, 46, 40, 40, 46, 46, 40, 40, /* 37 */ + 40, 40, 46, 46, 60, 40, 80, 60, /* 37 */ + 80, 60, 60, 60, 46, 46, 46, 80, /* 38 */ + 80, 46, 46, 80, 80, 60, 46, 46, /* 38 */ + 46, 46, 46, 46, 46, 46, 60, 80, /* 38 */ + 46, 46, 46, 46, 40, 40, 46, 40, /* 38 */ + 40, 40, 46, 46, 46, 46, 81, 81, /* 38 */ + 81, 81, 81, 81, 81, 81, 81, 81, /* 38 */ + 15, 46, 46, 46, 46, 46, 46, 46, /* 38 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 38 */ + 46, 46, 60, 80, 46, 40, 40, 40, /* 39 */ + 40, 40, 40, 46, 46, 46, 40, 40, /* 39 */ + 40, 46, 40, 40, 40, 40, 46, 46, /* 39 */ + 46, 40, 40, 46, 40, 46, 40, 40, /* 39 */ + 46, 46, 46, 40, 40, 46, 46, 46, /* 39 */ + 40, 40, 40, 46, 46, 46, 40, 40, /* 39 */ + 40, 40, 40, 40, 40, 40, 46, 40, /* 39 */ + 40, 40, 46, 46, 46, 46, 80, 80, /* 39 */ + 60, 80, 80, 46, 46, 46, 80, 80, /* 40 */ + 80, 46, 80, 80, 80, 60, 46, 46, /* 40 */ + 46, 46, 46, 46, 46, 46, 46, 80, /* 40 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 40 */ + 46, 46, 46, 46, 46, 46, 46, 81, /* 40 */ + 81, 81, 81, 81, 81, 81, 81, 81, /* 40 */ + 84, 19, 19, 46, 46, 46, 46, 46, /* 40 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 40 */ + 46, 80, 80, 80, 46, 40, 40, 40, /* 41 */ + 40, 40, 40, 40, 40, 46, 40, 40, /* 41 */ + 40, 46, 40, 40, 40, 40, 40, 40, /* 41 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 41 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 41 */ + 40, 46, 40, 40, 40, 40, 40, 40, /* 41 */ + 40, 40, 40, 40, 46, 40, 40, 40, /* 41 */ + 40, 40, 46, 46, 46, 46, 60, 60, /* 41 */ + 60, 80, 80, 80, 80, 46, 60, 60, /* 42 */ + 60, 46, 60, 60, 60, 60, 46, 46, /* 42 */ + 46, 46, 46, 46, 46, 60, 60, 46, /* 42 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 42 */ + 40, 40, 46, 46, 46, 46, 81, 81, /* 42 */ + 81, 81, 81, 81, 81, 81, 81, 81, /* 42 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 42 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 42 */ + 46, 46, 80, 80, 46, 40, 40, 40, /* 43 */ + 40, 40, 40, 40, 40, 46, 40, 40, /* 43 */ + 40, 46, 40, 40, 40, 40, 40, 40, /* 43 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 43 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 43 */ + 40, 46, 40, 40, 40, 40, 40, 40, /* 43 */ + 40, 40, 40, 40, 46, 40, 40, 40, /* 43 */ + 40, 40, 46, 46, 46, 46, 80, 60, /* 43 */ + 80, 80, 80, 80, 80, 46, 60, 80, /* 44 */ + 80, 46, 80, 80, 60, 60, 46, 46, /* 44 */ + 46, 46, 46, 46, 46, 80, 80, 46, /* 44 */ + 46, 46, 46, 46, 46, 46, 40, 46, /* 44 */ + 40, 40, 46, 46, 46, 46, 81, 81, /* 44 */ + 81, 81, 81, 81, 81, 81, 81, 81, /* 44 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 44 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 44 */ + 46, 46, 80, 80, 46, 40, 40, 40, /* 45 */ + 40, 40, 40, 40, 40, 46, 40, 40, /* 45 */ + 40, 46, 40, 40, 40, 40, 40, 40, /* 45 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 45 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 45 */ + 40, 46, 40, 40, 40, 40, 40, 40, /* 45 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 45 */ + 40, 40, 46, 46, 46, 46, 80, 80, /* 45 */ + 80, 60, 60, 60, 46, 46, 80, 80, /* 46 */ + 80, 46, 80, 80, 80, 60, 46, 46, /* 46 */ + 46, 46, 46, 46, 46, 46, 46, 80, /* 46 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 46 */ + 40, 40, 46, 46, 46, 46, 81, 81, /* 46 */ + 81, 81, 81, 81, 81, 81, 81, 81, /* 46 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 46 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 46 */ + 46, 40, 40, 40, 40, 40, 40, 40, /* 47 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */ + 40, 40, 40, 40, 40, 40, 40, 3, /* 47 */ + 40, 60, 40, 40, 60, 60, 60, 60, /* 47 */ + 60, 60, 60, 46, 46, 46, 46, 4, /* 47 */ + 40, 40, 40, 40, 40, 40, 59, 60, /* 48 */ + 60, 60, 60, 60, 60, 60, 60, 15, /* 48 */ + 9, 9, 9, 9, 9, 9, 9, 9, /* 48 */ + 9, 9, 3, 3, 46, 46, 46, 46, /* 48 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */ + 46, 40, 40, 46, 40, 46, 46, 40, /* 49 */ + 40, 46, 40, 46, 46, 40, 46, 46, /* 49 */ + 46, 46, 46, 46, 40, 40, 40, 40, /* 49 */ + 46, 40, 40, 40, 40, 40, 40, 40, /* 49 */ + 46, 40, 40, 40, 46, 40, 46, 40, /* 49 */ + 46, 46, 40, 40, 46, 40, 40, 3, /* 49 */ + 40, 60, 40, 40, 60, 60, 60, 60, /* 49 */ + 60, 60, 46, 60, 60, 40, 46, 46, /* 49 */ + 40, 40, 40, 40, 40, 46, 59, 46, /* 50 */ + 60, 60, 60, 60, 60, 60, 46, 46, /* 50 */ + 9, 9, 9, 9, 9, 9, 9, 9, /* 50 */ + 9, 9, 46, 46, 40, 40, 46, 46, /* 50 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */ + 15, 15, 15, 15, 3, 3, 3, 3, /* 51 */ + 3, 3, 3, 3, 3, 3, 3, 3, /* 51 */ + 3, 3, 3, 15, 15, 15, 15, 15, /* 51 */ + 60, 60, 15, 15, 15, 15, 15, 15, /* 51 */ + 78, 78, 78, 78, 78, 78, 78, 78, /* 51 */ + 78, 78, 85, 85, 85, 85, 85, 85, /* 51 */ + 85, 85, 85, 85, 15, 60, 15, 60, /* 51 */ + 15, 60, 5, 6, 5, 6, 80, 80, /* 51 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */ + 46, 40, 40, 40, 40, 40, 40, 40, /* 52 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */ + 40, 40, 46, 46, 46, 46, 46, 46, /* 52 */ + 46, 60, 60, 60, 60, 60, 60, 60, /* 52 */ + 60, 60, 60, 60, 60, 60, 60, 80, /* 52 */ + 60, 60, 60, 60, 60, 3, 60, 60, /* 53 */ + 60, 60, 60, 60, 46, 46, 46, 46, /* 53 */ + 60, 60, 60, 60, 60, 60, 46, 60, /* 53 */ + 46, 60, 60, 60, 60, 60, 60, 60, /* 53 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 53 */ + 60, 60, 60, 60, 60, 60, 46, 46, /* 53 */ + 46, 60, 60, 60, 60, 60, 60, 60, /* 53 */ + 46, 60, 46, 46, 46, 46, 46, 46, /* 53 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */ + 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */ + 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */ + 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */ + 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */ + 76, 76, 76, 76, 76, 76, 46, 46, /* 55 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 55 */ + 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */ + 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */ + 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */ + 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */ + 16, 16, 16, 16, 16, 16, 16, 46, /* 55 */ + 46, 46, 46, 3, 46, 46, 46, 46, /* 55 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ + 40, 40, 46, 46, 46, 46, 46, 40, /* 57 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ + 40, 40, 40, 46, 46, 46, 46, 46, /* 58 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ + 40, 40, 46, 46, 46, 46, 46, 46, /* 59 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ + 23, 24, 23, 24, 23, 24, 16, 16, /* 61 */ + 16, 16, 16, 16, 46, 46, 46, 46, /* 61 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ + 23, 24, 46, 46, 46, 46, 46, 46, /* 62 */ + 86, 86, 86, 86, 86, 86, 86, 86, /* 63 */ + 87, 87, 87, 87, 87, 87, 87, 87, /* 63 */ + 86, 86, 86, 86, 86, 86, 46, 46, /* 63 */ + 87, 87, 87, 87, 87, 87, 46, 46, /* 63 */ + 86, 86, 86, 86, 86, 86, 86, 86, /* 63 */ + 87, 87, 87, 87, 87, 87, 87, 87, /* 63 */ + 86, 86, 86, 86, 86, 86, 86, 86, /* 63 */ + 87, 87, 87, 87, 87, 87, 87, 87, /* 63 */ + 86, 86, 86, 86, 86, 86, 46, 46, /* 64 */ + 87, 87, 87, 87, 87, 87, 46, 46, /* 64 */ + 16, 86, 16, 86, 16, 86, 16, 86, /* 64 */ + 46, 87, 46, 87, 46, 87, 46, 87, /* 64 */ + 86, 86, 86, 86, 86, 86, 86, 86, /* 64 */ + 87, 87, 87, 87, 87, 87, 87, 87, /* 64 */ + 88, 88, 89, 89, 89, 89, 90, 90, /* 64 */ + 91, 91, 92, 92, 93, 93, 46, 46, /* 64 */ + 86, 86, 86, 86, 86, 86, 86, 86, /* 65 */ + 87, 87, 87, 87, 87, 87, 87, 87, /* 65 */ + 86, 86, 86, 86, 86, 86, 86, 86, /* 65 */ + 87, 87, 87, 87, 87, 87, 87, 87, /* 65 */ + 86, 86, 86, 86, 86, 86, 86, 86, /* 65 */ + 87, 87, 87, 87, 87, 87, 87, 87, /* 65 */ + 86, 86, 16, 94, 16, 46, 16, 16, /* 65 */ + 87, 87, 95, 95, 96, 11, 38, 11, /* 65 */ + 11, 11, 16, 94, 16, 46, 16, 16, /* 66 */ + 97, 97, 97, 97, 96, 11, 11, 11, /* 66 */ + 86, 86, 16, 16, 46, 46, 16, 16, /* 66 */ + 87, 87, 98, 98, 46, 11, 11, 11, /* 66 */ + 86, 86, 16, 16, 16, 99, 16, 16, /* 66 */ + 87, 87, 100, 100, 101, 11, 11, 11, /* 66 */ + 46, 46, 16, 94, 16, 46, 16, 16, /* 66 */ +102, 102, 103, 103, 96, 11, 11, 46, /* 66 */ + 2, 2, 2, 2, 2, 2, 2, 2, /* 67 */ + 2, 2, 2, 2, 104, 104, 104, 104, /* 67 */ + 8, 8, 8, 8, 8, 8, 3, 3, /* 67 */ + 5, 6, 5, 5, 5, 6, 5, 5, /* 67 */ + 3, 3, 3, 3, 3, 3, 3, 3, /* 67 */ +105, 106, 104, 104, 104, 104, 104, 46, /* 67 */ + 3, 3, 3, 3, 3, 3, 3, 3, /* 67 */ + 3, 5, 6, 3, 3, 3, 3, 12, /* 67 */ + 12, 3, 3, 3, 7, 5, 6, 46, /* 68 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ + 46, 46, 104, 104, 104, 104, 104, 104, /* 68 */ + 17, 46, 46, 46, 17, 17, 17, 17, /* 68 */ + 17, 17, 7, 7, 7, 5, 6, 16, /* 68 */ +107, 107, 107, 107, 107, 107, 107, 107, /* 69 */ +107, 107, 7, 7, 7, 5, 6, 46, /* 69 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */ + 4, 4, 4, 4, 4, 4, 4, 4, /* 69 */ + 4, 4, 4, 4, 46, 46, 46, 46, /* 69 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 70 */ + 60, 60, 60, 60, 60, 79, 79, 79, /* 70 */ + 79, 60, 46, 46, 46, 46, 46, 46, /* 70 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ + 15, 15, 38, 15, 15, 15, 15, 38, /* 71 */ + 15, 15, 16, 38, 38, 38, 16, 16, /* 71 */ + 38, 38, 38, 16, 15, 38, 15, 15, /* 71 */ + 38, 38, 38, 38, 38, 38, 15, 15, /* 71 */ + 15, 15, 15, 15, 38, 15, 38, 15, /* 71 */ + 38, 15, 38, 38, 38, 38, 16, 16, /* 71 */ + 38, 38, 15, 38, 16, 40, 40, 40, /* 71 */ + 40, 46, 46, 46, 46, 46, 46, 46, /* 71 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 72 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 72 */ + 46, 46, 46, 19, 19, 19, 19, 19, /* 72 */ + 19, 19, 19, 19, 19, 19, 19, 108, /* 72 */ +109, 109, 109, 109, 109, 109, 109, 109, /* 72 */ +109, 109, 109, 109, 110, 110, 110, 110, /* 72 */ +111, 111, 111, 111, 111, 111, 111, 111, /* 72 */ +111, 111, 111, 111, 112, 112, 112, 112, /* 72 */ +113, 113, 113, 46, 46, 46, 46, 46, /* 73 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 73 */ + 7, 7, 7, 7, 7, 15, 15, 15, /* 73 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */ + 15, 15, 7, 15, 7, 15, 15, 15, /* 74 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */ + 15, 15, 15, 46, 46, 46, 46, 46, /* 74 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 74 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 74 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ + 7, 7, 46, 46, 46, 46, 46, 46, /* 76 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 76 */ + 15, 46, 15, 15, 15, 15, 15, 15, /* 77 */ + 7, 7, 7, 7, 15, 15, 15, 15, /* 77 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */ + 7, 7, 15, 15, 15, 15, 15, 15, /* 77 */ + 15, 5, 6, 15, 15, 15, 15, 15, /* 77 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ + 15, 15, 15, 46, 46, 46, 46, 46, /* 78 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */ + 15, 15, 15, 15, 15, 46, 46, 46, /* 79 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 79 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 79 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 79 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 80 */ + 15, 15, 15, 46, 46, 46, 46, 46, /* 80 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 80 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 80 */ +114, 114, 114, 114, 114, 114, 114, 114, /* 80 */ +114, 114, 114, 114, 114, 114, 114, 114, /* 80 */ +114, 114, 114, 114, 82, 82, 82, 82, /* 80 */ + 82, 82, 82, 82, 82, 82, 82, 82, /* 80 */ + 82, 82, 82, 82, 82, 82, 82, 82, /* 81 */ +115, 115, 115, 115, 115, 115, 115, 115, /* 81 */ +115, 115, 115, 115, 115, 115, 115, 115, /* 81 */ +115, 115, 115, 115, 15, 15, 15, 15, /* 81 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 81 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 81 */ + 15, 15, 15, 15, 15, 15, 116, 116, /* 81 */ +116, 116, 116, 116, 116, 116, 116, 116, /* 81 */ +116, 116, 116, 116, 116, 116, 116, 116, /* 82 */ +116, 116, 116, 116, 116, 116, 116, 116, /* 82 */ +117, 117, 117, 117, 117, 117, 117, 117, /* 82 */ +117, 117, 117, 117, 117, 117, 117, 117, /* 82 */ +117, 117, 117, 117, 117, 117, 117, 117, /* 82 */ +117, 117, 118, 46, 46, 46, 46, 46, /* 82 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 82 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 82 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ + 15, 15, 15, 15, 15, 15, 46, 46, /* 84 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 84 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 85 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 85 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ + 15, 15, 15, 15, 46, 46, 46, 46, /* 86 */ + 46, 46, 15, 15, 15, 15, 15, 15, /* 86 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ + 46, 15, 15, 15, 15, 46, 15, 15, /* 87 */ + 15, 15, 46, 46, 15, 15, 15, 15, /* 87 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ + 46, 15, 15, 15, 15, 15, 15, 15, /* 87 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 88 */ + 15, 15, 15, 15, 46, 15, 46, 15, /* 88 */ + 15, 15, 15, 46, 46, 46, 15, 46, /* 88 */ + 15, 15, 15, 15, 15, 15, 15, 46, /* 88 */ + 46, 15, 15, 15, 15, 15, 15, 15, /* 88 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 88 */ + 46, 46, 46, 46, 46, 46, 119, 119, /* 88 */ +119, 119, 119, 119, 119, 119, 119, 119, /* 88 */ +114, 114, 114, 114, 114, 114, 114, 114, /* 89 */ +114, 114, 83, 83, 83, 83, 83, 83, /* 89 */ + 83, 83, 83, 83, 15, 46, 46, 46, /* 89 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 89 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 89 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 89 */ + 46, 15, 15, 15, 15, 15, 15, 15, /* 89 */ + 15, 15, 15, 15, 15, 15, 15, 46, /* 89 */ + 2, 3, 3, 3, 15, 59, 3, 120, /* 90 */ + 5, 6, 5, 6, 5, 6, 5, 6, /* 90 */ + 5, 6, 15, 15, 5, 6, 5, 6, /* 90 */ + 5, 6, 5, 6, 8, 5, 6, 5, /* 90 */ + 15, 121, 121, 121, 121, 121, 121, 121, /* 90 */ +121, 121, 60, 60, 60, 60, 60, 60, /* 90 */ + 8, 59, 59, 59, 59, 59, 15, 15, /* 90 */ + 46, 46, 46, 46, 46, 46, 46, 15, /* 90 */ + 46, 40, 40, 40, 40, 40, 40, 40, /* 91 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ + 40, 40, 40, 40, 40, 46, 46, 46, /* 92 */ + 46, 60, 60, 59, 59, 59, 59, 46, /* 92 */ + 46, 40, 40, 40, 40, 40, 40, 40, /* 92 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ + 40, 40, 40, 3, 59, 59, 59, 46, /* 93 */ + 46, 46, 46, 46, 46, 40, 40, 40, /* 94 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ + 40, 40, 40, 40, 40, 46, 46, 46, /* 94 */ + 46, 40, 40, 40, 40, 40, 40, 40, /* 94 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 95 */ + 40, 40, 40, 40, 40, 40, 40, 46, /* 95 */ + 15, 15, 85, 85, 85, 85, 15, 15, /* 95 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 95 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ + 15, 15, 15, 15, 15, 46, 46, 46, /* 96 */ + 85, 85, 85, 85, 85, 85, 85, 85, /* 96 */ + 85, 85, 15, 15, 15, 15, 15, 15, /* 96 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ + 15, 15, 15, 15, 46, 46, 46, 46, /* 97 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 97 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 97 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 97 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 97 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 97 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 97 */ + 15, 15, 15, 15, 46, 46, 46, 15, /* 97 */ +114, 114, 114, 114, 114, 114, 114, 114, /* 98 */ +114, 114, 15, 15, 15, 15, 15, 15, /* 98 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */ + 15, 46, 46, 46, 46, 46, 46, 46, /* 98 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 98 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ + 15, 15, 15, 15, 46, 46, 46, 46, /* 99 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ + 15, 15, 15, 15, 15, 15, 15, 46, /* 99 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ + 15, 15, 15, 15, 15, 15, 15, 46, /* 100 */ + 46, 46, 46, 15, 15, 15, 15, 15, /* 100 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ + 15, 15, 15, 15, 15, 15, 46, 46, /* 101 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ + 15, 15, 15, 15, 15, 15, 15, 46, /* 101 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */ + 40, 40, 40, 40, 40, 40, 46, 46, /* 102 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 102 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 102 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 102 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */ + 40, 40, 40, 40, 46, 46, 46, 46, /* 103 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 103 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 103 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 103 */ +122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ +122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ +122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ +122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ +122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ +122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ +122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ +122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ +123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ +123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ +123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ +123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ +123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ +123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ +123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ +123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ + 40, 40, 40, 40, 40, 40, 46, 46, /* 106 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 106 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 106 */ + 16, 16, 16, 16, 16, 16, 16, 46, /* 107 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 107 */ + 46, 46, 46, 16, 16, 16, 16, 16, /* 107 */ + 46, 46, 46, 46, 46, 46, 60, 40, /* 107 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 107 */ + 40, 7, 40, 40, 40, 40, 40, 40, /* 107 */ + 40, 40, 40, 40, 40, 40, 40, 46, /* 107 */ + 40, 40, 40, 40, 40, 46, 40, 46, /* 107 */ + 40, 40, 46, 40, 40, 46, 40, 40, /* 108 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ + 40, 40, 46, 46, 46, 46, 46, 46, /* 109 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 109 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 110 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 110 */ + 46, 46, 46, 40, 40, 40, 40, 40, /* 110 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ + 40, 40, 40, 40, 40, 40, 5, 6, /* 111 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 112 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 112 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ + 46, 46, 40, 40, 40, 40, 40, 40, /* 113 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 114 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 114 */ + 40, 40, 40, 40, 46, 46, 46, 46, /* 114 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ + 60, 60, 60, 60, 46, 46, 46, 46, /* 115 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ + 3, 8, 8, 12, 12, 5, 6, 5, /* 115 */ + 6, 5, 6, 5, 6, 5, 6, 5, /* 115 */ + 6, 5, 6, 5, 6, 46, 46, 46, /* 116 */ + 46, 3, 3, 3, 3, 12, 12, 12, /* 116 */ + 3, 3, 3, 46, 3, 3, 3, 3, /* 116 */ + 8, 5, 6, 5, 6, 5, 6, 3, /* 116 */ + 3, 3, 7, 8, 7, 7, 7, 46, /* 116 */ + 3, 4, 3, 3, 46, 46, 46, 46, /* 116 */ + 40, 40, 40, 46, 40, 46, 40, 40, /* 116 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 116 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ + 40, 40, 40, 40, 40, 46, 46, 104, /* 117 */ + 46, 3, 3, 3, 4, 3, 3, 3, /* 118 */ + 5, 6, 3, 7, 3, 8, 3, 3, /* 118 */ + 9, 9, 9, 9, 9, 9, 9, 9, /* 118 */ + 9, 9, 3, 3, 7, 7, 7, 3, /* 118 */ + 3, 10, 10, 10, 10, 10, 10, 10, /* 118 */ + 10, 10, 10, 10, 10, 10, 10, 10, /* 118 */ + 10, 10, 10, 10, 10, 10, 10, 10, /* 118 */ + 10, 10, 10, 5, 3, 6, 11, 12, /* 118 */ + 11, 13, 13, 13, 13, 13, 13, 13, /* 119 */ + 13, 13, 13, 13, 13, 13, 13, 13, /* 119 */ + 13, 13, 13, 13, 13, 13, 13, 13, /* 119 */ + 13, 13, 13, 5, 7, 6, 7, 46, /* 119 */ + 46, 3, 5, 6, 3, 3, 40, 40, /* 119 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 119 */ + 59, 40, 40, 40, 40, 40, 40, 40, /* 119 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 119 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ + 40, 40, 40, 40, 40, 40, 59, 59, /* 120 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ + 40, 40, 40, 40, 40, 40, 40, 46, /* 120 */ + 46, 46, 40, 40, 40, 40, 40, 40, /* 121 */ + 46, 46, 40, 40, 40, 40, 40, 40, /* 121 */ + 46, 46, 40, 40, 40, 40, 40, 40, /* 121 */ + 46, 46, 40, 40, 40, 46, 46, 46, /* 121 */ + 4, 4, 7, 11, 15, 4, 4, 46, /* 121 */ + 7, 7, 7, 7, 7, 15, 15, 46, /* 121 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 121 */ + 46, 46, 46, 46, 46, 15, 46, 46 /* 121 */ +}; + +/* The A table has 124 entries for a total of 496 bytes. */ + +const uint32 js_A[] = { +0x0001000F, /* 0 Cc, ignorable */ +0x0004000F, /* 1 Cc, whitespace */ +0x0004000C, /* 2 Zs, whitespace */ +0x00000018, /* 3 Po */ +0x0006001A, /* 4 Sc, currency */ +0x00000015, /* 5 Ps */ +0x00000016, /* 6 Pe */ +0x00000019, /* 7 Sm */ +0x00000014, /* 8 Pd */ +0x00036089, /* 9 Nd, identifier part, decimal 16 */ +0x0827FF81, /* 10 Lu, hasLower (add 32), identifier start, supradecimal 31 */ +0x0000001B, /* 11 Sk */ +0x00050017, /* 12 Pc, underscore */ +0x0817FF82, /* 13 Ll, hasUpper (subtract 32), identifier start, supradecimal 31 */ +0x0000000C, /* 14 Zs */ +0x0000001C, /* 15 So */ +0x00070182, /* 16 Ll, identifier start */ +0x0000600B, /* 17 No, decimal 16 */ +0x0000500B, /* 18 No, decimal 8 */ +0x0000800B, /* 19 No, strange */ +0x08270181, /* 20 Lu, hasLower (add 32), identifier start */ +0x08170182, /* 21 Ll, hasUpper (subtract 32), identifier start */ +0xE1D70182, /* 22 Ll, hasUpper (subtract -121), identifier start */ +0x00670181, /* 23 Lu, hasLower (add 1), identifier start */ +0x00570182, /* 24 Ll, hasUpper (subtract 1), identifier start */ +0xCE670181, /* 25 Lu, hasLower (add -199), identifier start */ +0x3A170182, /* 26 Ll, hasUpper (subtract 232), identifier start */ +0xE1E70181, /* 27 Lu, hasLower (add -121), identifier start */ +0x4B170182, /* 28 Ll, hasUpper (subtract 300), identifier start */ +0x34A70181, /* 29 Lu, hasLower (add 210), identifier start */ +0x33A70181, /* 30 Lu, hasLower (add 206), identifier start */ +0x33670181, /* 31 Lu, hasLower (add 205), identifier start */ +0x32A70181, /* 32 Lu, hasLower (add 202), identifier start */ +0x32E70181, /* 33 Lu, hasLower (add 203), identifier start */ +0x33E70181, /* 34 Lu, hasLower (add 207), identifier start */ +0x34E70181, /* 35 Lu, hasLower (add 211), identifier start */ +0x34670181, /* 36 Lu, hasLower (add 209), identifier start */ +0x35670181, /* 37 Lu, hasLower (add 213), identifier start */ +0x00070181, /* 38 Lu, identifier start */ +0x36A70181, /* 39 Lu, hasLower (add 218), identifier start */ +0x00070185, /* 40 Lo, identifier start */ +0x36670181, /* 41 Lu, hasLower (add 217), identifier start */ +0x36E70181, /* 42 Lu, hasLower (add 219), identifier start */ +0x00AF0181, /* 43 Lu, hasLower (add 2), hasTitle, identifier start */ +0x007F0183, /* 44 Lt, hasUpper (subtract 1), hasLower (add 1), hasTitle, identifier start */ +0x009F0182, /* 45 Ll, hasUpper (subtract 2), hasTitle, identifier start */ +0x00000000, /* 46 unassigned */ +0x34970182, /* 47 Ll, hasUpper (subtract 210), identifier start */ +0x33970182, /* 48 Ll, hasUpper (subtract 206), identifier start */ +0x33570182, /* 49 Ll, hasUpper (subtract 205), identifier start */ +0x32970182, /* 50 Ll, hasUpper (subtract 202), identifier start */ +0x32D70182, /* 51 Ll, hasUpper (subtract 203), identifier start */ +0x33D70182, /* 52 Ll, hasUpper (subtract 207), identifier start */ +0x34570182, /* 53 Ll, hasUpper (subtract 209), identifier start */ +0x34D70182, /* 54 Ll, hasUpper (subtract 211), identifier start */ +0x35570182, /* 55 Ll, hasUpper (subtract 213), identifier start */ +0x36970182, /* 56 Ll, hasUpper (subtract 218), identifier start */ +0x36570182, /* 57 Ll, hasUpper (subtract 217), identifier start */ +0x36D70182, /* 58 Ll, hasUpper (subtract 219), identifier start */ +0x00070084, /* 59 Lm, identifier start */ +0x00030086, /* 60 Mn, identifier part */ +0x09A70181, /* 61 Lu, hasLower (add 38), identifier start */ +0x09670181, /* 62 Lu, hasLower (add 37), identifier start */ +0x10270181, /* 63 Lu, hasLower (add 64), identifier start */ +0x0FE70181, /* 64 Lu, hasLower (add 63), identifier start */ +0x09970182, /* 65 Ll, hasUpper (subtract 38), identifier start */ +0x09570182, /* 66 Ll, hasUpper (subtract 37), identifier start */ +0x10170182, /* 67 Ll, hasUpper (subtract 64), identifier start */ +0x0FD70182, /* 68 Ll, hasUpper (subtract 63), identifier start */ +0x0F970182, /* 69 Ll, hasUpper (subtract 62), identifier start */ +0x0E570182, /* 70 Ll, hasUpper (subtract 57), identifier start */ +0x0BD70182, /* 71 Ll, hasUpper (subtract 47), identifier start */ +0x0D970182, /* 72 Ll, hasUpper (subtract 54), identifier start */ +0x15970182, /* 73 Ll, hasUpper (subtract 86), identifier start */ +0x14170182, /* 74 Ll, hasUpper (subtract 80), identifier start */ +0x14270181, /* 75 Lu, hasLower (add 80), identifier start */ +0x0C270181, /* 76 Lu, hasLower (add 48), identifier start */ +0x0C170182, /* 77 Ll, hasUpper (subtract 48), identifier start */ +0x00034089, /* 78 Nd, identifier part, decimal 0 */ +0x00000087, /* 79 Me */ +0x00030088, /* 80 Mc, identifier part */ +0x00037489, /* 81 Nd, identifier part, decimal 26 */ +0x00005A0B, /* 82 No, decimal 13 */ +0x00006E0B, /* 83 No, decimal 23 */ +0x0000740B, /* 84 No, decimal 26 */ +0x0000000B, /* 85 No */ +0xFE170182, /* 86 Ll, hasUpper (subtract -8), identifier start */ +0xFE270181, /* 87 Lu, hasLower (add -8), identifier start */ +0xED970182, /* 88 Ll, hasUpper (subtract -74), identifier start */ +0xEA970182, /* 89 Ll, hasUpper (subtract -86), identifier start */ +0xE7170182, /* 90 Ll, hasUpper (subtract -100), identifier start */ +0xE0170182, /* 91 Ll, hasUpper (subtract -128), identifier start */ +0xE4170182, /* 92 Ll, hasUpper (subtract -112), identifier start */ +0xE0970182, /* 93 Ll, hasUpper (subtract -126), identifier start */ +0xFDD70182, /* 94 Ll, hasUpper (subtract -9), identifier start */ +0xEDA70181, /* 95 Lu, hasLower (add -74), identifier start */ +0xFDE70181, /* 96 Lu, hasLower (add -9), identifier start */ +0xEAA70181, /* 97 Lu, hasLower (add -86), identifier start */ +0xE7270181, /* 98 Lu, hasLower (add -100), identifier start */ +0xFE570182, /* 99 Ll, hasUpper (subtract -7), identifier start */ +0xE4270181, /* 100 Lu, hasLower (add -112), identifier start */ +0xFE670181, /* 101 Lu, hasLower (add -7), identifier start */ +0xE0270181, /* 102 Lu, hasLower (add -128), identifier start */ +0xE0A70181, /* 103 Lu, hasLower (add -126), identifier start */ +0x00010010, /* 104 Cf, ignorable */ +0x0004000D, /* 105 Zl, whitespace */ +0x0004000E, /* 106 Zp, whitespace */ +0x0000400B, /* 107 No, decimal 0 */ +0x0000440B, /* 108 No, decimal 2 */ +0x0427438A, /* 109 Nl, hasLower (add 16), identifier start, decimal 1 */ +0x0427818A, /* 110 Nl, hasLower (add 16), identifier start, strange */ +0x0417638A, /* 111 Nl, hasUpper (subtract 16), identifier start, decimal 17 */ +0x0417818A, /* 112 Nl, hasUpper (subtract 16), identifier start, strange */ +0x0007818A, /* 113 Nl, identifier start, strange */ +0x0000420B, /* 114 No, decimal 1 */ +0x0000720B, /* 115 No, decimal 25 */ +0x06A0001C, /* 116 So, hasLower (add 26) */ +0x0690001C, /* 117 So, hasUpper (subtract 26) */ +0x00006C0B, /* 118 No, decimal 22 */ +0x0000560B, /* 119 No, decimal 11 */ +0x0007738A, /* 120 Nl, identifier start, decimal 25 */ +0x0007418A, /* 121 Nl, identifier start, decimal 0 */ +0x00000013, /* 122 Cs */ +0x00000012 /* 123 Co */ +}; + +const jschar js_uriReservedPlusPound_ucstr[] = + {';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '#', 0}; +const jschar js_uriUnescaped_ucstr[] = + {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '-', '_', '.', '!', '~', '*', '\'', '(', ')', 0}; + +/* + * This table allows efficient testing for the regular expression \w which is + * defined by ECMA-262 15.10.2.6 to be [0-9A-Z_a-z]. + */ +const bool js_alnum[] = { +/* 0 1 2 3 4 5 5 7 8 9 */ +/* 0 */ false, false, false, false, false, false, false, false, false, false, +/* 1 */ false, false, false, false, false, false, false, false, false, false, +/* 2 */ false, false, false, false, false, false, false, false, false, false, +/* 3 */ false, false, false, false, false, false, false, false, false, false, +/* 4 */ false, false, false, false, false, false, false, false, true, true, +/* 5 */ true, true, true, true, true, true, true, true, false, false, +/* 6 */ false, false, false, false, false, true, true, true, true, true, +/* 7 */ true, true, true, true, true, true, true, true, true, true, +/* 8 */ true, true, true, true, true, true, true, true, true, true, +/* 9 */ true, false, false, false, false, true, false, true, true, true, +/* 10 */ true, true, true, true, true, true, true, true, true, true, +/* 11 */ true, true, true, true, true, true, true, true, true, true, +/* 12 */ true, true, true, false, false, false, false, false +}; + +#define URI_CHUNK 64U + +static inline bool +TransferBufferToString(JSContext *cx, JSCharBuffer &cb, jsval *rval) +{ + JSString *str = js_NewStringFromCharBuffer(cx, cb); + if (!str) + return false; + *rval = STRING_TO_JSVAL(str); + return true;; +} + +/* + * ECMA 3, 15.1.3 URI Handling Function Properties + * + * The following are implementations of the algorithms + * given in the ECMA specification for the hidden functions + * 'Encode' and 'Decode'. + */ +static JSBool +Encode(JSContext *cx, JSString *str, const jschar *unescapedSet, + const jschar *unescapedSet2, jsval *rval) +{ + size_t length, j, k, L; + JSCharBuffer cb(cx); + const jschar *chars; + jschar c, c2; + uint32 v; + uint8 utf8buf[4]; + jschar hexBuf[4]; + static const char HexDigits[] = "0123456789ABCDEF"; /* NB: uppercase */ + + str->getCharsAndLength(chars, length); + if (length == 0) { + *rval = STRING_TO_JSVAL(cx->runtime->emptyString); + return JS_TRUE; + } + + /* From this point the control must goto bad on failures. */ + hexBuf[0] = '%'; + hexBuf[3] = 0; + for (k = 0; k < length; k++) { + c = chars[k]; + if (js_strchr(unescapedSet, c) || + (unescapedSet2 && js_strchr(unescapedSet2, c))) { + if (!cb.append(c)) + return JS_FALSE; + } else { + if ((c >= 0xDC00) && (c <= 0xDFFF)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_URI, NULL); + return JS_FALSE; + } + if (c < 0xD800 || c > 0xDBFF) { + v = c; + } else { + k++; + if (k == length) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_URI, NULL); + return JS_FALSE; + } + c2 = chars[k]; + if ((c2 < 0xDC00) || (c2 > 0xDFFF)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_URI, NULL); + return JS_FALSE; + } + v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000; + } + L = js_OneUcs4ToUtf8Char(utf8buf, v); + for (j = 0; j < L; j++) { + hexBuf[1] = HexDigits[utf8buf[j] >> 4]; + hexBuf[2] = HexDigits[utf8buf[j] & 0xf]; + if (!cb.append(hexBuf, 3)) + return JS_FALSE; + } + } + } + + return TransferBufferToString(cx, cb, rval); +} + +static JSBool +Decode(JSContext *cx, JSString *str, const jschar *reservedSet, jsval *rval) +{ + size_t length, start, k; + JSCharBuffer cb(cx); + const jschar *chars; + jschar c, H; + uint32 v; + jsuint B; + uint8 octets[4]; + intN j, n; + + str->getCharsAndLength(chars, length); + if (length == 0) { + *rval = STRING_TO_JSVAL(cx->runtime->emptyString); + return JS_TRUE; + } + + /* From this point the control must goto bad on failures. */ + for (k = 0; k < length; k++) { + c = chars[k]; + if (c == '%') { + start = k; + if ((k + 2) >= length) + goto report_bad_uri; + if (!JS7_ISHEX(chars[k+1]) || !JS7_ISHEX(chars[k+2])) + goto report_bad_uri; + B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]); + k += 2; + if (!(B & 0x80)) { + c = (jschar)B; + } else { + n = 1; + while (B & (0x80 >> n)) + n++; + if (n == 1 || n > 4) + goto report_bad_uri; + octets[0] = (uint8)B; + if (k + 3 * (n - 1) >= length) + goto report_bad_uri; + for (j = 1; j < n; j++) { + k++; + if (chars[k] != '%') + goto report_bad_uri; + if (!JS7_ISHEX(chars[k+1]) || !JS7_ISHEX(chars[k+2])) + goto report_bad_uri; + B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]); + if ((B & 0xC0) != 0x80) + goto report_bad_uri; + k += 2; + octets[j] = (char)B; + } + v = Utf8ToOneUcs4Char(octets, n); + if (v >= 0x10000) { + v -= 0x10000; + if (v > 0xFFFFF) + goto report_bad_uri; + c = (jschar)((v & 0x3FF) + 0xDC00); + H = (jschar)((v >> 10) + 0xD800); + if (!cb.append(H)) + return JS_FALSE; + } else { + c = (jschar)v; + } + } + if (js_strchr(reservedSet, c)) { + if (!cb.append(chars + start, k - start + 1)) + return JS_FALSE; + } else { + if (!cb.append(c)) + return JS_FALSE; + } + } else { + if (!cb.append(c)) + return JS_FALSE; + } + } + + return TransferBufferToString(cx, cb, rval); + + report_bad_uri: + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_URI); + /* FALL THROUGH */ + + return JS_FALSE; +} + +static JSBool +str_decodeURI(JSContext *cx, uintN argc, jsval *vp) +{ + JSString *str; + + str = ArgToRootedString(cx, argc, vp, 0); + if (!str) + return JS_FALSE; + return Decode(cx, str, js_uriReservedPlusPound_ucstr, vp); +} + +static JSBool +str_decodeURI_Component(JSContext *cx, uintN argc, jsval *vp) +{ + JSString *str; + + str = ArgToRootedString(cx, argc, vp, 0); + if (!str) + return JS_FALSE; + return Decode(cx, str, js_empty_ucstr, vp); +} + +static JSBool +str_encodeURI(JSContext *cx, uintN argc, jsval *vp) +{ + JSString *str; + + str = ArgToRootedString(cx, argc, vp, 0); + if (!str) + return JS_FALSE; + return Encode(cx, str, js_uriReservedPlusPound_ucstr, js_uriUnescaped_ucstr, + vp); +} + +static JSBool +str_encodeURI_Component(JSContext *cx, uintN argc, jsval *vp) +{ + JSString *str; + + str = ArgToRootedString(cx, argc, vp, 0); + if (!str) + return JS_FALSE; + return Encode(cx, str, js_uriUnescaped_ucstr, NULL, vp); +} + +/* + * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at + * least 4 bytes long. Return the number of UTF-8 bytes of data written. + */ +int +js_OneUcs4ToUtf8Char(uint8 *utf8Buffer, uint32 ucs4Char) +{ + int utf8Length = 1; + + JS_ASSERT(ucs4Char <= 0x10FFFF); + if (ucs4Char < 0x80) { + *utf8Buffer = (uint8)ucs4Char; + } else { + int i; + uint32 a = ucs4Char >> 11; + utf8Length = 2; + while (a) { + a >>= 5; + utf8Length++; + } + i = utf8Length; + while (--i) { + utf8Buffer[i] = (uint8)((ucs4Char & 0x3F) | 0x80); + ucs4Char >>= 6; + } + *utf8Buffer = (uint8)(0x100 - (1 << (8-utf8Length)) + ucs4Char); + } + return utf8Length; +} + +/* + * Convert a utf8 character sequence into a UCS-4 character and return that + * character. It is assumed that the caller already checked that the sequence + * is valid. + */ +static uint32 +Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length) +{ + uint32 ucs4Char; + uint32 minucs4Char; + /* from Unicode 3.1, non-shortest form is illegal */ + static const uint32 minucs4Table[] = { + 0x00000080, 0x00000800, 0x00010000 + }; + + JS_ASSERT(utf8Length >= 1 && utf8Length <= 4); + if (utf8Length == 1) { + ucs4Char = *utf8Buffer; + JS_ASSERT(!(ucs4Char & 0x80)); + } else { + JS_ASSERT((*utf8Buffer & (0x100 - (1 << (7-utf8Length)))) == + (0x100 - (1 << (8-utf8Length)))); + ucs4Char = *utf8Buffer++ & ((1<<(7-utf8Length))-1); + minucs4Char = minucs4Table[utf8Length-2]; + while (--utf8Length) { + JS_ASSERT((*utf8Buffer & 0xC0) == 0x80); + ucs4Char = ucs4Char<<6 | (*utf8Buffer++ & 0x3F); + } + if (JS_UNLIKELY(ucs4Char < minucs4Char)) { + ucs4Char = OVERLONG_UTF8; + } else if (ucs4Char == 0xFFFE || ucs4Char == 0xFFFF) { + ucs4Char = 0xFFFD; + } + } + return ucs4Char; +} + +#ifdef DEBUG + +JS_FRIEND_API(size_t) +js_PutEscapedStringImpl(char *buffer, size_t bufferSize, FILE *fp, + JSString *str, uint32 quote) +{ + const jschar *chars, *charsEnd; + size_t n; + const char *escape; + char c; + uintN u, hex, shift; + enum { + STOP, FIRST_QUOTE, LAST_QUOTE, CHARS, ESCAPE_START, ESCAPE_MORE + } state; + + JS_ASSERT(quote == 0 || quote == '\'' || quote == '"'); + JS_ASSERT_IF(buffer, bufferSize != 0); + JS_ASSERT_IF(!buffer, bufferSize == 0); + JS_ASSERT_IF(fp, !buffer); + + str->getCharsAndEnd(chars, charsEnd); + n = 0; + --bufferSize; + state = FIRST_QUOTE; + shift = 0; + hex = 0; + u = 0; + c = 0; /* to quell GCC warnings */ + + for (;;) { + switch (state) { + case STOP: + goto stop; + case FIRST_QUOTE: + state = CHARS; + goto do_quote; + case LAST_QUOTE: + state = STOP; + do_quote: + if (quote == 0) + continue; + c = (char)quote; + break; + case CHARS: + if (chars == charsEnd) { + state = LAST_QUOTE; + continue; + } + u = *chars++; + if (u < ' ') { + if (u != 0) { + escape = strchr(js_EscapeMap, (int)u); + if (escape) { + u = escape[1]; + goto do_escape; + } + } + goto do_hex_escape; + } + if (u < 127) { + if (u == quote || u == '\\') + goto do_escape; + c = (char)u; + } else if (u < 0x100) { + goto do_hex_escape; + } else { + shift = 16; + hex = u; + u = 'u'; + goto do_escape; + } + break; + do_hex_escape: + shift = 8; + hex = u; + u = 'x'; + do_escape: + c = '\\'; + state = ESCAPE_START; + break; + case ESCAPE_START: + JS_ASSERT(' ' <= u && u < 127); + c = (char)u; + state = ESCAPE_MORE; + break; + case ESCAPE_MORE: + if (shift == 0) { + state = CHARS; + continue; + } + shift -= 4; + u = 0xF & (hex >> shift); + c = (char)(u + (u < 10 ? '0' : 'A' - 10)); + break; + } + if (buffer) { + if (n == bufferSize) + break; + buffer[n] = c; + } else if (fp) { + fputc(c, fp); + } + n++; + } + stop: + if (buffer) + buffer[n] = '\0'; + return n; +} + +#endif diff --git a/ape-server/deps/js/src/jsstr.h b/ape-server/deps/js/src/jsstr.h new file mode 100755 index 0000000..e9bd805 --- /dev/null +++ b/ape-server/deps/js/src/jsstr.h @@ -0,0 +1,809 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsstr_h___ +#define jsstr_h___ +/* + * JS string type implementation. + * + * A JS string is a counted array of unicode characters. To support handoff + * of API client memory, the chars are allocated separately from the length, + * necessitating a pointer after the count, to form a separately allocated + * string descriptor. String descriptors are GC'ed, while their chars are + * allocated from the malloc heap. + */ +#include +#include "jspubtd.h" +#include "jsprvtd.h" +#include "jslock.h" + +JS_BEGIN_EXTERN_C + +#define JSSTRING_BIT(n) ((size_t)1 << (n)) +#define JSSTRING_BITMASK(n) (JSSTRING_BIT(n) - 1) + +class TraceRecorder; + +enum { + UNIT_STRING_LIMIT = 256U, + INT_STRING_LIMIT = 256U +}; + +extern jschar * +js_GetDependentStringChars(JSString *str); + +JS_STATIC_ASSERT(JS_BITS_PER_WORD >= 32); + +/* + * The GC-thing "string" type. + * + * When the DEPENDENT bit of the mLength field is unset, the mChars field + * points to a flat character array owned by its GC-thing descriptor. The + * array is terminated at index length by a zero character and the size of the + * array in bytes is (length + 1) * sizeof(jschar). The terminator is purely a + * backstop, in case the chars pointer flows out to native code that requires + * \u0000 termination. + * + * A flat string with the MUTABLE flag means that the string is accessible only + * from one thread and it is possible to turn it into a dependent string of the + * same length to optimize js_ConcatStrings. It is also possible to grow such a + * string, but extreme care must be taken to ensure that no other code relies + * on the original length of the string. + * + * A flat string with the ATOMIZED flag means that the string is hashed as + * an atom. This flag is used to avoid re-hashing the already-atomized string. + * + * Any string with the DEFLATED flag means that the string has an entry in the + * deflated string cache. The GC uses this flag to optimize string finalization + * and avoid an expensive cache lookup for strings that were never deflated. + * + * When the DEPENDENT flag is set, the string depends on characters of another + * string strongly referenced by the mBase field. The base member may point to + * another dependent string if chars() has not been called yet. + * + * The PREFIX flag determines the kind of the dependent string. When the flag + * is unset, the mLength field encodes both starting position relative to the + * base string and the number of characters in the dependent string, see + * DEPENDENT_START_MASK and DEPENDENT_LENGTH_MASK below for details. + * + * When the PREFIX flag is set, the dependent string is a prefix of the base + * string. The number of characters in the prefix is encoded using all non-flag + * bits of the mLength field and spans the same 0 .. SIZE_T_MAX/4 range as the + * length of the flat string. + * + * NB: Always use the length() and chars() accessor methods. + */ +struct JSString { + friend class TraceRecorder; + + friend JSAtom * + js_AtomizeString(JSContext *cx, JSString *str, uintN flags); + + friend JSString * JS_FASTCALL + js_ConcatStrings(JSContext *cx, JSString *left, JSString *right); + + size_t mLength; + union { + jschar *mChars; + JSString *mBase; + }; + + /* + * Definitions for flags stored in the high order bits of mLength. + * + * PREFIX and MUTABLE are two aliases for the same bit. PREFIX should be + * used only if DEPENDENT is set and MUTABLE should be used only if the + * string is flat. + * + * ATOMIZED is used only with flat, immutable strings. + */ + static const size_t DEPENDENT = JSSTRING_BIT(JS_BITS_PER_WORD - 1); + static const size_t PREFIX = JSSTRING_BIT(JS_BITS_PER_WORD - 2); + static const size_t MUTABLE = PREFIX; + static const size_t ATOMIZED = JSSTRING_BIT(JS_BITS_PER_WORD - 3); + static const size_t DEFLATED = JSSTRING_BIT(JS_BITS_PER_WORD - 4); +#if JS_BITS_PER_WORD > 32 + static const size_t LENGTH_BITS = 28; +#else + static const size_t LENGTH_BITS = JS_BITS_PER_WORD - 4; +#endif + static const size_t LENGTH_MASK = JSSTRING_BITMASK(LENGTH_BITS); + static const size_t DEPENDENT_LENGTH_BITS = 8; + static const size_t DEPENDENT_LENGTH_MASK = JSSTRING_BITMASK(DEPENDENT_LENGTH_BITS); + static const size_t DEPENDENT_START_BITS = LENGTH_BITS - DEPENDENT_LENGTH_BITS; + static const size_t DEPENDENT_START_SHIFT = DEPENDENT_LENGTH_BITS; + static const size_t DEPENDENT_START_MASK = JSSTRING_BITMASK(DEPENDENT_START_BITS); + + bool hasFlag(size_t flag) const { + return (mLength & flag) != 0; + } + + public: + static const size_t MAX_LENGTH = LENGTH_MASK; + static const size_t MAX_DEPENDENT_START = DEPENDENT_START_MASK; + static const size_t MAX_DEPENDENT_LENGTH = DEPENDENT_LENGTH_MASK; + + bool isDependent() const { + return hasFlag(DEPENDENT); + } + + bool isFlat() const { + return !isDependent(); + } + + bool isDeflated() const { + return hasFlag(DEFLATED); + } + + void setDeflated() { + JS_ATOMIC_SET_MASK((jsword *) &mLength, DEFLATED); + } + + bool isMutable() const { + return !isDependent() && hasFlag(MUTABLE); + } + + bool isAtomized() const { + return !isDependent() && hasFlag(ATOMIZED); + } + + JS_ALWAYS_INLINE jschar *chars() { + return isDependent() ? dependentChars() : flatChars(); + } + + JS_ALWAYS_INLINE size_t length() const { + return isDependent() ? dependentLength() : flatLength(); + } + + JS_ALWAYS_INLINE bool empty() const { + return length() == 0; + } + + JS_ALWAYS_INLINE void getCharsAndLength(const jschar *&chars, size_t &length) { + if (isDependent()) { + length = dependentLength(); + chars = dependentChars(); + } else { + length = flatLength(); + chars = flatChars(); + } + } + + JS_ALWAYS_INLINE void getCharsAndEnd(const jschar *&chars, const jschar *&end) { + end = isDependent() + ? dependentLength() + (chars = dependentChars()) + : flatLength() + (chars = flatChars()); + } + + /* Specific flat string initializer and accessor methods. */ + void initFlat(jschar *chars, size_t length) { + JS_ASSERT(length <= MAX_LENGTH); + mLength = length; + mChars = chars; + } + + jschar *flatChars() const { + JS_ASSERT(isFlat()); + return mChars; + } + + size_t flatLength() const { + JS_ASSERT(isFlat()); + return mLength & LENGTH_MASK; + } + + /* + * Special flat string initializer that preserves the JSSTR_DEFLATED flag. + * Use this method when reinitializing an existing string which may be + * hashed to its deflated bytes. Newborn strings must use initFlat. + */ + void reinitFlat(jschar *chars, size_t length) { + JS_ASSERT(length <= MAX_LENGTH); + mLength = (mLength & DEFLATED) | (length & ~DEFLATED); + mChars = chars; + } + + /* + * Methods to manipulate atomized and mutable flags of flat strings. It is + * safe to use these without extra locking due to the following properties: + * + * * We do not have a flatClearAtomized method, as a string remains + * atomized until the GC collects it. + * + * * A thread may call flatSetMutable only when it is the only + * thread accessing the string until a later call to + * flatClearMutable. + * + * * Multiple threads can call flatClearMutable but the function actually + * clears the mutable flag only when the flag is set -- in which case + * only one thread can access the string (see previous property). + * + * Thus, when multiple threads access the string, JSString::flatSetAtomized + * is the only function that can update the mLength field of the string by + * changing the mutable bit from 0 to 1. We call the method only after the + * string has been hashed. When some threads in js_ValueToStringId see that + * the flag is set, it knows that the string was atomized. + * + * On the other hand, if the thread sees that the flag is unset, it could + * be seeing a stale value when another thread has just atomized the string + * and set the flag. But this can lead only to an extra call to + * js_AtomizeString. This function would find that the string was already + * hashed and return it with the atomized bit set. + */ + void flatSetAtomized() { + JS_ASSERT(isFlat() && !isMutable()); + JS_STATIC_ASSERT(sizeof(mLength) == sizeof(jsword)); + JS_ATOMIC_SET_MASK((jsword *) &mLength, ATOMIZED); + } + + void flatSetMutable() { + JS_ASSERT(isFlat() && !isAtomized()); + mLength |= MUTABLE; + } + + void flatClearMutable() { + JS_ASSERT(isFlat()); + if (hasFlag(MUTABLE)) + mLength &= ~MUTABLE; + } + + void initDependent(JSString *bstr, size_t off, size_t len) { + JS_ASSERT(off <= MAX_DEPENDENT_START); + JS_ASSERT(len <= MAX_DEPENDENT_LENGTH); + mLength = DEPENDENT | (off << DEPENDENT_START_SHIFT) | len; + mBase = bstr; + } + + /* See JSString::reinitFlat. */ + void reinitDependent(JSString *bstr, size_t off, size_t len) { + JS_ASSERT(off <= MAX_DEPENDENT_START); + JS_ASSERT(len <= MAX_DEPENDENT_LENGTH); + mLength = DEPENDENT | (mLength & DEFLATED) | (off << DEPENDENT_START_SHIFT) | len; + mBase = bstr; + } + + JSString *dependentBase() const { + JS_ASSERT(isDependent()); + return mBase; + } + + bool dependentIsPrefix() const { + JS_ASSERT(isDependent()); + return hasFlag(PREFIX); + } + + JS_ALWAYS_INLINE jschar *dependentChars() { + return dependentBase()->isDependent() + ? js_GetDependentStringChars(this) + : dependentBase()->flatChars() + dependentStart(); + } + + JS_ALWAYS_INLINE size_t dependentStart() const { + return dependentIsPrefix() + ? 0 + : ((mLength >> DEPENDENT_START_SHIFT) & DEPENDENT_START_MASK); + } + + JS_ALWAYS_INLINE size_t dependentLength() const { + JS_ASSERT(isDependent()); + if (dependentIsPrefix()) + return mLength & LENGTH_MASK; + return mLength & DEPENDENT_LENGTH_MASK; + } + + void initPrefix(JSString *bstr, size_t len) { + JS_ASSERT(len <= MAX_LENGTH); + mLength = DEPENDENT | PREFIX | len; + mBase = bstr; + } + + /* See JSString::reinitFlat. */ + void reinitPrefix(JSString *bstr, size_t len) { + JS_ASSERT(len <= MAX_LENGTH); + mLength = DEPENDENT | PREFIX | (mLength & DEFLATED) | len; + mBase = bstr; + } + + JSString *prefixBase() const { + JS_ASSERT(isDependent() && dependentIsPrefix()); + return dependentBase(); + } + + void prefixSetBase(JSString *bstr) { + JS_ASSERT(isDependent() && dependentIsPrefix()); + mBase = bstr; + } + + static inline bool isUnitString(void *ptr) { + jsuword delta = reinterpret_cast(ptr) - + reinterpret_cast(unitStringTable); + if (delta >= UNIT_STRING_LIMIT * sizeof(JSString)) + return false; + + /* If ptr points inside the static array, it must be well-aligned. */ + JS_ASSERT(delta % sizeof(JSString) == 0); + return true; + } + + static inline bool isIntString(void *ptr) { + jsuword delta = reinterpret_cast(ptr) - + reinterpret_cast(intStringTable); + if (delta >= INT_STRING_LIMIT * sizeof(JSString)) + return false; + + /* If ptr points inside the static array, it must be well-aligned. */ + JS_ASSERT(delta % sizeof(JSString) == 0); + return true; + } + + static inline bool isStatic(void *ptr) { + return isUnitString(ptr) || isIntString(ptr); + } + +#ifdef __SUNPRO_CC +#pragma align 8 (__1cIJSStringPunitStringTable_, __1cIJSStringOintStringTable_) +#endif + + static JSString unitStringTable[]; + static JSString intStringTable[]; + static const char *deflatedIntStringTable[]; + + static JSString *unitString(jschar c); + static JSString *getUnitString(JSContext *cx, JSString *str, size_t index); + static JSString *intString(jsint i); +}; + +extern const jschar * +js_GetStringChars(JSContext *cx, JSString *str); + +extern JSString * JS_FASTCALL +js_ConcatStrings(JSContext *cx, JSString *left, JSString *right); + +extern const jschar * +js_UndependString(JSContext *cx, JSString *str); + +extern JSBool +js_MakeStringImmutable(JSContext *cx, JSString *str); + +extern JSString * JS_FASTCALL +js_toLowerCase(JSContext *cx, JSString *str); + +extern JSString * JS_FASTCALL +js_toUpperCase(JSContext *cx, JSString *str); + +struct JSSubString { + size_t length; + const jschar *chars; +}; + +extern jschar js_empty_ucstr[]; +extern JSSubString js_EmptySubString; + +/* Unicode character attribute lookup tables. */ +extern const uint8 js_X[]; +extern const uint8 js_Y[]; +extern const uint32 js_A[]; + +/* Enumerated Unicode general category types. */ +typedef enum JSCharType { + JSCT_UNASSIGNED = 0, + JSCT_UPPERCASE_LETTER = 1, + JSCT_LOWERCASE_LETTER = 2, + JSCT_TITLECASE_LETTER = 3, + JSCT_MODIFIER_LETTER = 4, + JSCT_OTHER_LETTER = 5, + JSCT_NON_SPACING_MARK = 6, + JSCT_ENCLOSING_MARK = 7, + JSCT_COMBINING_SPACING_MARK = 8, + JSCT_DECIMAL_DIGIT_NUMBER = 9, + JSCT_LETTER_NUMBER = 10, + JSCT_OTHER_NUMBER = 11, + JSCT_SPACE_SEPARATOR = 12, + JSCT_LINE_SEPARATOR = 13, + JSCT_PARAGRAPH_SEPARATOR = 14, + JSCT_CONTROL = 15, + JSCT_FORMAT = 16, + JSCT_PRIVATE_USE = 18, + JSCT_SURROGATE = 19, + JSCT_DASH_PUNCTUATION = 20, + JSCT_START_PUNCTUATION = 21, + JSCT_END_PUNCTUATION = 22, + JSCT_CONNECTOR_PUNCTUATION = 23, + JSCT_OTHER_PUNCTUATION = 24, + JSCT_MATH_SYMBOL = 25, + JSCT_CURRENCY_SYMBOL = 26, + JSCT_MODIFIER_SYMBOL = 27, + JSCT_OTHER_SYMBOL = 28 +} JSCharType; + +/* Character classifying and mapping macros, based on java.lang.Character. */ +#define JS_CCODE(c) (js_A[js_Y[(js_X[(uint16)(c)>>6]<<6)|((c)&0x3F)]]) +#define JS_CTYPE(c) (JS_CCODE(c) & 0x1F) + +#define JS_ISALPHA(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ + (1 << JSCT_LOWERCASE_LETTER) | \ + (1 << JSCT_TITLECASE_LETTER) | \ + (1 << JSCT_MODIFIER_LETTER) | \ + (1 << JSCT_OTHER_LETTER)) \ + >> JS_CTYPE(c)) & 1) + +#define JS_ISALNUM(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ + (1 << JSCT_LOWERCASE_LETTER) | \ + (1 << JSCT_TITLECASE_LETTER) | \ + (1 << JSCT_MODIFIER_LETTER) | \ + (1 << JSCT_OTHER_LETTER) | \ + (1 << JSCT_DECIMAL_DIGIT_NUMBER)) \ + >> JS_CTYPE(c)) & 1) + +/* A unicode letter, suitable for use in an identifier. */ +#define JS_ISLETTER(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ + (1 << JSCT_LOWERCASE_LETTER) | \ + (1 << JSCT_TITLECASE_LETTER) | \ + (1 << JSCT_MODIFIER_LETTER) | \ + (1 << JSCT_OTHER_LETTER) | \ + (1 << JSCT_LETTER_NUMBER)) \ + >> JS_CTYPE(c)) & 1) + +/* + * 'IdentifierPart' from ECMA grammar, is Unicode letter or combining mark or + * digit or connector punctuation. + */ +#define JS_ISIDPART(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ + (1 << JSCT_LOWERCASE_LETTER) | \ + (1 << JSCT_TITLECASE_LETTER) | \ + (1 << JSCT_MODIFIER_LETTER) | \ + (1 << JSCT_OTHER_LETTER) | \ + (1 << JSCT_LETTER_NUMBER) | \ + (1 << JSCT_NON_SPACING_MARK) | \ + (1 << JSCT_COMBINING_SPACING_MARK) | \ + (1 << JSCT_DECIMAL_DIGIT_NUMBER) | \ + (1 << JSCT_CONNECTOR_PUNCTUATION)) \ + >> JS_CTYPE(c)) & 1) + +/* Unicode control-format characters, ignored in input */ +#define JS_ISFORMAT(c) (((1 << JSCT_FORMAT) >> JS_CTYPE(c)) & 1) + +/* + * This table is used in JS_ISWORD. The definition has external linkage to + * allow the raw table data to be used in the regular expression compiler. + */ +extern const bool js_alnum[]; + +/* + * This macro performs testing for the regular expression word class \w, which + * is defined by ECMA-262 15.10.2.6 to be [0-9A-Z_a-z]. If we want a + * Unicode-friendlier definition of "word", we should rename this macro to + * something regexp-y. + */ +#define JS_ISWORD(c) ((c) < 128 && js_alnum[(c)]) + +#define JS_ISIDSTART(c) (JS_ISLETTER(c) || (c) == '_' || (c) == '$') +#define JS_ISIDENT(c) (JS_ISIDPART(c) || (c) == '_' || (c) == '$') + +#define JS_ISXMLSPACE(c) ((c) == ' ' || (c) == '\t' || (c) == '\r' || \ + (c) == '\n') +#define JS_ISXMLNSSTART(c) ((JS_CCODE(c) & 0x00000100) || (c) == '_') +#define JS_ISXMLNS(c) ((JS_CCODE(c) & 0x00000080) || (c) == '.' || \ + (c) == '-' || (c) == '_') +#define JS_ISXMLNAMESTART(c) (JS_ISXMLNSSTART(c) || (c) == ':') +#define JS_ISXMLNAME(c) (JS_ISXMLNS(c) || (c) == ':') + +#define JS_ISDIGIT(c) (JS_CTYPE(c) == JSCT_DECIMAL_DIGIT_NUMBER) + +static inline bool +JS_ISSPACE(jschar c) +{ + unsigned w = c; + + if (w < 256) + return (w <= ' ' && (w == ' ' || (9 <= w && w <= 0xD))) || w == 0xA0; + + return (JS_CCODE(w) & 0x00070000) == 0x00040000; +} + +#define JS_ISPRINT(c) ((c) < 128 && isprint(c)) + +#define JS_ISUPPER(c) (JS_CTYPE(c) == JSCT_UPPERCASE_LETTER) +#define JS_ISLOWER(c) (JS_CTYPE(c) == JSCT_LOWERCASE_LETTER) + +#define JS_TOUPPER(c) ((jschar) ((JS_CCODE(c) & 0x00100000) \ + ? (c) - ((int32)JS_CCODE(c) >> 22) \ + : (c))) +#define JS_TOLOWER(c) ((jschar) ((JS_CCODE(c) & 0x00200000) \ + ? (c) + ((int32)JS_CCODE(c) >> 22) \ + : (c))) + +/* + * Shorthands for ASCII (7-bit) decimal and hex conversion. + * Manually inline isdigit for performance; MSVC doesn't do this for us. + */ +#define JS7_ISDEC(c) ((((unsigned)(c)) - '0') <= 9) +#define JS7_UNDEC(c) ((c) - '0') +#define JS7_ISHEX(c) ((c) < 128 && isxdigit(c)) +#define JS7_UNHEX(c) (uintN)(JS7_ISDEC(c) ? (c) - '0' : 10 + tolower(c) - 'a') +#define JS7_ISLET(c) ((c) < 128 && isalpha(c)) + +/* Initialize per-runtime string state for the first context in the runtime. */ +extern JSBool +js_InitRuntimeStringState(JSContext *cx); + +extern JSBool +js_InitDeflatedStringCache(JSRuntime *rt); + +extern void +js_FinishRuntimeStringState(JSContext *cx); + +extern void +js_FinishDeflatedStringCache(JSRuntime *rt); + +/* Initialize the String class, returning its prototype object. */ +extern JSClass js_StringClass; + +extern JSObject * +js_InitStringClass(JSContext *cx, JSObject *obj); + +extern const char js_escape_str[]; +extern const char js_unescape_str[]; +extern const char js_uneval_str[]; +extern const char js_decodeURI_str[]; +extern const char js_encodeURI_str[]; +extern const char js_decodeURIComponent_str[]; +extern const char js_encodeURIComponent_str[]; + +/* GC-allocate a string descriptor for the given malloc-allocated chars. */ +extern JSString * +js_NewString(JSContext *cx, jschar *chars, size_t length); + +/* + * GC-allocate a string descriptor and steal the char buffer held by |cb|. + * This function takes responsibility for adding the terminating '\0' required + * by js_NewString. + */ +extern JSString * +js_NewStringFromCharBuffer(JSContext *cx, JSCharBuffer &cb); + +extern JSString * +js_NewDependentString(JSContext *cx, JSString *base, size_t start, + size_t length); + +/* Copy a counted string and GC-allocate a descriptor for it. */ +extern JSString * +js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n); + +/* Copy a C string and GC-allocate a descriptor for it. */ +extern JSString * +js_NewStringCopyZ(JSContext *cx, const jschar *s); + +/* + * Convert a value to a printable C string. + */ +typedef JSString *(*JSValueToStringFun)(JSContext *cx, jsval v); + +extern JS_FRIEND_API(const char *) +js_ValueToPrintable(JSContext *cx, jsval v, JSValueToStringFun v2sfun); + +#define js_ValueToPrintableString(cx,v) \ + js_ValueToPrintable(cx, v, js_ValueToString) + +#define js_ValueToPrintableSource(cx,v) \ + js_ValueToPrintable(cx, v, js_ValueToSource) + +/* + * Convert a value to a string, returning null after reporting an error, + * otherwise returning a new string reference. + */ +extern JS_FRIEND_API(JSString *) +js_ValueToString(JSContext *cx, jsval v); + +/* + * This function implements E-262-3 section 9.8, toString. Convert the given + * value to a string of jschars appended to the given buffer. On error, the + * passed buffer may have partial results appended. + */ +extern JS_FRIEND_API(JSBool) +js_ValueToCharBuffer(JSContext *cx, jsval v, JSCharBuffer &cb); + +/* + * Convert a value to its source expression, returning null after reporting + * an error, otherwise returning a new string reference. + */ +extern JS_FRIEND_API(JSString *) +js_ValueToSource(JSContext *cx, jsval v); + +/* + * Compute a hash function from str. The caller can call this function even if + * str is not a GC-allocated thing. + */ +extern uint32 +js_HashString(JSString *str); + +/* + * Test if strings are equal. The caller can call the function even if str1 + * or str2 are not GC-allocated things. + */ +extern JSBool JS_FASTCALL +js_EqualStrings(JSString *str1, JSString *str2); + +/* + * Return less than, equal to, or greater than zero depending on whether + * str1 is less than, equal to, or greater than str2. + */ +extern int32 JS_FASTCALL +js_CompareStrings(JSString *str1, JSString *str2); + +/* + * Boyer-Moore-Horspool superlinear search for pat:patlen in text:textlen. + * The patlen argument must be positive and no greater than sBMHPatLenMax. + * + * Return the index of pat in text, or -1 if not found. + */ +static const jsuint sBMHCharSetSize = 256; /* ISO-Latin-1 */ +static const jsuint sBMHPatLenMax = 255; /* skip table element is uint8 */ +static const jsint sBMHBadPattern = -2; /* return value if pat is not ISO-Latin-1 */ + +extern jsint +js_BoyerMooreHorspool(const jschar *text, jsuint textlen, + const jschar *pat, jsuint patlen); + +extern size_t +js_strlen(const jschar *s); + +extern jschar * +js_strchr(const jschar *s, jschar c); + +extern jschar * +js_strchr_limit(const jschar *s, jschar c, const jschar *limit); + +#define js_strncpy(t, s, n) memcpy((t), (s), (n) * sizeof(jschar)) + +/* + * Return s advanced past any Unicode white space characters. + */ +static inline const jschar * +js_SkipWhiteSpace(const jschar *s, const jschar *end) +{ + JS_ASSERT(s <= end); + while (s != end && JS_ISSPACE(*s)) + s++; + return s; +} + +/* + * Inflate bytes to JS chars and vice versa. Report out of memory via cx + * and return null on error, otherwise return the jschar or byte vector that + * was JS_malloc'ed. length is updated with the length of the new string in jschars. + */ +extern jschar * +js_InflateString(JSContext *cx, const char *bytes, size_t *length); + +extern char * +js_DeflateString(JSContext *cx, const jschar *chars, size_t length); + +/* + * Inflate bytes to JS chars into a buffer. 'chars' must be large enough for + * 'length' jschars. The buffer is NOT null-terminated. The destination length + * must be be initialized with the buffer size and will contain on return the + * number of copied chars. + */ +extern JSBool +js_InflateStringToBuffer(JSContext *cx, const char *bytes, size_t length, + jschar *chars, size_t *charsLength); + +/* + * Get number of bytes in the deflated sequence of characters. + */ +extern size_t +js_GetDeflatedStringLength(JSContext *cx, const jschar *chars, + size_t charsLength); + +/* + * Deflate JS chars to bytes into a buffer. 'bytes' must be large enough for + * 'length chars. The buffer is NOT null-terminated. The destination length + * must to be initialized with the buffer size and will contain on return the + * number of copied bytes. + */ +extern JSBool +js_DeflateStringToBuffer(JSContext *cx, const jschar *chars, + size_t charsLength, char *bytes, size_t *length); + +/* + * Associate bytes with str in the deflated string cache, returning true on + * successful association, false on out of memory. + */ +extern JSBool +js_SetStringBytes(JSContext *cx, JSString *str, char *bytes, size_t length); + +/* + * Find or create a deflated string cache entry for str that contains its + * characters chopped from Unicode code points into bytes. + */ +extern const char * +js_GetStringBytes(JSContext *cx, JSString *str); + +/* Remove a deflated string cache entry associated with str if any. */ +extern void +js_PurgeDeflatedStringCache(JSRuntime *rt, JSString *str); + +/* Export a few natives and a helper to other files in SpiderMonkey. */ +extern JSBool +js_str_escape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +extern JSBool +js_str_toString(JSContext *cx, uintN argc, jsval *vp); + +/* + * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at + * least 6 bytes long. Return the number of UTF-8 bytes of data written. + */ +extern int +js_OneUcs4ToUtf8Char(uint8 *utf8Buffer, uint32 ucs4Char); + +/* + * Write str into buffer escaping any non-printable or non-ASCII character. + * Guarantees that a NUL is at the end of the buffer. Returns the length of + * the written output, NOT including the NUL. If buffer is null, just returns + * the length of the output. If quote is not 0, it must be a single or double + * quote character that will quote the output. + * + * The function is only defined for debug builds. +*/ +#define js_PutEscapedString(buffer, bufferSize, str, quote) \ + js_PutEscapedStringImpl(buffer, bufferSize, NULL, str, quote) + +/* + * Write str into file escaping any non-printable or non-ASCII character. + * Returns the number of bytes written to file. If quote is not 0, it must + * be a single or double quote character that will quote the output. + * + * The function is only defined for debug builds. +*/ +#define js_FileEscapedString(file, str, quote) \ + (JS_ASSERT(file), js_PutEscapedStringImpl(NULL, 0, file, str, quote)) + +extern JS_FRIEND_API(size_t) +js_PutEscapedStringImpl(char *buffer, size_t bufferSize, FILE *fp, + JSString *str, uint32 quote); + +extern JSBool +js_String(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); + +JS_END_EXTERN_C + +#endif /* jsstr_h___ */ diff --git a/ape-server/deps/js/src/jsstrinlines.h b/ape-server/deps/js/src/jsstrinlines.h new file mode 100755 index 0000000..d1b122a --- /dev/null +++ b/ape-server/deps/js/src/jsstrinlines.h @@ -0,0 +1,75 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsstrinlines_h___ +#define jsstrinlines_h___ + +#include "jsstr.h" + +inline JSString * +JSString::unitString(jschar c) +{ + JS_ASSERT(c < UNIT_STRING_LIMIT); + return &unitStringTable[c]; +} + +inline JSString * +JSString::getUnitString(JSContext *cx, JSString *str, size_t index) +{ + JS_ASSERT(index < str->length()); + jschar c = str->chars()[index]; + if (c < UNIT_STRING_LIMIT) + return unitString(c); + return js_NewDependentString(cx, str, index, 1); +} + +inline JSString * +JSString::intString(jsint i) +{ + jsuint u = jsuint(i); + + JS_ASSERT(u < INT_STRING_LIMIT); + if (u < 10) { + /* To avoid two ATOMIZED JSString copies of 0-9. */ + return &JSString::unitStringTable['0' + u]; + } + return &JSString::intStringTable[u]; +} + +#endif /* jsstrinlines_h___ */ diff --git a/ape-server/deps/js/src/jstask.cpp b/ape-server/deps/js/src/jstask.cpp new file mode 100755 index 0000000..9071e63 --- /dev/null +++ b/ape-server/deps/js/src/jstask.cpp @@ -0,0 +1,126 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=99 ft=cpp: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla SpiderMonkey JavaScript 1.9.1 code, released + * June 30, 2009. + * + * The Initial Developer of the Original Code is + * Andreas Gal + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "jstask.h" + +#ifdef JS_THREADSAFE +static void start(void* arg) { + ((JSBackgroundThread*)arg)->work(); +} + +JSBackgroundThread::JSBackgroundThread() + : thread(NULL), stack(NULL), lock(NULL), wakeup(NULL), shutdown(false) +{ +} + +JSBackgroundThread::~JSBackgroundThread() +{ + if (wakeup) + PR_DestroyCondVar(wakeup); + if (lock) + PR_DestroyLock(lock); + /* PR_DestroyThread is not necessary. */ +} + +bool +JSBackgroundThread::init() +{ + if (!(lock = PR_NewLock())) + return false; + if (!(wakeup = PR_NewCondVar(lock))) + return false; + thread = PR_CreateThread(PR_USER_THREAD, start, this, PR_PRIORITY_LOW, + PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); + return !!thread; +} + +void +JSBackgroundThread::cancel() +{ + PR_Lock(lock); + if (shutdown) { + PR_Unlock(lock); + return; + } + shutdown = true; + PR_NotifyCondVar(wakeup); + PR_Unlock(lock); + PR_JoinThread(thread); +} + +void +JSBackgroundThread::work() +{ + PR_Lock(lock); + while (!shutdown) { + PR_WaitCondVar(wakeup, PR_INTERVAL_NO_TIMEOUT); + JSBackgroundTask* t; + while ((t = stack) != NULL) { + stack = t->next; + PR_Unlock(lock); + t->run(); + delete t; + PR_Lock(lock); + } + } + PR_Unlock(lock); +} + +bool +JSBackgroundThread::busy() +{ + return !!stack; // we tolerate some racing here +} + +void +JSBackgroundThread::schedule(JSBackgroundTask* task) +{ + PR_Lock(lock); + if (shutdown) { + PR_Unlock(lock); + task->run(); + delete task; + return; + } + task->next = stack; + stack = task; + PR_NotifyCondVar(wakeup); + PR_Unlock(lock); +} + +#endif diff --git a/ape-server/deps/js/src/jstask.h b/ape-server/deps/js/src/jstask.h new file mode 100755 index 0000000..30bc009 --- /dev/null +++ b/ape-server/deps/js/src/jstask.h @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=99 ft=cpp: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released + * June 30, 2009. + * + * The Initial Developer of the Original Code is + * Andreas Gal + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jstask_h___ +#define jstask_h___ + +class JSBackgroundTask { + friend class JSBackgroundThread; + JSBackgroundTask* next; + public: + virtual void run() = 0; +}; + +#ifdef JS_THREADSAFE + +#include "prthread.h" +#include "prlock.h" +#include "prcvar.h" + +class JSBackgroundThread { + PRThread* thread; + JSBackgroundTask* stack; + PRLock* lock; + PRCondVar* wakeup; + bool shutdown; + + public: + JSBackgroundThread(); + ~JSBackgroundThread(); + + bool init(); + void cancel(); + void work(); + bool busy(); + void schedule(JSBackgroundTask* task); +}; + +#else + +class JSBackgroundThread { + public: + void schedule(JSBackgroundTask* task) { + task->run(); + } +}; + +#endif + +#endif /* jstask_h___ */ diff --git a/ape-server/deps/js/src/jstl.h b/ape-server/deps/js/src/jstl.h new file mode 100755 index 0000000..4380c9e --- /dev/null +++ b/ape-server/deps/js/src/jstl.h @@ -0,0 +1,242 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=99 ft=cpp: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released + * July 16, 2009. + * + * The Initial Developer of the Original Code is + * the Mozilla Corporation. + * + * Contributor(s): + * Luke Wagner + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jstl_h_ +#define jstl_h_ + +#include "jsbit.h" + +namespace js { + +/* JavaScript Template Library. */ +namespace tl { + +/* Compute min/max/clamp. */ +template struct Min { + static const size_t result = i < j ? i : j; +}; +template struct Max { + static const size_t result = i > j ? i : j; +}; +template struct Clamp { + static const size_t result = i < min ? min : (i > max ? max : i); +}; + +/* Compute x^y. */ +template struct Pow { + static const size_t result = x * Pow::result; +}; +template struct Pow { + static const size_t result = 1; +}; + +/* Compute floor(log2(i)). */ +template struct FloorLog2 { + static const size_t result = 1 + FloorLog2::result; +}; +template <> struct FloorLog2<0> { /* Error */ }; +template <> struct FloorLog2<1> { static const size_t result = 0; }; + +/* Compute ceiling(log2(i)). */ +template struct CeilingLog2 { + static const size_t result = FloorLog2<2 * i - 1>::result; +}; + +/* Round up to the nearest power of 2. */ +template struct RoundUpPow2 { + static const size_t result = 1u << CeilingLog2::result; +}; +template <> struct RoundUpPow2<0> { + static const size_t result = 1; +}; + +/* Compute the number of bits in the given unsigned type. */ +template struct BitSize { + static const size_t result = sizeof(T) * JS_BITS_PER_BYTE; +}; + +/* Allow Assertions by only including the 'result' typedef if 'true'. */ +template struct StaticAssert {}; +template <> struct StaticAssert { typedef int result; }; + +/* Boolean test for whether two types are the same. */ +template struct IsSameType { + static const bool result = false; +}; +template struct IsSameType { + static const bool result = true; +}; + +/* + * Produce an N-bit mask, where N <= BitSize::result. Handle the + * language-undefined edge case when N = BitSize::result. + */ +template struct NBitMask { + typedef typename StaticAssert::result>::result _; + static const size_t result = ~((size_t(1) << N) - 1); +}; +template <> struct NBitMask::result> { + static const size_t result = size_t(-1); +}; + +/* + * For the unsigned integral type size_t, compute a mask M for N such that + * for all X, !(X & M) implies X * N will not overflow (w.r.t size_t) + */ +template struct MulOverflowMask { + static const size_t result = + NBitMask::result - CeilingLog2::result>::result; +}; +template <> struct MulOverflowMask<0> { /* Error */ }; +template <> struct MulOverflowMask<1> { static const size_t result = 0; }; + +/* + * Generate a mask for T such that if (X & sUnsafeRangeSizeMask), an X-sized + * array of T's is big enough to cause a ptrdiff_t overflow when subtracting + * a pointer to the end of the array from the beginning. + */ +template struct UnsafeRangeSizeMask { + /* + * The '2' factor means the top bit is clear, sizeof(T) converts from + * units of elements to bytes. + */ + static const size_t result = MulOverflowMask<2 * sizeof(T)>::result; +}; + +/* + * Traits class for identifying POD types. Until C++0x, there is no automatic + * way to detect PODs, so for the moment it is done manually. + */ +template struct IsPodType { static const bool result = false; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; + +/* Return the size/end of an array without using macros. */ +template inline T *ArraySize(T (&)[N]) { return N; } +template inline T *ArrayEnd(T (&arr)[N]) { return arr + N; } + +} /* namespace tl */ + +/* Useful for implementing containers that assert non-reentrancy */ +class ReentrancyGuard +{ +#ifdef DEBUG + bool &entered; +#endif + public: + template + ReentrancyGuard(T &obj) +#ifdef DEBUG + : entered(obj.mEntered) +#endif + { +#ifdef DEBUG + JS_ASSERT(!entered); + entered = true; +#endif + } + ~ReentrancyGuard() + { +#ifdef DEBUG + entered = false; +#endif + } +}; + +/* + * Round x up to the nearest power of 2. This function assumes that the most + * significant bit of x is not set, which would lead to overflow. + */ +static JS_ALWAYS_INLINE size_t +RoundUpPow2(size_t x) +{ + typedef tl::StaticAssert::result>::result _; + size_t log2 = JS_CEILING_LOG2W(x); + JS_ASSERT(log2 < tl::BitSize::result); + size_t result = size_t(1) << log2; + return result; +} + +/* + * Safely subtract two pointers when it is known that end > begin. This avoids + * the common compiler bug that if (size_t(end) - size_t(begin)) has the MSB + * set, the unsigned subtraction followed by right shift will produce -1, or + * size_t(-1), instead of the real difference. + */ +template +static JS_ALWAYS_INLINE size_t +PointerRangeSize(T *begin, T *end) +{ + return (size_t(end) - size_t(begin)) / sizeof(T); +} + +/* + * Allocation policies. These model the concept: + * - public copy constructor, assignment, destructor + * - void *malloc(size_t) + * Responsible for OOM reporting on NULL return value. + * - void *realloc(size_t) + * Responsible for OOM reporting on NULL return value. + * - void free(void *) + * - reportAllocOverflow() + * Called on overflow before the container returns NULL. + */ + +/* Policy for using system memory functions and doing no error reporting. */ +class SystemAllocPolicy +{ + public: + void *malloc(size_t bytes) { return ::malloc(bytes); } + void *realloc(void *p, size_t bytes) { return ::realloc(p, bytes); } + void free(void *p) { ::free(p); } + void reportAllocOverflow() const {} +}; + +} /* namespace js */ + +#endif /* jstl_h_ */ diff --git a/ape-server/deps/js/src/jstracer.cpp b/ape-server/deps/js/src/jstracer.cpp new file mode 100755 index 0000000..83a17c2 --- /dev/null +++ b/ape-server/deps/js/src/jstracer.cpp @@ -0,0 +1,14816 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=99: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released + * May 28, 2008. + * + * The Initial Developer of the Original Code is + * Brendan Eich + * + * Contributor(s): + * Andreas Gal + * Mike Shaver + * David Anderson + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "jsstdint.h" +#include "jsbit.h" // low-level (NSPR-based) headers next +#include "jsprf.h" +#include // standard headers next + +#if defined(_MSC_VER) || defined(__MINGW32__) +#include +#ifdef _MSC_VER +#define alloca _alloca +#endif +#endif +#ifdef SOLARIS +#include +#endif +#include + +#include "nanojit/nanojit.h" +#include "jsapi.h" // higher-level library and API headers +#include "jsarray.h" +#include "jsbool.h" +#include "jscntxt.h" +#include "jsdate.h" +#include "jsdbgapi.h" +#include "jsemit.h" +#include "jsfun.h" +#include "jsinterp.h" +#include "jsiter.h" +#include "jsmath.h" +#include "jsobj.h" +#include "jsopcode.h" +#include "jsregexp.h" +#include "jsscope.h" +#include "jsscript.h" +#include "jsstaticcheck.h" +#include "jstracer.h" +#include "jsxml.h" + +#include "jsatominlines.h" +#include "jsscopeinlines.h" +#include "jsscriptinlines.h" + +#include "jsautooplen.h" // generated headers last +#include "imacros.c.out" + +using namespace nanojit; + +#if JS_HAS_XML_SUPPORT +#define RETURN_VALUE_IF_XML(val, ret) \ + JS_BEGIN_MACRO \ + if (!JSVAL_IS_PRIMITIVE(val) && \ + OBJECT_IS_XML(BOGUS_CX, JSVAL_TO_OBJECT(val))) { \ + RETURN_VALUE("xml detected", ret); \ + } \ + JS_END_MACRO +#else +#define RETURN_IF_XML(val, ret) ((void) 0) +#endif + +#define RETURN_IF_XML_A(val) RETURN_VALUE_IF_XML(val, ARECORD_STOP) +#define RETURN_IF_XML(val) RETURN_VALUE_IF_XML(val, RECORD_STOP) + +/* + * Never use JSVAL_IS_BOOLEAN because it restricts the value (true, false) and + * the type. What you want to use is JSVAL_IS_SPECIAL(x) and then handle the + * undefined case properly (bug 457363). + */ +#undef JSVAL_IS_BOOLEAN +#define JSVAL_IS_BOOLEAN(x) JS_STATIC_ASSERT(0) + +JS_STATIC_ASSERT(sizeof(JSTraceType) == 1); + +/* Map to translate a type tag into a printable representation. */ +static const char typeChar[] = "OIDXSNBF"; +static const char tagChar[] = "OIDISIBI"; + +/* Blacklist parameters. */ + +/* + * Number of iterations of a loop where we start tracing. That is, we don't + * start tracing until the beginning of the HOTLOOP-th iteration. + */ +#define HOTLOOP 2 + +/* Attempt recording this many times before blacklisting permanently. */ +#define BL_ATTEMPTS 2 + +/* Skip this many hits before attempting recording again, after an aborted attempt. */ +#define BL_BACKOFF 32 + +/* Number of times we wait to exit on a side exit before we try to extend the tree. */ +#define HOTEXIT 1 + +/* Number of times we try to extend the tree along a side exit. */ +#define MAXEXIT 3 + +/* Maximum number of peer trees allowed. */ +#define MAXPEERS 9 + +/* Max number of hits to a RECURSIVE_UNLINKED exit before we trash the tree. */ +#define MAX_RECURSIVE_UNLINK_HITS 64 + +/* Max call depths for inlining. */ +#define MAX_CALLDEPTH 10 + +/* Max number of slots in a table-switch. */ +#define MAX_TABLE_SWITCH 256 + +/* Max memory needed to rebuild the interpreter stack when falling off trace. */ +#define MAX_INTERP_STACK_BYTES \ + (MAX_NATIVE_STACK_SLOTS * sizeof(jsval) + \ + MAX_CALL_STACK_ENTRIES * sizeof(JSInlineFrame) + \ + sizeof(JSInlineFrame)) /* possibly slow native frame at top of stack */ + +/* Max number of branches per tree. */ +#define MAX_BRANCHES 32 + +#define CHECK_STATUS(expr) \ + JS_BEGIN_MACRO \ + RecordingStatus _status = (expr); \ + if (_status != RECORD_CONTINUE) \ + return _status; \ + JS_END_MACRO + +#define CHECK_STATUS_A(expr) \ + JS_BEGIN_MACRO \ + AbortableRecordingStatus _status = InjectStatus((expr)); \ + if (_status != ARECORD_CONTINUE) \ + return _status; \ + JS_END_MACRO + +#ifdef JS_JIT_SPEW +#define RETURN_VALUE(msg, value) \ + JS_BEGIN_MACRO \ + debug_only_printf(LC_TMAbort, "trace stopped: %d: %s\n", __LINE__, (msg)); \ + return (value); \ + JS_END_MACRO +#else +#define RETURN_VALUE(msg, value) return (value) +#endif + +#define RETURN_STOP(msg) RETURN_VALUE(msg, RECORD_STOP) +#define RETURN_STOP_A(msg) RETURN_VALUE(msg, ARECORD_STOP) +#define RETURN_ERROR(msg) RETURN_VALUE(msg, RECORD_ERROR) +#define RETURN_ERROR_A(msg) RETURN_VALUE(msg, ARECORD_ERROR) + +#ifdef JS_JIT_SPEW +struct __jitstats { +#define JITSTAT(x) uint64 x; +#include "jitstats.tbl" +#undef JITSTAT +} jitstats = { 0LL, }; + +JS_STATIC_ASSERT(sizeof(jitstats) % sizeof(uint64) == 0); + +enum jitstat_ids { +#define JITSTAT(x) STAT ## x ## ID, +#include "jitstats.tbl" +#undef JITSTAT + STAT_IDS_TOTAL +}; + +static JSPropertySpec jitstats_props[] = { +#define JITSTAT(x) { #x, STAT ## x ## ID, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT }, +#include "jitstats.tbl" +#undef JITSTAT + { 0 } +}; + +static JSBool +jitstats_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + int index = -1; + + if (JSVAL_IS_STRING(id)) { + JSString* str = JSVAL_TO_STRING(id); + if (strcmp(JS_GetStringBytes(str), "HOTLOOP") == 0) { + *vp = INT_TO_JSVAL(HOTLOOP); + return JS_TRUE; + } + } + + if (JSVAL_IS_INT(id)) + index = JSVAL_TO_INT(id); + + uint64 result = 0; + switch (index) { +#define JITSTAT(x) case STAT ## x ## ID: result = jitstats.x; break; +#include "jitstats.tbl" +#undef JITSTAT + default: + *vp = JSVAL_VOID; + return JS_TRUE; + } + + if (result < JSVAL_INT_MAX) { + *vp = INT_TO_JSVAL(jsint(result)); + return JS_TRUE; + } + char retstr[64]; + JS_snprintf(retstr, sizeof retstr, "%llu", result); + *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, retstr)); + return JS_TRUE; +} + +JSClass jitstats_class = { + "jitstats", + 0, + JS_PropertyStub, JS_PropertyStub, + jitstats_getProperty, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, + JS_ConvertStub, NULL, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +void +js_InitJITStatsClass(JSContext *cx, JSObject *glob) +{ + JS_InitClass(cx, glob, NULL, &jitstats_class, NULL, 0, jitstats_props, NULL, NULL, NULL); +} + +#define AUDIT(x) (jitstats.x++) +#else +#define AUDIT(x) ((void)0) +#endif /* JS_JIT_SPEW */ + +/* + * INS_CONSTPTR can be used to embed arbitrary pointers into the native code. It should not + * be used directly to embed GC thing pointers. Instead, use the INS_CONSTOBJ/FUN/STR/SPROP + * variants which ensure that the embedded pointer will be kept alive across GCs. + */ + +#define INS_CONST(c) addName(lir->insImm(c), #c) +#define INS_CONSTPTR(p) addName(lir->insImmPtr(p), #p) +#define INS_CONSTWORD(v) addName(lir->insImmPtr((void *) (v)), #v) +#define INS_CONSTVAL(v) addName(insImmVal(v), #v) +#define INS_CONSTOBJ(obj) addName(insImmObj(obj), #obj) +#define INS_CONSTFUN(fun) addName(insImmFun(fun), #fun) +#define INS_CONSTSTR(str) addName(insImmStr(str), #str) +#define INS_CONSTSPROP(sprop) addName(insImmSprop(sprop), #sprop) +#define INS_ATOM(atom) INS_CONSTSTR(ATOM_TO_STRING(atom)) +#define INS_NULL() INS_CONSTPTR(NULL) +#define INS_VOID() INS_CONST(JSVAL_TO_SPECIAL(JSVAL_VOID)) + +static avmplus::AvmCore s_core = avmplus::AvmCore(); +static avmplus::AvmCore* core = &s_core; + +/* Allocator SPI implementation. */ + +void* +nanojit::Allocator::allocChunk(size_t nbytes) +{ + VMAllocator *vma = (VMAllocator*)this; + JS_ASSERT(!vma->outOfMemory()); + void *p = calloc(1, nbytes); + if (!p) { + JS_ASSERT(nbytes < sizeof(vma->mReserve)); + vma->mOutOfMemory = true; + p = (void*) &vma->mReserve[0]; + } + vma->mSize += nbytes; + return p; +} + +void +nanojit::Allocator::freeChunk(void *p) { + VMAllocator *vma = (VMAllocator*)this; + if (p != &vma->mReserve[0]) + free(p); +} + +void +nanojit::Allocator::postReset() { + VMAllocator *vma = (VMAllocator*)this; + vma->mOutOfMemory = false; + vma->mSize = 0; +} + +static void OutOfMemoryAbort() +{ + JS_NOT_REACHED("out of memory"); + abort(); +} + +#ifdef JS_JIT_SPEW +static void +DumpPeerStability(JSTraceMonitor* tm, const void* ip, JSObject* globalObj, uint32 globalShape, uint32 argc); +#endif + +/* + * We really need a better way to configure the JIT. Shaver, where is + * my fancy JIT object? + * + * NB: this is raced on, if jstracer.cpp should ever be running MT. + * I think it's harmless tho. + */ +static bool did_we_check_processor_features = false; + +/* ------ Debug logging control ------ */ + +/* + * All the logging control stuff lives in here. It is shared between + * all threads, but I think that's OK. + */ +LogControl js_LogController; + +#ifdef JS_JIT_SPEW + +/* + * NB: this is raced on too, if jstracer.cpp should ever be running MT. + * Also harmless. + */ +static bool did_we_set_up_debug_logging = false; + +static void +InitJITLogController() +{ + char *tm, *tmf; + uint32_t bits; + + js_LogController.lcbits = 0; + + tm = getenv("TRACEMONKEY"); + if (tm) { + fflush(NULL); + printf( + "The environment variable $TRACEMONKEY has been replaced by $TMFLAGS.\n" + "Try 'TMFLAGS=help js -j' for a list of options.\n" + ); + exit(0); + } + + tmf = getenv("TMFLAGS"); + if (!tmf) return; + + /* Using strstr() is really a cheap hack as far as flag decoding goes. */ + if (strstr(tmf, "help")) { + fflush(NULL); + printf( + "usage: TMFLAGS=option,option,option,... where options can be:\n" + "\n" + " help show this message\n" + " ------ options for jstracer & jsregexp ------\n" + " minimal ultra-minimalist output; try this first\n" + " full everything except 'treevis' and 'nocodeaddrs'\n" + " tracer tracer lifetime (FIXME:better description)\n" + " recorder trace recording stuff (FIXME:better description)\n" + " abort show trace recording aborts\n" + " stats show trace recording stats\n" + " regexp show compilation & entry for regexps\n" + " treevis spew that tracevis/tree.py can parse\n" + " ------ options for Nanojit ------\n" + " fragprofile count entries and exits for each fragment\n" + " liveness show LIR liveness at start of rdr pipeline\n" + " readlir show LIR as it enters the reader pipeline\n" + " aftersf show LIR after StackFilter\n" + " assembly show final aggregated assembly code\n" + " regalloc show regalloc state in 'assembly' output\n" + " activation show activation state in 'assembly' output\n" + " nocodeaddrs omit code addresses in 'assembly' output\n" + "\n" + ); + exit(0); + /*NOTREACHED*/ + } + + bits = 0; + + /* flags for jstracer.cpp */ + if (strstr(tmf, "minimal") || strstr(tmf, "full")) bits |= LC_TMMinimal; + if (strstr(tmf, "tracer") || strstr(tmf, "full")) bits |= LC_TMTracer; + if (strstr(tmf, "recorder") || strstr(tmf, "full")) bits |= LC_TMRecorder; + if (strstr(tmf, "abort") || strstr(tmf, "full")) bits |= LC_TMAbort; + if (strstr(tmf, "stats") || strstr(tmf, "full")) bits |= LC_TMStats; + if (strstr(tmf, "regexp") || strstr(tmf, "full")) bits |= LC_TMRegexp; + if (strstr(tmf, "treevis")) bits |= LC_TMTreeVis; + + /* flags for nanojit */ + if (strstr(tmf, "fragprofile")) bits |= LC_FragProfile; + if (strstr(tmf, "liveness") || strstr(tmf, "full")) bits |= LC_Liveness; + if (strstr(tmf, "activation") || strstr(tmf, "full")) bits |= LC_Activation; + if (strstr(tmf, "readlir") || strstr(tmf, "full")) bits |= LC_ReadLIR; + if (strstr(tmf, "aftersf") || strstr(tmf, "full")) bits |= LC_AfterSF; + if (strstr(tmf, "regalloc") || strstr(tmf, "full")) bits |= LC_RegAlloc; + if (strstr(tmf, "assembly") || strstr(tmf, "full")) bits |= LC_Assembly; + if (strstr(tmf, "nocodeaddrs")) bits |= LC_NoCodeAddrs; + + js_LogController.lcbits = bits; + return; + +} +#endif + +/* ------------------ Frag-level profiling support ------------------ */ + +#ifdef JS_JIT_SPEW + +/* + * All the allocations done by this profile data-collection and + * display machinery, are done in JSTraceMonitor::profAlloc. That is + * emptied out at the end of js_FinishJIT. It has a lifetime from + * js_InitJIT to js_FinishJIT, which exactly matches the span + * js_FragProfiling_init to js_FragProfiling_showResults. + */ +template +static +Seq* reverseInPlace(Seq* seq) +{ + Seq* prev = NULL; + Seq* curr = seq; + while (curr) { + Seq* next = curr->tail; + curr->tail = prev; + prev = curr; + curr = next; + } + return prev; +} + +// The number of top blocks to show in the profile +#define N_TOP_BLOCKS 50 + +// Contains profile info for a single guard +struct GuardPI { + uint32_t guardID; // identifying number + uint32_t count; // count. +}; + +struct FragPI { + uint32_t count; // entry count for this Fragment + uint32_t nStaticExits; // statically: the number of exits + size_t nCodeBytes; // statically: the number of insn bytes in the main fragment + size_t nExitBytes; // statically: the number of insn bytes in the exit paths + Seq* guards; // guards, each with its own count + uint32_t largestGuardID; // that exists in .guards +}; + +/* A mapping of Fragment.profFragID to FragPI */ +typedef HashMap FragStatsMap; + +void +js_FragProfiling_FragFinalizer(Fragment* f, JSTraceMonitor* tm) +{ + // Recover profiling data from 'f', which is logically at the end + // of its useful lifetime. + if (!(js_LogController.lcbits & LC_FragProfile)) + return; + + NanoAssert(f); + // Valid profFragIDs start at 1 + NanoAssert(f->profFragID >= 1); + // Should be called exactly once per Fragment. This will assert if + // you issue the same FragID to more than one Fragment. + NanoAssert(!tm->profTab->containsKey(f->profFragID)); + + FragPI pi = { f->profCount, + f->nStaticExits, + f->nCodeBytes, + f->nExitBytes, + NULL, 0 }; + + // Begin sanity check on the guards + SeqBuilder guardsBuilder(*tm->profAlloc); + GuardRecord* gr; + uint32_t nGs = 0; + uint32_t sumOfDynExits = 0; + for (gr = f->guardsForFrag; gr; gr = gr->nextInFrag) { + nGs++; + // Also copy the data into our auxiliary structure. + // f->guardsForFrag is in reverse order, and so this + // copy preserves that ordering (->add adds at end). + // Valid profGuardIDs start at 1. + NanoAssert(gr->profGuardID > 0); + sumOfDynExits += gr->profCount; + GuardPI gpi = { gr->profGuardID, gr->profCount }; + guardsBuilder.add(gpi); + if (gr->profGuardID > pi.largestGuardID) + pi.largestGuardID = gr->profGuardID; + } + pi.guards = guardsBuilder.get(); + // And put the guard list in forwards order + pi.guards = reverseInPlace(pi.guards); + + // Why is this so? Because nGs is the number of guards + // at the time the LIR was generated, whereas f->nStaticExits + // is the number of them observed by the time it makes it + // through to the assembler. It can be the case that LIR + // optimisation removes redundant guards; hence we expect + // nGs to always be the same or higher. + NanoAssert(nGs >= f->nStaticExits); + + // Also we can assert that the sum of the exit counts + // can't exceed the entry count. It'd be nice to assert that + // they are exactly equal, but we can't because we don't know + // how many times we got to the end of the trace. + NanoAssert(f->profCount >= sumOfDynExits); + + // End sanity check on guards + + tm->profTab->put(f->profFragID, pi); +} + +static void +js_FragProfiling_showResults(JSTraceMonitor* tm) +{ + uint32_t topFragID[N_TOP_BLOCKS]; + FragPI topPI[N_TOP_BLOCKS]; + uint64_t totCount = 0, cumulCount; + uint32_t totSE = 0; + size_t totCodeB = 0, totExitB = 0; + memset(topFragID, 0, sizeof(topFragID)); + memset(topPI, 0, sizeof(topPI)); + FragStatsMap::Iter iter(*tm->profTab); + while (iter.next()) { + uint32_t fragID = iter.key(); + FragPI pi = iter.value(); + uint32_t count = pi.count; + totCount += (uint64_t)count; + /* Find the rank for this entry, in tops */ + int r = N_TOP_BLOCKS-1; + while (true) { + if (r == -1) + break; + if (topFragID[r] == 0) { + r--; + continue; + } + if (count > topPI[r].count) { + r--; + continue; + } + break; + } + r++; + AvmAssert(r >= 0 && r <= N_TOP_BLOCKS); + /* This entry should be placed at topPI[r], and entries + at higher numbered slots moved up one. */ + if (r < N_TOP_BLOCKS) { + for (int s = N_TOP_BLOCKS-1; s > r; s--) { + topFragID[s] = topFragID[s-1]; + topPI[s] = topPI[s-1]; + } + topFragID[r] = fragID; + topPI[r] = pi; + } + } + + js_LogController.printf( + "\n----------------- Per-fragment execution counts ------------------\n"); + js_LogController.printf( + "\nTotal count = %llu\n\n", (unsigned long long int)totCount); + + js_LogController.printf( + " Entry counts Entry counts ----- Static -----\n"); + js_LogController.printf( + " ------Self------ ----Cumulative--- Exits Cbytes Xbytes FragID\n"); + js_LogController.printf("\n"); + + if (totCount == 0) + totCount = 1; /* avoid division by zero */ + cumulCount = 0; + int r; + for (r = 0; r < N_TOP_BLOCKS; r++) { + if (topFragID[r] == 0) + break; + cumulCount += (uint64_t)topPI[r].count; + js_LogController.printf("%3d: %5.2f%% %9u %6.2f%% %9llu" + " %3d %5u %5u %06u\n", + r, + (double)topPI[r].count * 100.0 / (double)totCount, + topPI[r].count, + (double)cumulCount * 100.0 / (double)totCount, + (unsigned long long int)cumulCount, + topPI[r].nStaticExits, + (unsigned int)topPI[r].nCodeBytes, + (unsigned int)topPI[r].nExitBytes, + topFragID[r]); + totSE += (uint32_t)topPI[r].nStaticExits; + totCodeB += topPI[r].nCodeBytes; + totExitB += topPI[r].nExitBytes; + } + js_LogController.printf("\nTotal displayed code bytes = %u, " + "exit bytes = %u\n" + "Total displayed static exits = %d\n\n", + (unsigned int)totCodeB, (unsigned int)totExitB, totSE); + + js_LogController.printf("Analysis by exit counts\n\n"); + + for (r = 0; r < N_TOP_BLOCKS; r++) { + if (topFragID[r] == 0) + break; + js_LogController.printf("FragID=%06u, total count %u:\n", topFragID[r], + topPI[r].count); + uint32_t madeItToEnd = topPI[r].count; + uint32_t totThisFrag = topPI[r].count; + if (totThisFrag == 0) + totThisFrag = 1; + GuardPI gpi; + // visit the guards, in forward order + for (Seq* guards = topPI[r].guards; guards; guards = guards->tail) { + gpi = (*guards).head; + if (gpi.count == 0) + continue; + madeItToEnd -= gpi.count; + js_LogController.printf(" GuardID=%03u %7u (%5.2f%%)\n", + gpi.guardID, gpi.count, + 100.0 * (double)gpi.count / (double)totThisFrag); + } + js_LogController.printf(" Looped (%03u) %7u (%5.2f%%)\n", + topPI[r].largestGuardID+1, + madeItToEnd, + 100.0 * (double)madeItToEnd / (double)totThisFrag); + NanoAssert(madeItToEnd <= topPI[r].count); // else unsigned underflow + js_LogController.printf("\n"); + } + + tm->profTab = NULL; +} + +#endif + +/* ----------------------------------------------------------------- */ + +#ifdef DEBUG +static const char* +getExitName(ExitType type) +{ + static const char* exitNames[] = + { + #define MAKE_EXIT_STRING(x) #x, + JS_TM_EXITCODES(MAKE_EXIT_STRING) + #undef MAKE_EXIT_STRING + NULL + }; + + JS_ASSERT(type < TOTAL_EXIT_TYPES); + + return exitNames[type]; +} + +static JSBool FASTCALL +PrintOnTrace(char* format, uint32 argc, double *argv) +{ + union { + struct { + uint32 lo; + uint32 hi; + } i; + double d; + char *cstr; + JSObject *o; + JSString *s; + } u; + +#define GET_ARG() JS_BEGIN_MACRO \ + if (argi >= argc) { \ + fprintf(out, "[too few args for format]"); \ + break; \ +} \ + u.d = argv[argi++]; \ + JS_END_MACRO + + FILE *out = stderr; + + uint32 argi = 0; + for (char *p = format; *p; ++p) { + if (*p != '%') { + putc(*p, out); + continue; + } + char ch = *++p; + if (!ch) { + fprintf(out, "[trailing %%]"); + continue; + } + + switch (ch) { + case 'a': + GET_ARG(); + fprintf(out, "[%u:%u 0x%x:0x%x %f]", u.i.lo, u.i.hi, u.i.lo, u.i.hi, u.d); + break; + case 'd': + GET_ARG(); + fprintf(out, "%d", u.i.lo); + break; + case 'u': + GET_ARG(); + fprintf(out, "%u", u.i.lo); + break; + case 'x': + GET_ARG(); + fprintf(out, "%x", u.i.lo); + break; + case 'f': + GET_ARG(); + fprintf(out, "%f", u.d); + break; + case 'o': + GET_ARG(); + js_DumpObject(u.o); + break; + case 's': + GET_ARG(); + { + size_t length = u.s->length(); + // protect against massive spew if u.s is a bad pointer. + if (length > 1 << 16) + length = 1 << 16; + jschar *chars = u.s->chars(); + for (unsigned i = 0; i < length; ++i) { + jschar co = chars[i]; + if (co < 128) + putc(co, out); + else if (co < 256) + fprintf(out, "\\u%02x", co); + else + fprintf(out, "\\u%04x", co); + } + } + break; + case 'S': + GET_ARG(); + fprintf(out, "%s", u.cstr); + break; + default: + fprintf(out, "[invalid %%%c]", *p); + } + } + +#undef GET_ARG + + return JS_TRUE; +} + +JS_DEFINE_CALLINFO_3(extern, BOOL, PrintOnTrace, CHARPTR, UINT32, DOUBLEPTR, 0, 0) + +// This version is not intended to be called directly: usually it is easier to +// use one of the other overloads. +void +TraceRecorder::tprint(const char *format, int count, nanojit::LIns *insa[]) +{ + size_t size = strlen(format) + 1; + char* data = (char*) traceMonitor->traceAlloc->alloc(size); + memcpy(data, format, size); + + double *args = (double*) traceMonitor->traceAlloc->alloc(count * sizeof(double)); + for (int i = 0; i < count; ++i) { + JS_ASSERT(insa[i]); + lir->insStorei(insa[i], INS_CONSTPTR(args), sizeof(double) * i); + } + + LIns* args_ins[] = { INS_CONSTPTR(args), INS_CONST(count), INS_CONSTPTR(data) }; + LIns* call_ins = lir->insCall(&PrintOnTrace_ci, args_ins); + guard(false, lir->ins_eq0(call_ins), MISMATCH_EXIT); +} + +// Generate a 'printf'-type call from trace for debugging. +void +TraceRecorder::tprint(const char *format) +{ + LIns* insa[] = { NULL }; + tprint(format, 0, insa); +} + +void +TraceRecorder::tprint(const char *format, LIns *ins) +{ + LIns* insa[] = { ins }; + tprint(format, 1, insa); +} + +void +TraceRecorder::tprint(const char *format, LIns *ins1, LIns *ins2) +{ + LIns* insa[] = { ins1, ins2 }; + tprint(format, 2, insa); +} + +void +TraceRecorder::tprint(const char *format, LIns *ins1, LIns *ins2, LIns *ins3) +{ + LIns* insa[] = { ins1, ins2, ins3 }; + tprint(format, 3, insa); +} + +void +TraceRecorder::tprint(const char *format, LIns *ins1, LIns *ins2, LIns *ins3, LIns *ins4) +{ + LIns* insa[] = { ins1, ins2, ins3, ins4 }; + tprint(format, 4, insa); +} + +void +TraceRecorder::tprint(const char *format, LIns *ins1, LIns *ins2, LIns *ins3, LIns *ins4, + LIns *ins5) +{ + LIns* insa[] = { ins1, ins2, ins3, ins4, ins5 }; + tprint(format, 5, insa); +} + +void +TraceRecorder::tprint(const char *format, LIns *ins1, LIns *ins2, LIns *ins3, LIns *ins4, + LIns *ins5, LIns *ins6) +{ + LIns* insa[] = { ins1, ins2, ins3, ins4, ins5, ins6 }; + tprint(format, 6, insa); +} +#endif + +/* + * The entire VM shares one oracle. Collisions and concurrent updates are + * tolerated and worst case cause performance regressions. + */ +static Oracle oracle; + +Tracker::Tracker() +{ + pagelist = NULL; +} + +Tracker::~Tracker() +{ + clear(); +} + +inline jsuword +Tracker::getTrackerPageBase(const void* v) const +{ + return jsuword(v) & ~TRACKER_PAGE_MASK; +} + +inline jsuword +Tracker::getTrackerPageOffset(const void* v) const +{ + return (jsuword(v) & TRACKER_PAGE_MASK) >> 2; +} + +struct Tracker::TrackerPage* +Tracker::findTrackerPage(const void* v) const +{ + jsuword base = getTrackerPageBase(v); + struct Tracker::TrackerPage* p = pagelist; + while (p) { + if (p->base == base) + return p; + p = p->next; + } + return NULL; +} + +struct Tracker::TrackerPage* +Tracker::addTrackerPage(const void* v) +{ + jsuword base = getTrackerPageBase(v); + struct TrackerPage* p = (struct TrackerPage*) calloc(1, sizeof(*p)); + p->base = base; + p->next = pagelist; + pagelist = p; + return p; +} + +void +Tracker::clear() +{ + while (pagelist) { + TrackerPage* p = pagelist; + pagelist = pagelist->next; + free(p); + } +} + +bool +Tracker::has(const void *v) const +{ + return get(v) != NULL; +} + +LIns* +Tracker::get(const void* v) const +{ + struct Tracker::TrackerPage* p = findTrackerPage(v); + if (!p) + return NULL; + return p->map[getTrackerPageOffset(v)]; +} + +void +Tracker::set(const void* v, LIns* i) +{ + struct Tracker::TrackerPage* p = findTrackerPage(v); + if (!p) + p = addTrackerPage(v); + p->map[getTrackerPageOffset(v)] = i; +} + +static inline jsuint +argSlots(JSStackFrame* fp) +{ + return JS_MAX(fp->argc, fp->fun->nargs); +} + +static inline bool +isNumber(jsval v) +{ + return JSVAL_IS_INT(v) || JSVAL_IS_DOUBLE(v); +} + +static inline jsdouble +asNumber(jsval v) +{ + JS_ASSERT(isNumber(v)); + if (JSVAL_IS_DOUBLE(v)) + return *JSVAL_TO_DOUBLE(v); + return (jsdouble)JSVAL_TO_INT(v); +} + +static inline bool +isInt32(jsval v) +{ + if (!isNumber(v)) + return false; + jsdouble d = asNumber(v); + jsint i; + return !!JSDOUBLE_IS_INT(d, i); +} + +static inline jsint +asInt32(jsval v) +{ + JS_ASSERT(isNumber(v)); + if (JSVAL_IS_INT(v)) + return JSVAL_TO_INT(v); +#ifdef DEBUG + jsint i; + JS_ASSERT(JSDOUBLE_IS_INT(*JSVAL_TO_DOUBLE(v), i)); +#endif + return jsint(*JSVAL_TO_DOUBLE(v)); +} + +/* Return TT_DOUBLE for all numbers (int and double) and the tag otherwise. */ +static inline JSTraceType +GetPromotedType(jsval v) +{ + if (JSVAL_IS_INT(v)) + return TT_DOUBLE; + if (JSVAL_IS_OBJECT(v)) { + if (JSVAL_IS_NULL(v)) + return TT_NULL; + if (HAS_FUNCTION_CLASS(JSVAL_TO_OBJECT(v))) + return TT_FUNCTION; + return TT_OBJECT; + } + uint8_t tag = JSVAL_TAG(v); + JS_ASSERT(tag == JSVAL_DOUBLE || tag == JSVAL_STRING || tag == JSVAL_SPECIAL); + JS_STATIC_ASSERT(static_cast(TT_DOUBLE) == JSVAL_DOUBLE); + JS_STATIC_ASSERT(static_cast(TT_STRING) == JSVAL_STRING); + JS_STATIC_ASSERT(static_cast(TT_PSEUDOBOOLEAN) == JSVAL_SPECIAL); + return JSTraceType(tag); +} + +/* Return TT_INT32 for all whole numbers that fit into signed 32-bit and the tag otherwise. */ +static inline JSTraceType +getCoercedType(jsval v) +{ + if (isInt32(v)) + return TT_INT32; + if (JSVAL_IS_OBJECT(v)) { + if (JSVAL_IS_NULL(v)) + return TT_NULL; + if (HAS_FUNCTION_CLASS(JSVAL_TO_OBJECT(v))) + return TT_FUNCTION; + return TT_OBJECT; + } + uint8_t tag = JSVAL_TAG(v); + JS_ASSERT(tag == JSVAL_DOUBLE || tag == JSVAL_STRING || tag == JSVAL_SPECIAL); + JS_STATIC_ASSERT(static_cast(TT_DOUBLE) == JSVAL_DOUBLE); + JS_STATIC_ASSERT(static_cast(TT_STRING) == JSVAL_STRING); + JS_STATIC_ASSERT(static_cast(TT_PSEUDOBOOLEAN) == JSVAL_SPECIAL); + return JSTraceType(tag); +} + +/* Constant seed and accumulate step borrowed from the DJB hash. */ + +const uintptr_t ORACLE_MASK = ORACLE_SIZE - 1; +JS_STATIC_ASSERT((ORACLE_MASK & ORACLE_SIZE) == 0); + +const uintptr_t FRAGMENT_TABLE_MASK = FRAGMENT_TABLE_SIZE - 1; +JS_STATIC_ASSERT((FRAGMENT_TABLE_MASK & FRAGMENT_TABLE_SIZE) == 0); + +const uintptr_t HASH_SEED = 5381; + +static inline void +HashAccum(uintptr_t& h, uintptr_t i, uintptr_t mask) +{ + h = ((h << 5) + h + (mask & i)) & mask; +} + +static JS_REQUIRES_STACK inline int +StackSlotHash(JSContext* cx, unsigned slot, const void* pc) +{ + uintptr_t h = HASH_SEED; + HashAccum(h, uintptr_t(cx->fp->script), ORACLE_MASK); + HashAccum(h, uintptr_t(pc), ORACLE_MASK); + HashAccum(h, uintptr_t(slot), ORACLE_MASK); + return int(h); +} + +static JS_REQUIRES_STACK inline int +GlobalSlotHash(JSContext* cx, unsigned slot) +{ + uintptr_t h = HASH_SEED; + JSStackFrame* fp = cx->fp; + + while (fp->down) + fp = fp->down; + + HashAccum(h, uintptr_t(fp->script), ORACLE_MASK); + HashAccum(h, uintptr_t(OBJ_SHAPE(JS_GetGlobalForObject(cx, fp->scopeChain))), ORACLE_MASK); + HashAccum(h, uintptr_t(slot), ORACLE_MASK); + return int(h); +} + +static inline int +PCHash(jsbytecode* pc) +{ + return int(uintptr_t(pc) & ORACLE_MASK); +} + +Oracle::Oracle() +{ + /* Grow the oracle bitsets to their (fixed) size here, once. */ + _stackDontDemote.set(ORACLE_SIZE-1); + _globalDontDemote.set(ORACLE_SIZE-1); + clear(); +} + +/* Tell the oracle that a certain global variable should not be demoted. */ +JS_REQUIRES_STACK void +Oracle::markGlobalSlotUndemotable(JSContext* cx, unsigned slot) +{ + #ifdef DEBUG_dvander + printf("MGSU: %d [%08x]: %d\n", slot, GlobalSlotHash(cx, slot), + _globalDontDemote.get(GlobalSlotHash(cx, slot))); + #endif + _globalDontDemote.set(GlobalSlotHash(cx, slot)); +} + +/* Consult with the oracle whether we shouldn't demote a certain global variable. */ +JS_REQUIRES_STACK bool +Oracle::isGlobalSlotUndemotable(JSContext* cx, unsigned slot) const +{ + #ifdef DEBUG_dvander + printf("IGSU: %d [%08x]: %d\n", slot, GlobalSlotHash(cx, slot), + _globalDontDemote.get(GlobalSlotHash(cx, slot))); + #endif + return _globalDontDemote.get(GlobalSlotHash(cx, slot)); +} + +/* Tell the oracle that a certain slot at a certain stack slot should not be demoted. */ +JS_REQUIRES_STACK void +Oracle::markStackSlotUndemotable(JSContext* cx, unsigned slot, const void* pc) +{ + #ifdef DEBUG_dvander + printf("MSSU: %p:%d [%08x]: %d\n", pc, slot, StackSlotHash(cx, slot, pc), + _stackDontDemote.get(StackSlotHash(cx, slot, pc))); + #endif + _stackDontDemote.set(StackSlotHash(cx, slot, pc)); +} + +JS_REQUIRES_STACK void +Oracle::markStackSlotUndemotable(JSContext* cx, unsigned slot) +{ + markStackSlotUndemotable(cx, slot, cx->fp->regs->pc); +} + +/* Consult with the oracle whether we shouldn't demote a certain slot. */ +JS_REQUIRES_STACK bool +Oracle::isStackSlotUndemotable(JSContext* cx, unsigned slot, const void* pc) const +{ + #ifdef DEBUG_dvander + printf("ISSU: %p:%d [%08x]: %d\n", pc, slot, StackSlotHash(cx, slot, pc), + _stackDontDemote.get(StackSlotHash(cx, slot, pc))); + #endif + return _stackDontDemote.get(StackSlotHash(cx, slot, pc)); +} + +JS_REQUIRES_STACK bool +Oracle::isStackSlotUndemotable(JSContext* cx, unsigned slot) const +{ + return isStackSlotUndemotable(cx, slot, cx->fp->regs->pc); +} + +/* Tell the oracle that a certain slot at a certain bytecode location should not be demoted. */ +void +Oracle::markInstructionUndemotable(jsbytecode* pc) +{ + _pcDontDemote.set(PCHash(pc)); +} + +/* Consult with the oracle whether we shouldn't demote a certain bytecode location. */ +bool +Oracle::isInstructionUndemotable(jsbytecode* pc) const +{ + return _pcDontDemote.get(PCHash(pc)); +} + +void +Oracle::clearDemotability() +{ + _stackDontDemote.reset(); + _globalDontDemote.reset(); + _pcDontDemote.reset(); +} + +JS_REQUIRES_STACK static JS_INLINE void +MarkSlotUndemotable(JSContext* cx, TreeInfo* ti, unsigned slot) +{ + if (slot < ti->nStackTypes) { + oracle.markStackSlotUndemotable(cx, slot); + return; + } + + uint16* gslots = ti->globalSlots->data(); + oracle.markGlobalSlotUndemotable(cx, gslots[slot - ti->nStackTypes]); +} + +JS_REQUIRES_STACK static JS_INLINE void +MarkSlotUndemotable(JSContext* cx, TreeInfo* ti, unsigned slot, const void* pc) +{ + if (slot < ti->nStackTypes) { + oracle.markStackSlotUndemotable(cx, slot, pc); + return; + } + + uint16* gslots = ti->globalSlots->data(); + oracle.markGlobalSlotUndemotable(cx, gslots[slot - ti->nStackTypes]); +} + +static JS_REQUIRES_STACK inline bool +IsSlotUndemotable(JSContext* cx, TreeInfo* ti, unsigned slot, const void* ip) +{ + if (slot < ti->nStackTypes) + return oracle.isStackSlotUndemotable(cx, slot, ip); + + uint16* gslots = ti->globalSlots->data(); + return oracle.isGlobalSlotUndemotable(cx, gslots[slot - ti->nStackTypes]); +} + +static JS_REQUIRES_STACK inline bool +IsSlotUndemotable(JSContext* cx, TreeInfo* ti, unsigned slot) +{ + return IsSlotUndemotable(cx, ti, slot, cx->fp->regs->pc); +} + +class FrameInfoCache +{ + struct Entry : public JSDHashEntryHdr + { + FrameInfo *fi; + }; + + static JSBool + MatchFrameInfo(JSDHashTable *table, const JSDHashEntryHdr *entry, const void *key) { + const FrameInfo* fi1 = ((const Entry*)entry)->fi; + const FrameInfo* fi2 = (const FrameInfo*)key; + if (memcmp(fi1, fi2, sizeof(FrameInfo)) != 0) + return JS_FALSE; + return memcmp(fi1->get_typemap(), fi2->get_typemap(), + fi1->callerHeight * sizeof(JSTraceType)) == 0; + } + + static JSDHashNumber + HashFrameInfo(JSDHashTable *table, const void *key) { + FrameInfo* fi = (FrameInfo*)key; + size_t len = sizeof(FrameInfo) + fi->callerHeight * sizeof(JSTraceType); + + JSDHashNumber h = 0; + const unsigned char *s = (const unsigned char*)fi; + for (size_t i = 0; i < len; i++, s++) + h = JS_ROTATE_LEFT32(h, 4) ^ *s; + return h; + } + + static const JSDHashTableOps FrameCacheOps; + + JSDHashTable *table; + VMAllocator *allocator; + + public: + FrameInfoCache(VMAllocator *allocator) : allocator(allocator) { + init(); + } + + ~FrameInfoCache() { + clear(); + } + + void clear() { + if (table) { + JS_DHashTableDestroy(table); + table = NULL; + } + } + + bool reset() { + clear(); + return init(); + } + + bool init() { + table = JS_NewDHashTable(&FrameCacheOps, NULL, sizeof(Entry), + JS_DHASH_DEFAULT_CAPACITY(32)); + return table != NULL; + } + + FrameInfo *memoize(const FrameInfo *fi) { + Entry *entry = (Entry*)JS_DHashTableOperate(table, fi, JS_DHASH_ADD); + if (!entry) + return NULL; + if (!entry->fi) { + FrameInfo* n = (FrameInfo*) + allocator->alloc(sizeof(FrameInfo) + fi->callerHeight * sizeof(JSTraceType)); + memcpy(n, fi, sizeof(FrameInfo) + fi->callerHeight * sizeof(JSTraceType)); + entry->fi = n; + } + return entry->fi; + } +}; + +const JSDHashTableOps FrameInfoCache::FrameCacheOps = +{ + JS_DHashAllocTable, + JS_DHashFreeTable, + FrameInfoCache::HashFrameInfo, + FrameInfoCache::MatchFrameInfo, + JS_DHashMoveEntryStub, + JS_DHashClearEntryStub, + JS_DHashFinalizeStub, + NULL +}; + + +struct PCHashEntry : public JSDHashEntryStub { + size_t count; +}; + +#define PC_HASH_COUNT 1024 + +static void +Blacklist(jsbytecode* pc) +{ + AUDIT(blacklisted); + JS_ASSERT(*pc == JSOP_TRACE || *pc == JSOP_NOP || *pc == JSOP_CALL); + if (*pc == JSOP_CALL) { + JS_ASSERT(*(pc + JSOP_CALL_LENGTH) == JSOP_TRACE || + *(pc + JSOP_CALL_LENGTH) == JSOP_NOP); + *(pc + JSOP_CALL_LENGTH) = JSOP_NOP; + } else if (*pc == JSOP_TRACE) { + *pc = JSOP_NOP; + } +} + +static bool +IsBlacklisted(jsbytecode* pc) +{ + if (*pc == JSOP_NOP) + return true; + if (*pc == JSOP_CALL) + return *(pc + JSOP_CALL_LENGTH) == JSOP_NOP; + return false; +} + +static void +Backoff(JSContext *cx, jsbytecode* pc, Fragment* tree = NULL) +{ + /* N.B. This code path cannot assume the recorder is/is not alive. */ + JSDHashTable *table = &JS_TRACE_MONITOR(cx).recordAttempts; + + if (table->ops) { + PCHashEntry *entry = (PCHashEntry *) + JS_DHashTableOperate(table, pc, JS_DHASH_ADD); + + if (entry) { + if (!entry->key) { + entry->key = pc; + JS_ASSERT(entry->count == 0); + } + JS_ASSERT(JS_DHASH_ENTRY_IS_LIVE(&(entry->hdr))); + if (entry->count++ > (BL_ATTEMPTS * MAXPEERS)) { + entry->count = 0; + Blacklist(pc); + return; + } + } + } + + if (tree) { + tree->hits() -= BL_BACKOFF; + + /* + * In case there is no entry or no table (due to OOM) or some + * serious imbalance in the recording-attempt distribution on a + * multitree, give each tree another chance to blacklist here as + * well. + */ + if (++tree->recordAttempts > BL_ATTEMPTS) + Blacklist(pc); + } +} + +static void +ResetRecordingAttempts(JSContext *cx, jsbytecode* pc) +{ + JSDHashTable *table = &JS_TRACE_MONITOR(cx).recordAttempts; + if (table->ops) { + PCHashEntry *entry = (PCHashEntry *) + JS_DHashTableOperate(table, pc, JS_DHASH_LOOKUP); + + if (JS_DHASH_ENTRY_IS_FREE(&(entry->hdr))) + return; + JS_ASSERT(JS_DHASH_ENTRY_IS_LIVE(&(entry->hdr))); + entry->count = 0; + } +} + +static inline size_t +FragmentHash(const void *ip, JSObject* globalObj, uint32 globalShape, uint32 argc) +{ + uintptr_t h = HASH_SEED; + HashAccum(h, uintptr_t(ip), FRAGMENT_TABLE_MASK); + HashAccum(h, uintptr_t(globalObj), FRAGMENT_TABLE_MASK); + HashAccum(h, uintptr_t(globalShape), FRAGMENT_TABLE_MASK); + HashAccum(h, uintptr_t(argc), FRAGMENT_TABLE_MASK); + return size_t(h); +} + +static void +RawLookupFirstPeer(JSTraceMonitor* tm, const void *ip, JSObject* globalObj, + uint32 globalShape, uint32 argc, + TreeFragment*& firstInBucket, TreeFragment**& prevTreeNextp) +{ + size_t h = FragmentHash(ip, globalObj, globalShape, argc); + TreeFragment** ppf = &tm->vmfragments[h]; + firstInBucket = *ppf; + for (; TreeFragment* pf = *ppf; ppf = &pf->next) { + if (pf->globalObj == globalObj && + pf->globalShape == globalShape && + pf->ip == ip && + pf->argc == argc) { + prevTreeNextp = ppf; + return; + } + } + prevTreeNextp = ppf; + return; +} + +static TreeFragment* +LookupLoop(JSTraceMonitor* tm, const void *ip, JSObject* globalObj, + uint32 globalShape, uint32 argc) +{ + TreeFragment *_, **prevTreeNextp; + RawLookupFirstPeer(tm, ip, globalObj, globalShape, argc, _, prevTreeNextp); + return *prevTreeNextp; +} + +static TreeFragment* +LookupOrAddLoop(JSTraceMonitor* tm, const void *ip, JSObject* globalObj, + uint32 globalShape, uint32 argc) +{ + TreeFragment *firstInBucket, **prevTreeNextp; + RawLookupFirstPeer(tm, ip, globalObj, globalShape, argc, firstInBucket, prevTreeNextp); + if (TreeFragment *f = *prevTreeNextp) + return f; + + verbose_only( + uint32_t profFragID = (js_LogController.lcbits & LC_FragProfile) + ? (++(tm->lastFragID)) : 0; + ) + TreeFragment* f = new (*tm->dataAlloc) TreeFragment(ip, globalObj, globalShape, argc + verbose_only(, profFragID)); + f->root = f; /* f is the root of a new tree */ + *prevTreeNextp = f; /* insert f at the end of the vmfragments bucket-list */ + f->next = NULL; + f->first = f; /* initialize peer-list at f */ + f->peer = NULL; + return f; +} + +static TreeFragment* +AddNewPeerToPeerList(JSTraceMonitor* tm, TreeFragment* peer) +{ + JS_ASSERT(peer); + verbose_only( + uint32_t profFragID = (js_LogController.lcbits & LC_FragProfile) + ? (++(tm->lastFragID)) : 0; + ) + TreeFragment* f = new (*tm->dataAlloc) TreeFragment(peer->ip, peer->globalObj, + peer->globalShape, peer->argc + verbose_only(, profFragID)); + f->root = f; /* f is the root of a new tree */ + f->first = peer->first; /* add f to peer list */ + f->peer = peer->peer; + peer->peer = f; + /* only the |first| Fragment of a peer list needs a valid |next| field */ + debug_only(f->next = (TreeFragment*)0xcdcdcdcd); + return f; +} + +#ifdef DEBUG +static void +AssertTreeIsUnique(JSTraceMonitor* tm, TreeFragment* f, TreeInfo* ti) +{ + JS_ASSERT(f->root == f); + + /* + * Check for duplicate entry type maps. This is always wrong and hints at + * trace explosion since we are trying to stabilize something without + * properly connecting peer edges. + */ + TreeInfo* ti_other; + for (TreeFragment* peer = LookupLoop(tm, f->ip, f->globalObj, f->globalShape, f->argc); + peer != NULL; + peer = peer->peer) { + if (!peer->code() || peer == f) + continue; + ti_other = peer->treeInfo; + JS_ASSERT(ti_other); + JS_ASSERT(!ti->typeMap.matches(ti_other->typeMap)); + } +} +#endif + +static void +AttemptCompilation(JSContext *cx, JSTraceMonitor* tm, JSObject* globalObj, jsbytecode* pc, + uint32 argc) +{ + /* If we already permanently blacklisted the location, undo that. */ + JS_ASSERT(*pc == JSOP_NOP || *pc == JSOP_TRACE || *pc == JSOP_CALL); + if (*pc == JSOP_NOP) + *pc = JSOP_TRACE; + ResetRecordingAttempts(cx, pc); + + /* Breathe new life into all peer fragments at the designated loop header. */ + TreeFragment* f = LookupLoop(tm, pc, globalObj, OBJ_SHAPE(globalObj), argc); + if (!f) { + /* + * If the global object's shape changed, we can't easily find the + * corresponding loop header via a hash table lookup. In this + * we simply bail here and hope that the fragment has another + * outstanding compilation attempt. This case is extremely rare. + */ + return; + } + JS_ASSERT(f->root == f); + f = f->first; + while (f) { + JS_ASSERT(f->root == f); + --f->recordAttempts; + f->hits() = HOTLOOP; + f = f->peer; + } +} + +// Forward declarations. +JS_DEFINE_CALLINFO_1(static, DOUBLE, i2f, INT32, 1, 1) +JS_DEFINE_CALLINFO_1(static, DOUBLE, u2f, UINT32, 1, 1) + +static bool +isi2f(LIns* i) +{ + if (i->isop(LIR_i2f)) + return true; + + if (nanojit::AvmCore::config.soft_float && + i->isop(LIR_qjoin) && + i->oprnd1()->isop(LIR_pcall) && + i->oprnd2()->isop(LIR_callh)) { + if (i->oprnd1()->callInfo() == &i2f_ci) + return true; + } + + return false; +} + +static bool +isu2f(LIns* i) +{ + if (i->isop(LIR_u2f)) + return true; + + if (nanojit::AvmCore::config.soft_float && + i->isop(LIR_qjoin) && + i->oprnd1()->isop(LIR_pcall) && + i->oprnd2()->isop(LIR_callh)) { + if (i->oprnd1()->callInfo() == &u2f_ci) + return true; + } + + return false; +} + +static LIns* +iu2fArg(LIns* i) +{ + if (nanojit::AvmCore::config.soft_float && + i->isop(LIR_qjoin)) { + return i->oprnd1()->arg(0); + } + + return i->oprnd1(); +} + +static LIns* +demote(LirWriter *out, LIns* i) +{ + if (i->isCall()) + return i->callArgN(0); + if (isi2f(i) || isu2f(i)) + return iu2fArg(i); + if (i->isconst()) + return i; + JS_ASSERT(i->isconstf()); + double cf = i->imm64f(); + int32_t ci = cf > 0x7fffffff ? uint32_t(cf) : int32_t(cf); + return out->insImm(ci); +} + +static bool +isPromoteInt(LIns* i) +{ + if (isi2f(i) || i->isconst()) + return true; + if (!i->isconstf()) + return false; + jsdouble d = i->imm64f(); + return d == jsdouble(jsint(d)) && !JSDOUBLE_IS_NEGZERO(d); +} + +static bool +isPromoteUint(LIns* i) +{ + if (isu2f(i) || i->isconst()) + return true; + if (!i->isconstf()) + return false; + jsdouble d = i->imm64f(); + return d == jsdouble(jsuint(d)) && !JSDOUBLE_IS_NEGZERO(d); +} + +static bool +isPromote(LIns* i) +{ + return isPromoteInt(i) || isPromoteUint(i); +} + +static bool +IsConst(LIns* i, int32_t c) +{ + return i->isconst() && i->imm32() == c; +} + +/* + * Determine whether this operand is guaranteed to not overflow the specified + * integer operation. + */ +static bool +IsOverflowSafe(LOpcode op, LIns* i) +{ + LIns* c; + switch (op) { + case LIR_add: + case LIR_sub: + return (i->isop(LIR_and) && ((c = i->oprnd2())->isconst()) && + ((c->imm32() & 0xc0000000) == 0)) || + (i->isop(LIR_rsh) && ((c = i->oprnd2())->isconst()) && + ((c->imm32() > 0))); + default: + JS_ASSERT(op == LIR_mul); + } + return (i->isop(LIR_and) && ((c = i->oprnd2())->isconst()) && + ((c->imm32() & 0xffff0000) == 0)) || + (i->isop(LIR_ush) && ((c = i->oprnd2())->isconst()) && + ((c->imm32() >= 16))); +} + +/* soft float support */ + +static jsdouble FASTCALL +fneg(jsdouble x) +{ + return -x; +} +JS_DEFINE_CALLINFO_1(static, DOUBLE, fneg, DOUBLE, 1, 1) + +static jsdouble FASTCALL +i2f(int32 i) +{ + return i; +} + +static jsdouble FASTCALL +u2f(jsuint u) +{ + return u; +} + +static int32 FASTCALL +fcmpeq(jsdouble x, jsdouble y) +{ + return x==y; +} +JS_DEFINE_CALLINFO_2(static, INT32, fcmpeq, DOUBLE, DOUBLE, 1, 1) + +static int32 FASTCALL +fcmplt(jsdouble x, jsdouble y) +{ + return x < y; +} +JS_DEFINE_CALLINFO_2(static, INT32, fcmplt, DOUBLE, DOUBLE, 1, 1) + +static int32 FASTCALL +fcmple(jsdouble x, jsdouble y) +{ + return x <= y; +} +JS_DEFINE_CALLINFO_2(static, INT32, fcmple, DOUBLE, DOUBLE, 1, 1) + +static int32 FASTCALL +fcmpgt(jsdouble x, jsdouble y) +{ + return x > y; +} +JS_DEFINE_CALLINFO_2(static, INT32, fcmpgt, DOUBLE, DOUBLE, 1, 1) + +static int32 FASTCALL +fcmpge(jsdouble x, jsdouble y) +{ + return x >= y; +} +JS_DEFINE_CALLINFO_2(static, INT32, fcmpge, DOUBLE, DOUBLE, 1, 1) + +static jsdouble FASTCALL +fmul(jsdouble x, jsdouble y) +{ + return x * y; +} +JS_DEFINE_CALLINFO_2(static, DOUBLE, fmul, DOUBLE, DOUBLE, 1, 1) + +static jsdouble FASTCALL +fadd(jsdouble x, jsdouble y) +{ + return x + y; +} +JS_DEFINE_CALLINFO_2(static, DOUBLE, fadd, DOUBLE, DOUBLE, 1, 1) + +static jsdouble FASTCALL +fdiv(jsdouble x, jsdouble y) +{ + return x / y; +} +JS_DEFINE_CALLINFO_2(static, DOUBLE, fdiv, DOUBLE, DOUBLE, 1, 1) + +static jsdouble FASTCALL +fsub(jsdouble x, jsdouble y) +{ + return x - y; +} +JS_DEFINE_CALLINFO_2(static, DOUBLE, fsub, DOUBLE, DOUBLE, 1, 1) + +// replace fpu ops with function calls +class SoftFloatFilter: public LirWriter +{ +public: + SoftFloatFilter(LirWriter *out) : LirWriter(out) + {} + + LIns *hi(LIns *q) { + return ins1(LIR_qhi, q); + } + LIns *lo(LIns *q) { + return ins1(LIR_qlo, q); + } + + LIns *split(LIns *a) { + if (a->isQuad() && !a->isop(LIR_qjoin)) { + // all quad-sized args must be qjoin's for soft-float + a = ins2(LIR_qjoin, lo(a), hi(a)); + } + return a; + } + + LIns *split(const CallInfo *call, LInsp args[]) { + LIns *lo = out->insCall(call, args); + LIns *hi = out->ins1(LIR_callh, lo); + return out->ins2(LIR_qjoin, lo, hi); + } + + LIns *fcall1(const CallInfo *call, LIns *a) { + LIns *args[] = { split(a) }; + return split(call, args); + } + + LIns *fcall2(const CallInfo *call, LIns *a, LIns *b) { + LIns *args[] = { split(b), split(a) }; + return split(call, args); + } + + LIns *fcmp(const CallInfo *call, LIns *a, LIns *b) { + LIns *args[] = { split(b), split(a) }; + return out->ins2(LIR_eq, out->insCall(call, args), out->insImm(1)); + } + + LIns *ins1(LOpcode op, LIns *a) { + switch (op) { + case LIR_i2f: + return fcall1(&i2f_ci, a); + case LIR_u2f: + return fcall1(&u2f_ci, a); + case LIR_fneg: + return fcall1(&fneg_ci, a); + case LIR_fret: + return out->ins1(op, split(a)); + default: + return out->ins1(op, a); + } + } + + LIns *ins2(LOpcode op, LIns *a, LIns *b) { + switch (op) { + case LIR_fadd: + return fcall2(&fadd_ci, a, b); + case LIR_fsub: + return fcall2(&fsub_ci, a, b); + case LIR_fmul: + return fcall2(&fmul_ci, a, b); + case LIR_fdiv: + return fcall2(&fdiv_ci, a, b); + case LIR_feq: + return fcmp(&fcmpeq_ci, a, b); + case LIR_flt: + return fcmp(&fcmplt_ci, a, b); + case LIR_fgt: + return fcmp(&fcmpgt_ci, a, b); + case LIR_fle: + return fcmp(&fcmple_ci, a, b); + case LIR_fge: + return fcmp(&fcmpge_ci, a, b); + default: + ; + } + return out->ins2(op, a, b); + } + + LIns *insCall(const CallInfo *ci, LInsp args[]) { + uint32_t argt = ci->_argtypes; + + for (uint32_t i = 0, argsizes = argt >> ARGSIZE_SHIFT; argsizes != 0; i++, argsizes >>= ARGSIZE_SHIFT) + args[i] = split(args[i]); + + if ((argt & ARGSIZE_MASK_ANY) == ARGSIZE_F) { + // this function returns a double as two 32bit values, so replace + // call with qjoin(qhi(call), call) + return split(ci, args); + } else { + return out->insCall(ci, args); + } + } +}; + +class FuncFilter: public LirWriter +{ +public: + FuncFilter(LirWriter* out): + LirWriter(out) + { + } + + LIns* ins2(LOpcode v, LIns* s0, LIns* s1) + { + if (s0 == s1 && v == LIR_feq) { + if (isPromote(s0)) { + // double(int) and double(uint) cannot be nan + return insImm(1); + } + if (s0->isop(LIR_fmul) || s0->isop(LIR_fsub) || s0->isop(LIR_fadd)) { + LIns* lhs = s0->oprnd1(); + LIns* rhs = s0->oprnd2(); + if (isPromote(lhs) && isPromote(rhs)) { + // add/sub/mul promoted ints can't be nan + return insImm(1); + } + } + } else if (LIR_feq <= v && v <= LIR_fge) { + if (isPromoteInt(s0) && isPromoteInt(s1)) { + // demote fcmp to cmp + v = LOpcode(v + (LIR_eq - LIR_feq)); + return out->ins2(v, demote(out, s0), demote(out, s1)); + } else if (isPromoteUint(s0) && isPromoteUint(s1)) { + // uint compare + v = LOpcode(v + (LIR_eq - LIR_feq)); + if (v != LIR_eq) + v = LOpcode(v + (LIR_ult - LIR_lt)); // cmp -> ucmp + return out->ins2(v, demote(out, s0), demote(out, s1)); + } + } else if (v == LIR_or && + s0->isop(LIR_lsh) && IsConst(s0->oprnd2(), 16) && + s1->isop(LIR_and) && IsConst(s1->oprnd2(), 0xffff)) { + LIns* msw = s0->oprnd1(); + LIns* lsw = s1->oprnd1(); + LIns* x; + LIns* y; + if (lsw->isop(LIR_add) && + lsw->oprnd1()->isop(LIR_and) && + lsw->oprnd2()->isop(LIR_and) && + IsConst(lsw->oprnd1()->oprnd2(), 0xffff) && + IsConst(lsw->oprnd2()->oprnd2(), 0xffff) && + msw->isop(LIR_add) && + msw->oprnd1()->isop(LIR_add) && + msw->oprnd2()->isop(LIR_rsh) && + msw->oprnd1()->oprnd1()->isop(LIR_rsh) && + msw->oprnd1()->oprnd2()->isop(LIR_rsh) && + IsConst(msw->oprnd2()->oprnd2(), 16) && + IsConst(msw->oprnd1()->oprnd1()->oprnd2(), 16) && + IsConst(msw->oprnd1()->oprnd2()->oprnd2(), 16) && + (x = lsw->oprnd1()->oprnd1()) == msw->oprnd1()->oprnd1()->oprnd1() && + (y = lsw->oprnd2()->oprnd1()) == msw->oprnd1()->oprnd2()->oprnd1() && + lsw == msw->oprnd2()->oprnd1()) { + return out->ins2(LIR_add, x, y); + } + } + + return out->ins2(v, s0, s1); + } + + LIns* insCall(const CallInfo *ci, LIns* args[]) + { + if (ci == &js_DoubleToUint32_ci) { + LIns* s0 = args[0]; + if (s0->isconstf()) + return out->insImm(js_DoubleToECMAUint32(s0->imm64f())); + if (isi2f(s0) || isu2f(s0)) + return iu2fArg(s0); + } else if (ci == &js_DoubleToInt32_ci) { + LIns* s0 = args[0]; + if (s0->isconstf()) + return out->insImm(js_DoubleToECMAInt32(s0->imm64f())); + if (s0->isop(LIR_fadd) || s0->isop(LIR_fsub)) { + LIns* lhs = s0->oprnd1(); + LIns* rhs = s0->oprnd2(); + if (isPromote(lhs) && isPromote(rhs)) { + LOpcode op = LOpcode(s0->opcode() & ~LIR64); + return out->ins2(op, demote(out, lhs), demote(out, rhs)); + } + } + if (isi2f(s0) || isu2f(s0)) + return iu2fArg(s0); + + // XXX ARM -- check for qjoin(call(UnboxDouble),call(UnboxDouble)) + if (s0->isCall()) { + const CallInfo* ci2 = s0->callInfo(); + if (ci2 == &js_UnboxDouble_ci) { + LIns* args2[] = { s0->callArgN(0) }; + return out->insCall(&js_UnboxInt32_ci, args2); + } else if (ci2 == &js_StringToNumber_ci) { + // callArgN's ordering is that as seen by the builtin, not as stored in + // args here. True story! + LIns* args2[] = { s0->callArgN(1), s0->callArgN(0) }; + return out->insCall(&js_StringToInt32_ci, args2); + } else if (ci2 == &js_String_p_charCodeAt0_ci) { + // Use a fast path builtin for a charCodeAt that converts to an int right away. + LIns* args2[] = { s0->callArgN(0) }; + return out->insCall(&js_String_p_charCodeAt0_int_ci, args2); + } else if (ci2 == &js_String_p_charCodeAt_ci) { + LIns* idx = s0->callArgN(1); + // If the index is not already an integer, force it to be an integer. + idx = isPromote(idx) + ? demote(out, idx) + : out->insCall(&js_DoubleToInt32_ci, &idx); + LIns* args2[] = { idx, s0->callArgN(0) }; + return out->insCall(&js_String_p_charCodeAt_int_ci, args2); + } + } + } else if (ci == &js_BoxDouble_ci) { + LIns* s0 = args[0]; + JS_ASSERT(s0->isQuad()); + if (isPromoteInt(s0)) { + LIns* args2[] = { demote(out, s0), args[1] }; + return out->insCall(&js_BoxInt32_ci, args2); + } + if (s0->isCall() && s0->callInfo() == &js_UnboxDouble_ci) + return s0->callArgN(0); + } + return out->insCall(ci, args); + } +}; + +/* + * Visit the values in the given JSStackFrame that the tracer cares about. This + * visitor function is (implicitly) the primary definition of the native stack + * area layout. There are a few other independent pieces of code that must be + * maintained to assume the same layout. They are marked like this: + * + * Duplicate native stack layout computation: see VisitFrameSlots header comment. + */ +template +static JS_REQUIRES_STACK bool +VisitFrameSlots(Visitor &visitor, unsigned depth, JSStackFrame *fp, + JSStackFrame *up) +{ + if (depth > 0 && !VisitFrameSlots(visitor, depth-1, fp->down, fp)) + return false; + + if (fp->argv) { + if (depth == 0) { + visitor.setStackSlotKind("args"); + if (!visitor.visitStackSlots(&fp->argv[-2], argSlots(fp) + 2, fp)) + return false; + } + visitor.setStackSlotKind("arguments"); + if (!visitor.visitStackSlots(&fp->argsobj, 1, fp)) + return false; + visitor.setStackSlotKind("var"); + if (!visitor.visitStackSlots(fp->slots, fp->script->nfixed, fp)) + return false; + } + visitor.setStackSlotKind("stack"); + JS_ASSERT(fp->regs->sp >= StackBase(fp)); + if (!visitor.visitStackSlots(StackBase(fp), + size_t(fp->regs->sp - StackBase(fp)), + fp)) { + return false; + } + if (up) { + int missing = up->fun->nargs - up->argc; + if (missing > 0) { + visitor.setStackSlotKind("missing"); + if (!visitor.visitStackSlots(fp->regs->sp, size_t(missing), fp)) + return false; + } + } + return true; +} + +template +static JS_REQUIRES_STACK JS_ALWAYS_INLINE bool +VisitStackSlots(Visitor &visitor, JSContext *cx, unsigned callDepth) +{ + return VisitFrameSlots(visitor, callDepth, cx->fp, NULL); +} + +template +static JS_REQUIRES_STACK JS_ALWAYS_INLINE void +VisitGlobalSlots(Visitor &visitor, JSContext *cx, JSObject *globalObj, + unsigned ngslots, uint16 *gslots) +{ + for (unsigned n = 0; n < ngslots; ++n) { + unsigned slot = gslots[n]; + visitor.visitGlobalSlot(&STOBJ_GET_SLOT(globalObj, slot), n, slot); + } +} + +template +static JS_REQUIRES_STACK JS_ALWAYS_INLINE void +VisitGlobalSlots(Visitor &visitor, JSContext *cx, TreeInfo *ti) +{ + JSObject* globalObj = ti->globalObj(); + SlotList& gslots = *ti->globalSlots; + VisitGlobalSlots(visitor, cx, globalObj, gslots.length(), gslots.data()); +} + +class AdjustCallerTypeVisitor; + +template +static JS_REQUIRES_STACK JS_ALWAYS_INLINE void +VisitGlobalSlots(Visitor &visitor, JSContext *cx, SlotList &gslots) +{ + VisitGlobalSlots(visitor, cx, JS_GetGlobalForObject(cx, cx->fp->scopeChain), + gslots.length(), gslots.data()); +} + + +template +static JS_REQUIRES_STACK JS_ALWAYS_INLINE void +VisitSlots(Visitor& visitor, JSContext* cx, JSObject* globalObj, + unsigned callDepth, unsigned ngslots, uint16* gslots) +{ + if (VisitStackSlots(visitor, cx, callDepth)) + VisitGlobalSlots(visitor, cx, globalObj, ngslots, gslots); +} + +template +static JS_REQUIRES_STACK JS_ALWAYS_INLINE void +VisitSlots(Visitor& visitor, JSContext* cx, unsigned callDepth, + unsigned ngslots, uint16* gslots) +{ + VisitSlots(visitor, cx, JS_GetGlobalForObject(cx, cx->fp->scopeChain), + callDepth, ngslots, gslots); +} + +template +static JS_REQUIRES_STACK JS_ALWAYS_INLINE void +VisitSlots(Visitor &visitor, JSContext *cx, JSObject *globalObj, + unsigned callDepth, const SlotList& slots) +{ + VisitSlots(visitor, cx, globalObj, callDepth, slots.length(), + slots.data()); +} + +template +static JS_REQUIRES_STACK JS_ALWAYS_INLINE void +VisitSlots(Visitor &visitor, JSContext *cx, unsigned callDepth, + const SlotList& slots) +{ + VisitSlots(visitor, cx, JS_GetGlobalForObject(cx, cx->fp->scopeChain), + callDepth, slots.length(), slots.data()); +} + + +class SlotVisitorBase { +#if defined JS_JIT_SPEW +protected: + char const *mStackSlotKind; +public: + SlotVisitorBase() : mStackSlotKind(NULL) {} + JS_ALWAYS_INLINE const char *stackSlotKind() { return mStackSlotKind; } + JS_ALWAYS_INLINE void setStackSlotKind(char const *k) { + mStackSlotKind = k; + } +#else +public: + JS_ALWAYS_INLINE const char *stackSlotKind() { return NULL; } + JS_ALWAYS_INLINE void setStackSlotKind(char const *k) {} +#endif +}; + +struct CountSlotsVisitor : public SlotVisitorBase +{ + unsigned mCount; + bool mDone; + jsval* mStop; +public: + JS_ALWAYS_INLINE CountSlotsVisitor(jsval* stop = NULL) : + mCount(0), + mDone(false), + mStop(stop) + {} + + JS_REQUIRES_STACK JS_ALWAYS_INLINE bool + visitStackSlots(jsval *vp, size_t count, JSStackFrame* fp) { + if (mDone) + return false; + if (mStop && size_t(mStop - vp) < count) { + mCount += size_t(mStop - vp); + mDone = true; + return false; + } + mCount += count; + return true; + } + + JS_ALWAYS_INLINE unsigned count() { + return mCount; + } + + JS_ALWAYS_INLINE bool stopped() { + return mDone; + } +}; + +/* + * Calculate the total number of native frame slots we need from this frame all + * the way back to the entry frame, including the current stack usage. + */ +JS_REQUIRES_STACK unsigned +NativeStackSlots(JSContext *cx, unsigned callDepth) +{ + JSStackFrame* fp = cx->fp; + unsigned slots = 0; + unsigned depth = callDepth; + for (;;) { + /* + * Duplicate native stack layout computation: see VisitFrameSlots + * header comment. + */ + unsigned operands = fp->regs->sp - StackBase(fp); + slots += operands; + if (fp->argv) + slots += fp->script->nfixed + 1 /*argsobj*/; + if (depth-- == 0) { + if (fp->argv) + slots += 2/*callee,this*/ + argSlots(fp); +#ifdef DEBUG + CountSlotsVisitor visitor; + VisitStackSlots(visitor, cx, callDepth); + JS_ASSERT(visitor.count() == slots && !visitor.stopped()); +#endif + return slots; + } + JSStackFrame* fp2 = fp; + fp = fp->down; + int missing = fp2->fun->nargs - fp2->argc; + if (missing > 0) + slots += missing; + } + JS_NOT_REACHED("NativeStackSlots"); +} + +class CaptureTypesVisitor : public SlotVisitorBase +{ + JSContext* mCx; + JSTraceType* mTypeMap; + JSTraceType* mPtr; + +public: + JS_ALWAYS_INLINE CaptureTypesVisitor(JSContext* cx, JSTraceType* typeMap) : + mCx(cx), + mTypeMap(typeMap), + mPtr(typeMap) + {} + + JS_REQUIRES_STACK JS_ALWAYS_INLINE void + visitGlobalSlot(jsval *vp, unsigned n, unsigned slot) { + JSTraceType type = getCoercedType(*vp); + if (type == TT_INT32 && + oracle.isGlobalSlotUndemotable(mCx, slot)) + type = TT_DOUBLE; + JS_ASSERT(type != TT_JSVAL); + debug_only_printf(LC_TMTracer, + "capture type global%d: %d=%c\n", + n, type, typeChar[type]); + *mPtr++ = type; + } + + JS_REQUIRES_STACK JS_ALWAYS_INLINE bool + visitStackSlots(jsval *vp, int count, JSStackFrame* fp) { + for (int i = 0; i < count; ++i) { + JSTraceType type = getCoercedType(vp[i]); + if (type == TT_INT32 && + oracle.isStackSlotUndemotable(mCx, length())) + type = TT_DOUBLE; + JS_ASSERT(type != TT_JSVAL); + debug_only_printf(LC_TMTracer, + "capture type %s%d: %d=%c\n", + stackSlotKind(), i, type, typeChar[type]); + *mPtr++ = type; + } + return true; + } + + JS_ALWAYS_INLINE uintptr_t length() { + return mPtr - mTypeMap; + } +}; + +/* + * Capture the type map for the selected slots of the global object and currently pending + * stack frames. + */ +JS_REQUIRES_STACK void +TypeMap::captureTypes(JSContext* cx, JSObject* globalObj, SlotList& slots, unsigned callDepth) +{ + setLength(NativeStackSlots(cx, callDepth) + slots.length()); + CaptureTypesVisitor visitor(cx, data()); + VisitSlots(visitor, cx, globalObj, callDepth, slots); + JS_ASSERT(visitor.length() == length()); +} + +JS_REQUIRES_STACK void +TypeMap::captureMissingGlobalTypes(JSContext* cx, JSObject* globalObj, SlotList& slots, unsigned stackSlots) +{ + unsigned oldSlots = length() - stackSlots; + int diff = slots.length() - oldSlots; + JS_ASSERT(diff >= 0); + setLength(length() + diff); + CaptureTypesVisitor visitor(cx, data() + stackSlots + oldSlots); + VisitGlobalSlots(visitor, cx, globalObj, diff, slots.data() + oldSlots); +} + +/* Compare this type map to another one and see whether they match. */ +bool +TypeMap::matches(TypeMap& other) const +{ + if (length() != other.length()) + return false; + return !memcmp(data(), other.data(), length()); +} + +void +TypeMap::fromRaw(JSTraceType* other, unsigned numSlots) +{ + unsigned oldLength = length(); + setLength(length() + numSlots); + for (unsigned i = 0; i < numSlots; i++) + get(oldLength + i) = other[i]; +} + +/* + * Use the provided storage area to create a new type map that contains the + * partial type map with the rest of it filled up from the complete type + * map. + */ +static void +MergeTypeMaps(JSTraceType** partial, unsigned* plength, JSTraceType* complete, unsigned clength, JSTraceType* mem) +{ + unsigned l = *plength; + JS_ASSERT(l < clength); + memcpy(mem, *partial, l * sizeof(JSTraceType)); + memcpy(mem + l, complete + l, (clength - l) * sizeof(JSTraceType)); + *partial = mem; + *plength = clength; +} + +/* Specializes a tree to any missing globals, including any dependent trees. */ +static JS_REQUIRES_STACK void +SpecializeTreesToMissingGlobals(JSContext* cx, JSObject* globalObj, TreeInfo* root) +{ + TreeInfo* ti = root; + + ti->typeMap.captureMissingGlobalTypes(cx, globalObj, *ti->globalSlots, ti->nStackTypes); + JS_ASSERT(ti->globalSlots->length() == ti->typeMap.length() - ti->nStackTypes); + + for (unsigned i = 0; i < root->dependentTrees.length(); i++) { + ti = root->dependentTrees[i]->treeInfo; + + /* ti can be NULL if we hit the recording tree in emitTreeCall; this is harmless. */ + if (ti && ti->nGlobalTypes() < ti->globalSlots->length()) + SpecializeTreesToMissingGlobals(cx, globalObj, ti); + } + for (unsigned i = 0; i < root->linkedTrees.length(); i++) { + ti = root->linkedTrees[i]->treeInfo; + if (ti && ti->nGlobalTypes() < ti->globalSlots->length()) + SpecializeTreesToMissingGlobals(cx, globalObj, ti); + } +} + +static JS_REQUIRES_STACK void +ResetJITImpl(JSContext* cx); + +#ifdef MOZ_TRACEVIS +static JS_INLINE JS_REQUIRES_STACK void +ResetJIT(JSContext* cx, TraceVisFlushReason r) +{ + js_LogTraceVisEvent(cx, S_RESET, r); + ResetJITImpl(cx); +} +#else +#define ResetJIT(cx, r) ResetJITImpl(cx) +#endif + +static void +TrashTree(JSContext* cx, TreeFragment* f); + +template +static T& +InitConst(const T &t) +{ + return const_cast(t); +} + +JS_REQUIRES_STACK +TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* anchor, VMFragment* fragment, + TreeInfo* ti, unsigned stackSlots, unsigned ngslots, JSTraceType* typeMap, + VMSideExit* innermost, jsbytecode* outer, uint32 outerArgc, + RecordReason recordReason) + : cx(cx), + traceMonitor(&JS_TRACE_MONITOR(cx)), + fragment(fragment), + treeInfo(ti), + recordReason(recordReason), + globalObj(ti->globalObj()), + outer(outer), + outerArgc(outerArgc), + lexicalBlock(cx->fp->blockChain), + anchor(anchor), + lir(NULL), + cx_ins(NULL), + eos_ins(NULL), + eor_ins(NULL), + loopLabel(NULL), + lirbuf(traceMonitor->lirbuf), + mark(*traceMonitor->traceAlloc), + numSideExitsBefore(treeInfo->sideExits.length()), + tracker(), + nativeFrameTracker(), + global_dslots(NULL), + callDepth(anchor ? anchor->calldepth : 0), + atoms(FrameAtomBase(cx, cx->fp)), + cfgMerges(&tempAlloc()), + trashSelf(false), + whichTreesToTrash(&tempAlloc()), + rval_ins(NULL), + native_rval_ins(NULL), + newobj_ins(NULL), + pendingSpecializedNative(NULL), + pendingUnboxSlot(NULL), + pendingGuardCondition(NULL), + pendingLoop(true), + generatedSpecializedNative(), + tempTypeMap(cx) +{ + JS_ASSERT(globalObj == JS_GetGlobalForObject(cx, cx->fp->scopeChain)); + JS_ASSERT(cx->fp->regs->pc == (jsbytecode*)fragment->ip); + JS_ASSERT(fragment->root == treeInfo->rootFragment); + JS_ASSERT_IF(fragment->root == fragment, !fragment->root->treeInfo); + JS_ASSERT(ti); + + /* + * Reset the fragment state we care about in case we got a recycled + * fragment. This includes resetting any profiling data we might have + * accumulated. + */ + fragment->lastIns = NULL; + fragment->setCode(NULL); + fragment->lirbuf = lirbuf; + verbose_only( fragment->profCount = 0; ) + verbose_only( fragment->nStaticExits = 0; ) + verbose_only( fragment->nCodeBytes = 0; ) + verbose_only( fragment->nExitBytes = 0; ) + verbose_only( fragment->guardNumberer = 1; ) + verbose_only( fragment->guardsForFrag = NULL; ) + verbose_only( fragment->loopLabel = NULL; ) + + /* + * Don't change fragment->profFragID, though. Once the identity of the + * Fragment is set up (for profiling purposes), we can't change it. + */ + + guardedShapeTable.ops = NULL; + +#ifdef JS_JIT_SPEW + debug_only_print0(LC_TMMinimal, "\n"); + debug_only_printf(LC_TMMinimal, "Recording starting from %s:%u@%u (FragID=%06u)\n", + ti->treeFileName, ti->treeLineNumber, ti->treePCOffset, + fragment->profFragID); + + debug_only_printf(LC_TMTracer, "globalObj=%p, shape=%d\n", + (void*)this->globalObj, OBJ_SHAPE(this->globalObj)); + debug_only_printf(LC_TMTreeVis, "TREEVIS RECORD FRAG=%p ANCHOR=%p\n", (void*)fragment, + (void*)anchor); +#endif + + nanojit::LirWriter*& lir = InitConst(this->lir); + lir = new (tempAlloc()) LirBufWriter(lirbuf); +#ifdef DEBUG + lir = new (tempAlloc()) SanityFilter(lir); +#endif + debug_only_stmt( + if (js_LogController.lcbits & LC_TMRecorder) { + lir = new (tempAlloc()) VerboseWriter(tempAlloc(), lir, lirbuf->names, + &js_LogController); + } + ) + if (nanojit::AvmCore::config.soft_float) + lir = new (tempAlloc()) SoftFloatFilter(lir); + lir = new (tempAlloc()) CseFilter(lir, tempAlloc()); + lir = new (tempAlloc()) ExprFilter(lir); + lir = new (tempAlloc()) FuncFilter(lir); +#ifdef DEBUG + lir = new (tempAlloc()) SanityFilter(lir); +#endif + lir->ins0(LIR_start); + + for (int i = 0; i < NumSavedRegs; ++i) + lir->insParam(i, 1); +#ifdef DEBUG + for (int i = 0; i < NumSavedRegs; ++i) + addName(lirbuf->savedRegs[i], regNames[Assembler::savedRegs[i]]); +#endif + + lirbuf->state = addName(lir->insParam(0, 0), "state"); + + if (fragment == fragment->root) + InitConst(loopLabel) = lir->ins0(LIR_label); + + // if profiling, drop a label, so the assembler knows to put a + // frag-entry-counter increment at this point. If there's a + // loopLabel, use that; else we'll have to make a dummy label + // especially for this purpose. + verbose_only( if (js_LogController.lcbits & LC_FragProfile) { + LIns* entryLabel = NULL; + if (fragment == fragment->root) { + entryLabel = loopLabel; + } else { + entryLabel = lir->ins0(LIR_label); + } + NanoAssert(entryLabel); + NanoAssert(!fragment->loopLabel); + fragment->loopLabel = entryLabel; + }) + + lirbuf->sp = addName(lir->insLoad(LIR_ldp, lirbuf->state, offsetof(InterpState, sp)), "sp"); + lirbuf->rp = addName(lir->insLoad(LIR_ldp, lirbuf->state, offsetof(InterpState, rp)), "rp"); + InitConst(cx_ins) = addName(lir->insLoad(LIR_ldp, lirbuf->state, offsetof(InterpState, cx)), "cx"); + InitConst(eos_ins) = addName(lir->insLoad(LIR_ldp, lirbuf->state, offsetof(InterpState, eos)), "eos"); + InitConst(eor_ins) = addName(lir->insLoad(LIR_ldp, lirbuf->state, offsetof(InterpState, eor)), "eor"); + + /* If we came from exit, we might not have enough global types. */ + if (ti->globalSlots->length() > ti->nGlobalTypes()) + SpecializeTreesToMissingGlobals(cx, globalObj, ti); + + /* read into registers all values on the stack and all globals we know so far */ + import(treeInfo, lirbuf->sp, stackSlots, ngslots, callDepth, typeMap); + + /* Finish handling RECURSIVE_SLURP_FAIL_EXIT in startRecorder. */ + if (anchor && anchor->exitType == RECURSIVE_SLURP_FAIL_EXIT) + return; + + if (fragment == fragment->root) { + /* + * We poll the operation callback request flag. It is updated asynchronously whenever + * the callback is to be invoked. + */ + LIns* x = lir->insLoad(LIR_ld, cx_ins, offsetof(JSContext, operationCallbackFlag)); + guard(true, lir->ins_eq0(x), snapshot(TIMEOUT_EXIT)); + } + + /* + * If we are attached to a tree call guard, make sure the guard the inner + * tree exited from is what we expect it to be. + */ + if (anchor && anchor->exitType == NESTED_EXIT) { + LIns* nested_ins = addName(lir->insLoad(LIR_ldp, lirbuf->state, + offsetof(InterpState, outermostTreeExitGuard)), + "outermostTreeExitGuard"); + guard(true, lir->ins2(LIR_peq, nested_ins, INS_CONSTPTR(innermost)), NESTED_EXIT); + } +} + +TraceRecorder::~TraceRecorder() +{ + /* Should already have been adjusted by callers before calling delete. */ + JS_ASSERT(traceMonitor->recorder != this); + JS_ASSERT(fragment->root == treeInfo->rootFragment); + + if (trashSelf) + TrashTree(cx, fragment->root); + + for (unsigned int i = 0; i < whichTreesToTrash.length(); i++) + TrashTree(cx, whichTreesToTrash[i]); + + /* Purge the tempAlloc used during recording. */ + tempAlloc().reset(); + traceMonitor->lirbuf->clear(); + + forgetGuardedShapes(); +} + +inline bool +JSTraceMonitor::outOfMemory() const +{ + return dataAlloc->outOfMemory() || + tempAlloc->outOfMemory() || + traceAlloc->outOfMemory(); +} + +/* + * This function destroys the recorder after a successful recording, possibly + * starting a suspended outer recorder. + */ +AbortableRecordingStatus +TraceRecorder::finishSuccessfully() +{ + JS_ASSERT(traceMonitor->recorder == this); + JS_ASSERT(fragment->lastIns && fragment->code()); + JS_ASSERT_IF(fragment == fragment->root, fragment->toTreeFragment()->treeInfo); + + AUDIT(traceCompleted); + mark.commit(); + + /* Grab local copies of members needed after |delete this|. */ + JSContext* localcx = cx; + JSTraceMonitor* localtm = traceMonitor; + + localtm->recorder = NULL; + delete this; + + /* Catch OOM that occurred during recording. */ + if (localtm->outOfMemory() || js_OverfullJITCache(localtm)) { + ResetJIT(localcx, FR_OOM); + return ARECORD_ABORTED; + } + return ARECORD_COMPLETED; +} + +/* This function aborts a recorder and any pending outer recorders. */ +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::finishAbort(const char* reason) +{ + JS_ASSERT(traceMonitor->recorder == this); + JS_ASSERT(!fragment->code()); + JS_ASSERT_IF(fragment == fragment->root, !fragment->toTreeFragment()->treeInfo); + + AUDIT(recorderAborted); +#ifdef DEBUG + debug_only_printf(LC_TMAbort, + "Abort recording of tree %s:%d@%d at %s:%d@%d: %s.\n", + treeInfo->treeFileName, + treeInfo->treeLineNumber, + treeInfo->treePCOffset, + cx->fp->script->filename, + js_FramePCToLineNumber(cx, cx->fp), + FramePCOffset(cx->fp), + reason); +#endif + Backoff(cx, (jsbytecode*) fragment->root->ip, fragment->root); + + /* + * If this is the primary trace and we didn't succeed compiling, trash the + * TreeInfo object. Otherwise, remove the VMSideExits we added while + * recording, which are about to be invalid. + * + * BIG FAT WARNING: resetting the length is only a valid strategy as long as + * there may be only one recorder active for a single TreeInfo at a time. + * Otherwise, we may be throwing away another recorder's valid side exits. + */ + if (fragment->root == fragment) { + TrashTree(cx, fragment->toTreeFragment()); + } else { + JS_ASSERT(numSideExitsBefore <= fragment->root->treeInfo->sideExits.length()); + fragment->root->treeInfo->sideExits.setLength(numSideExitsBefore); + } + + /* Grab local copies of members needed after |delete this|. */ + JSContext* localcx = cx; + JSTraceMonitor* localtm = traceMonitor; + + localtm->recorder = NULL; + delete this; + if (localtm->outOfMemory() || js_OverfullJITCache(localtm)) + ResetJIT(localcx, FR_OOM); + return ARECORD_ABORTED; +} + +/* Add debug information to a LIR instruction as we emit it. */ +inline LIns* +TraceRecorder::addName(LIns* ins, const char* name) +{ +#ifdef JS_JIT_SPEW + /* + * We'll only ask for verbose Nanojit when .lcbits > 0, so there's no point + * in adding names otherwise. + */ + if (js_LogController.lcbits > 0) + lirbuf->names->addName(ins, name); +#endif + return ins; +} + +inline LIns* +TraceRecorder::insImmVal(jsval val) +{ + if (JSVAL_IS_TRACEABLE(val)) + treeInfo->gcthings.addUnique(val); + return lir->insImmWord(val); +} + +inline LIns* +TraceRecorder::insImmObj(JSObject* obj) +{ + treeInfo->gcthings.addUnique(OBJECT_TO_JSVAL(obj)); + return lir->insImmPtr((void*)obj); +} + +inline LIns* +TraceRecorder::insImmFun(JSFunction* fun) +{ + treeInfo->gcthings.addUnique(OBJECT_TO_JSVAL(FUN_OBJECT(fun))); + return lir->insImmPtr((void*)fun); +} + +inline LIns* +TraceRecorder::insImmStr(JSString* str) +{ + treeInfo->gcthings.addUnique(STRING_TO_JSVAL(str)); + return lir->insImmPtr((void*)str); +} + +inline LIns* +TraceRecorder::insImmSprop(JSScopeProperty* sprop) +{ + treeInfo->sprops.addUnique(sprop); + return lir->insImmPtr((void*)sprop); +} + +inline LIns* +TraceRecorder::p2i(nanojit::LIns* ins) +{ +#ifdef NANOJIT_64BIT + return lir->ins1(LIR_qlo, ins); +#else + return ins; +#endif +} + +/* Determine the offset in the native global frame for a jsval we track. */ +ptrdiff_t +TraceRecorder::nativeGlobalOffset(jsval* p) const +{ + JS_ASSERT(isGlobal(p)); + if (size_t(p - globalObj->fslots) < JS_INITIAL_NSLOTS) + return size_t(p - globalObj->fslots) * sizeof(double); + return ((p - globalObj->dslots) + JS_INITIAL_NSLOTS) * sizeof(double); +} + +/* Determine whether a value is a global stack slot. */ +bool +TraceRecorder::isGlobal(jsval* p) const +{ + return ((size_t(p - globalObj->fslots) < JS_INITIAL_NSLOTS) || + (size_t(p - globalObj->dslots) < (STOBJ_NSLOTS(globalObj) - JS_INITIAL_NSLOTS))); +} + +/* + * Return the offset in the native stack for the given jsval. More formally, + * |p| must be the address of a jsval that is represented in the native stack + * area. The return value is the offset, from InterpState::stackBase, in bytes, + * where the native representation of |*p| is stored. To get the offset + * relative to InterpState::sp, subtract TreeInfo::nativeStackBase. + */ +JS_REQUIRES_STACK ptrdiff_t +TraceRecorder::nativeStackOffset(jsval* p) const +{ + CountSlotsVisitor visitor(p); + VisitStackSlots(visitor, cx, callDepth); + size_t offset = visitor.count() * sizeof(double); + + /* + * If it's not in a pending frame, it must be on the stack of the current + * frame above sp but below fp->slots + script->nslots. + */ + if (!visitor.stopped()) { + JS_ASSERT(size_t(p - cx->fp->slots) < cx->fp->script->nslots); + offset += size_t(p - cx->fp->regs->sp) * sizeof(double); + } + return offset; +} +/* + * Return the offset, from InterpState:sp, for the given jsval. Shorthand for: + * -TreeInfo::nativeStackBase + nativeStackOffset(p). + */ +inline JS_REQUIRES_STACK ptrdiff_t +TraceRecorder::nativespOffset(jsval* p) const +{ + return -treeInfo->nativeStackBase + nativeStackOffset(p); +} + +/* Track the maximum number of native frame slots we need during execution. */ +inline void +TraceRecorder::trackNativeStackUse(unsigned slots) +{ + if (slots > treeInfo->maxNativeStackSlots) + treeInfo->maxNativeStackSlots = slots; +} + +/* + * Unbox a jsval into a slot. Slots are wide enough to hold double values + * directly (instead of storing a pointer to them). We assert instead of + * type checking. The caller must ensure the types are compatible. + */ +static void +ValueToNative(JSContext* cx, jsval v, JSTraceType type, double* slot) +{ + uint8_t tag = JSVAL_TAG(v); + switch (type) { + case TT_OBJECT: + JS_ASSERT(tag == JSVAL_OBJECT); + JS_ASSERT(!JSVAL_IS_NULL(v) && !HAS_FUNCTION_CLASS(JSVAL_TO_OBJECT(v))); + *(JSObject**)slot = JSVAL_TO_OBJECT(v); + debug_only_printf(LC_TMTracer, + "object<%p:%s> ", (void*)JSVAL_TO_OBJECT(v), + JSVAL_IS_NULL(v) + ? "null" + : STOBJ_GET_CLASS(JSVAL_TO_OBJECT(v))->name); + return; + + case TT_INT32: + jsint i; + if (JSVAL_IS_INT(v)) + *(jsint*)slot = JSVAL_TO_INT(v); + else if (tag == JSVAL_DOUBLE && JSDOUBLE_IS_INT(*JSVAL_TO_DOUBLE(v), i)) + *(jsint*)slot = i; + else + JS_ASSERT(JSVAL_IS_INT(v)); + debug_only_printf(LC_TMTracer, "int<%d> ", *(jsint*)slot); + return; + + case TT_DOUBLE: + jsdouble d; + if (JSVAL_IS_INT(v)) + d = JSVAL_TO_INT(v); + else + d = *JSVAL_TO_DOUBLE(v); + JS_ASSERT(JSVAL_IS_INT(v) || JSVAL_IS_DOUBLE(v)); + *(jsdouble*)slot = d; + debug_only_printf(LC_TMTracer, "double<%g> ", d); + return; + + case TT_JSVAL: + JS_NOT_REACHED("found jsval type in an entry type map"); + return; + + case TT_STRING: + JS_ASSERT(tag == JSVAL_STRING); + *(JSString**)slot = JSVAL_TO_STRING(v); + debug_only_printf(LC_TMTracer, "string<%p> ", (void*)(*(JSString**)slot)); + return; + + case TT_NULL: + JS_ASSERT(tag == JSVAL_OBJECT); + *(JSObject**)slot = NULL; + debug_only_print0(LC_TMTracer, "null "); + return; + + case TT_PSEUDOBOOLEAN: + /* Watch out for pseudo-booleans. */ + JS_ASSERT(tag == JSVAL_SPECIAL); + *(JSBool*)slot = JSVAL_TO_SPECIAL(v); + debug_only_printf(LC_TMTracer, "pseudoboolean<%d> ", *(JSBool*)slot); + return; + + case TT_FUNCTION: { + JS_ASSERT(tag == JSVAL_OBJECT); + JSObject* obj = JSVAL_TO_OBJECT(v); + *(JSObject**)slot = obj; +#ifdef DEBUG + JSFunction* fun = GET_FUNCTION_PRIVATE(cx, obj); + debug_only_printf(LC_TMTracer, + "function<%p:%s> ", (void*) obj, + fun->atom + ? JS_GetStringBytes(ATOM_TO_STRING(fun->atom)) + : "unnamed"); +#endif + return; + } + } + + JS_NOT_REACHED("unexpected type"); +} + +void +JSTraceMonitor::flush() +{ + /* flush should only be called after all recorders have been aborted. */ + JS_ASSERT(!recorder); + AUDIT(cacheFlushed); + + // recover profiling data from expiring Fragments + verbose_only( + for (size_t i = 0; i < FRAGMENT_TABLE_SIZE; ++i) { + for (TreeFragment *f = vmfragments[i]; f; f = f->next) { + JS_ASSERT(f->root == f); + for (TreeFragment *p = f; p; p = p->peer) + js_FragProfiling_FragFinalizer(p, this); + } + } + ) + + verbose_only( + for (Seq* f = branches; f; f = f->tail) + js_FragProfiling_FragFinalizer(f->head, this); + ) + + frameCache->reset(); + dataAlloc->reset(); + traceAlloc->reset(); + codeAlloc->reset(); + tempAlloc->reset(); + reTempAlloc->reset(); + + Allocator& alloc = *dataAlloc; + + for (size_t i = 0; i < MONITOR_N_GLOBAL_STATES; ++i) { + globalStates[i].globalShape = -1; + globalStates[i].globalSlots = new (alloc) SlotList(&alloc); + } + + assembler = new (alloc) Assembler(*codeAlloc, alloc, alloc, core, &js_LogController); + lirbuf = new (alloc) LirBuffer(*tempAlloc); + reLirBuf = new (alloc) LirBuffer(*reTempAlloc); + verbose_only( branches = NULL; ) + +#ifdef DEBUG + labels = new (alloc) LabelMap(alloc, &js_LogController); + reLirBuf->names = + lirbuf->names = new (alloc) LirNameMap(alloc, labels); +#endif + + memset(&vmfragments[0], 0, FRAGMENT_TABLE_SIZE * sizeof(TreeFragment*)); + reFragments = new (alloc) REHashMap(alloc); + + needFlush = JS_FALSE; +} + +static inline void +MarkTreeInfo(JSTracer* trc, TreeInfo *ti) +{ + jsval* vp = ti->gcthings.data(); + unsigned len = ti->gcthings.length(); + while (len--) { + jsval v = *vp++; + JS_SET_TRACING_NAME(trc, "jitgcthing"); + JS_CallTracer(trc, JSVAL_TO_TRACEABLE(v), JSVAL_TRACE_KIND(v)); + } + JSScopeProperty** spropp = ti->sprops.data(); + len = ti->sprops.length(); + while (len--) { + JSScopeProperty* sprop = *spropp++; + sprop->trace(trc); + } +} + +void +JSTraceMonitor::mark(JSTracer* trc) +{ + if (!trc->context->runtime->gcFlushCodeCaches) { + for (size_t i = 0; i < FRAGMENT_TABLE_SIZE; ++i) { + TreeFragment* f = vmfragments[i]; + while (f) { + if (TreeInfo* ti = f->treeInfo) + MarkTreeInfo(trc, ti); + TreeFragment* peer = f->peer; + while (peer) { + if (TreeInfo* ti = peer->treeInfo) + MarkTreeInfo(trc, ti); + peer = peer->peer; + } + f = f->next; + } + } + if (recorder) + MarkTreeInfo(trc, recorder->getTreeInfo()); + } +} + +/* + * Box a value from the native stack back into the jsval format. Integers that + * are too large to fit into a jsval are automatically boxed into + * heap-allocated doubles. + */ +bool +js_NativeToValue(JSContext* cx, jsval& v, JSTraceType type, double* slot) +{ + bool ok; + jsint i; + jsdouble d; + switch (type) { + case TT_OBJECT: + v = OBJECT_TO_JSVAL(*(JSObject**)slot); + JS_ASSERT(v != JSVAL_ERROR_COOKIE); /* don't leak JSVAL_ERROR_COOKIE */ + debug_only_printf(LC_TMTracer, + "object<%p:%s> ", (void*)JSVAL_TO_OBJECT(v), + JSVAL_IS_NULL(v) + ? "null" + : STOBJ_GET_CLASS(JSVAL_TO_OBJECT(v))->name); + break; + + case TT_INT32: + i = *(jsint*)slot; + debug_only_printf(LC_TMTracer, "int<%d> ", i); + store_int: + if (INT_FITS_IN_JSVAL(i)) { + v = INT_TO_JSVAL(i); + break; + } + d = (jsdouble)i; + goto store_double; + case TT_DOUBLE: + d = *slot; + debug_only_printf(LC_TMTracer, "double<%g> ", d); + if (JSDOUBLE_IS_INT(d, i)) + goto store_int; + store_double: + ok = js_NewDoubleInRootedValue(cx, d, &v); + if (!ok) { + js_ReportOutOfMemory(cx); + return false; + } + return true; + + case TT_JSVAL: + v = *(jsval*)slot; + JS_ASSERT(v != JSVAL_ERROR_COOKIE); /* don't leak JSVAL_ERROR_COOKIE */ + debug_only_printf(LC_TMTracer, "box<%p> ", (void*)v); + break; + + case TT_STRING: + v = STRING_TO_JSVAL(*(JSString**)slot); + debug_only_printf(LC_TMTracer, "string<%p> ", (void*)(*(JSString**)slot)); + break; + + case TT_NULL: + JS_ASSERT(*(JSObject**)slot == NULL); + v = JSVAL_NULL; + debug_only_printf(LC_TMTracer, "null<%p> ", (void*)(*(JSObject**)slot)); + break; + + case TT_PSEUDOBOOLEAN: + /* Watch out for pseudo-booleans. */ + v = SPECIAL_TO_JSVAL(*(JSBool*)slot); + debug_only_printf(LC_TMTracer, "boolean<%d> ", *(JSBool*)slot); + break; + + case TT_FUNCTION: { + JS_ASSERT(HAS_FUNCTION_CLASS(*(JSObject**)slot)); + v = OBJECT_TO_JSVAL(*(JSObject**)slot); +#ifdef DEBUG + JSFunction* fun = GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(v)); + debug_only_printf(LC_TMTracer, + "function<%p:%s> ", (void*)JSVAL_TO_OBJECT(v), + fun->atom + ? JS_GetStringBytes(ATOM_TO_STRING(fun->atom)) + : "unnamed"); +#endif + break; + } + } + return true; +} + +class BuildNativeFrameVisitor : public SlotVisitorBase +{ + JSContext *mCx; + JSTraceType *mTypeMap; + double *mGlobal; + double *mStack; +public: + BuildNativeFrameVisitor(JSContext *cx, + JSTraceType *typemap, + double *global, + double *stack) : + mCx(cx), + mTypeMap(typemap), + mGlobal(global), + mStack(stack) + {} + + JS_REQUIRES_STACK JS_ALWAYS_INLINE void + visitGlobalSlot(jsval *vp, unsigned n, unsigned slot) { + debug_only_printf(LC_TMTracer, "global%d: ", n); + ValueToNative(mCx, *vp, *mTypeMap++, &mGlobal[slot]); + } + + JS_REQUIRES_STACK JS_ALWAYS_INLINE bool + visitStackSlots(jsval *vp, int count, JSStackFrame* fp) { + for (int i = 0; i < count; ++i) { + debug_only_printf(LC_TMTracer, "%s%d: ", stackSlotKind(), i); + ValueToNative(mCx, *vp++, *mTypeMap++, mStack++); + } + return true; + } +}; + +static JS_REQUIRES_STACK void +BuildNativeFrame(JSContext *cx, JSObject *globalObj, unsigned callDepth, + unsigned ngslots, uint16 *gslots, + JSTraceType *typeMap, double *global, double *stack) +{ + BuildNativeFrameVisitor visitor(cx, typeMap, global, stack); + VisitSlots(visitor, cx, globalObj, callDepth, ngslots, gslots); + debug_only_print0(LC_TMTracer, "\n"); +} + +class FlushNativeGlobalFrameVisitor : public SlotVisitorBase +{ + JSContext *mCx; + JSTraceType *mTypeMap; + double *mGlobal; +public: + FlushNativeGlobalFrameVisitor(JSContext *cx, + JSTraceType *typeMap, + double *global) : + mCx(cx), + mTypeMap(typeMap), + mGlobal(global) + {} + + JS_REQUIRES_STACK JS_ALWAYS_INLINE void + visitGlobalSlot(jsval *vp, unsigned n, unsigned slot) { + debug_only_printf(LC_TMTracer, "global%d=", n); + JS_ASSERT(JS_THREAD_DATA(mCx)->waiveGCQuota); + if (!js_NativeToValue(mCx, *vp, *mTypeMap++, &mGlobal[slot])) + OutOfMemoryAbort(); + } +}; + +class FlushNativeStackFrameVisitor : public SlotVisitorBase +{ + JSContext *mCx; + const JSTraceType *mInitTypeMap; + const JSTraceType *mTypeMap; + double *mStack; + jsval *mStop; + unsigned mIgnoreSlots; +public: + FlushNativeStackFrameVisitor(JSContext *cx, + const JSTraceType *typeMap, + double *stack, + jsval *stop, + unsigned ignoreSlots) : + mCx(cx), + mInitTypeMap(typeMap), + mTypeMap(typeMap), + mStack(stack), + mStop(stop), + mIgnoreSlots(ignoreSlots) + {} + + const JSTraceType* getTypeMap() + { + return mTypeMap; + } + + JS_REQUIRES_STACK JS_ALWAYS_INLINE bool + visitStackSlots(jsval *vp, size_t count, JSStackFrame* fp) { + JS_ASSERT(JS_THREAD_DATA(mCx)->waiveGCQuota); + for (size_t i = 0; i < count; ++i) { + if (vp == mStop) + return false; + debug_only_printf(LC_TMTracer, "%s%u=", stackSlotKind(), unsigned(i)); + if (unsigned(mTypeMap - mInitTypeMap) >= mIgnoreSlots) { + if (!js_NativeToValue(mCx, *vp, *mTypeMap, mStack)) + OutOfMemoryAbort(); + } + vp++; + mTypeMap++; + mStack++; + } + return true; + } +}; + +/* Box the given native frame into a JS frame. This is infallible. */ +static JS_REQUIRES_STACK void +FlushNativeGlobalFrame(JSContext *cx, JSObject *globalObj, double *global, unsigned ngslots, + uint16 *gslots, JSTraceType *typemap) +{ + FlushNativeGlobalFrameVisitor visitor(cx, typemap, global); + VisitGlobalSlots(visitor, cx, globalObj, ngslots, gslots); + debug_only_print0(LC_TMTracer, "\n"); +} + +/* + * Returns the number of values on the native stack, excluding the innermost + * frame. This walks all FrameInfos on the native frame stack and sums the + * slot usage of each frame. + */ +static int32 +StackDepthFromCallStack(InterpState* state, uint32 callDepth) +{ + int32 nativeStackFramePos = 0; + + // Duplicate native stack layout computation: see VisitFrameSlots header comment. + for (FrameInfo** fip = state->callstackBase; fip < state->rp + callDepth; fip++) + nativeStackFramePos += (*fip)->callerHeight; + return nativeStackFramePos; +} + +/* + * Generic function to read upvars on trace from slots of active frames. + * T Traits type parameter. Must provide static functions: + * interp_get(fp, slot) Read the value out of an interpreter frame. + * native_slot(argc, slot) Return the position of the desired value in the on-trace + * stack frame (with position 0 being callee). + * + * upvarLevel Static level of the function containing the upvar definition + * slot Identifies the value to get. The meaning is defined by the traits type. + * callDepth Call depth of current point relative to trace entry + */ +template +inline JSTraceType +GetUpvarOnTrace(JSContext* cx, uint32 upvarLevel, int32 slot, uint32 callDepth, double* result) +{ + InterpState* state = cx->interpState; + FrameInfo** fip = state->rp + callDepth; + + /* + * First search the FrameInfo call stack for an entry containing our + * upvar, namely one with level == upvarLevel. The first FrameInfo is a + * transition from the entry frame to some callee. However, it is not + * known (from looking at the FrameInfo) whether the entry frame had a + * callee. Rather than special-case this or insert more logic into the + * loop, instead just stop before that FrameInfo (i.e. |> base| instead of + * |>= base|), and let the code after the loop handle it. + */ + int32 stackOffset = StackDepthFromCallStack(state, callDepth); + while (--fip > state->callstackBase) { + FrameInfo* fi = *fip; + + /* + * The loop starts aligned to the top of the stack, so move down to the first meaningful + * callee. Then read the callee directly from the frame. + */ + stackOffset -= fi->callerHeight; + JSObject* callee = *(JSObject**)(&state->stackBase[stackOffset]); + JSFunction* fun = GET_FUNCTION_PRIVATE(cx, callee); + uintN calleeLevel = fun->u.i.script->staticLevel; + if (calleeLevel == upvarLevel) { + /* + * Now find the upvar's value in the native stack. stackOffset is + * the offset of the start of the activation record corresponding + * to *fip in the native stack. + */ + uint32 native_slot = T::native_slot(fi->callerArgc, slot); + *result = state->stackBase[stackOffset + native_slot]; + return fi->get_typemap()[native_slot]; + } + } + + // Next search the trace entry frame, which is not in the FrameInfo stack. + if (state->outermostTree->script->staticLevel == upvarLevel) { + uint32 argc = state->outermostTree->rootFragment->argc; + uint32 native_slot = T::native_slot(argc, slot); + *result = state->stackBase[native_slot]; + return state->callstackBase[0]->get_typemap()[native_slot]; + } + + /* + * If we did not find the upvar in the frames for the active traces, + * then we simply get the value from the interpreter state. + */ + JS_ASSERT(upvarLevel < JS_DISPLAY_SIZE); + JSStackFrame* fp = cx->display[upvarLevel]; + jsval v = T::interp_get(fp, slot); + JSTraceType type = getCoercedType(v); + ValueToNative(cx, v, type, result); + return type; +} + +// For this traits type, 'slot' is the argument index, which may be -2 for callee. +struct UpvarArgTraits { + static jsval interp_get(JSStackFrame* fp, int32 slot) { + return fp->argv[slot]; + } + + static uint32 native_slot(uint32 argc, int32 slot) { + return 2 /*callee,this*/ + slot; + } +}; + +uint32 JS_FASTCALL +GetUpvarArgOnTrace(JSContext* cx, uint32 upvarLevel, int32 slot, uint32 callDepth, double* result) +{ + return GetUpvarOnTrace(cx, upvarLevel, slot, callDepth, result); +} + +// For this traits type, 'slot' is an index into the local slots array. +struct UpvarVarTraits { + static jsval interp_get(JSStackFrame* fp, int32 slot) { + return fp->slots[slot]; + } + + static uint32 native_slot(uint32 argc, int32 slot) { + return 3 /*callee,this,arguments*/ + argc + slot; + } +}; + +uint32 JS_FASTCALL +GetUpvarVarOnTrace(JSContext* cx, uint32 upvarLevel, int32 slot, uint32 callDepth, double* result) +{ + return GetUpvarOnTrace(cx, upvarLevel, slot, callDepth, result); +} + +/* + * For this traits type, 'slot' is an index into the stack area (within slots, + * after nfixed) of a frame with no function. (On trace, the top-level frame is + * the only one that can have no function.) + */ +struct UpvarStackTraits { + static jsval interp_get(JSStackFrame* fp, int32 slot) { + return fp->slots[slot + fp->script->nfixed]; + } + + static uint32 native_slot(uint32 argc, int32 slot) { + /* + * Locals are not imported by the tracer when the frame has no + * function, so we do not add fp->script->nfixed. + */ + JS_ASSERT(argc == 0); + return slot; + } +}; + +uint32 JS_FASTCALL +GetUpvarStackOnTrace(JSContext* cx, uint32 upvarLevel, int32 slot, uint32 callDepth, + double* result) +{ + return GetUpvarOnTrace(cx, upvarLevel, slot, callDepth, result); +} + +// Parameters needed to access a value from a closure on trace. +struct ClosureVarInfo +{ + jsid id; + uint32 slot; + uint32 callDepth; + uint32 resolveFlags; +}; + +/* + * Generic function to read upvars from Call objects of active heavyweight functions. + * call Callee Function object in which the upvar is accessed. + */ +template +inline uint32 +GetFromClosure(JSContext* cx, JSObject* call, const ClosureVarInfo* cv, double* result) +{ + JS_ASSERT(OBJ_GET_CLASS(cx, call) == &js_CallClass); + + InterpState* state = cx->interpState; + +#ifdef DEBUG + int32 stackOffset = StackDepthFromCallStack(state, cv->callDepth); + FrameInfo** fip = state->rp + cv->callDepth; + while (--fip > state->callstackBase) { + FrameInfo* fi = *fip; + JSObject* callee = *(JSObject**)(&state->stackBase[stackOffset]); + if (callee == call) { + // This is not reachable as long as JSOP_LAMBDA is not traced: + // - The upvar is found at this point only if the upvar was defined on a frame that was + // entered on this trace. + // - The upvar definition must be (dynamically, and thus on trace) before the definition + // of the function that uses the upvar. + // - Therefore, if the upvar is found at this point, the function definition JSOP_LAMBDA + // is on the trace. + JS_NOT_REACHED("JSOP_NAME variable found in outer trace"); + } + stackOffset -= fi->callerHeight; + } +#endif + + /* + * Here we specifically want to check the call object of the trace entry frame. + */ + uint32 slot = cv->slot; + VOUCH_DOES_NOT_REQUIRE_STACK(); + if (cx->fp->callobj == call) { + slot = T::adj_slot(cx->fp, slot); + *result = state->stackBase[slot]; + return state->callstackBase[0]->get_typemap()[slot]; + } + + JSStackFrame* fp = (JSStackFrame*) call->getPrivate(); + jsval v; + if (fp) { + v = T::slots(fp)[slot]; + } else { + JS_ASSERT(cv->resolveFlags != JSRESOLVE_INFER); + JSAutoResolveFlags rf(cx, cv->resolveFlags); +#ifdef DEBUG + JSBool rv = +#endif + js_GetPropertyHelper(cx, call, cv->id, JSGET_METHOD_BARRIER, &v); + JS_ASSERT(rv); + } + JSTraceType type = getCoercedType(v); + ValueToNative(cx, v, type, result); + return type; +} + +struct ArgClosureTraits +{ + static inline uint32 adj_slot(JSStackFrame* fp, uint32 slot) { return fp->argc + slot; } + static inline jsval* slots(JSStackFrame* fp) { return fp->argv; } +private: + ArgClosureTraits(); +}; + +uint32 JS_FASTCALL +GetClosureArg(JSContext* cx, JSObject* callee, const ClosureVarInfo* cv, double* result) +{ + return GetFromClosure(cx, callee, cv, result); +} + +struct VarClosureTraits +{ + static inline uint32 adj_slot(JSStackFrame* fp, uint32 slot) { return slot; } + static inline jsval* slots(JSStackFrame* fp) { return fp->slots; } +private: + VarClosureTraits(); +}; + +uint32 JS_FASTCALL +GetClosureVar(JSContext* cx, JSObject* callee, const ClosureVarInfo* cv, double* result) +{ + return GetFromClosure(cx, callee, cv, result); +} + +/** + * Box the given native stack frame into the virtual machine stack. This + * is infallible. + * + * @param callDepth the distance between the entry frame into our trace and + * cx->fp when we make this call. If this is not called as a + * result of a nested exit, callDepth is 0. + * @param mp an array of JSTraceTypes that indicate what the types of the things + * on the stack are. + * @param np pointer to the native stack. We want to copy values from here to + * the JS stack as needed. + * @param stopFrame if non-null, this frame and everything above it should not + * be restored. + * @return the number of things we popped off of np. + */ +static JS_REQUIRES_STACK int +FlushNativeStackFrame(JSContext* cx, unsigned callDepth, const JSTraceType* mp, double* np, + JSStackFrame* stopFrame, unsigned ignoreSlots) +{ + jsval* stopAt = stopFrame ? &stopFrame->argv[-2] : NULL; + + /* Root all string and object references first (we don't need to call the GC for this). */ + FlushNativeStackFrameVisitor visitor(cx, mp, np, stopAt, ignoreSlots); + VisitStackSlots(visitor, cx, callDepth); + + // Restore thisv from the now-restored argv[-1] in each pending frame. + // Keep in mind that we didn't restore frames at stopFrame and above! + // Scope to keep |fp| from leaking into the macros we're using. + { + unsigned n = callDepth+1; // +1 to make sure we restore the entry frame + JSStackFrame* fp = cx->fp; + if (stopFrame) { + for (; fp != stopFrame; fp = fp->down) { + JS_ASSERT(n != 0); + --n; + } + + // Skip over stopFrame itself. + JS_ASSERT(n != 0); + --n; + fp = fp->down; + } + for (; n != 0; fp = fp->down) { + --n; + if (fp->argv) { + if (fp->argsobj && + js_GetArgsPrivateNative(JSVAL_TO_OBJECT(fp->argsobj))) { + JSVAL_TO_OBJECT(fp->argsobj)->setPrivate(fp); + } + + JS_ASSERT(JSVAL_IS_OBJECT(fp->argv[-1])); + JS_ASSERT(HAS_FUNCTION_CLASS(fp->calleeObject())); + JS_ASSERT(GET_FUNCTION_PRIVATE(cx, fp->callee()) == fp->fun); + + /* + * SynthesizeFrame sets scopeChain to NULL, because we can't calculate the + * correct scope chain until we have the final callee. Calculate the real + * scope object here. + */ + if (!fp->scopeChain) { + fp->scopeChain = OBJ_GET_PARENT(cx, fp->calleeObject()); + if (fp->fun->flags & JSFUN_HEAVYWEIGHT) { + /* + * Set hookData to null because the failure case for js_GetCallObject + * involves it calling the debugger hook. + * + * Allocating the Call object must not fail, so use an object + * previously reserved by ExecuteTree if needed. + */ + void* hookData = ((JSInlineFrame*)fp)->hookData; + ((JSInlineFrame*)fp)->hookData = NULL; + JS_ASSERT(JS_THREAD_DATA(cx)->waiveGCQuota); +#ifdef DEBUG + JSObject *obj = +#endif + js_GetCallObject(cx, fp); + JS_ASSERT(obj); + ((JSInlineFrame*)fp)->hookData = hookData; + } + } + fp->thisv = fp->argv[-1]; + if (fp->flags & JSFRAME_CONSTRUCTING) // constructors always compute 'this' + fp->flags |= JSFRAME_COMPUTED_THIS; + } + } + } + debug_only_print0(LC_TMTracer, "\n"); + return visitor.getTypeMap() - mp; +} + +/* Emit load instructions onto the trace that read the initial stack state. */ +JS_REQUIRES_STACK void +TraceRecorder::import(LIns* base, ptrdiff_t offset, jsval* p, JSTraceType t, + const char *prefix, uintN index, JSStackFrame *fp) +{ + LIns* ins; + if (t == TT_INT32) { /* demoted */ + JS_ASSERT(isInt32(*p)); + + /* + * Ok, we have a valid demotion attempt pending, so insert an integer + * read and promote it to double since all arithmetic operations expect + * to see doubles on entry. The first op to use this slot will emit a + * f2i cast which will cancel out the i2f we insert here. + */ + ins = lir->insLoad(LIR_ld, base, offset); + ins = lir->ins1(LIR_i2f, ins); + } else { + JS_ASSERT_IF(t != TT_JSVAL, isNumber(*p) == (t == TT_DOUBLE)); + if (t == TT_DOUBLE) { + ins = lir->insLoad(LIR_ldq, base, offset); + } else if (t == TT_PSEUDOBOOLEAN) { + ins = lir->insLoad(LIR_ld, base, offset); + } else { + ins = lir->insLoad(LIR_ldp, base, offset); + } + } + checkForGlobalObjectReallocation(); + tracker.set(p, ins); + +#ifdef DEBUG + char name[64]; + JS_ASSERT(strlen(prefix) < 10); + void* mark = NULL; + jsuword* localNames = NULL; + const char* funName = NULL; + if (*prefix == 'a' || *prefix == 'v') { + mark = JS_ARENA_MARK(&cx->tempPool); + if (fp->fun->hasLocalNames()) + localNames = js_GetLocalNameArray(cx, fp->fun, &cx->tempPool); + funName = fp->fun->atom ? js_AtomToPrintableString(cx, fp->fun->atom) : ""; + } + if (!strcmp(prefix, "argv")) { + if (index < fp->fun->nargs) { + JSAtom *atom = JS_LOCAL_NAME_TO_ATOM(localNames[index]); + JS_snprintf(name, sizeof name, "$%s.%s", funName, js_AtomToPrintableString(cx, atom)); + } else { + JS_snprintf(name, sizeof name, "$%s.", funName, index); + } + } else if (!strcmp(prefix, "vars")) { + JSAtom *atom = JS_LOCAL_NAME_TO_ATOM(localNames[fp->fun->nargs + index]); + JS_snprintf(name, sizeof name, "$%s.%s", funName, js_AtomToPrintableString(cx, atom)); + } else { + JS_snprintf(name, sizeof name, "$%s%d", prefix, index); + } + + if (mark) + JS_ARENA_RELEASE(&cx->tempPool, mark); + addName(ins, name); + + static const char* typestr[] = { + "object", "int", "double", "jsval", "string", "null", "boolean", "function" + }; + debug_only_printf(LC_TMTracer, "import vp=%p name=%s type=%s flags=%d\n", + (void*)p, name, typestr[t & 7], t >> 3); +#endif +} + +class ImportGlobalSlotVisitor : public SlotVisitorBase +{ + TraceRecorder &mRecorder; + LIns *mBase; + JSTraceType *mTypemap; +public: + ImportGlobalSlotVisitor(TraceRecorder &recorder, + LIns *base, + JSTraceType *typemap) : + mRecorder(recorder), + mBase(base), + mTypemap(typemap) + {} + + JS_REQUIRES_STACK JS_ALWAYS_INLINE void + visitGlobalSlot(jsval *vp, unsigned n, unsigned slot) { + JS_ASSERT(*mTypemap != TT_JSVAL); + mRecorder.import(mBase, mRecorder.nativeGlobalOffset(vp), + vp, *mTypemap++, "global", n, NULL); + } +}; + +class ImportBoxedStackSlotVisitor : public SlotVisitorBase +{ + TraceRecorder &mRecorder; + LIns *mBase; + ptrdiff_t mStackOffset; + JSTraceType *mTypemap; + JSStackFrame *mFp; +public: + ImportBoxedStackSlotVisitor(TraceRecorder &recorder, + LIns *base, + ptrdiff_t stackOffset, + JSTraceType *typemap) : + mRecorder(recorder), + mBase(base), + mStackOffset(stackOffset), + mTypemap(typemap) + {} + + JS_REQUIRES_STACK JS_ALWAYS_INLINE bool + visitStackSlots(jsval *vp, size_t count, JSStackFrame* fp) { + for (size_t i = 0; i < count; ++i) { + if (*mTypemap == TT_JSVAL) { + mRecorder.import(mBase, mStackOffset, vp, TT_JSVAL, + "jsval", i, fp); + LIns *vp_ins = mRecorder.unbox_jsval(*vp, mRecorder.get(vp), + mRecorder.copy(mRecorder.anchor)); + mRecorder.set(vp, vp_ins); + } + vp++; + mTypemap++; + mStackOffset += sizeof(double); + } + return true; + } +}; + +class ImportUnboxedStackSlotVisitor : public SlotVisitorBase +{ + TraceRecorder &mRecorder; + LIns *mBase; + ptrdiff_t mStackOffset; + JSTraceType *mTypemap; + JSStackFrame *mFp; +public: + ImportUnboxedStackSlotVisitor(TraceRecorder &recorder, + LIns *base, + ptrdiff_t stackOffset, + JSTraceType *typemap) : + mRecorder(recorder), + mBase(base), + mStackOffset(stackOffset), + mTypemap(typemap) + {} + + JS_REQUIRES_STACK JS_ALWAYS_INLINE bool + visitStackSlots(jsval *vp, size_t count, JSStackFrame* fp) { + for (size_t i = 0; i < count; ++i) { + if (*mTypemap != TT_JSVAL) { + mRecorder.import(mBase, mStackOffset, vp++, *mTypemap, + stackSlotKind(), i, fp); + } + mTypemap++; + mStackOffset += sizeof(double); + } + return true; + } +}; + +JS_REQUIRES_STACK void +TraceRecorder::import(TreeInfo* treeInfo, LIns* sp, unsigned stackSlots, unsigned ngslots, + unsigned callDepth, JSTraceType* typeMap) +{ + /* + * If we get a partial list that doesn't have all the types (i.e. recording + * from a side exit that was recorded but we added more global slots + * later), merge the missing types from the entry type map. This is safe + * because at the loop edge we verify that we have compatible types for all + * globals (entry type and loop edge type match). While a different trace + * of the tree might have had a guard with a different type map for these + * slots we just filled in here (the guard we continue from didn't know + * about them), since we didn't take that particular guard the only way we + * could have ended up here is if that other trace had at its end a + * compatible type distribution with the entry map. Since that's exactly + * what we used to fill in the types our current side exit didn't provide, + * this is always safe to do. + */ + + JSTraceType* globalTypeMap = typeMap + stackSlots; + unsigned length = treeInfo->nGlobalTypes(); + + /* + * This is potentially the typemap of the side exit and thus shorter than + * the tree's global type map. + */ + if (ngslots < length) { + MergeTypeMaps(&globalTypeMap /* out param */, &ngslots /* out param */, + treeInfo->globalTypeMap(), length, + (JSTraceType*)alloca(sizeof(JSTraceType) * length)); + } + JS_ASSERT(ngslots == treeInfo->nGlobalTypes()); + ptrdiff_t offset = -treeInfo->nativeStackBase; + + /* + * Check whether there are any values on the stack we have to unbox and do + * that first before we waste any time fetching the state from the stack. + */ + if (!anchor || anchor->exitType != RECURSIVE_SLURP_FAIL_EXIT) { + ImportBoxedStackSlotVisitor boxedStackVisitor(*this, sp, offset, typeMap); + VisitStackSlots(boxedStackVisitor, cx, callDepth); + } + + ImportGlobalSlotVisitor globalVisitor(*this, eos_ins, globalTypeMap); + VisitGlobalSlots(globalVisitor, cx, globalObj, ngslots, + treeInfo->globalSlots->data()); + + if (!anchor || anchor->exitType != RECURSIVE_SLURP_FAIL_EXIT) { + ImportUnboxedStackSlotVisitor unboxedStackVisitor(*this, sp, offset, + typeMap); + VisitStackSlots(unboxedStackVisitor, cx, callDepth); + } +} + +JS_REQUIRES_STACK bool +TraceRecorder::isValidSlot(JSScope* scope, JSScopeProperty* sprop) +{ + uint32 setflags = (js_CodeSpec[*cx->fp->regs->pc].format & (JOF_SET | JOF_INCDEC | JOF_FOR)); + + if (setflags) { + if (!SPROP_HAS_STUB_SETTER(sprop)) + RETURN_VALUE("non-stub setter", false); + if (sprop->attrs & JSPROP_READONLY) + RETURN_VALUE("writing to a read-only property", false); + } + + /* This check applies even when setflags == 0. */ + if (setflags != JOF_SET && !SPROP_HAS_STUB_GETTER(sprop)) { + JS_ASSERT(!sprop->isMethod()); + RETURN_VALUE("non-stub getter", false); + } + + if (!SPROP_HAS_VALID_SLOT(sprop, scope)) + RETURN_VALUE("slotless obj property", false); + + return true; +} + +/* Lazily import a global slot if we don't already have it in the tracker. */ +JS_REQUIRES_STACK bool +TraceRecorder::lazilyImportGlobalSlot(unsigned slot) +{ + if (slot != uint16(slot)) /* we use a table of 16-bit ints, bail out if that's not enough */ + return false; + + /* + * If the global object grows too large, alloca in ExecuteTree might fail, + * so abort tracing on global objects with unreasonably many slots. + */ + if (STOBJ_NSLOTS(globalObj) > MAX_GLOBAL_SLOTS) + return false; + jsval* vp = &STOBJ_GET_SLOT(globalObj, slot); + if (known(vp)) + return true; /* we already have it */ + unsigned index = treeInfo->globalSlots->length(); + + /* Add the slot to the list of interned global slots. */ + JS_ASSERT(treeInfo->nGlobalTypes() == treeInfo->globalSlots->length()); + treeInfo->globalSlots->add(slot); + JSTraceType type = getCoercedType(*vp); + if (type == TT_INT32 && oracle.isGlobalSlotUndemotable(cx, slot)) + type = TT_DOUBLE; + treeInfo->typeMap.add(type); + import(eos_ins, slot*sizeof(double), vp, type, "global", index, NULL); + SpecializeTreesToMissingGlobals(cx, globalObj, treeInfo); + return true; +} + +/* Write back a value onto the stack or global frames. */ +LIns* +TraceRecorder::writeBack(LIns* i, LIns* base, ptrdiff_t offset, bool demote) +{ + /* + * Sink all type casts targeting the stack into the side exit by simply storing the original + * (uncasted) value. Each guard generates the side exit map based on the types of the + * last stores to every stack location, so it's safe to not perform them on-trace. + */ + if (demote && isPromoteInt(i)) + i = ::demote(lir, i); + return lir->insStorei(i, base, offset); +} + +/* Update the tracker, then issue a write back store. */ +JS_REQUIRES_STACK void +TraceRecorder::set(jsval* p, LIns* i, bool initializing, bool demote) +{ + JS_ASSERT(i != NULL); + JS_ASSERT(initializing || known(p)); + checkForGlobalObjectReallocation(); + tracker.set(p, i); + + /* + * If we are writing to this location for the first time, calculate the + * offset into the native frame manually. Otherwise just look up the last + * load or store associated with the same source address (p) and use the + * same offset/base. + */ + LIns* x = nativeFrameTracker.get(p); + if (!x) { + if (isGlobal(p)) + x = writeBack(i, eos_ins, nativeGlobalOffset(p), demote); + else + x = writeBack(i, lirbuf->sp, nativespOffset(p), demote); + nativeFrameTracker.set(p, x); + } else { + JS_ASSERT(x->isop(LIR_sti) || x->isop(LIR_stqi)); + + int disp; + LIns *base = x->oprnd2(); +#ifdef NANOJIT_ARM + if (base->isop(LIR_piadd)) { + disp = base->oprnd2()->imm32(); + base = base->oprnd1(); + } else +#endif + disp = x->disp(); + + JS_ASSERT(base == lirbuf->sp || base == eos_ins); + JS_ASSERT(disp == ((base == lirbuf->sp) + ? nativespOffset(p) + : nativeGlobalOffset(p))); + + writeBack(i, base, disp, demote); + } +} + +JS_REQUIRES_STACK LIns* +TraceRecorder::get(jsval* p) +{ + JS_ASSERT(known(p)); + checkForGlobalObjectReallocation(); + return tracker.get(p); +} + +JS_REQUIRES_STACK LIns* +TraceRecorder::addr(jsval* p) +{ + return isGlobal(p) + ? lir->ins2(LIR_piadd, eos_ins, INS_CONSTWORD(nativeGlobalOffset(p))) + : lir->ins2(LIR_piadd, lirbuf->sp, + INS_CONSTWORD(nativespOffset(p))); +} + +JS_REQUIRES_STACK bool +TraceRecorder::known(jsval* p) +{ + checkForGlobalObjectReallocation(); + return tracker.has(p); +} + +/* + * The dslots of the global object are sometimes reallocated by the interpreter. + * This function check for that condition and re-maps the entries of the tracker + * accordingly. + */ +JS_REQUIRES_STACK void +TraceRecorder::checkForGlobalObjectReallocation() +{ + if (global_dslots != globalObj->dslots) { + debug_only_print0(LC_TMTracer, + "globalObj->dslots relocated, updating tracker\n"); + jsval* src = global_dslots; + jsval* dst = globalObj->dslots; + jsuint length = globalObj->dslots[-1] - JS_INITIAL_NSLOTS; + LIns** map = (LIns**)alloca(sizeof(LIns*) * length); + for (jsuint n = 0; n < length; ++n) { + map[n] = tracker.get(src); + tracker.set(src++, NULL); + } + for (jsuint n = 0; n < length; ++n) + tracker.set(dst++, map[n]); + global_dslots = globalObj->dslots; + } +} + +/* Determine whether the current branch is a loop edge (taken or not taken). */ +static JS_REQUIRES_STACK bool +IsLoopEdge(jsbytecode* pc, jsbytecode* header) +{ + switch (*pc) { + case JSOP_IFEQ: + case JSOP_IFNE: + return ((pc + GET_JUMP_OFFSET(pc)) == header); + case JSOP_IFEQX: + case JSOP_IFNEX: + return ((pc + GET_JUMPX_OFFSET(pc)) == header); + default: + JS_ASSERT((*pc == JSOP_AND) || (*pc == JSOP_ANDX) || + (*pc == JSOP_OR) || (*pc == JSOP_ORX)); + } + return false; +} + +class AdjustCallerGlobalTypesVisitor : public SlotVisitorBase +{ + TraceRecorder &mRecorder; + JSContext *mCx; + nanojit::LirBuffer *mLirbuf; + nanojit::LirWriter *mLir; + JSTraceType *mTypeMap; +public: + AdjustCallerGlobalTypesVisitor(TraceRecorder &recorder, + JSTraceType *typeMap) : + mRecorder(recorder), + mCx(mRecorder.cx), + mLirbuf(mRecorder.lirbuf), + mLir(mRecorder.lir), + mTypeMap(typeMap) + {} + + JSTraceType* getTypeMap() + { + return mTypeMap; + } + + JS_REQUIRES_STACK JS_ALWAYS_INLINE void + visitGlobalSlot(jsval *vp, unsigned n, unsigned slot) { + LIns *ins = mRecorder.get(vp); + bool isPromote = isPromoteInt(ins); + if (isPromote && *mTypeMap == TT_DOUBLE) { + mLir->insStorei(mRecorder.get(vp), mRecorder.eos_ins, + mRecorder.nativeGlobalOffset(vp)); + + /* + * Aggressively undo speculation so the inner tree will compile + * if this fails. + */ + oracle.markGlobalSlotUndemotable(mCx, slot); + } + JS_ASSERT(!(!isPromote && *mTypeMap == TT_INT32)); + ++mTypeMap; + } +}; + +class AdjustCallerStackTypesVisitor : public SlotVisitorBase +{ + TraceRecorder &mRecorder; + JSContext *mCx; + nanojit::LirBuffer *mLirbuf; + nanojit::LirWriter *mLir; + unsigned mSlotnum; + JSTraceType *mTypeMap; +public: + AdjustCallerStackTypesVisitor(TraceRecorder &recorder, + JSTraceType *typeMap) : + mRecorder(recorder), + mCx(mRecorder.cx), + mLirbuf(mRecorder.lirbuf), + mLir(mRecorder.lir), + mSlotnum(0), + mTypeMap(typeMap) + {} + + JSTraceType* getTypeMap() + { + return mTypeMap; + } + + JS_REQUIRES_STACK JS_ALWAYS_INLINE bool + visitStackSlots(jsval *vp, size_t count, JSStackFrame* fp) { + for (size_t i = 0; i < count; ++i) { + LIns *ins = mRecorder.get(vp); + bool isPromote = isPromoteInt(ins); + if (isPromote && *mTypeMap == TT_DOUBLE) { + mLir->insStorei(mRecorder.get(vp), mLirbuf->sp, + mRecorder.nativespOffset(vp)); + + /* + * Aggressively undo speculation so the inner tree will compile + * if this fails. + */ + oracle.markStackSlotUndemotable(mCx, mSlotnum); + } + JS_ASSERT(!(!isPromote && *mTypeMap == TT_INT32)); + ++vp; + ++mTypeMap; + ++mSlotnum; + } + return true; + } +}; + +/* + * Promote slots if necessary to match the called tree's type map. This + * function is infallible and must only be called if we are certain that it is + * possible to reconcile the types for each slot in the inner and outer trees. + */ +JS_REQUIRES_STACK void +TraceRecorder::adjustCallerTypes(TreeFragment* f) +{ + TreeInfo* ti = f->treeInfo; + + AdjustCallerGlobalTypesVisitor globalVisitor(*this, ti->globalTypeMap()); + VisitGlobalSlots(globalVisitor, cx, *treeInfo->globalSlots); + + AdjustCallerStackTypesVisitor stackVisitor(*this, ti->stackTypeMap()); + VisitStackSlots(stackVisitor, cx, 0); + + JS_ASSERT(f == f->root); +} + +JS_REQUIRES_STACK JSTraceType +TraceRecorder::determineSlotType(jsval* vp) +{ + JSTraceType m; + LIns* i = get(vp); + if (isNumber(*vp)) { + m = isPromoteInt(i) ? TT_INT32 : TT_DOUBLE; + } else if (JSVAL_IS_OBJECT(*vp)) { + if (JSVAL_IS_NULL(*vp)) + m = TT_NULL; + else if (HAS_FUNCTION_CLASS(JSVAL_TO_OBJECT(*vp))) + m = TT_FUNCTION; + else + m = TT_OBJECT; + } else { + JS_ASSERT(JSVAL_TAG(*vp) == JSVAL_STRING || JSVAL_IS_SPECIAL(*vp)); + JS_STATIC_ASSERT(static_cast(TT_STRING) == JSVAL_STRING); + JS_STATIC_ASSERT(static_cast(TT_PSEUDOBOOLEAN) == JSVAL_SPECIAL); + m = JSTraceType(JSVAL_TAG(*vp)); + } + JS_ASSERT(m != TT_INT32 || isInt32(*vp)); + return m; +} + +class DetermineTypesVisitor : public SlotVisitorBase +{ + TraceRecorder &mRecorder; + JSTraceType *mTypeMap; +public: + DetermineTypesVisitor(TraceRecorder &recorder, + JSTraceType *typeMap) : + mRecorder(recorder), + mTypeMap(typeMap) + {} + + JS_REQUIRES_STACK JS_ALWAYS_INLINE void + visitGlobalSlot(jsval *vp, unsigned n, unsigned slot) { + *mTypeMap++ = mRecorder.determineSlotType(vp); + } + + JS_REQUIRES_STACK JS_ALWAYS_INLINE bool + visitStackSlots(jsval *vp, size_t count, JSStackFrame* fp) { + for (size_t i = 0; i < count; ++i) + *mTypeMap++ = mRecorder.determineSlotType(vp++); + return true; + } + + JSTraceType* getTypeMap() + { + return mTypeMap; + } +}; + +#if defined JS_JIT_SPEW +JS_REQUIRES_STACK static void +TreevisLogExit(JSContext* cx, VMSideExit* exit) +{ + debug_only_printf(LC_TMTreeVis, "TREEVIS ADDEXIT EXIT=%p TYPE=%s FRAG=%p PC=%p FILE=\"%s\"" + " LINE=%d OFFS=%d", (void*)exit, getExitName(exit->exitType), + (void*)exit->from, (void*)cx->fp->regs->pc, cx->fp->script->filename, + js_FramePCToLineNumber(cx, cx->fp), FramePCOffset(cx->fp)); + debug_only_print0(LC_TMTreeVis, " STACK=\""); + for (unsigned i = 0; i < exit->numStackSlots; i++) + debug_only_printf(LC_TMTreeVis, "%c", typeChar[exit->stackTypeMap()[i]]); + debug_only_print0(LC_TMTreeVis, "\" GLOBALS=\""); + for (unsigned i = 0; i < exit->numGlobalSlots; i++) + debug_only_printf(LC_TMTreeVis, "%c", typeChar[exit->globalTypeMap()[i]]); + debug_only_print0(LC_TMTreeVis, "\"\n"); +} +#endif + +JS_REQUIRES_STACK VMSideExit* +TraceRecorder::snapshot(ExitType exitType) +{ + JSStackFrame* fp = cx->fp; + JSFrameRegs* regs = fp->regs; + jsbytecode* pc = regs->pc; + + /* + * Check for a return-value opcode that needs to restart at the next + * instruction. + */ + const JSCodeSpec& cs = js_CodeSpec[*pc]; + + /* + * When calling a _FAIL native, make the snapshot's pc point to the next + * instruction after the CALL or APPLY. Even on failure, a _FAIL native + * must not be called again from the interpreter. + */ + bool resumeAfter = (pendingSpecializedNative && + JSTN_ERRTYPE(pendingSpecializedNative) == FAIL_STATUS); + if (resumeAfter) { + JS_ASSERT(*pc == JSOP_CALL || *pc == JSOP_APPLY || *pc == JSOP_NEW || + *pc == JSOP_SETPROP || *pc == JSOP_SETNAME); + pc += cs.length; + regs->pc = pc; + MUST_FLOW_THROUGH("restore_pc"); + } + + /* + * Generate the entry map for the (possibly advanced) pc and stash it in + * the trace. + */ + unsigned stackSlots = NativeStackSlots(cx, callDepth); + + /* + * It's sufficient to track the native stack use here since all stores + * above the stack watermark defined by guards are killed. + */ + trackNativeStackUse(stackSlots + 1); + + /* Capture the type map into a temporary location. */ + unsigned ngslots = treeInfo->globalSlots->length(); + unsigned typemap_size = (stackSlots + ngslots) * sizeof(JSTraceType); + + /* Use the recorder-local temporary type map. */ + JSTraceType* typemap = NULL; + if (tempTypeMap.resize(typemap_size)) + typemap = tempTypeMap.begin(); /* crash if resize() fails. */ + + /* + * Determine the type of a store by looking at the current type of the + * actual value the interpreter is using. For numbers we have to check what + * kind of store we used last (integer or double) to figure out what the + * side exit show reflect in its typemap. + */ + DetermineTypesVisitor detVisitor(*this, typemap); + VisitSlots(detVisitor, cx, callDepth, ngslots, + treeInfo->globalSlots->data()); + JS_ASSERT(unsigned(detVisitor.getTypeMap() - typemap) == + ngslots + stackSlots); + + /* + * If this snapshot is for a side exit that leaves a boxed jsval result on + * the stack, make a note of this in the typemap. Examples include the + * builtinStatus guard after calling a _FAIL builtin, a JSFastNative, or + * GetPropertyByName; and the type guard in unbox_jsval after such a call + * (also at the beginning of a trace branched from such a type guard). + */ + if (pendingUnboxSlot || + (pendingSpecializedNative && (pendingSpecializedNative->flags & JSTN_UNBOX_AFTER))) { + unsigned pos = stackSlots - 1; + if (pendingUnboxSlot == cx->fp->regs->sp - 2) + pos = stackSlots - 2; + typemap[pos] = TT_JSVAL; + } + + /* Now restore the the original pc (after which early returns are ok). */ + if (resumeAfter) { + MUST_FLOW_LABEL(restore_pc); + regs->pc = pc - cs.length; + } else { + /* + * If we take a snapshot on a goto, advance to the target address. This + * avoids inner trees returning on a break goto, which the outer + * recorder then would confuse with a break in the outer tree. + */ + if (*pc == JSOP_GOTO) + pc += GET_JUMP_OFFSET(pc); + else if (*pc == JSOP_GOTOX) + pc += GET_JUMPX_OFFSET(pc); + } + + /* + * Check if we already have a matching side exit; if so we can return that + * side exit instead of creating a new one. + */ + VMSideExit** exits = treeInfo->sideExits.data(); + unsigned nexits = treeInfo->sideExits.length(); + if (exitType == LOOP_EXIT) { + for (unsigned n = 0; n < nexits; ++n) { + VMSideExit* e = exits[n]; + if (e->pc == pc && e->imacpc == fp->imacpc && + ngslots == e->numGlobalSlots && + !memcmp(exits[n]->fullTypeMap(), typemap, typemap_size)) { + AUDIT(mergedLoopExits); +#if defined JS_JIT_SPEW + TreevisLogExit(cx, e); +#endif + return e; + } + } + } + + /* We couldn't find a matching side exit, so create a new one. */ + VMSideExit* exit = (VMSideExit*) + traceAlloc().alloc(sizeof(VMSideExit) + (stackSlots + ngslots) * sizeof(JSTraceType)); + + /* Setup side exit structure. */ + exit->from = fragment; + exit->calldepth = callDepth; + exit->numGlobalSlots = ngslots; + exit->numStackSlots = stackSlots; + exit->numStackSlotsBelowCurrentFrame = cx->fp->argv ? + nativeStackOffset(&cx->fp->argv[-2]) / sizeof(double) : + 0; + exit->exitType = exitType; + exit->block = fp->blockChain; + if (fp->blockChain) + treeInfo->gcthings.addUnique(OBJECT_TO_JSVAL(fp->blockChain)); + exit->pc = pc; + exit->imacpc = fp->imacpc; + exit->sp_adj = (stackSlots * sizeof(double)) - treeInfo->nativeStackBase; + exit->rp_adj = exit->calldepth * sizeof(FrameInfo*); + exit->nativeCalleeWord = 0; + exit->lookupFlags = js_InferFlags(cx, 0); + memcpy(exit->fullTypeMap(), typemap, typemap_size); + +#if defined JS_JIT_SPEW + TreevisLogExit(cx, exit); +#endif + return exit; +} + +JS_REQUIRES_STACK GuardRecord* +TraceRecorder::createGuardRecord(VMSideExit* exit) +{ + GuardRecord* gr = new (traceAlloc()) GuardRecord(); + + gr->exit = exit; + exit->addGuard(gr); + + // gr->profCount is calloc'd to zero + verbose_only( + gr->profGuardID = fragment->guardNumberer++; + gr->nextInFrag = fragment->guardsForFrag; + fragment->guardsForFrag = gr; + ) + + return gr; +} + +/* + * Emit a guard for condition (cond), expecting to evaluate to boolean result + * (expected) and using the supplied side exit if the conditon doesn't hold. + */ +JS_REQUIRES_STACK void +TraceRecorder::guard(bool expected, LIns* cond, VMSideExit* exit) +{ + debug_only_printf(LC_TMRecorder, + " About to try emitting guard code for " + "SideExit=%p exitType=%s\n", + (void*)exit, getExitName(exit->exitType)); + + GuardRecord* guardRec = createGuardRecord(exit); + + if (exit->exitType == LOOP_EXIT) + treeInfo->sideExits.add(exit); + + if (!cond->isCond()) { + expected = !expected; + cond = cond->isQuad() ? lir->ins_peq0(cond) : lir->ins_eq0(cond); + } + + LIns* guardIns = + lir->insGuard(expected ? LIR_xf : LIR_xt, cond, guardRec); + if (!guardIns) { + debug_only_print0(LC_TMRecorder, + " redundant guard, eliminated, no codegen\n"); + } +} + +JS_REQUIRES_STACK VMSideExit* +TraceRecorder::copy(VMSideExit* copy) +{ + size_t typemap_size = copy->numGlobalSlots + copy->numStackSlots; + VMSideExit* exit = (VMSideExit*) + traceAlloc().alloc(sizeof(VMSideExit) + typemap_size * sizeof(JSTraceType)); + + /* Copy side exit structure. */ + memcpy(exit, copy, sizeof(VMSideExit) + typemap_size * sizeof(JSTraceType)); + exit->guards = NULL; + exit->from = fragment; + exit->target = NULL; + + if (exit->exitType == LOOP_EXIT) + treeInfo->sideExits.add(exit); +#if defined JS_JIT_SPEW + TreevisLogExit(cx, exit); +#endif + return exit; +} + +/* + * Emit a guard for condition (cond), expecting to evaluate to boolean result + * (expected) and generate a side exit with type exitType to jump to if the + * condition does not hold. + */ +JS_REQUIRES_STACK void +TraceRecorder::guard(bool expected, LIns* cond, ExitType exitType) +{ + guard(expected, cond, snapshot(exitType)); +} + +/* + * Determine whether any context associated with the same thread as cx is + * executing native code. + */ +static inline bool +ProhibitFlush(JSContext* cx) +{ + if (cx->interpState) // early out if the given is in native code + return true; + + JSCList *cl; + +#ifdef JS_THREADSAFE + JSThread* thread = cx->thread; + for (cl = thread->contextList.next; cl != &thread->contextList; cl = cl->next) + if (CX_FROM_THREAD_LINKS(cl)->interpState) + return true; +#else + JSRuntime* rt = cx->runtime; + for (cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next) + if (js_ContextFromLinkField(cl)->interpState) + return true; +#endif + return false; +} + +static JS_REQUIRES_STACK void +ResetJITImpl(JSContext* cx) +{ + if (!TRACING_ENABLED(cx)) + return; + JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx); + debug_only_print0(LC_TMTracer, "Flushing cache.\n"); + if (tm->recorder) + js_AbortRecording(cx, "flush cache"); + if (ProhibitFlush(cx)) { + debug_only_print0(LC_TMTracer, "Deferring JIT flush due to deep bail.\n"); + tm->needFlush = JS_TRUE; + return; + } + tm->flush(); +} + +JS_REQUIRES_STACK void +js_ResetJIT(JSContext* cx) +{ + ResetJIT(cx, FR_OOM); +} + +/* Compile the current fragment. */ +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::compile() +{ +#ifdef MOZ_TRACEVIS + TraceVisStateObj tvso(cx, S_COMPILE); +#endif + + if (traceMonitor->needFlush) { + ResetJIT(cx, FR_DEEP_BAIL); + return ARECORD_ABORTED; + } + if (treeInfo->maxNativeStackSlots >= MAX_NATIVE_STACK_SLOTS) { + debug_only_print0(LC_TMTracer, "Blacklist: excessive stack use.\n"); + Blacklist((jsbytecode*) fragment->root->ip); + return ARECORD_STOP; + } + if (anchor && anchor->exitType != CASE_EXIT) + ++fragment->root->branchCount; + if (outOfMemory()) + return ARECORD_STOP; + + Assembler *assm = traceMonitor->assembler; + JS_ASSERT(assm->error() == nanojit::None); + nanojit::compile(assm, fragment, tempAlloc() verbose_only(, traceMonitor->labels)); + + if (assm->error() != nanojit::None) { + assm->setError(nanojit::None); + debug_only_print0(LC_TMTracer, "Blacklisted: error during compilation\n"); + Blacklist((jsbytecode*) fragment->root->ip); + return ARECORD_STOP; + } + + if (outOfMemory()) + return ARECORD_STOP; + ResetRecordingAttempts(cx, (jsbytecode*) fragment->ip); + ResetRecordingAttempts(cx, (jsbytecode*) fragment->root->ip); + if (anchor) { +#ifdef NANOJIT_IA32 + if (anchor->exitType == CASE_EXIT) + assm->patch(anchor, anchor->switchInfo); + else +#endif + assm->patch(anchor); + } + JS_ASSERT(fragment->code()); + JS_ASSERT_IF(fragment == fragment->root, !fragment->root->treeInfo); + if (fragment == fragment->root) + fragment->root->treeInfo = treeInfo; + + /* :TODO: windows support */ +#if defined DEBUG && !defined WIN32 + const char* filename = cx->fp->script->filename; + char* label = (char*)js_malloc((filename ? strlen(filename) : 7) + 16); + sprintf(label, "%s:%u", filename ? filename : "", + js_FramePCToLineNumber(cx, cx->fp)); + traceMonitor->labels->add(fragment, sizeof(Fragment), 0, label); + js_free(label); +#endif + return ARECORD_CONTINUE; +} + +static void +JoinPeers(Assembler* assm, VMSideExit* exit, TreeFragment* target) +{ + exit->target = target; + assm->patch(exit); + + debug_only_printf(LC_TMTreeVis, "TREEVIS JOIN ANCHOR=%p FRAG=%p\n", (void*)exit, (void*)target); + + if (exit->root() == target) + return; + + target->treeInfo->dependentTrees.addUnique(exit->root()); + exit->root()->treeInfo->linkedTrees.addUnique(target); +} + +/* Results of trying to connect an arbitrary type A with arbitrary type B */ +enum TypeCheckResult +{ + TypeCheck_Okay, /* Okay: same type */ + TypeCheck_Promote, /* Okay: Type A needs f2i() */ + TypeCheck_Demote, /* Okay: Type A needs i2f() */ + TypeCheck_Undemote, /* Bad: Slot is undemotable */ + TypeCheck_Bad /* Bad: incompatible types */ +}; + +class SlotMap : public SlotVisitorBase +{ + public: + struct SlotInfo + { + SlotInfo() + : vp(NULL), promoteInt(false), lastCheck(TypeCheck_Bad) + {} + SlotInfo(jsval* vp, bool promoteInt) + : vp(vp), promoteInt(promoteInt), lastCheck(TypeCheck_Bad), type(getCoercedType(*vp)) + {} + SlotInfo(jsval* vp, JSTraceType t) + : vp(vp), promoteInt(t == TT_INT32), lastCheck(TypeCheck_Bad), type(t) + {} + jsval *vp; + bool promoteInt; + TypeCheckResult lastCheck; + JSTraceType type; + }; + + SlotMap(TraceRecorder& rec) + : mRecorder(rec), + mCx(rec.cx), + slots(NULL) + { + } + + JS_REQUIRES_STACK JS_ALWAYS_INLINE void + visitGlobalSlot(jsval *vp, unsigned n, unsigned slot) + { + addSlot(vp); + } + + JS_ALWAYS_INLINE SlotMap::SlotInfo& + operator [](unsigned i) + { + return slots[i]; + } + + JS_ALWAYS_INLINE SlotMap::SlotInfo& + get(unsigned i) + { + return slots[i]; + } + + JS_ALWAYS_INLINE unsigned + length() + { + return slots.length(); + } + + /** + * Possible return states: + * + * TypeConsensus_Okay: All types are compatible. Caller must go through slot list and handle + * promote/demotes. + * TypeConsensus_Bad: Types are not compatible. Individual type check results are undefined. + * TypeConsensus_Undemotes: Types would be compatible if slots were marked as undemotable + * before recording began. Caller can go through slot list and mark + * such slots as undemotable. + */ + JS_REQUIRES_STACK TypeConsensus + checkTypes(TreeInfo* ti) + { + if (length() != ti->typeMap.length()) + return TypeConsensus_Bad; + + bool has_undemotes = false; + for (unsigned i = 0; i < length(); i++) { + TypeCheckResult result = checkType(i, ti->typeMap[i]); + if (result == TypeCheck_Bad) + return TypeConsensus_Bad; + if (result == TypeCheck_Undemote) + has_undemotes = true; + slots[i].lastCheck = result; + } + if (has_undemotes) + return TypeConsensus_Undemotes; + return TypeConsensus_Okay; + } + + JS_REQUIRES_STACK JS_ALWAYS_INLINE void + addSlot(jsval* vp) + { + slots.add(SlotInfo(vp, isPromoteInt(mRecorder.get(vp)))); + } + + JS_REQUIRES_STACK JS_ALWAYS_INLINE void + addSlot(JSTraceType t) + { + slots.add(SlotInfo(NULL, t)); + } + + JS_REQUIRES_STACK JS_ALWAYS_INLINE void + addSlot(jsval *vp, JSTraceType t) + { + slots.add(SlotInfo(vp, t)); + } + + JS_REQUIRES_STACK void + markUndemotes() + { + for (unsigned i = 0; i < length(); i++) { + if (get(i).lastCheck == TypeCheck_Undemote) + MarkSlotUndemotable(mRecorder.cx, mRecorder.treeInfo, i); + } + } + + JS_REQUIRES_STACK virtual void + adjustTail(TypeConsensus consensus) + { + } + + JS_REQUIRES_STACK virtual void + adjustTypes() + { + for (unsigned i = 0; i < length(); i++) + adjustType(get(i)); + } + + protected: + JS_REQUIRES_STACK virtual void + adjustType(SlotInfo& info) { + JS_ASSERT(info.lastCheck != TypeCheck_Undemote && info.lastCheck != TypeCheck_Bad); + if (info.lastCheck == TypeCheck_Promote) { + JS_ASSERT(info.type == TT_INT32 || info.type == TT_DOUBLE); + mRecorder.set(info.vp, mRecorder.f2i(mRecorder.get(info.vp))); + } else if (info.lastCheck == TypeCheck_Demote) { + JS_ASSERT(info.type == TT_INT32 || info.type == TT_DOUBLE); + JS_ASSERT(mRecorder.get(info.vp)->isQuad()); + + /* Never demote this final i2f. */ + mRecorder.set(info.vp, mRecorder.get(info.vp), false, false); + } + } + + private: + TypeCheckResult + checkType(unsigned i, JSTraceType t) + { + debug_only_printf(LC_TMTracer, + "checkType slot %d: interp=%c typemap=%c isNum=%d promoteInt=%d\n", + i, + typeChar[slots[i].type], + typeChar[t], + slots[i].type == TT_INT32 || slots[i].type == TT_DOUBLE, + slots[i].promoteInt); + switch (t) { + case TT_INT32: + if (slots[i].type != TT_INT32 && slots[i].type != TT_DOUBLE) + return TypeCheck_Bad; /* Not a number? Type mismatch. */ + /* This is always a type mismatch, we can't close a double to an int. */ + if (!slots[i].promoteInt) + return TypeCheck_Undemote; + /* Looks good, slot is an int32, the last instruction should be promotable. */ + JS_ASSERT_IF(slots[i].vp, isInt32(*slots[i].vp) && slots[i].promoteInt); + return slots[i].vp ? TypeCheck_Promote : TypeCheck_Okay; + case TT_DOUBLE: + if (slots[i].type != TT_INT32 && slots[i].type != TT_DOUBLE) + return TypeCheck_Bad; /* Not a number? Type mismatch. */ + if (slots[i].promoteInt) + return slots[i].vp ? TypeCheck_Demote : TypeCheck_Bad; + return TypeCheck_Okay; + default: + return slots[i].type == t ? TypeCheck_Okay : TypeCheck_Bad; + } + JS_NOT_REACHED("shouldn't fall through type check switch"); + } + protected: + TraceRecorder& mRecorder; + JSContext* mCx; + Queue slots; +}; + +class DefaultSlotMap : public SlotMap +{ + public: + DefaultSlotMap(TraceRecorder& tr) : SlotMap(tr) + { + } + + JS_REQUIRES_STACK JS_ALWAYS_INLINE bool + visitStackSlots(jsval *vp, size_t count, JSStackFrame* fp) + { + for (size_t i = 0; i < count; i++) + addSlot(&vp[i]); + return true; + } +}; + +JS_REQUIRES_STACK TypeConsensus +TraceRecorder::selfTypeStability(SlotMap& slotMap) +{ + debug_only_printf(LC_TMTracer, "Checking type stability against self=%p\n", (void*)fragment); + TypeConsensus consensus = slotMap.checkTypes(treeInfo); + + /* Best case: loop jumps back to its own header */ + if (consensus == TypeConsensus_Okay) + return TypeConsensus_Okay; + + /* If the only thing keeping this loop from being stable is undemotions, then mark relevant + * slots as undemotable. + */ + if (consensus == TypeConsensus_Undemotes) + slotMap.markUndemotes(); + + return consensus; +} + +JS_REQUIRES_STACK TypeConsensus +TraceRecorder::peerTypeStability(SlotMap& slotMap, const void* ip, TreeFragment** pPeer) +{ + /* See if there are any peers that would make this stable */ + TreeFragment* root = fragment->root; + TreeFragment* peer = LookupLoop(traceMonitor, ip, root->globalObj, root->globalShape, root->argc); + + /* This condition is possible with recursion */ + JS_ASSERT_IF(!peer, fragment->root->ip != ip); + if (!peer) + return TypeConsensus_Bad; + bool onlyUndemotes = false; + for (; peer != NULL; peer = peer->peer) { + if (!peer->treeInfo || peer == fragment) + continue; + debug_only_printf(LC_TMTracer, "Checking type stability against peer=%p\n", (void*)peer); + TypeConsensus consensus = slotMap.checkTypes(peer->treeInfo); + if (consensus == TypeConsensus_Okay) { + *pPeer = peer; + /* Return this even though there will be linkage; the trace itself is not stable. + * Caller should inspect ppeer to check for a compatible peer. + */ + return TypeConsensus_Okay; + } + if (consensus == TypeConsensus_Undemotes) + onlyUndemotes = true; + } + + return onlyUndemotes ? TypeConsensus_Undemotes : TypeConsensus_Bad; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::closeLoop() +{ + return closeLoop(snapshot(UNSTABLE_LOOP_EXIT)); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::closeLoop(VMSideExit* exit) +{ + DefaultSlotMap slotMap(*this); + VisitSlots(slotMap, cx, 0, *treeInfo->globalSlots); + return closeLoop(slotMap, exit); +} + +/* + * Complete and compile a trace and link it to the existing tree if + * appropriate. Returns ARECORD_ABORTED or ARECORD_STOP, depending on whether + * the recorder was deleted. Outparam is always set. + */ +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::closeLoop(SlotMap& slotMap, VMSideExit* exit) +{ + /* + * We should have arrived back at the loop header, and hence we don't want + * to be in an imacro here and the opcode should be either JSOP_TRACE or, in + * case this loop was blacklisted in the meantime, JSOP_NOP. + */ + JS_ASSERT((*cx->fp->regs->pc == JSOP_TRACE || *cx->fp->regs->pc == JSOP_NOP || + *cx->fp->regs->pc == JSOP_RETURN) && !cx->fp->imacpc); + + if (callDepth != 0) { + debug_only_print0(LC_TMTracer, + "Blacklisted: stack depth mismatch, possible recursion.\n"); + Blacklist((jsbytecode*) fragment->root->ip); + trashSelf = true; + return ARECORD_STOP; + } + + JS_ASSERT_IF(exit->exitType == UNSTABLE_LOOP_EXIT, + exit->numStackSlots == treeInfo->nStackTypes); + JS_ASSERT_IF(exit->exitType != UNSTABLE_LOOP_EXIT, exit->exitType == RECURSIVE_UNLINKED_EXIT); + JS_ASSERT_IF(exit->exitType == RECURSIVE_UNLINKED_EXIT, + exit->recursive_pc != fragment->root->ip); + + TreeFragment* peer = NULL; + TreeFragment* root = fragment->root; + + TypeConsensus consensus = TypeConsensus_Bad; + + if (exit->exitType == UNSTABLE_LOOP_EXIT) + consensus = selfTypeStability(slotMap); + if (consensus != TypeConsensus_Okay) { + const void* ip = exit->exitType == RECURSIVE_UNLINKED_EXIT ? + exit->recursive_pc : fragment->root->ip; + TypeConsensus peerConsensus = peerTypeStability(slotMap, ip, &peer); + /* If there was a semblance of a stable peer (even if not linkable), keep the result. */ + if (peerConsensus != TypeConsensus_Bad) + consensus = peerConsensus; + } + +#if DEBUG + if (consensus != TypeConsensus_Okay || peer) + AUDIT(unstableLoopVariable); +#endif + + JS_ASSERT(!trashSelf); + + /* + * This exit is indeed linkable to something now. Process any promote or + * demotes that are pending in the slot map. + */ + if (consensus == TypeConsensus_Okay) + slotMap.adjustTypes(); + + /* Give up-recursion a chance to pop the stack frame. */ + slotMap.adjustTail(consensus); + + if (consensus != TypeConsensus_Okay || peer) { + fragment->lastIns = lir->insGuard(LIR_x, NULL, createGuardRecord(exit)); + + /* If there is a peer, there must have been an "Okay" consensus. */ + JS_ASSERT_IF(peer, consensus == TypeConsensus_Okay); + + /* Compile as a type-unstable loop, and hope for a connection later. */ + if (!peer) { + /* + * If such a fragment does not exist, let's compile the loop ahead + * of time anyway. Later, if the loop becomes type stable, we will + * connect these two fragments together. + */ + debug_only_print0(LC_TMTracer, + "Trace has unstable loop variable with no stable peer, " + "compiling anyway.\n"); + UnstableExit* uexit = new (traceAlloc()) UnstableExit; + uexit->fragment = fragment; + uexit->exit = exit; + uexit->next = treeInfo->unstableExits; + treeInfo->unstableExits = uexit; + } else { + JS_ASSERT(peer->code()); + exit->target = peer; + debug_only_printf(LC_TMTracer, + "Joining type-unstable trace to target fragment %p.\n", + (void*)peer); + peer->treeInfo->dependentTrees.addUnique(fragment->root); + treeInfo->linkedTrees.addUnique(peer); + } + } else { + exit->exitType = LOOP_EXIT; + debug_only_printf(LC_TMTreeVis, "TREEVIS CHANGEEXIT EXIT=%p TYPE=%s\n", (void*)exit, + getExitName(LOOP_EXIT)); + + JS_ASSERT((fragment == fragment->root) == !!loopLabel); + if (loopLabel) { + lir->insBranch(LIR_j, NULL, loopLabel); + lir->ins1(LIR_live, lirbuf->state); + } + + exit->target = fragment->root; + fragment->lastIns = lir->insGuard(LIR_x, NULL, createGuardRecord(exit)); + } + + CHECK_STATUS_A(compile()); + + debug_only_printf(LC_TMTreeVis, "TREEVIS CLOSELOOP EXIT=%p PEER=%p\n", (void*)exit, (void*)peer); + + peer = LookupLoop(traceMonitor, root->ip, root->globalObj, root->globalShape, root->argc); + JS_ASSERT(peer); + joinEdgesToEntry(peer); + + debug_only_stmt(DumpPeerStability(traceMonitor, peer->ip, peer->globalObj, + peer->globalShape, peer->argc);) + + debug_only_print0(LC_TMTracer, + "updating specializations on dependent and linked trees\n"); + if (fragment->root->treeInfo) + SpecializeTreesToMissingGlobals(cx, globalObj, fragment->root->treeInfo); + + /* + * If this is a newly formed tree, and the outer tree has not been compiled yet, we + * should try to compile the outer tree again. + */ + if (outer) + AttemptCompilation(cx, traceMonitor, globalObj, outer, outerArgc); +#ifdef JS_JIT_SPEW + debug_only_printf(LC_TMMinimal, + "Recording completed at %s:%u@%u via closeLoop (FragID=%06u)\n", + cx->fp->script->filename, + js_FramePCToLineNumber(cx, cx->fp), + FramePCOffset(cx->fp), + fragment->profFragID); + debug_only_print0(LC_TMMinimal, "\n"); +#endif + + return finishSuccessfully(); +} + +static void +FullMapFromExit(TypeMap& typeMap, VMSideExit* exit) +{ + typeMap.setLength(0); + typeMap.fromRaw(exit->stackTypeMap(), exit->numStackSlots); + typeMap.fromRaw(exit->globalTypeMap(), exit->numGlobalSlots); + /* Include globals that were later specialized at the root of the tree. */ + if (exit->numGlobalSlots < exit->root()->treeInfo->nGlobalTypes()) { + typeMap.fromRaw(exit->root()->treeInfo->globalTypeMap() + exit->numGlobalSlots, + exit->root()->treeInfo->nGlobalTypes() - exit->numGlobalSlots); + } +} + +static JS_REQUIRES_STACK TypeConsensus +TypeMapLinkability(JSContext* cx, const TypeMap& typeMap, TreeFragment* peer) +{ + const TypeMap& peerMap = peer->treeInfo->typeMap; + unsigned minSlots = JS_MIN(typeMap.length(), peerMap.length()); + TypeConsensus consensus = TypeConsensus_Okay; + for (unsigned i = 0; i < minSlots; i++) { + if (typeMap[i] == peerMap[i]) + continue; + if (typeMap[i] == TT_INT32 && peerMap[i] == TT_DOUBLE && + IsSlotUndemotable(cx, peer->treeInfo, i, peer->ip)) { + consensus = TypeConsensus_Undemotes; + } else { + return TypeConsensus_Bad; + } + } + return consensus; +} + +static JS_REQUIRES_STACK unsigned +FindUndemotesInTypemaps(JSContext* cx, const TypeMap& typeMap, TreeInfo* treeInfo, + Queue& undemotes) +{ + undemotes.setLength(0); + unsigned minSlots = JS_MIN(typeMap.length(), treeInfo->typeMap.length()); + for (unsigned i = 0; i < minSlots; i++) { + if (typeMap[i] == TT_INT32 && treeInfo->typeMap[i] == TT_DOUBLE) { + undemotes.add(i); + } else if (typeMap[i] != treeInfo->typeMap[i]) { + return 0; + } + } + for (unsigned i = 0; i < undemotes.length(); i++) + MarkSlotUndemotable(cx, treeInfo, undemotes[i]); + return undemotes.length(); +} + +JS_REQUIRES_STACK void +TraceRecorder::joinEdgesToEntry(TreeFragment* peer_root) +{ + if (fragment->root != fragment) + return; + + TypeMap typeMap(NULL); + Queue undemotes(NULL); + + for (TreeFragment* peer = peer_root; peer; peer = peer->peer) { + TreeInfo* ti = peer->treeInfo; + if (!ti) + continue; + UnstableExit* uexit = ti->unstableExits; + while (uexit != NULL) { + /* :TODO: these exits go somewhere else. */ + if (uexit->exit->exitType == RECURSIVE_UNLINKED_EXIT) { + uexit = uexit->next; + continue; + } + /* Build the full typemap for this unstable exit */ + FullMapFromExit(typeMap, uexit->exit); + /* Check its compatibility against this tree */ + TypeConsensus consensus = TypeMapLinkability(cx, typeMap, fragment->root); + JS_ASSERT_IF(consensus == TypeConsensus_Okay, peer != fragment); + if (consensus == TypeConsensus_Okay) { + debug_only_printf(LC_TMTracer, + "Joining type-stable trace to target exit %p->%p.\n", + (void*)uexit->fragment, (void*)uexit->exit); + /* It's okay! Link together and remove the unstable exit. */ + JoinPeers(traceMonitor->assembler, uexit->exit, (TreeFragment*)fragment); + uexit = ti->removeUnstableExit(uexit->exit); + } else { + /* Check for int32->double slots that suggest trashing. */ + if (FindUndemotesInTypemaps(cx, typeMap, treeInfo, undemotes)) { + JS_ASSERT(peer == uexit->fragment->root); + if (fragment == peer) + trashSelf = true; + else + whichTreesToTrash.addUnique(uexit->fragment->root); + return; + } + uexit = uexit->next; + } + } + } +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::endLoop() +{ + return endLoop(snapshot(LOOP_EXIT)); +} + +/* Emit an always-exit guard and compile the tree (used for break statements. */ +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::endLoop(VMSideExit* exit) +{ + if (callDepth != 0) { + debug_only_print0(LC_TMTracer, "Blacklisted: stack depth mismatch, possible recursion.\n"); + Blacklist((jsbytecode*) fragment->root->ip); + trashSelf = true; + return ARECORD_STOP; + } + + if (recordReason != Record_Branch) + RETURN_STOP_A("control flow should have been recursive"); + + fragment->lastIns = + lir->insGuard(LIR_x, NULL, createGuardRecord(exit)); + + CHECK_STATUS_A(compile()); + + debug_only_printf(LC_TMTreeVis, "TREEVIS ENDLOOP EXIT=%p\n", (void*)exit); + + TreeFragment* root = fragment->root; + joinEdgesToEntry(LookupLoop(traceMonitor, root->ip, root->globalObj, + root->globalShape, root->argc)); + debug_only_stmt(DumpPeerStability(traceMonitor, root->ip, root->globalObj, + root->globalShape, root->argc);) + + /* + * Note: this must always be done, in case we added new globals on trace + * and haven't yet propagated those to linked and dependent trees. + */ + debug_only_print0(LC_TMTracer, + "updating specializations on dependent and linked trees\n"); + if (fragment->root->treeInfo) + SpecializeTreesToMissingGlobals(cx, globalObj, fragment->root->treeInfo); + + /* + * If this is a newly formed tree, and the outer tree has not been compiled + * yet, we should try to compile the outer tree again. + */ + if (outer) + AttemptCompilation(cx, traceMonitor, globalObj, outer, outerArgc); +#ifdef JS_JIT_SPEW + debug_only_printf(LC_TMMinimal, + "Recording completed at %s:%u@%u via endLoop (FragID=%06u)\n", + cx->fp->script->filename, + js_FramePCToLineNumber(cx, cx->fp), + FramePCOffset(cx->fp), + fragment->profFragID); + debug_only_print0(LC_TMTracer, "\n"); +#endif + + return finishSuccessfully(); +} + +/* Emit code to adjust the stack to match the inner tree's stack expectations. */ +JS_REQUIRES_STACK void +TraceRecorder::prepareTreeCall(TreeFragment* inner, LIns*& inner_sp_ins) +{ + TreeInfo* ti = inner->treeInfo; + inner_sp_ins = lirbuf->sp; + VMSideExit* exit = snapshot(OOM_EXIT); + + /* + * The inner tree expects to be called from the current frame. If the outer + * tree (this trace) is currently inside a function inlining code + * (calldepth > 0), we have to advance the native stack pointer such that + * we match what the inner trace expects to see. We move it back when we + * come out of the inner tree call. + */ + if (callDepth > 0) { + /* + * Calculate the amount we have to lift the native stack pointer by to + * compensate for any outer frames that the inner tree doesn't expect + * but the outer tree has. + */ + ptrdiff_t sp_adj = nativeStackOffset(&cx->fp->argv[-2]); + + /* Calculate the amount we have to lift the call stack by. */ + ptrdiff_t rp_adj = callDepth * sizeof(FrameInfo*); + + /* + * Guard that we have enough stack space for the tree we are trying to + * call on top of the new value for sp. + */ + debug_only_printf(LC_TMTracer, + "sp_adj=%lld outer=%lld inner=%lld\n", + (long long int)sp_adj, + (long long int)treeInfo->nativeStackBase, + (long long int)ti->nativeStackBase); + ptrdiff_t sp_offset = + - treeInfo->nativeStackBase /* rebase sp to beginning of outer tree's stack */ + + sp_adj /* adjust for stack in outer frame inner tree can't see */ + + ti->maxNativeStackSlots * sizeof(double); /* plus the inner tree's stack */ + LIns* sp_top = lir->ins2(LIR_piadd, lirbuf->sp, INS_CONSTWORD(sp_offset)); + guard(true, lir->ins2(LIR_plt, sp_top, eos_ins), exit); + + /* Guard that we have enough call stack space. */ + ptrdiff_t rp_offset = rp_adj + ti->maxCallDepth * sizeof(FrameInfo*); + LIns* rp_top = lir->ins2(LIR_piadd, lirbuf->rp, INS_CONSTWORD(rp_offset)); + guard(true, lir->ins2(LIR_plt, rp_top, eor_ins), exit); + + sp_offset = + - treeInfo->nativeStackBase /* rebase sp to beginning of outer tree's stack */ + + sp_adj /* adjust for stack in outer frame inner tree can't see */ + + ti->nativeStackBase; /* plus the inner tree's stack base */ + /* We have enough space, so adjust sp and rp to their new level. */ + lir->insStorei(inner_sp_ins = lir->ins2(LIR_piadd, lirbuf->sp, INS_CONSTWORD(sp_offset)), + lirbuf->state, offsetof(InterpState, sp)); + lir->insStorei(lir->ins2(LIR_piadd, lirbuf->rp, INS_CONSTWORD(rp_adj)), + lirbuf->state, offsetof(InterpState, rp)); + } + + /* + * The inner tree will probably access stack slots. So tell nanojit not to + * discard or defer stack writes before emitting the call tree code. + * + * (The ExitType of this snapshot is nugatory. The exit can't be taken.) + */ + GuardRecord* guardRec = createGuardRecord(exit); + lir->insGuard(LIR_xbarrier, NULL, guardRec); +} + +static unsigned +BuildGlobalTypeMapFromInnerTree(Queue& typeMap, VMSideExit* inner) +{ +#if defined DEBUG + unsigned initialSlots = typeMap.length(); +#endif + /* First, use the innermost exit's global typemap. */ + typeMap.add(inner->globalTypeMap(), inner->numGlobalSlots); + + /* Add missing global types from the innermost exit's tree. */ + TreeInfo* innerTree = inner->root()->treeInfo; + unsigned slots = inner->numGlobalSlots; + if (slots < innerTree->nGlobalTypes()) { + typeMap.add(innerTree->globalTypeMap() + slots, innerTree->nGlobalTypes() - slots); + slots = innerTree->nGlobalTypes(); + } + JS_ASSERT(typeMap.length() - initialSlots == slots); + return slots; +} + +/* Record a call to an inner tree. */ +JS_REQUIRES_STACK void +TraceRecorder::emitTreeCall(TreeFragment* inner, VMSideExit* exit, LIns* inner_sp_ins) +{ + /* Invoke the inner tree. */ + LIns* args[] = { lirbuf->state }; /* reverse order */ + /* Construct a call info structure for the target tree. */ + CallInfo* ci = new (traceAlloc()) CallInfo(); + ci->_address = uintptr_t(inner->code()); + JS_ASSERT(ci->_address); + ci->_argtypes = ARGSIZE_P | ARGSIZE_P << ARGSIZE_SHIFT; + ci->_cse = ci->_fold = 0; + ci->_abi = ABI_FASTCALL; +#ifdef DEBUG + ci->_name = "fragment"; +#endif + LIns* rec = lir->insCall(ci, args); + LIns* lr = lir->insLoad(LIR_ldp, rec, offsetof(GuardRecord, exit)); + LIns* nested = lir->insBranch(LIR_jt, + lir->ins2i(LIR_eq, + lir->insLoad(LIR_ld, lr, offsetof(VMSideExit, exitType)), + NESTED_EXIT), + NULL); + + /* + * If the tree exits on a regular (non-nested) guard, keep updating lastTreeExitGuard + * with that guard. If we mismatch on a tree call guard, this will contain the last + * non-nested guard we encountered, which is the innermost loop or branch guard. + */ + lir->insStorei(lr, lirbuf->state, offsetof(InterpState, lastTreeExitGuard)); + LIns* done1 = lir->insBranch(LIR_j, NULL, NULL); + + /* + * The tree exited on a nested guard. This only occurs once a tree call guard mismatches + * and we unwind the tree call stack. We store the first (innermost) tree call guard in state + * and we will try to grow the outer tree the failing call was in starting at that guard. + */ + nested->setTarget(lir->ins0(LIR_label)); + LIns* done2 = lir->insBranch(LIR_jf, + lir->ins_peq0(lir->insLoad(LIR_ldp, + lirbuf->state, + offsetof(InterpState, lastTreeCallGuard))), + NULL); + lir->insStorei(lr, lirbuf->state, offsetof(InterpState, lastTreeCallGuard)); + lir->insStorei(lir->ins2(LIR_piadd, + lir->insLoad(LIR_ldp, lirbuf->state, offsetof(InterpState, rp)), + lir->ins_i2p(lir->ins2i(LIR_lsh, + lir->insLoad(LIR_ld, lr, offsetof(VMSideExit, calldepth)), + sizeof(void*) == 4 ? 2 : 3))), + lirbuf->state, + offsetof(InterpState, rpAtLastTreeCall)); + LIns* label = lir->ins0(LIR_label); + done1->setTarget(label); + done2->setTarget(label); + + /* + * Keep updating outermostTreeExit so that InterpState always contains the most recent + * side exit. + */ + lir->insStorei(lr, lirbuf->state, offsetof(InterpState, outermostTreeExitGuard)); + + /* Read back all registers, in case the called tree changed any of them. */ +#ifdef DEBUG + JSTraceType* map; + size_t i; + map = exit->globalTypeMap(); + for (i = 0; i < exit->numGlobalSlots; i++) + JS_ASSERT(map[i] != TT_JSVAL); + map = exit->stackTypeMap(); + for (i = 0; i < exit->numStackSlots; i++) + JS_ASSERT(map[i] != TT_JSVAL); +#endif + + /* + * Bug 502604 - It is illegal to extend from the outer typemap without + * first extending from the inner. Make a new typemap here. + */ + TypeMap fullMap(NULL); + fullMap.add(exit->stackTypeMap(), exit->numStackSlots); + BuildGlobalTypeMapFromInnerTree(fullMap, exit); + + TreeInfo* ti = inner->treeInfo; + import(ti, inner_sp_ins, exit->numStackSlots, fullMap.length() - exit->numStackSlots, + exit->calldepth, fullMap.data()); + + /* Restore sp and rp to their original values (we still have them in a register). */ + if (callDepth > 0) { + lir->insStorei(lirbuf->sp, lirbuf->state, offsetof(InterpState, sp)); + lir->insStorei(lirbuf->rp, lirbuf->state, offsetof(InterpState, rp)); + } + + /* + * Guard that we come out of the inner tree along the same side exit we came out when + * we called the inner tree at recording time. + */ + VMSideExit* nestedExit = snapshot(NESTED_EXIT); + JS_ASSERT(exit->exitType == LOOP_EXIT); + guard(true, lir->ins2(LIR_peq, lr, INS_CONSTPTR(exit)), nestedExit); + debug_only_printf(LC_TMTreeVis, "TREEVIS TREECALL INNER=%p EXIT=%p GUARD=%p\n", (void*)inner, + (void*)nestedExit, (void*)exit); + + /* Register us as a dependent tree of the inner tree. */ + inner->treeInfo->dependentTrees.addUnique(fragment->root); + treeInfo->linkedTrees.addUnique(inner); +} + +/* Add a if/if-else control-flow merge point to the list of known merge points. */ +JS_REQUIRES_STACK void +TraceRecorder::trackCfgMerges(jsbytecode* pc) +{ + /* If we hit the beginning of an if/if-else, then keep track of the merge point after it. */ + JS_ASSERT((*pc == JSOP_IFEQ) || (*pc == JSOP_IFEQX)); + jssrcnote* sn = js_GetSrcNote(cx->fp->script, pc); + if (sn != NULL) { + if (SN_TYPE(sn) == SRC_IF) { + cfgMerges.add((*pc == JSOP_IFEQ) + ? pc + GET_JUMP_OFFSET(pc) + : pc + GET_JUMPX_OFFSET(pc)); + } else if (SN_TYPE(sn) == SRC_IF_ELSE) + cfgMerges.add(pc + js_GetSrcNoteOffset(sn, 0)); + } +} + +/* + * Invert the direction of the guard if this is a loop edge that is not + * taken (thin loop). + */ +JS_REQUIRES_STACK void +TraceRecorder::emitIf(jsbytecode* pc, bool cond, LIns* x) +{ + ExitType exitType; + if (IsLoopEdge(pc, (jsbytecode*)fragment->root->ip)) { + exitType = LOOP_EXIT; + + /* + * If we are about to walk out of the loop, generate code for the + * inverse loop condition, pretending we recorded the case that stays + * on trace. + */ + if ((*pc == JSOP_IFEQ || *pc == JSOP_IFEQX) == cond) { + JS_ASSERT(*pc == JSOP_IFNE || *pc == JSOP_IFNEX || *pc == JSOP_IFEQ || *pc == JSOP_IFEQX); + debug_only_print0(LC_TMTracer, + "Walking out of the loop, terminating it anyway.\n"); + cond = !cond; + } + + /* + * Conditional guards do not have to be emitted if the condition is + * constant. We make a note whether the loop condition is true or false + * here, so we later know whether to emit a loop edge or a loop end. + */ + if (x->isconst()) { + pendingLoop = (x->imm32() == int32(cond)); + return; + } + } else { + exitType = BRANCH_EXIT; + } + if (!x->isconst()) + guard(cond, x, exitType); +} + +/* Emit code for a fused IFEQ/IFNE. */ +JS_REQUIRES_STACK void +TraceRecorder::fuseIf(jsbytecode* pc, bool cond, LIns* x) +{ + if (*pc == JSOP_IFEQ || *pc == JSOP_IFNE) { + emitIf(pc, cond, x); + if (*pc == JSOP_IFEQ) + trackCfgMerges(pc); + } +} + +/* Check whether we have reached the end of the trace. */ +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::checkTraceEnd(jsbytecode *pc) +{ + if (IsLoopEdge(pc, (jsbytecode*)fragment->root->ip)) { + /* + * If we compile a loop, the trace should have a zero stack balance at + * the loop edge. Currently we are parked on a comparison op or + * IFNE/IFEQ, so advance pc to the loop header and adjust the stack + * pointer and pretend we have reached the loop header. + */ + if (pendingLoop) { + JS_ASSERT(!cx->fp->imacpc && (pc == cx->fp->regs->pc || pc == cx->fp->regs->pc + 1)); + bool fused = pc != cx->fp->regs->pc; + JSFrameRegs orig = *cx->fp->regs; + + cx->fp->regs->pc = (jsbytecode*)fragment->root->ip; + cx->fp->regs->sp -= fused ? 2 : 1; + + JSContext* localcx = cx; + AbortableRecordingStatus ars = closeLoop(); + *localcx->fp->regs = orig; + return ars; + } else { + return endLoop(); + } + } + return ARECORD_CONTINUE; +} + +bool +TraceRecorder::hasMethod(JSObject* obj, jsid id) +{ + if (!obj) + return false; + + JSObject* pobj; + JSProperty* prop; + int protoIndex = obj->lookupProperty(cx, id, &pobj, &prop); + if (protoIndex < 0 || !prop) + return false; + + bool found = false; + if (OBJ_IS_NATIVE(pobj)) { + JSScope* scope = OBJ_SCOPE(pobj); + JSScopeProperty* sprop = (JSScopeProperty*) prop; + + if (SPROP_HAS_STUB_GETTER_OR_IS_METHOD(sprop) && + SPROP_HAS_VALID_SLOT(sprop, scope)) { + jsval v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot); + if (VALUE_IS_FUNCTION(cx, v)) { + found = true; + if (!scope->branded()) { + scope->brandingShapeChange(cx, sprop->slot, v); + scope->setBranded(); + } + } + } + } + + pobj->dropProperty(cx, prop); + return found; +} + +JS_REQUIRES_STACK bool +TraceRecorder::hasIteratorMethod(JSObject* obj) +{ + JS_ASSERT(cx->fp->regs->sp + 2 <= cx->fp->slots + cx->fp->script->nslots); + + return hasMethod(obj, ATOM_TO_JSID(cx->runtime->atomState.iteratorAtom)); +} + +void +nanojit::StackFilter::getTops(LIns* guard, int& spTop, int& rpTop) +{ + VMSideExit* e = (VMSideExit*)guard->record()->exit; + spTop = e->sp_adj; + rpTop = e->rp_adj; +} + +#if defined NJ_VERBOSE +void +nanojit::LirNameMap::formatGuard(LIns *i, char *out) +{ + VMSideExit *x; + + x = (VMSideExit *)i->record()->exit; + sprintf(out, + "%s: %s %s -> pc=%p imacpc=%p sp%+ld rp%+ld (GuardID=%03d)", + formatRef(i), + lirNames[i->opcode()], + i->oprnd1() ? formatRef(i->oprnd1()) : "", + (void *)x->pc, + (void *)x->imacpc, + (long int)x->sp_adj, + (long int)x->rp_adj, + i->record()->profGuardID); +} +#endif + +/* + * Check whether the shape of the global object has changed. The return value + * indicates whether the recorder is still active. If 'false', any active + * recording has been aborted and the JIT may have been reset. + */ +static JS_REQUIRES_STACK bool +CheckGlobalObjectShape(JSContext* cx, JSTraceMonitor* tm, JSObject* globalObj, + uint32 *shape = NULL, SlotList** slots = NULL) +{ + if (tm->needFlush) { + ResetJIT(cx, FR_DEEP_BAIL); + return false; + } + + if (STOBJ_NSLOTS(globalObj) > MAX_GLOBAL_SLOTS) { + if (tm->recorder) + js_AbortRecording(cx, "too many slots in global object"); + return false; + } + + uint32 globalShape = OBJ_SHAPE(globalObj); + + if (tm->recorder) { + TreeFragment* root = tm->recorder->getFragment()->root; + TreeInfo* ti = tm->recorder->getTreeInfo(); + + /* Check the global shape matches the recorder's treeinfo's shape. */ + if (globalObj != root->globalObj || globalShape != root->globalShape) { + AUDIT(globalShapeMismatchAtEntry); + debug_only_printf(LC_TMTracer, + "Global object/shape mismatch (%p/%u vs. %p/%u), flushing cache.\n", + (void*)globalObj, globalShape, (void*)root->globalObj, + root->globalShape); + Backoff(cx, (jsbytecode*) root->ip); + ResetJIT(cx, FR_GLOBAL_SHAPE_MISMATCH); + return false; + } + if (shape) + *shape = globalShape; + if (slots) + *slots = ti->globalSlots; + return true; + } + + /* No recorder, search for a tracked global-state (or allocate one). */ + for (size_t i = 0; i < MONITOR_N_GLOBAL_STATES; ++i) { + GlobalState &state = tm->globalStates[i]; + + if (state.globalShape == uint32(-1)) { + state.globalObj = globalObj; + state.globalShape = globalShape; + JS_ASSERT(state.globalSlots); + JS_ASSERT(state.globalSlots->length() == 0); + } + + if (state.globalObj == globalObj && state.globalShape == globalShape) { + if (shape) + *shape = globalShape; + if (slots) + *slots = state.globalSlots; + return true; + } + } + + /* No currently-tracked-global found and no room to allocate, abort. */ + AUDIT(globalShapeMismatchAtEntry); + debug_only_printf(LC_TMTracer, + "No global slotlist for global shape %u, flushing cache.\n", + globalShape); + ResetJIT(cx, FR_GLOBALS_FULL); + return false; +} + +/* + * Return whether or not the recorder could be started. If 'false', the JIT has + * been reset in response to an OOM. + */ +bool JS_REQUIRES_STACK +TraceRecorder::startRecorder(JSContext* cx, VMSideExit* anchor, VMFragment* f, + TreeInfo* ti, unsigned stackSlots, unsigned ngslots, + JSTraceType* typeMap, VMSideExit* expectedInnerExit, + jsbytecode* outer, uint32 outerArgc, RecordReason recordReason) +{ + JSTraceMonitor *tm = &JS_TRACE_MONITOR(cx); + JS_ASSERT(!tm->needFlush); + JS_ASSERT_IF(cx->fp->imacpc, f->root != f); + + tm->recorder = new TraceRecorder(cx, anchor, f, ti, stackSlots, + ngslots, typeMap, expectedInnerExit, + outer, outerArgc, recordReason); + + if (!tm->recorder || tm->outOfMemory() || js_OverfullJITCache(tm)) { + ResetJIT(cx, FR_OOM); + return false; + } + + /* + * If slurping failed, there's no reason to start recording again. Emit LIR + * to capture the rest of the slots, then immediately compile and finish. + */ + if (anchor && anchor->exitType == RECURSIVE_SLURP_FAIL_EXIT) { + tm->recorder->slurpDownFrames((jsbytecode*)anchor->recursive_pc - JSOP_CALL_LENGTH); + if (tm->recorder) + tm->recorder->finishAbort("Failed to slurp down frames"); + return false; + } + + return true; +} + +static void +TrashTree(JSContext* cx, TreeFragment* f) +{ + JS_ASSERT((!f->code()) == (!f->treeInfo)); + JS_ASSERT(f == f->root); + debug_only_printf(LC_TMTreeVis, "TREEVIS TRASH FRAG=%p\n", (void*)f); + + if (!f->code()) + return; + AUDIT(treesTrashed); + debug_only_print0(LC_TMTracer, "Trashing tree info.\n"); + TreeInfo* ti = f->treeInfo; + f->treeInfo = NULL; + f->setCode(NULL); + TreeFragment** data = ti->dependentTrees.data(); + unsigned length = ti->dependentTrees.length(); + for (unsigned n = 0; n < length; ++n) + TrashTree(cx, data[n]); + data = ti->linkedTrees.data(); + length = ti->linkedTrees.length(); + for (unsigned n = 0; n < length; ++n) + TrashTree(cx, data[n]); +} + +static int +SynthesizeFrame(JSContext* cx, const FrameInfo& fi, JSObject* callee) +{ + VOUCH_DOES_NOT_REQUIRE_STACK(); + + JSFunction* fun = GET_FUNCTION_PRIVATE(cx, callee); + JS_ASSERT(FUN_INTERPRETED(fun)); + + /* Assert that we have a correct sp distance from cx->fp->slots in fi. */ + JSStackFrame* fp = cx->fp; + JS_ASSERT_IF(!fi.imacpc, + js_ReconstructStackDepth(cx, fp->script, fi.pc) == + uintN(fi.spdist - fp->script->nfixed)); + + uintN nframeslots = JS_HOWMANY(sizeof(JSInlineFrame), sizeof(jsval)); + JSScript* script = fun->u.i.script; + size_t nbytes = (nframeslots + script->nslots) * sizeof(jsval); + + /* Code duplicated from inline_call: case in js_Interpret (FIXME). */ + JSArena* a = cx->stackPool.current; + void* newmark = (void*) a->avail; + uintN argc = fi.get_argc(); + jsval* vp = fp->slots + fi.spdist - (2 + argc); + uintN missing = 0; + jsval* newsp; + + if (fun->nargs > argc) { + const JSFrameRegs& regs = *fp->regs; + + newsp = vp + 2 + fun->nargs; + JS_ASSERT(newsp > regs.sp); + if ((jsuword) newsp <= a->limit) { + if ((jsuword) newsp > a->avail) + a->avail = (jsuword) newsp; + jsval* argsp = newsp; + do { + *--argsp = JSVAL_VOID; + } while (argsp != regs.sp); + missing = 0; + } else { + missing = fun->nargs - argc; + nbytes += (2 + fun->nargs) * sizeof(jsval); + } + } + + /* Allocate the inline frame with its vars and operands. */ + if (a->avail + nbytes <= a->limit) { + newsp = (jsval *) a->avail; + a->avail += nbytes; + JS_ASSERT(missing == 0); + } else { + JS_ARENA_ALLOCATE_CAST(newsp, jsval *, &cx->stackPool, nbytes); + if (!newsp) + OutOfMemoryAbort(); + + /* + * Move args if the missing ones overflow arena a, then push + * undefined for the missing args. + */ + if (missing) { + memcpy(newsp, vp, (2 + argc) * sizeof(jsval)); + vp = newsp; + newsp = vp + 2 + argc; + do { + *newsp++ = JSVAL_VOID; + } while (--missing != 0); + } + } + + /* Claim space for the stack frame and initialize it. */ + JSInlineFrame* newifp = (JSInlineFrame *) newsp; + newsp += nframeslots; + + newifp->frame.callobj = NULL; + newifp->frame.argsobj = NULL; + newifp->frame.varobj = NULL; + newifp->frame.script = script; + newifp->frame.fun = fun; + + bool constructing = fi.is_constructing(); + newifp->frame.argc = argc; + newifp->callerRegs.pc = fi.pc; + newifp->callerRegs.sp = fp->slots + fi.spdist; + fp->imacpc = fi.imacpc; + +#ifdef DEBUG + if (fi.block != fp->blockChain) { + for (JSObject* obj = fi.block; obj != fp->blockChain; obj = STOBJ_GET_PARENT(obj)) + JS_ASSERT(obj); + } +#endif + fp->blockChain = fi.block; + + newifp->frame.argv = newifp->callerRegs.sp - argc; + JS_ASSERT(newifp->frame.argv); +#ifdef DEBUG + // Initialize argv[-1] to a known-bogus value so we'll catch it if + // someone forgets to initialize it later. + newifp->frame.argv[-1] = JSVAL_HOLE; +#endif + JS_ASSERT(newifp->frame.argv >= StackBase(fp) + 2); + + newifp->frame.rval = JSVAL_VOID; + newifp->frame.down = fp; + newifp->frame.annotation = NULL; + newifp->frame.scopeChain = NULL; // will be updated in FlushNativeStackFrame + newifp->frame.flags = constructing ? JSFRAME_CONSTRUCTING : 0; + newifp->frame.dormantNext = NULL; + newifp->frame.blockChain = NULL; + newifp->mark = newmark; + newifp->frame.thisv = JSVAL_NULL; // will be updated in FlushNativeStackFrame + + newifp->frame.regs = fp->regs; + newifp->frame.regs->pc = script->code; + newifp->frame.regs->sp = newsp + script->nfixed; + newifp->frame.imacpc = NULL; + newifp->frame.slots = newsp; + if (script->staticLevel < JS_DISPLAY_SIZE) { + JSStackFrame **disp = &cx->display[script->staticLevel]; + newifp->frame.displaySave = *disp; + *disp = &newifp->frame; + } + + /* + * Note that fp->script is still the caller's script; set the callee + * inline frame's idea of caller version from its version. + */ + newifp->callerVersion = (JSVersion) fp->script->version; + + // After this paragraph, fp and cx->fp point to the newly synthesized frame. + fp->regs = &newifp->callerRegs; + fp = cx->fp = &newifp->frame; + + /* + * If there's a call hook, invoke it to compute the hookData used by + * debuggers that cooperate with the interpreter. + */ + JSInterpreterHook hook = cx->debugHooks->callHook; + if (hook) { + newifp->hookData = hook(cx, fp, JS_TRUE, 0, cx->debugHooks->callHookData); + } else { + newifp->hookData = NULL; + } + + /* + * Duplicate native stack layout computation: see VisitFrameSlots header comment. + * + * FIXME - We must count stack slots from caller's operand stack up to (but + * not including) callee's, including missing arguments. Could we shift + * everything down to the caller's fp->slots (where vars start) and avoid + * some of the complexity? + */ + return (fi.spdist - fp->down->script->nfixed) + + ((fun->nargs > fp->argc) ? fun->nargs - fp->argc : 0) + + script->nfixed + 1/*argsobj*/; +} + +static void +SynthesizeSlowNativeFrame(InterpState& state, JSContext *cx, VMSideExit *exit) +{ + VOUCH_DOES_NOT_REQUIRE_STACK(); + + void *mark; + JSInlineFrame *ifp; + + /* This allocation is infallible: ExecuteTree reserved enough stack. */ + mark = JS_ARENA_MARK(&cx->stackPool); + JS_ARENA_ALLOCATE_CAST(ifp, JSInlineFrame *, &cx->stackPool, sizeof(JSInlineFrame)); + if (!ifp) + OutOfMemoryAbort(); + + JSStackFrame *fp = &ifp->frame; + fp->regs = NULL; + fp->imacpc = NULL; + fp->slots = NULL; + fp->callobj = NULL; + fp->argsobj = NULL; + fp->varobj = cx->fp->varobj; + fp->script = NULL; + fp->thisv = state.nativeVp[1]; + fp->argc = state.nativeVpLen - 2; + fp->argv = state.nativeVp + 2; + fp->fun = GET_FUNCTION_PRIVATE(cx, fp->calleeObject()); + fp->rval = JSVAL_VOID; + fp->down = cx->fp; + fp->annotation = NULL; + JS_ASSERT(cx->fp->scopeChain); + fp->scopeChain = cx->fp->scopeChain; + fp->blockChain = NULL; + fp->flags = exit->constructing() ? JSFRAME_CONSTRUCTING : 0; + fp->dormantNext = NULL; + fp->displaySave = NULL; + + ifp->mark = mark; + cx->fp = fp; +} + +/* + * Create a TreeInfo in preparation for starting a recorder. If one cannot be + * allocated, reset the JIT and return NULL. + */ +static JS_REQUIRES_STACK TreeInfo* +CreateTreeInfo(JSContext* cx, TreeFragment* f, JSObject* globalObj, SlotList* globalSlots) +{ + JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx); + + /* Set up the VM-private treeInfo structure for this fragment. */ + TreeInfo* ti = new (*tm->traceAlloc) TreeInfo(tm->dataAlloc, f, globalSlots); + + /* Capture the coerced type of each active slot in the type map. */ + ti->typeMap.captureTypes(cx, globalObj, *globalSlots, 0 /* callDepth */); + ti->nStackTypes = ti->typeMap.length() - globalSlots->length(); + +#ifdef DEBUG + AssertTreeIsUnique(tm, f, ti); + ti->treeFileName = cx->fp->script->filename; + ti->treeLineNumber = js_FramePCToLineNumber(cx, cx->fp); + ti->treePCOffset = FramePCOffset(cx->fp); +#endif +#ifdef JS_JIT_SPEW + debug_only_printf(LC_TMTreeVis, "TREEVIS CREATETREE ROOT=%p PC=%p FILE=\"%s\" LINE=%d OFFS=%d", + (void*)f, f->ip, ti->treeFileName, ti->treeLineNumber, + FramePCOffset(cx->fp)); + debug_only_print0(LC_TMTreeVis, " STACK=\""); + for (unsigned i = 0; i < ti->nStackTypes; i++) + debug_only_printf(LC_TMTreeVis, "%c", typeChar[ti->typeMap[i]]); + debug_only_print0(LC_TMTreeVis, "\" GLOBALS=\""); + for (unsigned i = 0; i < ti->nGlobalTypes(); i++) + debug_only_printf(LC_TMTreeVis, "%c", typeChar[ti->typeMap[ti->nStackTypes + i]]); + debug_only_print0(LC_TMTreeVis, "\"\n"); +#endif + + /* Determine the native frame layout at the entry point. */ + unsigned entryNativeStackSlots = ti->nStackTypes; + JS_ASSERT(entryNativeStackSlots == NativeStackSlots(cx, 0 /* callDepth */)); + ti->nativeStackBase = (entryNativeStackSlots - + (cx->fp->regs->sp - StackBase(cx->fp))) * sizeof(double); + ti->maxNativeStackSlots = entryNativeStackSlots; + ti->maxCallDepth = 0; + ti->script = cx->fp->script; + + return ti; +} + +static JS_REQUIRES_STACK bool +RecordTree(JSContext* cx, JSTraceMonitor* tm, TreeFragment* peer, jsbytecode* outer, + uint32 outerArgc, JSObject* globalObj, uint32 globalShape, + SlotList* globalSlots, uint32 argc, RecordReason reason) +{ + /* Try to find an unused peer fragment, or allocate a new one. */ + TreeFragment* f = peer; + while (f->code() && f->peer) + f = f->peer; + if (f->code()) + f = AddNewPeerToPeerList(tm, f); + JS_ASSERT(f->root == f); + + /* save a local copy for use after JIT flush */ + const void* localRootIP = f->root->ip; + + /* Make sure the global type map didn't change on us. */ + if (!CheckGlobalObjectShape(cx, tm, globalObj)) { + Backoff(cx, (jsbytecode*) localRootIP); + return false; + } + + AUDIT(recorderStarted); + + if (tm->outOfMemory() || js_OverfullJITCache(tm)) { + Backoff(cx, (jsbytecode*) f->root->ip); + ResetJIT(cx, FR_OOM); + debug_only_print0(LC_TMTracer, + "Out of memory recording new tree, flushing cache.\n"); + return false; + } + + JS_ASSERT(!f->code() && !f->treeInfo); + + TreeInfo* ti = CreateTreeInfo(cx, f, globalObj, globalSlots); + if (!ti) + return false; + + /* Recording primary trace. */ + return TraceRecorder::startRecorder(cx, NULL, f, ti, + ti->nStackTypes, + ti->globalSlots->length(), + ti->typeMap.data(), NULL, + outer, outerArgc, reason); +} + +static JS_REQUIRES_STACK TypeConsensus +FindLoopEdgeTarget(JSContext* cx, VMSideExit* exit, TreeFragment** peerp) +{ + TreeFragment* from = exit->root(); + TreeInfo* from_ti = from->treeInfo; + + JS_ASSERT(from->code()); + + TypeMap typeMap(NULL); + FullMapFromExit(typeMap, exit); + JS_ASSERT(typeMap.length() - exit->numStackSlots == from_ti->nGlobalTypes()); + + /* Mark all double slots as undemotable */ + uint16* gslots = from_ti->globalSlots->data(); + for (unsigned i = 0; i < typeMap.length(); i++) { + if (typeMap[i] == TT_DOUBLE) { + if (exit->exitType == RECURSIVE_UNLINKED_EXIT) { + if (i < exit->numStackSlots) + oracle.markStackSlotUndemotable(cx, i, exit->recursive_pc); + else + oracle.markGlobalSlotUndemotable(cx, gslots[i - exit->numStackSlots]); + } + if (i < from_ti->nStackTypes) + oracle.markStackSlotUndemotable(cx, i, from->ip); + else if (i >= exit->numStackSlots) + oracle.markGlobalSlotUndemotable(cx, gslots[i - exit->numStackSlots]); + } + } + + JS_ASSERT(exit->exitType == UNSTABLE_LOOP_EXIT || + (exit->exitType == RECURSIVE_UNLINKED_EXIT && exit->recursive_pc)); + + TreeFragment* firstPeer = NULL; + if (exit->exitType == UNSTABLE_LOOP_EXIT || exit->recursive_pc == from->ip) { + firstPeer = from->first; + } else { + firstPeer = LookupLoop(&JS_TRACE_MONITOR(cx), exit->recursive_pc, from->globalObj, + from->globalShape, from->argc); + } + + for (TreeFragment* peer = firstPeer; peer; peer = peer->peer) { + TreeInfo* peer_ti = peer->treeInfo; + if (!peer_ti) + continue; + JS_ASSERT(peer->argc == from->argc); + JS_ASSERT(exit->numStackSlots == peer_ti->nStackTypes); + TypeConsensus consensus = TypeMapLinkability(cx, typeMap, peer); + if (consensus == TypeConsensus_Okay || consensus == TypeConsensus_Undemotes) { + *peerp = peer; + return consensus; + } + } + + return TypeConsensus_Bad; +} + +UnstableExit* +TreeInfo::removeUnstableExit(VMSideExit* exit) +{ + /* Now erase this exit from the unstable exit list. */ + UnstableExit** tail = &this->unstableExits; + for (UnstableExit* uexit = this->unstableExits; uexit != NULL; uexit = uexit->next) { + if (uexit->exit == exit) { + *tail = uexit->next; + return *tail; + } + tail = &uexit->next; + } + JS_NOT_REACHED("exit not in unstable exit list"); + return NULL; +} + +static JS_REQUIRES_STACK bool +AttemptToStabilizeTree(JSContext* cx, JSObject* globalObj, VMSideExit* exit, jsbytecode* outer, + uint32 outerArgc) +{ + JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx); + if (tm->needFlush) { + ResetJIT(cx, FR_DEEP_BAIL); + return false; + } + + TreeFragment* from = exit->root(); + TreeInfo* from_ti = from->treeInfo; + + TreeFragment* peer = NULL; + TypeConsensus consensus = FindLoopEdgeTarget(cx, exit, &peer); + if (consensus == TypeConsensus_Okay) { + TreeInfo* peer_ti = peer->treeInfo; + JS_ASSERT(from_ti->globalSlots == peer_ti->globalSlots); + JS_ASSERT_IF(exit->exitType == UNSTABLE_LOOP_EXIT, + from_ti->nStackTypes == peer_ti->nStackTypes); + JS_ASSERT(exit->numStackSlots == peer_ti->nStackTypes); + /* Patch this exit to its peer */ + JoinPeers(tm->assembler, exit, peer); + /* + * Update peer global types. The |from| fragment should already be updated because it on + * the execution path, and somehow connected to the entry trace. + */ + if (peer_ti->nGlobalTypes() < peer_ti->globalSlots->length()) + SpecializeTreesToMissingGlobals(cx, globalObj, peer_ti); + JS_ASSERT(from_ti->nGlobalTypes() == from_ti->globalSlots->length()); + /* This exit is no longer unstable, so remove it. */ + if (exit->exitType == UNSTABLE_LOOP_EXIT) + from_ti->removeUnstableExit(exit); + debug_only_stmt(DumpPeerStability(tm, peer->ip, from->globalObj, from->globalShape, from->argc);) + return false; + } else if (consensus == TypeConsensus_Undemotes) { + /* The original tree is unconnectable, so trash it. */ + TrashTree(cx, peer); + return false; + } + + /* Don't bother recording if the exit doesn't expect this PC */ + if (exit->exitType == RECURSIVE_UNLINKED_EXIT) { + if (++exit->hitcount >= MAX_RECURSIVE_UNLINK_HITS) { + Blacklist((jsbytecode*)from->ip); + TrashTree(cx, from); + return false; + } + if (exit->recursive_pc != cx->fp->regs->pc) + return false; + from = LookupLoop(tm, exit->recursive_pc, from->globalObj, from->globalShape, cx->fp->argc); + if (!from) + return false; + /* use stale TI for RecordTree - since from might not have one anymore. */ + } + + JS_ASSERT(from == from->root); + + /* If this tree has been blacklisted, don't try to record a new one. */ + if (*(jsbytecode*)from->ip == JSOP_NOP) + return false; + + return RecordTree(cx, tm, from->first, outer, outerArgc, from->globalObj, + from->globalShape, from_ti->globalSlots, cx->fp->argc, + Record_Branch); +} + +static JS_REQUIRES_STACK VMFragment* +CreateBranchFragment(JSContext* cx, TreeFragment* root, VMSideExit* anchor) +{ + JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx); + + verbose_only( + uint32_t profFragID = (js_LogController.lcbits & LC_FragProfile) + ? (++(tm->lastFragID)) : 0; + ) + + VMFragment* f = new (*tm->dataAlloc) VMFragment(cx->fp->regs->pc verbose_only(, profFragID)); + + debug_only_printf(LC_TMTreeVis, "TREEVIS CREATEBRANCH ROOT=%p FRAG=%p PC=%p FILE=\"%s\"" + " LINE=%d ANCHOR=%p OFFS=%d\n", + (void*)root, (void*)f, (void*)cx->fp->regs->pc, cx->fp->script->filename, + js_FramePCToLineNumber(cx, cx->fp), (void*)anchor, + FramePCOffset(cx->fp)); + verbose_only( tm->branches = new (*tm->dataAlloc) Seq(f, tm->branches); ) + + f->root = root; + if (anchor) + anchor->target = f; + return f; +} + +static JS_REQUIRES_STACK bool +AttemptToExtendTree(JSContext* cx, VMSideExit* anchor, VMSideExit* exitedFrom, jsbytecode* outer +#ifdef MOZ_TRACEVIS + , TraceVisStateObj* tvso = NULL +#endif + ) +{ + JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx); + JS_ASSERT(!tm->recorder); + + if (tm->needFlush) { + ResetJIT(cx, FR_DEEP_BAIL); +#ifdef MOZ_TRACEVIS + if (tvso) tvso->r = R_FAIL_EXTEND_FLUSH; +#endif + return false; + } + + TreeFragment* f = anchor->root(); + JS_ASSERT(f->treeInfo); + + /* + * Don't grow trees above a certain size to avoid code explosion due to + * tail duplication. + */ + if (f->branchCount >= MAX_BRANCHES) { +#ifdef MOZ_TRACEVIS + if (tvso) tvso->r = R_FAIL_EXTEND_MAX_BRANCHES; +#endif + return false; + } + + VMFragment* c = (VMFragment*)anchor->target; + if (!c) { + c = CreateBranchFragment(cx, f, anchor); + } else { + /* + * If we are recycling a fragment, it might have a different ip so reset it + * here. This can happen when attaching a branch to a NESTED_EXIT, which + * might extend along separate paths (i.e. after the loop edge, and after a + * return statement). + */ + c->ip = cx->fp->regs->pc; + JS_ASSERT(c->root == f); + } + + debug_only_printf(LC_TMTracer, + "trying to attach another branch to the tree (hits = %d)\n", c->hits()); + + int32_t& hits = c->hits(); + if (outer || (hits++ >= HOTEXIT && hits <= HOTEXIT+MAXEXIT)) { + /* start tracing secondary trace from this point */ + unsigned stackSlots; + unsigned ngslots; + JSTraceType* typeMap; + TypeMap fullMap(NULL); + if (!exitedFrom) { + /* + * If we are coming straight from a simple side exit, just use that + * exit's type map as starting point. + */ + ngslots = anchor->numGlobalSlots; + stackSlots = anchor->numStackSlots; + typeMap = anchor->fullTypeMap(); + } else { + /* + * If we side-exited on a loop exit and continue on a nesting + * guard, the nesting guard (anchor) has the type information for + * everything below the current scope, and the actual guard we + * exited from has the types for everything in the current scope + * (and whatever it inlined). We have to merge those maps here. + */ + VMSideExit* e1 = anchor; + VMSideExit* e2 = exitedFrom; + fullMap.add(e1->stackTypeMap(), e1->numStackSlotsBelowCurrentFrame); + fullMap.add(e2->stackTypeMap(), e2->numStackSlots); + stackSlots = fullMap.length(); + ngslots = BuildGlobalTypeMapFromInnerTree(fullMap, e2); + JS_ASSERT(ngslots >= e1->numGlobalSlots); // inner tree must have all globals + JS_ASSERT(ngslots == fullMap.length() - stackSlots); + typeMap = fullMap.data(); + } + JS_ASSERT(ngslots >= anchor->numGlobalSlots); + bool rv = TraceRecorder::startRecorder(cx, anchor, c, f->treeInfo, + stackSlots, ngslots, typeMap, exitedFrom, + outer, cx->fp->argc, Record_Branch); +#ifdef MOZ_TRACEVIS + if (!rv && tvso) + tvso->r = R_FAIL_EXTEND_START; +#endif + return rv; + } +#ifdef MOZ_TRACEVIS + if (tvso) tvso->r = R_FAIL_EXTEND_COLD; +#endif + return false; +} + +static JS_REQUIRES_STACK VMSideExit* +ExecuteTree(JSContext* cx, TreeFragment* f, uintN& inlineCallCount, + VMSideExit** innermostNestedGuardp); + +JS_REQUIRES_STACK bool +TraceRecorder::recordLoopEdge(JSContext* cx, TraceRecorder* r, uintN& inlineCallCount) +{ +#ifdef JS_THREADSAFE + if (OBJ_SCOPE(JS_GetGlobalForObject(cx, cx->fp->scopeChain))->title.ownercx != cx) { + js_AbortRecording(cx, "Global object not owned by this context"); + return false; /* we stay away from shared global objects */ + } +#endif + + JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx); + + /* Process needFlush and deep abort requests. */ + if (tm->needFlush) { + ResetJIT(cx, FR_DEEP_BAIL); + return false; + } + + JS_ASSERT(r->fragment && !r->fragment->lastIns); + TreeFragment* root = r->fragment->root; + TreeFragment* first = LookupOrAddLoop(tm, cx->fp->regs->pc, root->globalObj, + root->globalShape, cx->fp->argc); + + /* + * Make sure the shape of the global object still matches (this might flush + * the JIT cache). + */ + JSObject* globalObj = JS_GetGlobalForObject(cx, cx->fp->scopeChain); + uint32 globalShape = -1; + SlotList* globalSlots = NULL; + if (!CheckGlobalObjectShape(cx, tm, globalObj, &globalShape, &globalSlots)) { + JS_ASSERT(!tm->recorder); + return false; + } + + debug_only_printf(LC_TMTracer, + "Looking for type-compatible peer (%s:%d@%d)\n", + cx->fp->script->filename, + js_FramePCToLineNumber(cx, cx->fp), + FramePCOffset(cx->fp)); + + // Find a matching inner tree. If none can be found, compile one. + TreeFragment* f = r->findNestedCompatiblePeer(first); + if (!f || !f->code()) { + AUDIT(noCompatInnerTrees); + + TreeFragment* outerFragment = root; + jsbytecode* outer = (jsbytecode*) outerFragment->ip; + uint32 outerArgc = outerFragment->argc; + uint32 argc = cx->fp->argc; + js_AbortRecording(cx, "No compatible inner tree"); + + return RecordTree(cx, tm, first, outer, outerArgc, globalObj, globalShape, + globalSlots, argc, Record_Branch); + } + + return r->attemptTreeCall(f, inlineCallCount) == ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::attemptTreeCall(TreeFragment* f, uintN& inlineCallCount) +{ + /* + * It is absolutely forbidden to have recursive loops tree call themselves + * because it could accidentally pop frames owned by the parent call, and + * there is no way to deal with this yet. We could have to set a "start of + * poppable rp stack" variable, and if that unequals "real start of rp stack", + * it would be illegal to pop frames. + * -- + * In the interim, just do tree calls knowing that they won't go into + * recursive trees that can pop parent frames. + */ + if (f->treeInfo->script == cx->fp->script) { + if (f->treeInfo->recursion >= Recursion_Unwinds) { + Blacklist(cx->fp->script->code); + js_AbortRecording(cx, "Inner tree is an unsupported type of recursion"); + return ARECORD_ABORTED; + } else { + f->treeInfo->recursion = Recursion_Disallowed; + } + } + + adjustCallerTypes(f); + LIns* inner_sp_ins; + prepareTreeCall(f, inner_sp_ins); + +#ifdef DEBUG + unsigned oldInlineCallCount = inlineCallCount; +#endif + + JSContext *localCx = cx; + + VMSideExit* innermostNestedGuard = NULL; + VMSideExit* lr = ExecuteTree(cx, f, inlineCallCount, &innermostNestedGuard); + + /* ExecuteTree can reenter the interpreter and kill |this|. */ + if (!TRACE_RECORDER(localCx)) + return ARECORD_ABORTED; + + if (!lr) { + js_AbortRecording(cx, "Couldn't call inner tree"); + return ARECORD_ABORTED; + } + + TreeFragment* outerFragment = fragment->root; + jsbytecode* outer = (jsbytecode*) outerFragment->ip; + switch (lr->exitType) { + case RECURSIVE_LOOP_EXIT: + case LOOP_EXIT: + /* If the inner tree exited on an unknown loop exit, grow the tree around it. */ + if (innermostNestedGuard) { + js_AbortRecording(cx, "Inner tree took different side exit, abort current " + "recording and grow nesting tree"); + return AttemptToExtendTree(localCx, innermostNestedGuard, lr, outer) ? + ARECORD_CONTINUE : ARECORD_ABORTED; + } + + JS_ASSERT(oldInlineCallCount == inlineCallCount); + + /* Emit a call to the inner tree and continue recording the outer tree trace. */ + emitTreeCall(f, lr, inner_sp_ins); + return ARECORD_CONTINUE; + + case UNSTABLE_LOOP_EXIT: + { + /* Abort recording so the inner loop can become type stable. */ + JSObject* _globalObj = globalObj; + js_AbortRecording(cx, "Inner tree is trying to stabilize, abort outer recording"); + return AttemptToStabilizeTree(localCx, _globalObj, lr, outer, outerFragment->argc) ? + ARECORD_CONTINUE : ARECORD_ABORTED; + } + + case OVERFLOW_EXIT: + oracle.markInstructionUndemotable(cx->fp->regs->pc); + /* FALL THROUGH */ + case RECURSIVE_SLURP_FAIL_EXIT: + case RECURSIVE_SLURP_MISMATCH_EXIT: + case RECURSIVE_MISMATCH_EXIT: + case RECURSIVE_EMPTY_RP_EXIT: + case BRANCH_EXIT: + case CASE_EXIT: { + /* Abort recording the outer tree, extend the inner tree. */ + js_AbortRecording(cx, "Inner tree is trying to grow, abort outer recording"); + return AttemptToExtendTree(localCx, lr, NULL, outer) ? ARECORD_CONTINUE : ARECORD_ABORTED; + } + + case NESTED_EXIT: + JS_NOT_REACHED("NESTED_EXIT should be replaced by innermost side exit"); + default: + debug_only_printf(LC_TMTracer, "exit_type=%s\n", getExitName(lr->exitType)); + js_AbortRecording(cx, "Inner tree not suitable for calling"); + return ARECORD_ABORTED; + } +} + +static bool +IsEntryTypeCompatible(jsval* vp, JSTraceType* m) +{ + unsigned tag = JSVAL_TAG(*vp); + + debug_only_printf(LC_TMTracer, "%c/%c ", tagChar[tag], typeChar[*m]); + + switch (*m) { + case TT_OBJECT: + if (tag == JSVAL_OBJECT && !JSVAL_IS_NULL(*vp) && + !HAS_FUNCTION_CLASS(JSVAL_TO_OBJECT(*vp))) { + return true; + } + debug_only_printf(LC_TMTracer, "object != tag%u ", tag); + return false; + case TT_INT32: + jsint i; + if (JSVAL_IS_INT(*vp)) + return true; + if (tag == JSVAL_DOUBLE && JSDOUBLE_IS_INT(*JSVAL_TO_DOUBLE(*vp), i)) + return true; + debug_only_printf(LC_TMTracer, "int != tag%u(value=%lu) ", tag, (unsigned long)*vp); + return false; + case TT_DOUBLE: + if (JSVAL_IS_INT(*vp) || tag == JSVAL_DOUBLE) + return true; + debug_only_printf(LC_TMTracer, "double != tag%u ", tag); + return false; + case TT_JSVAL: + JS_NOT_REACHED("shouldn't see jsval type in entry"); + return false; + case TT_STRING: + if (tag == JSVAL_STRING) + return true; + debug_only_printf(LC_TMTracer, "string != tag%u ", tag); + return false; + case TT_NULL: + if (JSVAL_IS_NULL(*vp)) + return true; + debug_only_printf(LC_TMTracer, "null != tag%u ", tag); + return false; + case TT_PSEUDOBOOLEAN: + if (tag == JSVAL_SPECIAL) + return true; + debug_only_printf(LC_TMTracer, "bool != tag%u ", tag); + return false; + default: + JS_ASSERT(*m == TT_FUNCTION); + if (tag == JSVAL_OBJECT && !JSVAL_IS_NULL(*vp) && + HAS_FUNCTION_CLASS(JSVAL_TO_OBJECT(*vp))) { + return true; + } + debug_only_printf(LC_TMTracer, "fun != tag%u ", tag); + return false; + } +} + +class TypeCompatibilityVisitor : public SlotVisitorBase +{ + TraceRecorder &mRecorder; + JSContext *mCx; + JSTraceType *mTypeMap; + unsigned mStackSlotNum; + bool mOk; +public: + TypeCompatibilityVisitor (TraceRecorder &recorder, + JSTraceType *typeMap) : + mRecorder(recorder), + mCx(mRecorder.cx), + mTypeMap(typeMap), + mStackSlotNum(0), + mOk(true) + {} + + JS_REQUIRES_STACK JS_ALWAYS_INLINE void + visitGlobalSlot(jsval *vp, unsigned n, unsigned slot) { + debug_only_printf(LC_TMTracer, "global%d=", n); + if (!IsEntryTypeCompatible(vp, mTypeMap)) { + mOk = false; + } else if (!isPromoteInt(mRecorder.get(vp)) && *mTypeMap == TT_INT32) { + oracle.markGlobalSlotUndemotable(mCx, slot); + mOk = false; + } else if (JSVAL_IS_INT(*vp) && *mTypeMap == TT_DOUBLE) { + oracle.markGlobalSlotUndemotable(mCx, slot); + } + mTypeMap++; + } + + JS_REQUIRES_STACK JS_ALWAYS_INLINE bool + visitStackSlots(jsval *vp, size_t count, JSStackFrame* fp) { + for (size_t i = 0; i < count; ++i) { + debug_only_printf(LC_TMTracer, "%s%u=", stackSlotKind(), unsigned(i)); + if (!IsEntryTypeCompatible(vp, mTypeMap)) { + mOk = false; + } else if (!isPromoteInt(mRecorder.get(vp)) && *mTypeMap == TT_INT32) { + oracle.markStackSlotUndemotable(mCx, mStackSlotNum); + mOk = false; + } else if (JSVAL_IS_INT(*vp) && *mTypeMap == TT_DOUBLE) { + oracle.markStackSlotUndemotable(mCx, mStackSlotNum); + } + vp++; + mTypeMap++; + mStackSlotNum++; + } + return true; + } + + bool isOk() { + return mOk; + } +}; + +JS_REQUIRES_STACK TreeFragment* +TraceRecorder::findNestedCompatiblePeer(TreeFragment* f) +{ + JSTraceMonitor* tm; + + tm = &JS_TRACE_MONITOR(cx); + unsigned int ngslots = treeInfo->globalSlots->length(); + + TreeInfo* ti; + for (; f != NULL; f = f->peer) { + if (!f->code()) + continue; + + ti = f->treeInfo; + + debug_only_printf(LC_TMTracer, "checking nested types %p: ", (void*)f); + + if (ngslots > ti->nGlobalTypes()) + SpecializeTreesToMissingGlobals(cx, globalObj, ti); + + /* + * Determine whether the typemap of the inner tree matches the outer + * tree's current state. If the inner tree expects an integer, but the + * outer tree doesn't guarantee an integer for that slot, we mark the + * slot undemotable and mismatch here. This will force a new tree to be + * compiled that accepts a double for the slot. If the inner tree + * expects a double, but the outer tree has an integer, we can proceed, + * but we mark the location undemotable. + */ + TypeCompatibilityVisitor visitor(*this, ti->typeMap.data()); + VisitSlots(visitor, cx, 0, *treeInfo->globalSlots); + + debug_only_printf(LC_TMTracer, " %s\n", visitor.isOk() ? "match" : ""); + if (visitor.isOk()) + return f; + } + + return NULL; +} + +class CheckEntryTypeVisitor : public SlotVisitorBase +{ + bool mOk; + JSTraceType *mTypeMap; +public: + CheckEntryTypeVisitor(JSTraceType *typeMap) : + mOk(true), + mTypeMap(typeMap) + {} + + JS_ALWAYS_INLINE void checkSlot(jsval *vp, char const *name, int i) { + debug_only_printf(LC_TMTracer, "%s%d=", name, i); + JS_ASSERT(*(uint8_t*)mTypeMap != 0xCD); + mOk = IsEntryTypeCompatible(vp, mTypeMap++); + } + + JS_REQUIRES_STACK JS_ALWAYS_INLINE void + visitGlobalSlot(jsval *vp, unsigned n, unsigned slot) { + if (mOk) + checkSlot(vp, "global", n); + } + + JS_REQUIRES_STACK JS_ALWAYS_INLINE bool + visitStackSlots(jsval *vp, size_t count, JSStackFrame* fp) { + for (size_t i = 0; i < count; ++i) { + if (!mOk) + break; + checkSlot(vp++, stackSlotKind(), i); + } + return mOk; + } + + bool isOk() { + return mOk; + } +}; + +/** + * Check if types are usable for trace execution. + * + * @param cx Context. + * @param ti Tree info of peer we're testing. + * @return True if compatible (with or without demotions), false otherwise. + */ +static JS_REQUIRES_STACK bool +CheckEntryTypes(JSContext* cx, JSObject* globalObj, TreeInfo* ti) +{ + unsigned int ngslots = ti->globalSlots->length(); + + JS_ASSERT(ti->nStackTypes == NativeStackSlots(cx, 0)); + + if (ngslots > ti->nGlobalTypes()) + SpecializeTreesToMissingGlobals(cx, globalObj, ti); + + JS_ASSERT(ti->typeMap.length() == NativeStackSlots(cx, 0) + ngslots); + JS_ASSERT(ti->typeMap.length() == ti->nStackTypes + ngslots); + JS_ASSERT(ti->nGlobalTypes() == ngslots); + + CheckEntryTypeVisitor visitor(ti->typeMap.data()); + VisitSlots(visitor, cx, 0, *ti->globalSlots); + + debug_only_print0(LC_TMTracer, "\n"); + return visitor.isOk(); +} + +/** + * Find an acceptable entry tree given a PC. + * + * @param cx Context. + * @param globalObj Global object. + * @param f First peer fragment. + * @param nodemote If true, will try to find a peer that does not require demotion. + * @out count Number of fragments consulted. + */ +static JS_REQUIRES_STACK TreeFragment* +FindVMCompatiblePeer(JSContext* cx, JSObject* globalObj, TreeFragment* f, uintN& count) +{ + count = 0; + for (; f != NULL; f = f->peer) { + if (!f->treeInfo) + continue; + debug_only_printf(LC_TMTracer, + "checking vm types %p (ip: %p): ", (void*)f, f->ip); + if (CheckEntryTypes(cx, globalObj, f->treeInfo)) + return f; + ++count; + } + return NULL; +} + +/* + * For the native stacks and global frame, reuse the storage in |tm->storage|. + * This reuse depends on the invariant that only one trace uses |tm->storage| at + * a time. This is subtley correct in lieu of deep bail; see comment for + * |deepBailSp| in js_DeepBail. + */ +JS_ALWAYS_INLINE +InterpState::InterpState(JSContext* cx, JSTraceMonitor* tm, TreeInfo* ti, + uintN& inlineCallCount, VMSideExit** innermostNestedGuardp) + : cx(cx), + stackBase(tm->storage.stack()), + sp(stackBase + ti->nativeStackBase / sizeof(double)), + eos(tm->storage.global()), + callstackBase(tm->storage.callstack()), + sor(callstackBase), + rp(callstackBase), + eor(callstackBase + JS_MIN(MAX_CALL_STACK_ENTRIES, + JS_MAX_INLINE_CALL_COUNT - inlineCallCount)), + lastTreeExitGuard(NULL), + lastTreeCallGuard(NULL), + rpAtLastTreeCall(NULL), + outermostTree(ti), + inlineCallCountp(&inlineCallCount), + innermostNestedGuardp(innermostNestedGuardp), +#ifdef EXECUTE_TREE_TIMER + startTime(rdtsc()), +#endif + builtinStatus(0), + nativeVp(NULL) +{ + JS_ASSERT(!tm->tracecx); + tm->tracecx = cx; + prev = cx->interpState; + cx->interpState = this; + + JS_ASSERT(eos == stackBase + MAX_NATIVE_STACK_SLOTS); + JS_ASSERT(sp < eos); + + /* + * inlineCallCount has already been incremented, if being invoked from + * EnterFrame. It is okay to have a 0-frame restriction since the JIT + * might not need any frames. + */ + JS_ASSERT(inlineCallCount <= JS_MAX_INLINE_CALL_COUNT); + +#ifdef DEBUG + /* + * Cannot 0xCD-fill global frame since it may overwrite a bailed outer + * ExecuteTree's 0xdeadbeefdeadbeef marker. + */ + memset(tm->storage.stack(), 0xCD, MAX_NATIVE_STACK_SLOTS * sizeof(double)); + memset(tm->storage.callstack(), 0xCD, MAX_CALL_STACK_ENTRIES * sizeof(FrameInfo*)); +#endif +} + +JS_ALWAYS_INLINE +InterpState::~InterpState() +{ + JS_ASSERT(!nativeVp); + + cx->interpState = prev; + JS_TRACE_MONITOR(cx).tracecx = NULL; +} + +/* Call |f|, return the exit taken. */ +static JS_ALWAYS_INLINE VMSideExit* +ExecuteTrace(JSContext* cx, Fragment* f, InterpState& state) +{ + JS_ASSERT(!cx->bailExit); + union { NIns *code; GuardRecord* (FASTCALL *func)(InterpState*); } u; + u.code = f->code(); + GuardRecord* rec; +#if defined(JS_NO_FASTCALL) && defined(NANOJIT_IA32) + SIMULATE_FASTCALL(rec, state, NULL, u.func); +#else + rec = u.func(&state); +#endif + JS_ASSERT(!cx->bailExit); + return (VMSideExit*)rec->exit; +} + +/* Check whether our assumptions about the incoming scope-chain are upheld. */ +static JS_REQUIRES_STACK JS_ALWAYS_INLINE bool +ScopeChainCheck(JSContext* cx, TreeInfo* ti, TreeFragment* f) +{ + JS_ASSERT(ti->globalObj() == f->globalObj); + JS_ASSERT(ti->globalObj() == JS_GetGlobalForObject(cx, cx->fp->scopeChain)); + + /* + * The JIT records and expects to execute with two scope-chain + * assumptions baked-in: + * + * 1. That the bottom of the scope chain is global, in the sense of + * JSCLASS_IS_GLOBAL. + * + * 2. That the scope chain between fp and the global is free of + * "unusual" native objects such as HTML forms or other funny + * things. + * + * #2 is checked here while following the scope-chain links, via + * js_IsCacheableNonGlobalScope, which consults a whitelist of known + * class types; once a global is found, it's checked for #1. Failing + * either check causes an early return from execution. + */ + JSObject* child = cx->fp->scopeChain; + while (JSObject* parent = OBJ_GET_PARENT(cx, child)) { + if (!js_IsCacheableNonGlobalScope(child)) { + debug_only_print0(LC_TMTracer,"Blacklist: non-cacheable object on scope chain.\n"); + Blacklist((jsbytecode*) f->root->ip); + return false; + } + child = parent; + } + JS_ASSERT(child == f->globalObj); + + if (!(OBJ_GET_CLASS(cx, f->globalObj)->flags & JSCLASS_IS_GLOBAL)) { + debug_only_print0(LC_TMTracer, "Blacklist: non-global at root of scope chain.\n"); + Blacklist((jsbytecode*) f->root->ip); + return false; + } + + /* Make sure the global object is sane. */ + JS_ASSERT(STOBJ_NSLOTS(f->globalObj) <= MAX_GLOBAL_SLOTS); + JS_ASSERT(ti->nGlobalTypes() == ti->globalSlots->length()); + JS_ASSERT_IF(ti->globalSlots->length() != 0, + OBJ_SHAPE(f->globalObj) == f->globalShape); + return true; +} + +static void +LeaveTree(InterpState&, VMSideExit* lr); + +static JS_REQUIRES_STACK VMSideExit* +ExecuteTree(JSContext* cx, TreeFragment* f, uintN& inlineCallCount, + VMSideExit** innermostNestedGuardp) +{ +#ifdef MOZ_TRACEVIS + TraceVisStateObj tvso(cx, S_EXECUTE); +#endif + JS_ASSERT(f->root == f && f->code() && f->treeInfo); + JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx); + TreeInfo* ti = f->treeInfo; + + if (!ScopeChainCheck(cx, ti, f)) + return NULL; + + /* Initialize trace state. */ + InterpState state(cx, tm, ti, inlineCallCount, innermostNestedGuardp); + double* stack = tm->storage.stack(); + double* global = tm->storage.global(); + JSObject* globalObj = f->globalObj; + unsigned ngslots = ti->globalSlots->length(); + uint16* gslots = ti->globalSlots->data(); + + BuildNativeFrame(cx, globalObj, 0 /* callDepth */, ngslots, gslots, + ti->typeMap.data(), global, stack); + + AUDIT(traceTriggered); + debug_only_printf(LC_TMTracer, + "entering trace at %s:%u@%u, native stack slots: %u code: %p\n", + cx->fp->script->filename, + js_FramePCToLineNumber(cx, cx->fp), + FramePCOffset(cx->fp), + ti->maxNativeStackSlots, + f->code()); + + debug_only_stmt(uint32 globalSlots = STOBJ_NSLOTS(globalObj);) + debug_only_stmt(*(uint64*)&tm->storage.global()[globalSlots] = 0xdeadbeefdeadbeefLL;) + + /* Execute trace. */ +#ifdef MOZ_TRACEVIS + VMSideExit* lr = (TraceVisStateObj(cx, S_NATIVE), ExecuteTrace(cx, f, state)); +#else + VMSideExit* lr = ExecuteTrace(cx, f, state); +#endif + + JS_ASSERT(*(uint64*)&tm->storage.global()[globalSlots] == 0xdeadbeefdeadbeefLL); + JS_ASSERT_IF(lr->exitType == LOOP_EXIT, !lr->calldepth); + + /* Restore interpreter state. */ + LeaveTree(state, lr); + return state.innermost; +} + +class Guardian { + bool *flagp; +public: + Guardian(bool *flagp) { + this->flagp = flagp; + JS_ASSERT(!*flagp); + *flagp = true; + } + + ~Guardian() { + JS_ASSERT(*flagp); + *flagp = false; + } +}; + +static JS_FORCES_STACK void +LeaveTree(InterpState& state, VMSideExit* lr) +{ + VOUCH_DOES_NOT_REQUIRE_STACK(); + + JSContext* cx = state.cx; + + /* Temporary waive the soft GC quota to make sure LeaveTree() doesn't fail. */ + Guardian waiver(&JS_THREAD_DATA(cx)->waiveGCQuota); + + FrameInfo** callstack = state.callstackBase; + double* stack = state.stackBase; + + /* + * Except if we find that this is a nested bailout, the guard the call + * returned is the one we have to use to adjust pc and sp. + */ + VMSideExit* innermost = lr; + + /* + * While executing a tree we do not update state.sp and state.rp even if + * they grow. Instead, guards tell us by how much sp and rp should be + * incremented in case of a side exit. When calling a nested tree, however, + * we actively adjust sp and rp. If we have such frames from outer trees on + * the stack, then rp will have been adjusted. Before we can process the + * stack of the frames of the tree we directly exited from, we have to + * first work our way through the outer frames and generate interpreter + * frames for them. Once the call stack (rp) is empty, we can process the + * final frames (which again are not directly visible and only the guard we + * exited on will tells us about). + */ + FrameInfo** rp = (FrameInfo**)state.rp; + if (lr->exitType == NESTED_EXIT) { + VMSideExit* nested = state.lastTreeCallGuard; + if (!nested) { + /* + * If lastTreeCallGuard is not set in state, we only have a single + * level of nesting in this exit, so lr itself is the innermost and + * outermost nested guard, and hence we set nested to lr. The + * calldepth of the innermost guard is not added to state.rp, so we + * do it here manually. For a nesting depth greater than 1 the + * call tree code already added the innermost guard's calldepth + * to state.rpAtLastTreeCall. + */ + nested = lr; + rp += lr->calldepth; + } else { + /* + * During unwinding state.rp gets overwritten at every step and we + * restore it here to its state at the innermost nested guard. The + * builtin already added the calldepth of that innermost guard to + * rpAtLastTreeCall. + */ + rp = (FrameInfo**)state.rpAtLastTreeCall; + } + innermost = state.lastTreeExitGuard; + if (state.innermostNestedGuardp) + *state.innermostNestedGuardp = nested; + JS_ASSERT(nested); + JS_ASSERT(nested->exitType == NESTED_EXIT); + JS_ASSERT(state.lastTreeExitGuard); + JS_ASSERT(state.lastTreeExitGuard->exitType != NESTED_EXIT); + } + + int32_t bs = state.builtinStatus; + bool bailed = innermost->exitType == STATUS_EXIT && (bs & JSBUILTIN_BAILED); + if (bailed) { + /* + * Deep-bail case. + * + * A _FAIL native already called LeaveTree. We already reconstructed + * the interpreter stack, in pre-call state, with pc pointing to the + * CALL/APPLY op, for correctness. Then we continued in native code. + * + * First, if we just returned from a slow native, pop its stack frame. + */ + if (!cx->fp->script) { + JSStackFrame *fp = cx->fp; + JS_ASSERT(FUN_SLOW_NATIVE(fp->fun)); + JS_ASSERT(!fp->regs); + JS_ASSERT(fp->down->regs != &((JSInlineFrame *) fp)->callerRegs); + cx->fp = fp->down; + JS_ARENA_RELEASE(&cx->stackPool, ((JSInlineFrame *) fp)->mark); + } + JS_ASSERT(cx->fp->script); + + if (!(bs & JSBUILTIN_ERROR)) { + /* + * The builtin or native deep-bailed but finished successfully + * (no exception or error). + * + * After it returned, the JIT code stored the results of the + * builtin or native at the top of the native stack and then + * immediately flunked the guard on state->builtinStatus. + * + * Now LeaveTree has been called again from the tail of + * ExecuteTree. We are about to return to the interpreter. Adjust + * the top stack frame to resume on the next op. + */ + JSFrameRegs* regs = cx->fp->regs; + JSOp op = (JSOp) *regs->pc; + JS_ASSERT(op == JSOP_CALL || op == JSOP_APPLY || op == JSOP_NEW || + op == JSOP_GETPROP || op == JSOP_GETTHISPROP || op == JSOP_GETARGPROP || + op == JSOP_GETLOCALPROP || op == JSOP_LENGTH || + op == JSOP_GETELEM || op == JSOP_CALLELEM || + op == JSOP_SETPROP || op == JSOP_SETNAME || op == JSOP_SETMETHOD || + op == JSOP_SETELEM || op == JSOP_INITELEM || + op == JSOP_INSTANCEOF); + + /* + * JSOP_SETELEM can be coalesced with a JSOP_POP in the interpeter. + * Since this doesn't re-enter the recorder, the post-state snapshot + * is invalid. Fix it up here. + */ + if (op == JSOP_SETELEM && JSOp(regs->pc[JSOP_SETELEM_LENGTH]) == JSOP_POP) { + regs->sp -= js_CodeSpec[JSOP_SETELEM].nuses; + regs->sp += js_CodeSpec[JSOP_SETELEM].ndefs; + regs->pc += JSOP_SETELEM_LENGTH; + op = JSOP_POP; + } + + const JSCodeSpec& cs = js_CodeSpec[op]; + regs->sp -= (cs.format & JOF_INVOKE) ? GET_ARGC(regs->pc) + 2 : cs.nuses; + regs->sp += cs.ndefs; + regs->pc += cs.length; + JS_ASSERT_IF(!cx->fp->imacpc, + cx->fp->slots + cx->fp->script->nfixed + + js_ReconstructStackDepth(cx, cx->fp->script, regs->pc) == + regs->sp); + + /* + * If there's a tree call around the point that we deep exited at, + * then state.sp and state.rp were restored to their original + * values before the tree call and sp might be less than deepBailSp, + * which we sampled when we were told to deep bail. + */ + JS_ASSERT(state.deepBailSp >= state.stackBase && state.sp <= state.deepBailSp); + + /* + * As explained above, the JIT code stored a result value or values + * on the native stack. Transfer them to the interpreter stack now. + * (Some opcodes, like JSOP_CALLELEM, produce two values, hence the + * loop.) + */ + JSTraceType* typeMap = innermost->stackTypeMap(); + for (int i = 1; i <= cs.ndefs; i++) { + if (!js_NativeToValue(cx, + regs->sp[-i], + typeMap[innermost->numStackSlots - i], + (jsdouble *) state.deepBailSp + + innermost->sp_adj / sizeof(jsdouble) - i)) { + OutOfMemoryAbort(); + } + } + } + return; + } + + /* Save the innermost FrameInfo for guardUpRecursion */ + if (innermost->exitType == RECURSIVE_MISMATCH_EXIT) { + /* There should never be a static calldepth for a recursive mismatch. */ + JS_ASSERT(innermost->calldepth == 0); + /* There must be at least one item on the rp stack. */ + JS_ASSERT(callstack < rp); + /* :TODO: don't be all squirrelin' this in here */ + innermost->recursive_down = *(rp - 1); + } + + /* Slurp failure should have no frames */ + JS_ASSERT_IF(innermost->exitType == RECURSIVE_SLURP_FAIL_EXIT, + innermost->calldepth == 0 && callstack == rp); + + while (callstack < rp) { + FrameInfo* fi = *callstack; + /* Peek at the callee native slot in the not-yet-synthesized down frame. */ + JSObject* callee = *(JSObject**)&stack[fi->callerHeight]; + + /* + * Synthesize a stack frame and write out the values in it using the + * type map pointer on the native call stack. + */ + SynthesizeFrame(cx, *fi, callee); + int slots = FlushNativeStackFrame(cx, 1 /* callDepth */, (*callstack)->get_typemap(), + stack, cx->fp, 0); +#ifdef DEBUG + JSStackFrame* fp = cx->fp; + debug_only_printf(LC_TMTracer, + "synthesized deep frame for %s:%u@%u, slots=%d, fi=%p\n", + fp->script->filename, + js_FramePCToLineNumber(cx, fp), + FramePCOffset(fp), + slots, + (void*)*callstack); +#endif + /* + * Keep track of the additional frames we put on the interpreter stack + * and the native stack slots we consumed. + */ + ++*state.inlineCallCountp; + ++callstack; + stack += slots; + } + + /* + * We already synthesized the frames around the innermost guard. Here we + * just deal with additional frames inside the tree we are bailing out + * from. + */ + JS_ASSERT(rp == callstack); + unsigned calldepth = innermost->calldepth; + unsigned calldepth_slots = 0; + unsigned calleeOffset = 0; + for (unsigned n = 0; n < calldepth; ++n) { + /* Peek at the callee native slot in the not-yet-synthesized down frame. */ + calleeOffset += callstack[n]->callerHeight; + JSObject* callee = *(JSObject**)&stack[calleeOffset]; + + /* Reconstruct the frame. */ + calldepth_slots += SynthesizeFrame(cx, *callstack[n], callee); + ++*state.inlineCallCountp; +#ifdef DEBUG + JSStackFrame* fp = cx->fp; + debug_only_printf(LC_TMTracer, + "synthesized shallow frame for %s:%u@%u\n", + fp->script->filename, js_FramePCToLineNumber(cx, fp), + FramePCOffset(fp)); +#endif + } + + /* + * Adjust sp and pc relative to the tree we exited from (not the tree we + * entered into). These are our final values for sp and pc since + * SynthesizeFrame has already taken care of all frames in between. But + * first we recover fp->blockChain, which comes from the side exit + * struct. + */ + JSStackFrame* fp = cx->fp; + + fp->blockChain = innermost->block; + + /* + * If we are not exiting from an inlined frame, the state->sp is spbase. + * Otherwise spbase is whatever slots frames around us consume. + */ + fp->regs->pc = innermost->pc; + fp->imacpc = innermost->imacpc; + fp->regs->sp = StackBase(fp) + (innermost->sp_adj / sizeof(double)) - calldepth_slots; + JS_ASSERT_IF(!fp->imacpc, + fp->slots + fp->script->nfixed + + js_ReconstructStackDepth(cx, fp->script, fp->regs->pc) == fp->regs->sp); + +#ifdef EXECUTE_TREE_TIMER + uint64 cycles = rdtsc() - state.startTime; +#elif defined(JS_JIT_SPEW) + uint64 cycles = 0; +#endif + + debug_only_printf(LC_TMTracer, + "leaving trace at %s:%u@%u, op=%s, lr=%p, exitType=%s, sp=%lld, " + "calldepth=%d, cycles=%llu\n", + fp->script->filename, + js_FramePCToLineNumber(cx, fp), + FramePCOffset(fp), + js_CodeName[fp->imacpc ? *fp->imacpc : *fp->regs->pc], + (void*)lr, + getExitName(lr->exitType), + (long long int)(fp->regs->sp - StackBase(fp)), + calldepth, + (unsigned long long int)cycles); + + /* + * If this trace is part of a tree, later branches might have added + * additional globals for which we don't have any type information + * available in the side exit. We merge in this information from the entry + * type-map. See also the comment in the constructor of TraceRecorder + * regarding why this is always safe to do. + */ + TreeInfo* outermostTree = state.outermostTree; + uint16* gslots = outermostTree->globalSlots->data(); + unsigned ngslots = outermostTree->globalSlots->length(); + JS_ASSERT(ngslots == outermostTree->nGlobalTypes()); + JSTraceType* globalTypeMap; + + /* Are there enough globals? */ + Queue typeMap(0); + if (innermost->numGlobalSlots == ngslots) { + /* Yes. This is the ideal fast path. */ + globalTypeMap = innermost->globalTypeMap(); + } else { + /* + * No. Merge the typemap of the innermost entry and exit together. This + * should always work because it is invalid for nested trees or linked + * trees to have incompatible types. Thus, whenever a new global type + * is lazily added into a tree, all dependent and linked trees are + * immediately specialized (see bug 476653). + */ + JS_ASSERT(innermost->root()->treeInfo->nGlobalTypes() == ngslots); + JS_ASSERT(innermost->root()->treeInfo->nGlobalTypes() > innermost->numGlobalSlots); + typeMap.ensure(ngslots); +#ifdef DEBUG + unsigned check_ngslots = +#endif + BuildGlobalTypeMapFromInnerTree(typeMap, innermost); + JS_ASSERT(check_ngslots == ngslots); + globalTypeMap = typeMap.data(); + } + + /* Write back the topmost native stack frame. */ + unsigned ignoreSlots = innermost->exitType == RECURSIVE_SLURP_FAIL_EXIT ? + innermost->numStackSlots - 1 : 0; +#ifdef DEBUG + int slots = +#endif + FlushNativeStackFrame(cx, innermost->calldepth, + innermost->stackTypeMap(), + stack, NULL, ignoreSlots); + JS_ASSERT(unsigned(slots) == innermost->numStackSlots); + + if (innermost->nativeCalleeWord) + SynthesizeSlowNativeFrame(state, cx, innermost); + + /* Write back interned globals. */ + JS_ASSERT(state.eos == state.stackBase + MAX_NATIVE_STACK_SLOTS); + JSObject* globalObj = outermostTree->globalObj(); + FlushNativeGlobalFrame(cx, globalObj, state.eos, ngslots, gslots, globalTypeMap); +#ifdef DEBUG + /* Verify that our state restoration worked. */ + for (JSStackFrame* fp = cx->fp; fp; fp = fp->down) { + JS_ASSERT_IF(fp->argv, JSVAL_IS_OBJECT(fp->argv[-1])); + } +#endif +#ifdef JS_JIT_SPEW + if (innermost->exitType != TIMEOUT_EXIT) + AUDIT(sideExitIntoInterpreter); + else + AUDIT(timeoutIntoInterpreter); +#endif + + state.innermost = innermost; +} + +JS_REQUIRES_STACK bool +js_MonitorLoopEdge(JSContext* cx, uintN& inlineCallCount, RecordReason reason) +{ +#ifdef MOZ_TRACEVIS + TraceVisStateObj tvso(cx, S_MONITOR); +#endif + + JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx); + + /* Is the recorder currently active? */ + if (tm->recorder) { + jsbytecode* innerLoopHeaderPC = cx->fp->regs->pc; + + if (TraceRecorder::recordLoopEdge(cx, tm->recorder, inlineCallCount)) + return true; + + /* + * recordLoopEdge will invoke an inner tree if we have a matching + * one. If we arrive here, that tree didn't run to completion and + * instead we mis-matched or the inner tree took a side exit other than + * the loop exit. We are thus no longer guaranteed to be parked on the + * same loop header js_MonitorLoopEdge was called for. In fact, this + * might not even be a loop header at all. Hence if the program counter + * no longer hovers over the inner loop header, return to the + * interpreter and do not attempt to trigger or record a new tree at + * this location. + */ + if (innerLoopHeaderPC != cx->fp->regs->pc) { +#ifdef MOZ_TRACEVIS + tvso.r = R_INNER_SIDE_EXIT; +#endif + return false; + } + } + JS_ASSERT(!tm->recorder); + + /* + * Make sure the shape of the global object still matches (this might flush + * the JIT cache). + */ + JSObject* globalObj = JS_GetGlobalForObject(cx, cx->fp->scopeChain); + uint32 globalShape = -1; + SlotList* globalSlots = NULL; + + if (!CheckGlobalObjectShape(cx, tm, globalObj, &globalShape, &globalSlots)) { + Backoff(cx, cx->fp->regs->pc); + return false; + } + + /* Do not enter the JIT code with a pending operation callback. */ + if (cx->operationCallbackFlag) { +#ifdef MOZ_TRACEVIS + tvso.r = R_CALLBACK_PENDING; +#endif + return false; + } + + jsbytecode* pc = cx->fp->regs->pc; + uint32 argc = cx->fp->argc; + + TreeFragment* f = LookupOrAddLoop(tm, pc, globalObj, globalShape, argc); + + /* + * If we have no code in the anchor and no peers, we definitively won't be + * able to activate any trees, so start compiling. + */ + if (!f->code() && !f->peer) { + record: + if (++f->hits() < HOTLOOP) { +#ifdef MOZ_TRACEVIS + tvso.r = f->hits() < 1 ? R_BACKED_OFF : R_COLD; +#endif + return false; + } + + /* + * We can give RecordTree the root peer. If that peer is already taken, + * it will walk the peer list and find us a free slot or allocate a new + * tree if needed. + */ + bool rv = RecordTree(cx, tm, f->first, NULL, 0, globalObj, globalShape, + globalSlots, argc, reason); +#ifdef MOZ_TRACEVIS + if (!rv) + tvso.r = R_FAIL_RECORD_TREE; +#endif + return rv; + } + + debug_only_printf(LC_TMTracer, + "Looking for compat peer %d@%d, from %p (ip: %p)\n", + js_FramePCToLineNumber(cx, cx->fp), + FramePCOffset(cx->fp), (void*)f, f->ip); + + uintN count; + TreeFragment* match = FindVMCompatiblePeer(cx, globalObj, f, count); + if (!match) { + if (count < MAXPEERS) + goto record; + + /* + * If we hit the max peers ceiling, don't try to lookup fragments all + * the time. That's expensive. This must be a rather type-unstable loop. + */ + debug_only_print0(LC_TMTracer, "Blacklisted: too many peer trees.\n"); + Blacklist((jsbytecode*) f->root->ip); +#ifdef MOZ_TRACEVIS + tvso.r = R_MAX_PEERS; +#endif + return false; + } + + /* + * Trees that only unwind recursive frames usually won't do much work, and + * most time will be spent entering and exiting ExecuteTree(). There's no + * benefit to doing this until the down-recursive side completes. + */ + if (match->treeInfo->recursion == Recursion_Unwinds) + return false; + + VMSideExit* lr = NULL; + VMSideExit* innermostNestedGuard = NULL; + + lr = ExecuteTree(cx, match, inlineCallCount, &innermostNestedGuard); + if (!lr) { +#ifdef MOZ_TRACEVIS + tvso.r = R_FAIL_EXECUTE_TREE; +#endif + return false; + } + + /* + * If we exit on a branch, or on a tree call guard, try to grow the inner + * tree (in case of a branch exit), or the tree nested around the tree we + * exited from (in case of the tree call guard). + */ + bool rv; + switch (lr->exitType) { + case RECURSIVE_UNLINKED_EXIT: + case UNSTABLE_LOOP_EXIT: + rv = AttemptToStabilizeTree(cx, globalObj, lr, NULL, 0); +#ifdef MOZ_TRACEVIS + if (!rv) + tvso.r = R_FAIL_STABILIZE; +#endif + return rv; + + case OVERFLOW_EXIT: + oracle.markInstructionUndemotable(cx->fp->regs->pc); + /* FALL THROUGH */ + case RECURSIVE_SLURP_FAIL_EXIT: + case RECURSIVE_SLURP_MISMATCH_EXIT: + case RECURSIVE_EMPTY_RP_EXIT: + case RECURSIVE_MISMATCH_EXIT: + case BRANCH_EXIT: + case CASE_EXIT: + return AttemptToExtendTree(cx, lr, NULL, NULL +#ifdef MOZ_TRACEVIS + , &tvso +#endif + ); + + case RECURSIVE_LOOP_EXIT: + case LOOP_EXIT: + if (innermostNestedGuard) + return AttemptToExtendTree(cx, innermostNestedGuard, lr, NULL +#ifdef MOZ_TRACEVIS + , &tvso +#endif + ); +#ifdef MOZ_TRACEVIS + tvso.r = R_NO_EXTEND_OUTER; +#endif + return false; + +#ifdef MOZ_TRACEVIS + case MISMATCH_EXIT: tvso.r = R_MISMATCH_EXIT; return false; + case OOM_EXIT: tvso.r = R_OOM_EXIT; return false; + case TIMEOUT_EXIT: tvso.r = R_TIMEOUT_EXIT; return false; + case DEEP_BAIL_EXIT: tvso.r = R_DEEP_BAIL_EXIT; return false; + case STATUS_EXIT: tvso.r = R_STATUS_EXIT; return false; +#endif + + default: + /* + * No, this was an unusual exit (i.e. out of memory/GC), so just resume + * interpretation. + */ +#ifdef MOZ_TRACEVIS + tvso.r = R_OTHER_EXIT; +#endif + return false; + } +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::monitorRecording(JSOp op) +{ + JSTraceMonitor &localtm = JS_TRACE_MONITOR(cx); + debug_only_stmt( JSContext *localcx = cx; ) + + /* Process needFlush requests now. */ + if (localtm.needFlush) { + ResetJIT(cx, FR_DEEP_BAIL); + return ARECORD_ABORTED; + } + JS_ASSERT(!fragment->lastIns); + + /* + * Clear one-shot state used to communicate between record_JSOP_CALL and post- + * opcode-case-guts record hook (record_NativeCallComplete). + */ + pendingSpecializedNative = NULL; + newobj_ins = NULL; + + /* Handle one-shot request from finishGetProp or INSTANCEOF to snapshot post-op state and guard. */ + if (pendingGuardCondition) { + guard(true, pendingGuardCondition, STATUS_EXIT); + pendingGuardCondition = NULL; + } + + /* Handle one-shot request to unbox the result of a property get. */ + if (pendingUnboxSlot) { + LIns* val_ins = get(pendingUnboxSlot); + val_ins = unbox_jsval(*pendingUnboxSlot, val_ins, snapshot(BRANCH_EXIT)); + set(pendingUnboxSlot, val_ins); + pendingUnboxSlot = 0; + } + + debug_only_stmt( + if (js_LogController.lcbits & LC_TMRecorder) { + js_Disassemble1(cx, cx->fp->script, cx->fp->regs->pc, + cx->fp->imacpc + ? 0 : cx->fp->regs->pc - cx->fp->script->code, + !cx->fp->imacpc, stdout); + } + ) + + /* + * If op is not a break or a return from a loop, continue recording and + * follow the trace. We check for imacro-calling bytecodes inside each + * switch case to resolve the if (JSOP_IS_IMACOP(x)) conditions at compile + * time. + */ + + AbortableRecordingStatus status; +#ifdef DEBUG + bool wasInImacro = (cx->fp->imacpc != NULL); +#endif + switch (op) { + default: + status = ARECORD_ERROR; + goto stop_recording; +# define OPDEF(x,val,name,token,length,nuses,ndefs,prec,format) \ + case x: \ + status = this->record_##x(); \ + if (JSOP_IS_IMACOP(x)) \ + goto imacro; \ + break; +# include "jsopcode.tbl" +# undef OPDEF + } + + /* Careful, |this| may have been deleted. */ + JS_ASSERT(status != ARECORD_IMACRO); + JS_ASSERT_IF(!wasInImacro, localcx->fp->imacpc == NULL); + + imacro: + /* |this| may be killed while recording. */ + if (status == ARECORD_COMPLETED) { + JS_ASSERT(localtm.recorder != this); + return localtm.recorder ? ARECORD_CONTINUE : ARECORD_COMPLETED; + } + if (status == ARECORD_ABORTED) { + JS_ASSERT(!localtm.recorder); + return ARECORD_ABORTED; + } + + stop_recording: + /* Handle lazy abort / OOM. */ + if (outOfMemory() || js_OverfullJITCache(&localtm)) { + ResetJIT(cx, FR_OOM); + return ARECORD_ABORTED; + } + if (StatusAbortsRecording(status)) { + js_AbortRecording(cx, js_CodeName[op]); + return ARECORD_ABORTED; + } + + return status; +} + +JS_REQUIRES_STACK void +js_AbortRecording(JSContext* cx, const char* reason) +{ +#ifdef DEBUG + JS_ASSERT(TRACE_RECORDER(cx)); + TRACE_RECORDER(cx)->finishAbort(reason); +#else + TRACE_RECORDER(cx)->finishAbort("[no reason]"); +#endif +} + +#if defined NANOJIT_IA32 +static bool +CheckForSSE2() +{ + char *c = getenv("X86_FORCE_SSE2"); + if (c) + return (!strcmp(c, "true") || + !strcmp(c, "1") || + !strcmp(c, "yes")); + + int features = 0; +#if defined _MSC_VER + __asm + { + pushad + mov eax, 1 + cpuid + mov features, edx + popad + } +#elif defined __GNUC__ + asm("xchg %%esi, %%ebx\n" /* we can't clobber ebx on gcc (PIC register) */ + "mov $0x01, %%eax\n" + "cpuid\n" + "mov %%edx, %0\n" + "xchg %%esi, %%ebx\n" + : "=m" (features) + : /* We have no inputs */ + : "%eax", "%esi", "%ecx", "%edx" + ); +#elif defined __SUNPRO_C || defined __SUNPRO_CC + asm("push %%ebx\n" + "mov $0x01, %%eax\n" + "cpuid\n" + "pop %%ebx\n" + : "=d" (features) + : /* We have no inputs */ + : "%eax", "%ecx" + ); +#endif + return (features & (1<<26)) != 0; +} +#endif + +#if defined(NANOJIT_ARM) + +#if defined(_MSC_VER) && defined(WINCE) + +// these come in from jswince.asm +extern "C" int js_arm_try_thumb_op(); +extern "C" int js_arm_try_armv6t2_op(); +extern "C" int js_arm_try_armv5_op(); +extern "C" int js_arm_try_armv6_op(); +extern "C" int js_arm_try_armv7_op(); +extern "C" int js_arm_try_vfp_op(); + +static bool +js_arm_check_thumb() { + bool ret = false; + __try { + js_arm_try_thumb_op(); + ret = true; + } __except(GetExceptionCode() == EXCEPTION_ILLEGAL_INSTRUCTION) { + ret = false; + } + return ret; +} + +static bool +js_arm_check_thumb2() { + bool ret = false; + __try { + js_arm_try_armv6t2_op(); + ret = true; + } __except(GetExceptionCode() == EXCEPTION_ILLEGAL_INSTRUCTION) { + ret = false; + } + return ret; +} + +static unsigned int +js_arm_check_arch() { + unsigned int arch = 4; + __try { + js_arm_try_armv5_op(); + arch = 5; + js_arm_try_armv6_op(); + arch = 6; + js_arm_try_armv7_op(); + arch = 7; + } __except(GetExceptionCode() == EXCEPTION_ILLEGAL_INSTRUCTION) { + } + return arch; +} + +static bool +js_arm_check_vfp() { +#ifdef WINCE_WINDOWS_MOBILE + return false; +#else + bool ret = false; + __try { + js_arm_try_vfp_op(); + ret = true; + } __except(GetExceptionCode() == EXCEPTION_ILLEGAL_INSTRUCTION) { + ret = false; + } + return ret; +#endif +} + +#define HAVE_ENABLE_DISABLE_DEBUGGER_EXCEPTIONS 1 + +/* See "Suppressing Exception Notifications while Debugging", at + * http://msdn.microsoft.com/en-us/library/ms924252.aspx + */ +static void +js_disable_debugger_exceptions() +{ + // 2 == TLSSLOT_KERNEL + DWORD kctrl = (DWORD) TlsGetValue(2); + // 0x12 = TLSKERN_NOFAULT | TLSKERN_NOFAULTMSG + kctrl |= 0x12; + TlsSetValue(2, (LPVOID) kctrl); +} + +static void +js_enable_debugger_exceptions() +{ + // 2 == TLSSLOT_KERNEL + DWORD kctrl = (DWORD) TlsGetValue(2); + // 0x12 = TLSKERN_NOFAULT | TLSKERN_NOFAULTMSG + kctrl &= ~0x12; + TlsSetValue(2, (LPVOID) kctrl); +} + +#elif defined(__GNUC__) && defined(AVMPLUS_LINUX) + +#include +#include +#include +#include +#include +#include +#include +#include + +// Assume ARMv4 by default. +static unsigned int arm_arch = 4; +static bool arm_has_thumb = false; +static bool arm_has_vfp = false; +static bool arm_has_neon = false; +static bool arm_has_iwmmxt = false; +static bool arm_tests_initialized = false; + +static void +arm_read_auxv() { + int fd; + Elf32_auxv_t aux; + + fd = open("/proc/self/auxv", O_RDONLY); + if (fd > 0) { + while (read(fd, &aux, sizeof(Elf32_auxv_t))) { + if (aux.a_type == AT_HWCAP) { + uint32_t hwcap = aux.a_un.a_val; + if (getenv("ARM_FORCE_HWCAP")) + hwcap = strtoul(getenv("ARM_FORCE_HWCAP"), NULL, 0); + // hardcode these values to avoid depending on specific versions + // of the hwcap header, e.g. HWCAP_NEON + arm_has_thumb = (hwcap & 4) != 0; + arm_has_vfp = (hwcap & 64) != 0; + arm_has_iwmmxt = (hwcap & 512) != 0; + // this flag is only present on kernel 2.6.29 + arm_has_neon = (hwcap & 4096) != 0; + } else if (aux.a_type == AT_PLATFORM) { + const char *plat = (const char*) aux.a_un.a_val; + if (getenv("ARM_FORCE_PLATFORM")) + plat = getenv("ARM_FORCE_PLATFORM"); + // The platform string has the form "v[0-9][lb]". The "l" or "b" indicate little- + // or big-endian variants and the digit indicates the version of the platform. + // We can only accept ARMv4 and above, but allow anything up to ARMv9 for future + // processors. Architectures newer than ARMv7 are assumed to be + // backwards-compatible with ARMv7. + if ((plat[0] == 'v') && + (plat[1] >= '4') && (plat[1] <= '9') && + ((plat[2] == 'l') || (plat[2] == 'b'))) + { + arm_arch = plat[1] - '0'; + } + else + { + // For production code, ignore invalid (or unexpected) platform strings and + // fall back to the default. For debug code, use an assertion to catch this + // when not running in scratchbox. + if (getenv("_SBOX_DIR") == NULL) + JS_ASSERT(false); + } + } + } + close (fd); + + // if we don't have 2.6.29, we have to do this hack; set + // the env var to trust HWCAP. + if (!getenv("ARM_TRUST_HWCAP") && (arm_arch >= 7)) + arm_has_neon = true; + } + + arm_tests_initialized = true; +} + +static bool +js_arm_check_thumb() { + if (!arm_tests_initialized) + arm_read_auxv(); + + return arm_has_thumb; +} + +static bool +js_arm_check_thumb2() { + if (!arm_tests_initialized) + arm_read_auxv(); + + // ARMv6T2 also supports Thumb2, but Linux doesn't provide an easy way to test for this as + // there is no associated bit in auxv. ARMv7 always supports Thumb2, and future architectures + // are assumed to be backwards-compatible. + return (arm_arch >= 7); +} + +static unsigned int +js_arm_check_arch() { + if (!arm_tests_initialized) + arm_read_auxv(); + + return arm_arch; +} + +static bool +js_arm_check_vfp() { + if (!arm_tests_initialized) + arm_read_auxv(); + + return arm_has_vfp; +} + +#else +#warning Not sure how to check for architecture variant on your platform. Assuming ARMv4. +static bool +js_arm_check_thumb() { return false; } +static bool +js_arm_check_thumb2() { return false; } +static unsigned int +js_arm_check_arch() { return 4; } +static bool +js_arm_check_vfp() { return false; } +#endif + +#ifndef HAVE_ENABLE_DISABLE_DEBUGGER_EXCEPTIONS +static void +js_enable_debugger_exceptions() { } +static void +js_disable_debugger_exceptions() { } +#endif + +#endif /* NANOJIT_ARM */ + +#define K *1024 +#define M K K +#define G K M + +void +js_SetMaxCodeCacheBytes(JSContext* cx, uint32 bytes) +{ + JSTraceMonitor* tm = &JS_THREAD_DATA(cx)->traceMonitor; + JS_ASSERT(tm->codeAlloc && tm->dataAlloc && tm->traceAlloc); + if (bytes > 1 G) + bytes = 1 G; + if (bytes < 128 K) + bytes = 128 K; + tm->maxCodeCacheBytes = bytes; +} + +void +js_InitJIT(JSTraceMonitor *tm) +{ +#if defined JS_JIT_SPEW + tm->profAlloc = NULL; + /* Set up debug logging. */ + if (!did_we_set_up_debug_logging) { + InitJITLogController(); + did_we_set_up_debug_logging = true; + } + /* Set up fragprofiling, if required. */ + if (js_LogController.lcbits & LC_FragProfile) { + tm->profAlloc = new VMAllocator(); + tm->profTab = new (*tm->profAlloc) FragStatsMap(*tm->profAlloc); + } + tm->lastFragID = 0; +#else + memset(&js_LogController, 0, sizeof(js_LogController)); +#endif + + if (!did_we_check_processor_features) { +#if defined NANOJIT_IA32 + avmplus::AvmCore::config.use_cmov = + avmplus::AvmCore::config.sse2 = CheckForSSE2(); + avmplus::AvmCore::config.fixed_esp = true; +#endif +#if defined NANOJIT_ARM + + js_disable_debugger_exceptions(); + + bool arm_vfp = js_arm_check_vfp(); + bool arm_thumb = js_arm_check_thumb(); + bool arm_thumb2 = js_arm_check_thumb2(); + unsigned int arm_arch = js_arm_check_arch(); + + js_enable_debugger_exceptions(); + + avmplus::AvmCore::config.vfp = arm_vfp; + avmplus::AvmCore::config.soft_float = !arm_vfp; + avmplus::AvmCore::config.thumb = arm_thumb; + avmplus::AvmCore::config.thumb2 = arm_thumb2; + avmplus::AvmCore::config.arch = arm_arch; + + // Sanity-check the configuration detection. + // * We don't understand architectures prior to ARMv4. + JS_ASSERT(arm_arch >= 4); + // * All architectures support Thumb with the possible exception of ARMv4. + JS_ASSERT((arm_thumb) || (arm_arch == 4)); + // * Only ARMv6T2 and ARMv7(+) support Thumb2, but ARMv6 does not. + JS_ASSERT((arm_thumb2) || (arm_arch <= 6)); + // * All architectures that support Thumb2 also support Thumb. + JS_ASSERT((arm_thumb2 && arm_thumb) || (!arm_thumb2)); +#endif + did_we_check_processor_features = true; + } + + /* Set the default size for the code cache to 16MB. */ + tm->maxCodeCacheBytes = 16 M; + + if (!tm->recordAttempts.ops) { + JS_DHashTableInit(&tm->recordAttempts, JS_DHashGetStubOps(), + NULL, sizeof(PCHashEntry), + JS_DHASH_DEFAULT_CAPACITY(PC_HASH_COUNT)); + } + + JS_ASSERT(!tm->dataAlloc && !tm->traceAlloc && !tm->codeAlloc); + tm->dataAlloc = new VMAllocator(); + tm->traceAlloc = new VMAllocator(); + tm->tempAlloc = new VMAllocator(); + tm->reTempAlloc = new VMAllocator(); + tm->codeAlloc = new CodeAlloc(); + tm->frameCache = new FrameInfoCache(tm->dataAlloc); + tm->flush(); + verbose_only( tm->branches = NULL; ) + +#if !defined XP_WIN + debug_only(memset(&jitstats, 0, sizeof(jitstats))); +#endif + +#ifdef JS_JIT_SPEW + /* Architecture properties used by test cases. */ + jitstats.archIsIA32 = 0; + jitstats.archIs64BIT = 0; + jitstats.archIsARM = 0; + jitstats.archIsSPARC = 0; + jitstats.archIsPPC = 0; +#if defined NANOJIT_IA32 + jitstats.archIsIA32 = 1; +#endif +#if defined NANOJIT_64BIT + jitstats.archIs64BIT = 1; +#endif +#if defined NANOJIT_ARM + jitstats.archIsARM = 1; +#endif +#if defined NANOJIT_SPARC + jitstats.archIsSPARC = 1; +#endif +#if defined NANOJIT_PPC + jitstats.archIsPPC = 1; +#endif +#if defined NANOJIT_X64 + jitstats.archIsAMD64 = 1; +#endif +#endif +} + +void +js_FinishJIT(JSTraceMonitor *tm) +{ + JS_ASSERT(!tm->recorder); + +#ifdef JS_JIT_SPEW + if (jitstats.recorderStarted) { + char sep = ':'; + debug_only_print0(LC_TMStats, "recorder"); +#define RECORDER_JITSTAT(_ident, _name) \ + debug_only_printf(LC_TMStats, "%c " _name "(%llu)", sep, \ + (unsigned long long int)jitstats._ident); \ + sep = ','; +#define JITSTAT(x) /* nothing */ +#include "jitstats.tbl" +#undef JITSTAT +#undef RECORDER_JITSTAT + debug_only_print0(LC_TMStats, "\n"); + + sep = ':'; + debug_only_print0(LC_TMStats, "monitor"); +#define MONITOR_JITSTAT(_ident, _name) \ + debug_only_printf(LC_TMStats, "%c " _name "(%llu)", sep, \ + (unsigned long long int)jitstats._ident); \ + sep = ','; +#define JITSTAT(x) /* nothing */ +#include "jitstats.tbl" +#undef JITSTAT +#undef MONITOR_JITSTAT + debug_only_print0(LC_TMStats, "\n"); + } +#endif + + if (tm->recordAttempts.ops) + JS_DHashTableFinish(&tm->recordAttempts); + +#ifdef DEBUG + // Recover profiling data from expiring Fragments, and display + // final results. + if (js_LogController.lcbits & LC_FragProfile) { + for (Seq* f = tm->branches; f; f = f->tail) { + js_FragProfiling_FragFinalizer(f->head, tm); + } + for (size_t i = 0; i < FRAGMENT_TABLE_SIZE; ++i) { + for (TreeFragment *f = tm->vmfragments[i]; f; f = f->next) { + JS_ASSERT(f->root == f); + for (TreeFragment *p = f; p; p = p->peer) + js_FragProfiling_FragFinalizer(p, tm); + } + } + REHashMap::Iter iter(*(tm->reFragments)); + while (iter.next()) { + VMFragment* frag = (VMFragment*)iter.value(); + js_FragProfiling_FragFinalizer(frag, tm); + } + + js_FragProfiling_showResults(tm); + delete tm->profAlloc; + + } else { + NanoAssert(!tm->profTab); + NanoAssert(!tm->profAlloc); + } +#endif + + memset(&tm->vmfragments[0], 0, FRAGMENT_TABLE_SIZE * sizeof(TreeFragment*)); + + if (tm->frameCache) { + delete tm->frameCache; + tm->frameCache = NULL; + } + + if (tm->codeAlloc) { + delete tm->codeAlloc; + tm->codeAlloc = NULL; + } + + if (tm->dataAlloc) { + delete tm->dataAlloc; + tm->dataAlloc = NULL; + } + + if (tm->traceAlloc) { + delete tm->traceAlloc; + tm->traceAlloc = NULL; + } + + if (tm->tempAlloc) { + delete tm->tempAlloc; + tm->tempAlloc = NULL; + } + + if (tm->reTempAlloc) { + delete tm->reTempAlloc; + tm->reTempAlloc = NULL; + } +} + +void +js_PurgeJITOracle() +{ + oracle.clear(); +} + +static JSDHashOperator +PurgeScriptRecordingAttempts(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, void *arg) +{ + PCHashEntry *e = (PCHashEntry *)hdr; + JSScript *script = (JSScript *)arg; + jsbytecode *pc = (jsbytecode *)e->key; + + if (JS_UPTRDIFF(pc, script->code) < script->length) + return JS_DHASH_REMOVE; + return JS_DHASH_NEXT; +} + + +JS_REQUIRES_STACK void +js_PurgeScriptFragments(JSContext* cx, JSScript* script) +{ + if (!TRACING_ENABLED(cx)) + return; + debug_only_printf(LC_TMTracer, + "Purging fragments for JSScript %p.\n", (void*)script); + + JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx); + for (size_t i = 0; i < FRAGMENT_TABLE_SIZE; ++i) { + TreeFragment** fragp = &tm->vmfragments[i]; + while (TreeFragment* frag = *fragp) { + if (JS_UPTRDIFF(frag->ip, script->code) < script->length) { + /* This fragment is associated with the script. */ + debug_only_printf(LC_TMTracer, + "Disconnecting TreeFragment %p " + "with ip %p, in range [%p,%p).\n", + (void*)frag, frag->ip, script->code, + script->code + script->length); + + JS_ASSERT(frag->root == frag); + *fragp = frag->next; + do { + verbose_only( js_FragProfiling_FragFinalizer(frag, tm); ) + TrashTree(cx, frag); + } while ((frag = frag->peer) != NULL); + continue; + } + fragp = &frag->next; + } + } + + JS_DHashTableEnumerate(&tm->recordAttempts, PurgeScriptRecordingAttempts, script); +} + +bool +js_OverfullJITCache(JSTraceMonitor* tm) +{ + /* + * You might imagine the outOfMemory flag on the allocator is sufficient + * to model the notion of "running out of memory", but there are actually + * two separate issues involved: + * + * 1. The process truly running out of memory: malloc() or mmap() + * failed. + * + * 2. The limit we put on the "intended size" of the tracemonkey code + * cache, in pages, has been exceeded. + * + * Condition 1 doesn't happen very often, but we're obliged to try to + * safely shut down and signal the rest of spidermonkey when it + * does. Condition 2 happens quite regularly. + * + * Presently, the code in this file doesn't check the outOfMemory condition + * often enough, and frequently misuses the unchecked results of + * lirbuffer insertions on the asssumption that it will notice the + * outOfMemory flag "soon enough" when it returns to the monitorRecording + * function. This turns out to be a false assumption if we use outOfMemory + * to signal condition 2: we regularly provoke "passing our intended + * size" and regularly fail to notice it in time to prevent writing + * over the end of an artificially self-limited LIR buffer. + * + * To mitigate, though not completely solve, this problem, we're + * modeling the two forms of memory exhaustion *separately* for the + * time being: condition 1 is handled by the outOfMemory flag inside + * nanojit, and condition 2 is being handled independently *here*. So + * we construct our allocators to use all available memory they like, + * and only report outOfMemory to us when there is literally no OS memory + * left. Merely purging our cache when we hit our highwater mark is + * handled by the (few) callers of this function. + * + */ + jsuint maxsz = tm->maxCodeCacheBytes; + VMAllocator *dataAlloc = tm->dataAlloc; + VMAllocator *traceAlloc = tm->traceAlloc; + CodeAlloc *codeAlloc = tm->codeAlloc; + + return (codeAlloc->size() + dataAlloc->size() + traceAlloc->size() > maxsz); +} + +JS_FORCES_STACK JS_FRIEND_API(void) +js_DeepBail(JSContext *cx) +{ + JS_ASSERT(JS_ON_TRACE(cx)); + + /* + * Exactly one context on the current thread is on trace. Find out which + * one. (Most callers cannot guarantee that it's cx.) + */ + JSTraceMonitor *tm = &JS_TRACE_MONITOR(cx); + JSContext *tracecx = tm->tracecx; + + /* It's a bug if a non-FAIL_STATUS builtin gets here. */ + JS_ASSERT(tracecx->bailExit); + + tm->tracecx = NULL; + debug_only_print0(LC_TMTracer, "Deep bail.\n"); + LeaveTree(*tracecx->interpState, tracecx->bailExit); + tracecx->bailExit = NULL; + + InterpState* state = tracecx->interpState; + state->builtinStatus |= JSBUILTIN_BAILED; + + /* + * Between now and the LeaveTree in ExecuteTree, |tm->storage| may be reused + * if another trace executes before the currently executing native returns. + * However, all such traces will complete by the time the currently + * executing native returns and the return value is written to the native + * stack. After that point, no traces may execute until the LeaveTree in + * ExecuteTree, hence the invariant is maintained that only one trace uses + * |tm->storage| at a time. + */ + state->deepBailSp = state->sp; +} + +JS_REQUIRES_STACK jsval& +TraceRecorder::argval(unsigned n) const +{ + JS_ASSERT(n < cx->fp->fun->nargs); + return cx->fp->argv[n]; +} + +JS_REQUIRES_STACK jsval& +TraceRecorder::varval(unsigned n) const +{ + JS_ASSERT(n < cx->fp->script->nslots); + return cx->fp->slots[n]; +} + +JS_REQUIRES_STACK jsval& +TraceRecorder::stackval(int n) const +{ + jsval* sp = cx->fp->regs->sp; + return sp[n]; +} + +JS_REQUIRES_STACK LIns* +TraceRecorder::scopeChain() const +{ + return lir->insLoad(LIR_ldp, + lir->insLoad(LIR_ldp, cx_ins, offsetof(JSContext, fp)), + offsetof(JSStackFrame, scopeChain)); +} + +/* + * Return the frame of a call object if that frame is part of the current + * trace. |depthp| is an optional outparam: if it is non-null, it will be + * filled in with the depth of the call object's frame relevant to cx->fp. + */ +JS_REQUIRES_STACK JSStackFrame* +TraceRecorder::frameIfInRange(JSObject* obj, unsigned* depthp) const +{ + JSStackFrame* ofp = (JSStackFrame*) obj->getPrivate(); + JSStackFrame* fp = cx->fp; + for (unsigned depth = 0; depth <= callDepth; ++depth) { + if (fp == ofp) { + if (depthp) + *depthp = depth; + return ofp; + } + if (!(fp = fp->down)) + break; + } + return NULL; +} + +JS_DEFINE_CALLINFO_4(extern, UINT32, GetClosureVar, CONTEXT, OBJECT, CVIPTR, DOUBLEPTR, 0, 0) +JS_DEFINE_CALLINFO_4(extern, UINT32, GetClosureArg, CONTEXT, OBJECT, CVIPTR, DOUBLEPTR, 0, 0) + +/* + * Search the scope chain for a property lookup operation at the current PC and + * generate LIR to access the given property. Return RECORD_CONTINUE on success, + * otherwise abort and return RECORD_STOP. There are 3 outparams: + * + * vp the address of the current property value + * ins LIR instruction representing the property value on trace + * NameResult describes how to look up name; see comment for NameResult in jstracer.h + */ +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::scopeChainProp(JSObject* obj, jsval*& vp, LIns*& ins, NameResult& nr) +{ + JS_ASSERT(obj != globalObj); + + JSTraceMonitor &localtm = *traceMonitor; + + JSAtom* atom = atoms[GET_INDEX(cx->fp->regs->pc)]; + JSObject* obj2; + JSProperty* prop; + bool ok = js_FindProperty(cx, ATOM_TO_JSID(atom), &obj, &obj2, &prop); + + /* js_FindProperty can reenter the interpreter and kill |this|. */ + if (!localtm.recorder) + return ARECORD_ABORTED; + + if (!ok) + RETURN_ERROR_A("error in js_FindProperty"); + + if (!prop) + RETURN_STOP_A("failed to find name in non-global scope chain"); + + if (obj == globalObj) { + // Even if the property is on the global object, we must guard against + // the creation of properties that shadow the property in the middle + // of the scope chain if we are in a function. + if (cx->fp->argv) { + LIns* obj_ins; + JSObject* parent = STOBJ_GET_PARENT(cx->fp->calleeObject()); + LIns* parent_ins = stobj_get_parent(get(&cx->fp->argv[-2])); + CHECK_STATUS_A(traverseScopeChain(parent, parent_ins, obj, obj_ins)); + } + + JSScopeProperty* sprop = (JSScopeProperty*) prop; + + if (obj2 != obj) { + obj2->dropProperty(cx, prop); + RETURN_STOP_A("prototype property"); + } + if (!isValidSlot(OBJ_SCOPE(obj), sprop)) { + obj2->dropProperty(cx, prop); + return ARECORD_STOP; + } + if (!lazilyImportGlobalSlot(sprop->slot)) { + obj2->dropProperty(cx, prop); + RETURN_STOP_A("lazy import of global slot failed"); + } + vp = &STOBJ_GET_SLOT(obj, sprop->slot); + ins = get(vp); + obj2->dropProperty(cx, prop); + nr.tracked = true; + return ARECORD_CONTINUE; + } + + if (obj == obj2 && OBJ_GET_CLASS(cx, obj) == &js_CallClass) + return InjectStatus(callProp(obj, prop, ATOM_TO_JSID(atom), vp, ins, nr)); + + obj2->dropProperty(cx, prop); + RETURN_STOP_A("fp->scopeChain is not global or active call object"); +} + +/* + * Generate LIR to access a property of a Call object. + */ +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::callProp(JSObject* obj, JSProperty* prop, jsid id, jsval*& vp, + LIns*& ins, NameResult& nr) +{ + JSScopeProperty *sprop = (JSScopeProperty*) prop; + + JSOp op = JSOp(*cx->fp->regs->pc); + uint32 setflags = (js_CodeSpec[op].format & (JOF_SET | JOF_INCDEC | JOF_FOR)); + if (setflags && (sprop->attrs & JSPROP_READONLY)) + RETURN_STOP("writing to a read-only property"); + + uintN slot = sprop->shortid; + + vp = NULL; + uintN upvar_slot = SPROP_INVALID_SLOT; + JSStackFrame* cfp = (JSStackFrame*) obj->getPrivate(); + if (cfp) { + if (sprop->getter == js_GetCallArg) { + JS_ASSERT(slot < cfp->fun->nargs); + vp = &cfp->argv[slot]; + upvar_slot = slot; + nr.v = *vp; + } else if (sprop->getter == js_GetCallVar) { + JS_ASSERT(slot < cfp->script->nslots); + vp = &cfp->slots[slot]; + upvar_slot = cx->fp->fun->nargs + slot; + nr.v = *vp; + } else { + RETURN_STOP("dynamic property of Call object"); + } + obj->dropProperty(cx, prop); + + if (frameIfInRange(obj)) { + // At this point we are guaranteed to be looking at an active call oject + // whose properties are stored in the corresponding JSStackFrame. + ins = get(vp); + nr.tracked = true; + return RECORD_CONTINUE; + } + } else { + // Call objects do not yet have sprop->isMethod() properties, but they + // should. See bug 514046, for which this code is future-proof. Remove + // this comment when that bug is fixed (so, FIXME: 514046). +#ifdef DEBUG + JSBool rv = +#endif + js_GetPropertyHelper(cx, obj, sprop->id, + (op == JSOP_CALLNAME) + ? JSGET_NO_METHOD_BARRIER + : JSGET_METHOD_BARRIER, + &nr.v); + JS_ASSERT(rv); + obj->dropProperty(cx, prop); + } + + LIns* obj_ins; + JSObject* parent = STOBJ_GET_PARENT(cx->fp->calleeObject()); + LIns* parent_ins = stobj_get_parent(get(&cx->fp->argv[-2])); + CHECK_STATUS(traverseScopeChain(parent, parent_ins, obj, obj_ins)); + + ClosureVarInfo* cv = new (traceAlloc()) ClosureVarInfo(); + cv->id = id; + cv->slot = slot; + cv->callDepth = callDepth; + cv->resolveFlags = cx->resolveFlags == JSRESOLVE_INFER + ? js_InferFlags(cx, 0) + : cx->resolveFlags; + + LIns* outp = lir->insAlloc(sizeof(double)); + LIns* args[] = { + outp, + INS_CONSTPTR(cv), + obj_ins, + cx_ins + }; + const CallInfo* ci; + if (sprop->getter == js_GetCallArg) + ci = &GetClosureArg_ci; + else + ci = &GetClosureVar_ci; + + LIns* call_ins = lir->insCall(ci, args); + JSTraceType type = getCoercedType(nr.v); + guard(true, + addName(lir->ins2(LIR_eq, call_ins, lir->insImm(type)), + "guard(type-stable name access)"), + BRANCH_EXIT); + ins = stackLoad(outp, type); + nr.tracked = false; + nr.obj = obj; + nr.obj_ins = obj_ins; + nr.sprop = sprop; + return RECORD_CONTINUE; +} + +JS_REQUIRES_STACK LIns* +TraceRecorder::arg(unsigned n) +{ + return get(&argval(n)); +} + +JS_REQUIRES_STACK void +TraceRecorder::arg(unsigned n, LIns* i) +{ + set(&argval(n), i); +} + +JS_REQUIRES_STACK LIns* +TraceRecorder::var(unsigned n) +{ + return get(&varval(n)); +} + +JS_REQUIRES_STACK void +TraceRecorder::var(unsigned n, LIns* i) +{ + set(&varval(n), i); +} + +JS_REQUIRES_STACK LIns* +TraceRecorder::stack(int n) +{ + return get(&stackval(n)); +} + +JS_REQUIRES_STACK void +TraceRecorder::stack(int n, LIns* i) +{ + set(&stackval(n), i, n >= 0); +} + +JS_REQUIRES_STACK LIns* +TraceRecorder::alu(LOpcode v, jsdouble v0, jsdouble v1, LIns* s0, LIns* s1) +{ + /* + * To even consider this operation for demotion, both operands have to be + * integers and the oracle must not give us a negative hint for the + * instruction. + */ + if (oracle.isInstructionUndemotable(cx->fp->regs->pc) || !isPromoteInt(s0) || !isPromoteInt(s1)) { + out: + if (v == LIR_fmod) { + LIns* args[] = { s1, s0 }; + return lir->insCall(&js_dmod_ci, args); + } + LIns* result = lir->ins2(v, s0, s1); + JS_ASSERT_IF(s0->isconstf() && s1->isconstf(), result->isconstf()); + return result; + } + + jsdouble r; + switch (v) { + case LIR_fadd: + r = v0 + v1; + break; + case LIR_fsub: + r = v0 - v1; + break; +#if !defined NANOJIT_ARM + case LIR_fmul: + r = v0 * v1; + if (r == 0.0) + goto out; + break; +#endif +#if defined NANOJIT_IA32 || defined NANOJIT_X64 + case LIR_fdiv: + if (v1 == 0) + goto out; + r = v0 / v1; + break; + case LIR_fmod: + if (v0 < 0 || v1 == 0 || (s1->isconstf() && v1 < 0)) + goto out; + r = js_dmod(v0, v1); + break; +#endif + default: + goto out; + } + + /* + * The result must be an integer at record time, otherwise there is no + * point in trying to demote it. + */ + if (jsint(r) != r || JSDOUBLE_IS_NEGZERO(r)) + goto out; + + LIns* d0 = ::demote(lir, s0); + LIns* d1 = ::demote(lir, s1); + + /* + * Speculatively emit an integer operation, betting that at runtime we + * will get integer results again. + */ + VMSideExit* exit; + LIns* result; + switch (v) { +#if defined NANOJIT_IA32 || defined NANOJIT_X64 + case LIR_fdiv: + if (d0->isconst() && d1->isconst()) + return lir->ins1(LIR_i2f, lir->insImm(jsint(r))); + + exit = snapshot(OVERFLOW_EXIT); + + /* + * If the divisor is greater than zero its always safe to execute + * the division. If not, we have to make sure we are not running + * into -2147483648 / -1, because it can raise an overflow exception. + */ + if (!d1->isconst()) { + LIns* gt = lir->insBranch(LIR_jt, lir->ins2i(LIR_gt, d1, 0), NULL); + guard(false, lir->ins_eq0(d1), exit); + guard(false, lir->ins2(LIR_and, + lir->ins2i(LIR_eq, d0, 0x80000000), + lir->ins2i(LIR_eq, d1, -1)), exit); + gt->setTarget(lir->ins0(LIR_label)); + } else { + if (d1->imm32() == -1) + guard(false, lir->ins2i(LIR_eq, d0, 0x80000000), exit); + } + result = lir->ins2(v = LIR_div, d0, d1); + + /* As long the modulus is zero, the result is an integer. */ + guard(true, lir->ins_eq0(lir->ins1(LIR_mod, result)), exit); + + /* Don't lose a -0. */ + guard(false, lir->ins_eq0(result), exit); + break; + + case LIR_fmod: { + if (d0->isconst() && d1->isconst()) + return lir->ins1(LIR_i2f, lir->insImm(jsint(r))); + + exit = snapshot(OVERFLOW_EXIT); + + /* Make sure we don't trigger division by zero at runtime. */ + if (!d1->isconst()) + guard(false, lir->ins_eq0(d1), exit); + result = lir->ins1(v = LIR_mod, lir->ins2(LIR_div, d0, d1)); + + /* If the result is not 0, it is always within the integer domain. */ + LIns* branch = lir->insBranch(LIR_jf, lir->ins_eq0(result), NULL); + + /* + * If the result is zero, we must exit if the lhs is negative since + * the result is -0 in this case, which is not in the integer domain. + */ + guard(false, lir->ins2i(LIR_lt, d0, 0), exit); + branch->setTarget(lir->ins0(LIR_label)); + break; + } +#endif + + default: + v = (LOpcode)((int)v & ~LIR64); + result = lir->ins2(v, d0, d1); + + /* + * If the operands guarantee that the result will be an integer (i.e. + * z = x + y with 0 <= (x|y) <= 0xffff guarantees z <= fffe0001), we + * don't have to guard against an overflow. Otherwise we emit a guard + * that will inform the oracle and cause a non-demoted trace to be + * attached that uses floating-point math for this operation. + */ + if (!result->isconst() && (!IsOverflowSafe(v, d0) || !IsOverflowSafe(v, d1))) { + exit = snapshot(OVERFLOW_EXIT); + guard(false, lir->ins1(LIR_ov, result), exit); + if (v == LIR_mul) // make sure we don't lose a -0 + guard(false, lir->ins_eq0(result), exit); + } + break; + } + JS_ASSERT_IF(d0->isconst() && d1->isconst(), + result->isconst() && result->imm32() == jsint(r)); + return lir->ins1(LIR_i2f, result); +} + +LIns* +TraceRecorder::f2i(LIns* f) +{ + return lir->insCall(&js_DoubleToInt32_ci, &f); +} + +JS_REQUIRES_STACK LIns* +TraceRecorder::makeNumberInt32(LIns* f) +{ + JS_ASSERT(f->isQuad()); + LIns* x; + if (!isPromote(f)) { + x = f2i(f); + guard(true, lir->ins2(LIR_feq, f, lir->ins1(LIR_i2f, x)), MISMATCH_EXIT); + } else { + x = ::demote(lir, f); + } + return x; +} + +JS_REQUIRES_STACK LIns* +TraceRecorder::stringify(jsval& v) +{ + LIns* v_ins = get(&v); + if (JSVAL_IS_STRING(v)) + return v_ins; + + LIns* args[] = { v_ins, cx_ins }; + const CallInfo* ci; + if (JSVAL_IS_NUMBER(v)) { + ci = &js_NumberToString_ci; + } else if (JSVAL_IS_SPECIAL(v)) { + ci = &js_BooleanOrUndefinedToString_ci; + } else { + /* + * Callers must deal with non-primitive (non-null object) values by + * calling an imacro. We don't try to guess about which imacro, with + * what valueOf hint, here. + */ + JS_ASSERT(JSVAL_IS_NULL(v)); + return INS_ATOM(cx->runtime->atomState.nullAtom); + } + + v_ins = lir->insCall(ci, args); + guard(false, lir->ins_peq0(v_ins), OOM_EXIT); + return v_ins; +} + +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::call_imacro(jsbytecode* imacro) +{ + JSStackFrame* fp = cx->fp; + JSFrameRegs* regs = fp->regs; + + /* We cannot nest imacros, only tail-call. */ + if (fp->imacpc) { + /* Dereference is safe since imacros are JSOP_STOP-terminated. */ + if (regs->pc[js_CodeSpec[*regs->pc].length] != JSOP_STOP) + return RECORD_STOP; + regs->pc = imacro; + return RECORD_IMACRO; + } + + fp->imacpc = regs->pc; + regs->pc = imacro; + atoms = COMMON_ATOMS_START(&cx->runtime->atomState); + return RECORD_IMACRO; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::ifop() +{ + jsval& v = stackval(-1); + LIns* v_ins = get(&v); + bool cond; + LIns* x; + + if (JSVAL_IS_NULL(v)) { + cond = false; + x = lir->insImm(0); + } else if (!JSVAL_IS_PRIMITIVE(v)) { + cond = true; + x = lir->insImm(1); + } else if (JSVAL_IS_SPECIAL(v)) { + /* Test for boolean is true, negate later if we are testing for false. */ + cond = JSVAL_TO_SPECIAL(v) == JS_TRUE; + x = lir->ins2i(LIR_eq, v_ins, 1); + } else if (isNumber(v)) { + jsdouble d = asNumber(v); + cond = !JSDOUBLE_IS_NaN(d) && d; + x = lir->ins2(LIR_and, + lir->ins2(LIR_feq, v_ins, v_ins), + lir->ins_eq0(lir->ins2(LIR_feq, v_ins, lir->insImmf(0)))); + } else if (JSVAL_IS_STRING(v)) { + cond = JSVAL_TO_STRING(v)->length() != 0; + x = lir->ins2(LIR_piand, + lir->insLoad(LIR_ldp, + v_ins, + (int)offsetof(JSString, mLength)), + INS_CONSTWORD(JSString::LENGTH_MASK)); + } else { + JS_NOT_REACHED("ifop"); + return ARECORD_STOP; + } + + jsbytecode* pc = cx->fp->regs->pc; + emitIf(pc, cond, x); + return checkTraceEnd(pc); +} + +#ifdef NANOJIT_IA32 +/* + * Record LIR for a tableswitch or tableswitchx op. We record LIR only the + * "first" time we hit the op. Later, when we start traces after exiting that + * trace, we just patch. + */ +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::tableswitch() +{ + jsval& v = stackval(-1); + + /* No need to guard if the condition can't match any of the cases. */ + if (!isNumber(v)) + return ARECORD_CONTINUE; + + /* No need to guard if the condition is constant. */ + LIns* v_ins = f2i(get(&v)); + if (v_ins->isconst() || v_ins->isconstq()) + return ARECORD_CONTINUE; + + jsbytecode* pc = cx->fp->regs->pc; + /* Starting a new trace after exiting a trace via switch. */ + if (anchor && + (anchor->exitType == CASE_EXIT || anchor->exitType == DEFAULT_EXIT) && + fragment->ip == pc) { + return ARECORD_CONTINUE; + } + + /* Decode jsop. */ + jsint low, high; + if (*pc == JSOP_TABLESWITCH) { + pc += JUMP_OFFSET_LEN; + low = GET_JUMP_OFFSET(pc); + pc += JUMP_OFFSET_LEN; + high = GET_JUMP_OFFSET(pc); + } else { + pc += JUMPX_OFFSET_LEN; + low = GET_JUMPX_OFFSET(pc); + pc += JUMPX_OFFSET_LEN; + high = GET_JUMPX_OFFSET(pc); + } + + /* Cap maximum table-switch size for modesty. */ + if ((high + 1 - low) > MAX_TABLE_SWITCH) + return InjectStatus(switchop()); + + /* Generate switch LIR. */ + SwitchInfo* si = new (traceAlloc()) SwitchInfo(); + si->count = high + 1 - low; + si->table = 0; + si->index = (uint32) -1; + LIns* diff = lir->ins2(LIR_sub, v_ins, lir->insImm(low)); + LIns* cmp = lir->ins2(LIR_ult, diff, lir->insImm(si->count)); + lir->insGuard(LIR_xf, cmp, createGuardRecord(snapshot(DEFAULT_EXIT))); + lir->insStorei(diff, lir->insImmPtr(&si->index), 0); + VMSideExit* exit = snapshot(CASE_EXIT); + exit->switchInfo = si; + LIns* guardIns = lir->insGuard(LIR_xtbl, diff, createGuardRecord(exit)); + fragment->lastIns = guardIns; + CHECK_STATUS_A(compile()); + return finishSuccessfully(); +} +#endif + +static JS_ALWAYS_INLINE int32_t +UnboxBooleanOrUndefined(jsval v) +{ + /* Although this says 'special', we really only expect 3 special values: */ + JS_ASSERT(v == JSVAL_TRUE || v == JSVAL_FALSE || v == JSVAL_VOID); + return JSVAL_TO_SPECIAL(v); +} + +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::switchop() +{ + jsval& v = stackval(-1); + LIns* v_ins = get(&v); + + /* No need to guard if the condition is constant. */ + if (v_ins->isconst() || v_ins->isconstq()) + return RECORD_CONTINUE; + if (isNumber(v)) { + jsdouble d = asNumber(v); + guard(true, + addName(lir->ins2(LIR_feq, v_ins, lir->insImmf(d)), + "guard(switch on numeric)"), + BRANCH_EXIT); + } else if (JSVAL_IS_STRING(v)) { + LIns* args[] = { INS_CONSTSTR(JSVAL_TO_STRING(v)), v_ins }; + guard(true, + addName(lir->ins_eq0(lir->ins_eq0(lir->insCall(&js_EqualStrings_ci, args))), + "guard(switch on string)"), + BRANCH_EXIT); + } else if (JSVAL_IS_SPECIAL(v)) { + guard(true, + addName(lir->ins2(LIR_eq, v_ins, lir->insImm(UnboxBooleanOrUndefined(v))), + "guard(switch on boolean)"), + BRANCH_EXIT); + } else { + RETURN_STOP("switch on object or null"); + } + return RECORD_CONTINUE; +} + +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::inc(jsval& v, jsint incr, bool pre) +{ + LIns* v_ins = get(&v); + CHECK_STATUS(inc(v, v_ins, incr, pre)); + set(&v, v_ins); + return RECORD_CONTINUE; +} + +/* + * On exit, v_ins is the incremented unboxed value, and the appropriate value + * (pre- or post-increment as described by pre) is stacked. + */ +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::inc(jsval v, LIns*& v_ins, jsint incr, bool pre) +{ + LIns* v_after; + CHECK_STATUS(incHelper(v, v_ins, v_after, incr)); + + const JSCodeSpec& cs = js_CodeSpec[*cx->fp->regs->pc]; + JS_ASSERT(cs.ndefs == 1); + stack(-cs.nuses, pre ? v_after : v_ins); + v_ins = v_after; + return RECORD_CONTINUE; +} + +/* + * Do an increment operation without storing anything to the stack. + */ +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::incHelper(jsval v, LIns* v_ins, LIns*& v_after, jsint incr) +{ + if (!isNumber(v)) + RETURN_STOP("can only inc numbers"); + v_after = alu(LIR_fadd, asNumber(v), incr, v_ins, lir->insImmf(incr)); + return RECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::incProp(jsint incr, bool pre) +{ + jsval& l = stackval(-1); + if (JSVAL_IS_PRIMITIVE(l)) + RETURN_STOP_A("incProp on primitive"); + + JSObject* obj = JSVAL_TO_OBJECT(l); + LIns* obj_ins = get(&l); + + uint32 slot; + LIns* v_ins; + CHECK_STATUS_A(prop(obj, obj_ins, &slot, &v_ins, NULL)); + + if (slot == SPROP_INVALID_SLOT) + RETURN_STOP_A("incProp on invalid slot"); + + jsval& v = STOBJ_GET_SLOT(obj, slot); + CHECK_STATUS_A(inc(v, v_ins, incr, pre)); + + LIns* dslots_ins = NULL; + stobj_set_slot(obj_ins, slot, dslots_ins, box_jsval(v, v_ins)); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::incElem(jsint incr, bool pre) +{ + jsval& r = stackval(-1); + jsval& l = stackval(-2); + jsval* vp; + LIns* v_ins; + LIns* addr_ins; + + if (JSVAL_IS_PRIMITIVE(l) || !JSVAL_IS_INT(r) || + !guardDenseArray(JSVAL_TO_OBJECT(l), get(&l))) { + return RECORD_STOP; + } + + CHECK_STATUS(denseArrayElement(l, r, vp, v_ins, addr_ins)); + if (!addr_ins) // if we read a hole, abort + return RECORD_STOP; + CHECK_STATUS(inc(*vp, v_ins, incr, pre)); + lir->insStorei(box_jsval(*vp, v_ins), addr_ins, 0); + return RECORD_CONTINUE; +} + +static bool +EvalCmp(LOpcode op, double l, double r) +{ + bool cond; + switch (op) { + case LIR_feq: + cond = (l == r); + break; + case LIR_flt: + cond = l < r; + break; + case LIR_fgt: + cond = l > r; + break; + case LIR_fle: + cond = l <= r; + break; + case LIR_fge: + cond = l >= r; + break; + default: + JS_NOT_REACHED("unexpected comparison op"); + return false; + } + return cond; +} + +static bool +EvalCmp(LOpcode op, JSString* l, JSString* r) +{ + if (op == LIR_feq) + return !!js_EqualStrings(l, r); + return EvalCmp(op, js_CompareStrings(l, r), 0); +} + +JS_REQUIRES_STACK void +TraceRecorder::strictEquality(bool equal, bool cmpCase) +{ + jsval& r = stackval(-1); + jsval& l = stackval(-2); + LIns* l_ins = get(&l); + LIns* r_ins = get(&r); + LIns* x; + bool cond; + + JSTraceType ltag = GetPromotedType(l); + if (ltag != GetPromotedType(r)) { + cond = !equal; + x = lir->insImm(cond); + } else if (ltag == TT_STRING) { + LIns* args[] = { r_ins, l_ins }; + x = lir->ins2i(LIR_eq, lir->insCall(&js_EqualStrings_ci, args), equal); + cond = !!js_EqualStrings(JSVAL_TO_STRING(l), JSVAL_TO_STRING(r)); + } else { + LOpcode op; + if (ltag == TT_DOUBLE) + op = LIR_feq; + else if (ltag == TT_NULL || ltag == TT_OBJECT || ltag == TT_FUNCTION) + op = LIR_peq; + else + op = LIR_eq; + x = lir->ins2(op, l_ins, r_ins); + if (!equal) + x = lir->ins_eq0(x); + cond = (ltag == TT_DOUBLE) + ? asNumber(l) == asNumber(r) + : l == r; + } + cond = (cond == equal); + + if (cmpCase) { + /* Only guard if the same path may not always be taken. */ + if (!x->isconst()) + guard(cond, x, BRANCH_EXIT); + return; + } + + set(&l, x); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::equality(bool negate, bool tryBranchAfterCond) +{ + jsval& rval = stackval(-1); + jsval& lval = stackval(-2); + LIns* l_ins = get(&lval); + LIns* r_ins = get(&rval); + + return equalityHelper(lval, rval, l_ins, r_ins, negate, tryBranchAfterCond, lval); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::equalityHelper(jsval l, jsval r, LIns* l_ins, LIns* r_ins, + bool negate, bool tryBranchAfterCond, + jsval& rval) +{ + LOpcode op = LIR_eq; + bool cond; + LIns* args[] = { NULL, NULL }; + + /* + * The if chain below closely mirrors that found in 11.9.3, in general + * deviating from that ordering of ifs only to account for SpiderMonkey's + * conflation of booleans and undefined and for the possibility of + * confusing objects and null. Note carefully the spec-mandated recursion + * in the final else clause, which terminates because Number == T recurs + * only if T is Object, but that must recur again to convert Object to + * primitive, and ToPrimitive throws if the object cannot be converted to + * a primitive value (which would terminate recursion). + */ + + if (GetPromotedType(l) == GetPromotedType(r)) { + if (JSVAL_TAG(l) == JSVAL_OBJECT || JSVAL_IS_SPECIAL(l)) { + if (JSVAL_TAG(l) == JSVAL_OBJECT) + op = LIR_peq; + cond = (l == r); + } else if (JSVAL_IS_STRING(l)) { + args[0] = r_ins, args[1] = l_ins; + l_ins = lir->insCall(&js_EqualStrings_ci, args); + r_ins = lir->insImm(1); + cond = !!js_EqualStrings(JSVAL_TO_STRING(l), JSVAL_TO_STRING(r)); + } else { + JS_ASSERT(isNumber(l) && isNumber(r)); + cond = (asNumber(l) == asNumber(r)); + op = LIR_feq; + } + } else if (JSVAL_IS_NULL(l) && JSVAL_IS_SPECIAL(r)) { + l_ins = lir->insImm(JSVAL_TO_SPECIAL(JSVAL_VOID)); + cond = (r == JSVAL_VOID); + } else if (JSVAL_IS_SPECIAL(l) && JSVAL_IS_NULL(r)) { + r_ins = lir->insImm(JSVAL_TO_SPECIAL(JSVAL_VOID)); + cond = (l == JSVAL_VOID); + } else if (isNumber(l) && JSVAL_IS_STRING(r)) { + args[0] = r_ins, args[1] = cx_ins; + r_ins = lir->insCall(&js_StringToNumber_ci, args); + cond = (asNumber(l) == js_StringToNumber(cx, JSVAL_TO_STRING(r))); + op = LIR_feq; + } else if (JSVAL_IS_STRING(l) && isNumber(r)) { + args[0] = l_ins, args[1] = cx_ins; + l_ins = lir->insCall(&js_StringToNumber_ci, args); + cond = (js_StringToNumber(cx, JSVAL_TO_STRING(l)) == asNumber(r)); + op = LIR_feq; + } else { + if (JSVAL_IS_SPECIAL(l)) { + bool isVoid = !!JSVAL_IS_VOID(l); + guard(isVoid, + lir->ins2(LIR_eq, l_ins, INS_CONST(JSVAL_TO_SPECIAL(JSVAL_VOID))), + BRANCH_EXIT); + if (!isVoid) { + args[0] = l_ins, args[1] = cx_ins; + l_ins = lir->insCall(&js_BooleanOrUndefinedToNumber_ci, args); + l = (l == JSVAL_VOID) + ? cx->runtime->NaNValue + : INT_TO_JSVAL(l == JSVAL_TRUE); + return equalityHelper(l, r, l_ins, r_ins, negate, + tryBranchAfterCond, rval); + } + } else if (JSVAL_IS_SPECIAL(r)) { + bool isVoid = !!JSVAL_IS_VOID(r); + guard(isVoid, + lir->ins2(LIR_eq, r_ins, INS_CONST(JSVAL_TO_SPECIAL(JSVAL_VOID))), + BRANCH_EXIT); + if (!isVoid) { + args[0] = r_ins, args[1] = cx_ins; + r_ins = lir->insCall(&js_BooleanOrUndefinedToNumber_ci, args); + r = (r == JSVAL_VOID) + ? cx->runtime->NaNValue + : INT_TO_JSVAL(r == JSVAL_TRUE); + return equalityHelper(l, r, l_ins, r_ins, negate, + tryBranchAfterCond, rval); + } + } else { + if ((JSVAL_IS_STRING(l) || isNumber(l)) && !JSVAL_IS_PRIMITIVE(r)) { + RETURN_IF_XML_A(r); + return InjectStatus(call_imacro(equality_imacros.any_obj)); + } + if (!JSVAL_IS_PRIMITIVE(l) && (JSVAL_IS_STRING(r) || isNumber(r))) { + RETURN_IF_XML_A(l); + return InjectStatus(call_imacro(equality_imacros.obj_any)); + } + } + + l_ins = lir->insImm(0); + r_ins = lir->insImm(1); + cond = false; + } + + /* If the operands aren't numbers, compare them as integers. */ + LIns* x = lir->ins2(op, l_ins, r_ins); + if (negate) { + x = lir->ins_eq0(x); + cond = !cond; + } + + jsbytecode* pc = cx->fp->regs->pc; + + /* + * Don't guard if the same path is always taken. If it isn't, we have to + * fuse comparisons and the following branch, because the interpreter does + * that. + */ + if (tryBranchAfterCond) + fuseIf(pc + 1, cond, x); + + /* + * There is no need to write out the result of this comparison if the trace + * ends on this operation. + */ + if (pc[1] == JSOP_IFNE || pc[1] == JSOP_IFEQ) + CHECK_STATUS_A(checkTraceEnd(pc + 1)); + + /* + * We update the stack after the guard. This is safe since the guard bails + * out at the comparison and the interpreter will therefore re-execute the + * comparison. This way the value of the condition doesn't have to be + * calculated and saved on the stack in most cases. + */ + set(&rval, x); + + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::relational(LOpcode op, bool tryBranchAfterCond) +{ + jsval& r = stackval(-1); + jsval& l = stackval(-2); + LIns* x = NULL; + bool cond; + LIns* l_ins = get(&l); + LIns* r_ins = get(&r); + bool fp = false; + jsdouble lnum, rnum; + + /* + * 11.8.5 if either argument is an object with a function-valued valueOf + * property; if both arguments are objects with non-function-valued valueOf + * properties, abort. + */ + if (!JSVAL_IS_PRIMITIVE(l)) { + RETURN_IF_XML_A(l); + if (!JSVAL_IS_PRIMITIVE(r)) { + RETURN_IF_XML_A(r); + return InjectStatus(call_imacro(binary_imacros.obj_obj)); + } + return InjectStatus(call_imacro(binary_imacros.obj_any)); + } + if (!JSVAL_IS_PRIMITIVE(r)) { + RETURN_IF_XML_A(r); + return InjectStatus(call_imacro(binary_imacros.any_obj)); + } + + /* 11.8.5 steps 3, 16-21. */ + if (JSVAL_IS_STRING(l) && JSVAL_IS_STRING(r)) { + LIns* args[] = { r_ins, l_ins }; + l_ins = lir->insCall(&js_CompareStrings_ci, args); + r_ins = lir->insImm(0); + cond = EvalCmp(op, JSVAL_TO_STRING(l), JSVAL_TO_STRING(r)); + goto do_comparison; + } + + /* 11.8.5 steps 4-5. */ + if (!JSVAL_IS_NUMBER(l)) { + LIns* args[] = { l_ins, cx_ins }; + switch (JSVAL_TAG(l)) { + case JSVAL_SPECIAL: + l_ins = lir->insCall(&js_BooleanOrUndefinedToNumber_ci, args); + break; + case JSVAL_STRING: + l_ins = lir->insCall(&js_StringToNumber_ci, args); + break; + case JSVAL_OBJECT: + if (JSVAL_IS_NULL(l)) { + l_ins = lir->insImmf(0.0); + break; + } + // FALL THROUGH + case JSVAL_INT: + case JSVAL_DOUBLE: + default: + JS_NOT_REACHED("JSVAL_IS_NUMBER if int/double, objects should " + "have been handled at start of method"); + RETURN_STOP_A("safety belt"); + } + } + if (!JSVAL_IS_NUMBER(r)) { + LIns* args[] = { r_ins, cx_ins }; + switch (JSVAL_TAG(r)) { + case JSVAL_SPECIAL: + r_ins = lir->insCall(&js_BooleanOrUndefinedToNumber_ci, args); + break; + case JSVAL_STRING: + r_ins = lir->insCall(&js_StringToNumber_ci, args); + break; + case JSVAL_OBJECT: + if (JSVAL_IS_NULL(r)) { + r_ins = lir->insImmf(0.0); + break; + } + // FALL THROUGH + case JSVAL_INT: + case JSVAL_DOUBLE: + default: + JS_NOT_REACHED("JSVAL_IS_NUMBER if int/double, objects should " + "have been handled at start of method"); + RETURN_STOP_A("safety belt"); + } + } + { + jsval tmp = JSVAL_NULL; + JSAutoTempValueRooter tvr(cx, 1, &tmp); + + tmp = l; + lnum = js_ValueToNumber(cx, &tmp); + tmp = r; + rnum = js_ValueToNumber(cx, &tmp); + } + cond = EvalCmp(op, lnum, rnum); + fp = true; + + /* 11.8.5 steps 6-15. */ + do_comparison: + /* + * If the result is not a number or it's not a quad, we must use an integer + * compare. + */ + if (!fp) { + JS_ASSERT(op >= LIR_feq && op <= LIR_fge); + op = LOpcode(op + (LIR_eq - LIR_feq)); + } + x = lir->ins2(op, l_ins, r_ins); + + jsbytecode* pc = cx->fp->regs->pc; + + /* + * Don't guard if the same path is always taken. If it isn't, we have to + * fuse comparisons and the following branch, because the interpreter does + * that. + */ + if (tryBranchAfterCond) + fuseIf(pc + 1, cond, x); + + /* + * There is no need to write out the result of this comparison if the trace + * ends on this operation. + */ + if (pc[1] == JSOP_IFNE || pc[1] == JSOP_IFEQ) + CHECK_STATUS_A(checkTraceEnd(pc + 1)); + + /* + * We update the stack after the guard. This is safe since the guard bails + * out at the comparison and the interpreter will therefore re-execute the + * comparison. This way the value of the condition doesn't have to be + * calculated and saved on the stack in most cases. + */ + set(&l, x); + + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::unary(LOpcode op) +{ + jsval& v = stackval(-1); + bool intop = !(op & LIR64); + if (isNumber(v)) { + LIns* a = get(&v); + if (intop) + a = f2i(a); + a = lir->ins1(op, a); + if (intop) + a = lir->ins1(LIR_i2f, a); + set(&v, a); + return RECORD_CONTINUE; + } + return RECORD_STOP; +} + +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::binary(LOpcode op) +{ + jsval& r = stackval(-1); + jsval& l = stackval(-2); + + if (!JSVAL_IS_PRIMITIVE(l)) { + RETURN_IF_XML(l); + if (!JSVAL_IS_PRIMITIVE(r)) { + RETURN_IF_XML(r); + return call_imacro(binary_imacros.obj_obj); + } + return call_imacro(binary_imacros.obj_any); + } + if (!JSVAL_IS_PRIMITIVE(r)) { + RETURN_IF_XML(r); + return call_imacro(binary_imacros.any_obj); + } + + bool intop = !(op & LIR64); + LIns* a = get(&l); + LIns* b = get(&r); + + bool leftIsNumber = isNumber(l); + jsdouble lnum = leftIsNumber ? asNumber(l) : 0; + + bool rightIsNumber = isNumber(r); + jsdouble rnum = rightIsNumber ? asNumber(r) : 0; + + if ((op >= LIR_sub && op <= LIR_ush) || // sub, mul, (callh), or, xor, (not,) lsh, rsh, ush + (op >= LIR_fsub && op <= LIR_fmod)) { // fsub, fmul, fdiv, fmod + LIns* args[2]; + if (JSVAL_IS_STRING(l)) { + args[0] = a; + args[1] = cx_ins; + a = lir->insCall(&js_StringToNumber_ci, args); + lnum = js_StringToNumber(cx, JSVAL_TO_STRING(l)); + leftIsNumber = true; + } + if (JSVAL_IS_STRING(r)) { + args[0] = b; + args[1] = cx_ins; + b = lir->insCall(&js_StringToNumber_ci, args); + rnum = js_StringToNumber(cx, JSVAL_TO_STRING(r)); + rightIsNumber = true; + } + } + if (JSVAL_IS_SPECIAL(l)) { + LIns* args[] = { a, cx_ins }; + a = lir->insCall(&js_BooleanOrUndefinedToNumber_ci, args); + lnum = js_BooleanOrUndefinedToNumber(cx, JSVAL_TO_SPECIAL(l)); + leftIsNumber = true; + } + if (JSVAL_IS_SPECIAL(r)) { + LIns* args[] = { b, cx_ins }; + b = lir->insCall(&js_BooleanOrUndefinedToNumber_ci, args); + rnum = js_BooleanOrUndefinedToNumber(cx, JSVAL_TO_SPECIAL(r)); + rightIsNumber = true; + } + if (leftIsNumber && rightIsNumber) { + if (intop) { + LIns *args[] = { a }; + a = lir->insCall(op == LIR_ush ? &js_DoubleToUint32_ci : &js_DoubleToInt32_ci, args); + b = f2i(b); + } + a = alu(op, lnum, rnum, a, b); + if (intop) + a = lir->ins1(op == LIR_ush ? LIR_u2f : LIR_i2f, a); + set(&l, a); + return RECORD_CONTINUE; + } + return RECORD_STOP; +} + +struct GuardedShapeEntry : public JSDHashEntryStub +{ + JSObject* obj; +}; + +#if defined DEBUG_notme && defined XP_UNIX +#include + +static FILE* shapefp = NULL; + +static void +DumpShape(JSObject* obj, const char* prefix) +{ + JSScope* scope = OBJ_SCOPE(obj); + + if (!shapefp) { + shapefp = fopen("/tmp/shapes.dump", "w"); + if (!shapefp) + return; + } + + fprintf(shapefp, "\n%s: shape %u flags %x\n", prefix, scope->shape, scope->flags); + for (JSScopeProperty* sprop = scope->lastProperty(); sprop; sprop = sprop->parent) { + if (JSID_IS_ATOM(sprop->id)) { + fprintf(shapefp, " %s", JS_GetStringBytes(JSVAL_TO_STRING(ID_TO_VALUE(sprop->id)))); + } else { + JS_ASSERT(!JSID_IS_OBJECT(sprop->id)); + fprintf(shapefp, " %d", JSID_TO_INT(sprop->id)); + } + fprintf(shapefp, " %u %p %p %x %x %d\n", + sprop->slot, sprop->getter, sprop->setter, sprop->attrs, sprop->flags, + sprop->shortid); + } + fflush(shapefp); +} + +static JSDHashOperator +DumpShapeEnumerator(JSDHashTable* table, JSDHashEntryHdr* hdr, uint32 number, void* arg) +{ + GuardedShapeEntry* entry = (GuardedShapeEntry*) hdr; + const char* prefix = (const char*) arg; + + DumpShape(entry->obj, prefix); + return JS_DHASH_NEXT; +} + +void +TraceRecorder::dumpGuardedShapes(const char* prefix) +{ + if (guardedShapeTable.ops) + JS_DHashTableEnumerate(&guardedShapeTable, DumpShapeEnumerator, (void*) prefix); +} +#endif /* DEBUG_notme && XP_UNIX */ + +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::guardShape(LIns* obj_ins, JSObject* obj, uint32 shape, const char* guardName, + LIns* map_ins, VMSideExit* exit) +{ + if (!guardedShapeTable.ops) { + JS_DHashTableInit(&guardedShapeTable, JS_DHashGetStubOps(), NULL, + sizeof(GuardedShapeEntry), JS_DHASH_MIN_SIZE); + } + + // Test (with add if missing) for a remembered guard for (obj_ins, obj). + GuardedShapeEntry* entry = (GuardedShapeEntry*) + JS_DHashTableOperate(&guardedShapeTable, obj_ins, JS_DHASH_ADD); + if (!entry) { + JS_ReportOutOfMemory(cx); + return RECORD_ERROR; + } + + // If already guarded, check that the shape matches. + if (entry->key) { + JS_ASSERT(entry->key == obj_ins); + JS_ASSERT(entry->obj == obj); + return RECORD_CONTINUE; + } + + // Not yet guarded. Remember obj_ins along with obj (for invalidation). + entry->key = obj_ins; + entry->obj = obj; + +#if defined DEBUG_notme && defined XP_UNIX + DumpShape(obj, "guard"); + fprintf(shapefp, "for obj_ins %p\n", obj_ins); +#endif + + // Finally, emit the shape guard. + LIns* shape_ins = addName(lir->insLoad(LIR_ld, map_ins, offsetof(JSScope, shape)), "shape"); + guard(true, + addName(lir->ins2i(LIR_eq, shape_ins, shape), guardName), + exit); + return RECORD_CONTINUE; +} + +static JSDHashOperator +ForgetGuardedShapesForObject(JSDHashTable* table, JSDHashEntryHdr* hdr, uint32 number, void* arg) +{ + GuardedShapeEntry* entry = (GuardedShapeEntry*) hdr; + if (entry->obj == arg) { +#if defined DEBUG_notme && defined XP_UNIX + DumpShape(entry->obj, "forget"); +#endif + return JS_DHASH_REMOVE; + } + return JS_DHASH_NEXT; +} + +void +TraceRecorder::forgetGuardedShapesForObject(JSObject* obj) +{ + if (guardedShapeTable.ops) + JS_DHashTableEnumerate(&guardedShapeTable, ForgetGuardedShapesForObject, obj); +} + +void +TraceRecorder::forgetGuardedShapes() +{ + if (guardedShapeTable.ops) { +#if defined DEBUG_notme && defined XP_UNIX + dumpGuardedShapes("forget-all"); +#endif + JS_DHashTableFinish(&guardedShapeTable); + guardedShapeTable.ops = NULL; + } +} + +JS_STATIC_ASSERT(offsetof(JSObjectOps, objectMap) == 0); + +inline LIns* +TraceRecorder::map(LIns* obj_ins) +{ + return addName(lir->insLoad(LIR_ldp, obj_ins, (int) offsetof(JSObject, map)), "map"); +} + +bool +TraceRecorder::map_is_native(JSObjectMap* map, LIns* map_ins, LIns*& ops_ins, size_t op_offset) +{ + JS_ASSERT(op_offset < sizeof(JSObjectOps)); + JS_ASSERT(op_offset % sizeof(void *) == 0); + +#define OP(ops) (*(void **) ((uint8 *) (ops) + op_offset)) + void* ptr = OP(map->ops); + if (ptr != OP(&js_ObjectOps)) + return false; +#undef OP + + ops_ins = addName(lir->insLoad(LIR_ldcp, map_ins, int(offsetof(JSObjectMap, ops))), "ops"); + LIns* n = lir->insLoad(LIR_ldcp, ops_ins, op_offset); + guard(true, + addName(lir->ins2(LIR_peq, n, INS_CONSTPTR(ptr)), "guard(native-map)"), + BRANCH_EXIT); + + return true; +} + +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::guardNativePropertyOp(JSObject* aobj, LIns* map_ins) +{ + /* + * Interpreter calls to PROPERTY_CACHE_TEST guard on native object ops + * which is required to use native objects (those whose maps are scopes), + * or even more narrow conditions required because the cache miss case + * will call a particular object-op (js_GetProperty, js_SetProperty). + * + * We parameterize using offsetof and guard on match against the hook at + * the given offset in js_ObjectOps. TraceRecorder::record_JSOP_SETPROP + * guards the js_SetProperty case. + */ + uint32 format = js_CodeSpec[*cx->fp->regs->pc].format; + uint32 mode = JOF_MODE(format); + + // No need to guard native-ness of global object. + JS_ASSERT(OBJ_IS_NATIVE(globalObj)); + if (aobj != globalObj) { + size_t op_offset = offsetof(JSObjectOps, objectMap); + if (mode == JOF_PROP || mode == JOF_VARPROP) { + op_offset = (format & JOF_SET) + ? offsetof(JSObjectOps, setProperty) + : offsetof(JSObjectOps, getProperty); + } else { + JS_ASSERT(mode == JOF_NAME); + } + + LIns* ops_ins; + if (!map_is_native(aobj->map, map_ins, ops_ins, op_offset)) + RETURN_STOP("non-native map"); + } + return RECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::test_property_cache(JSObject* obj, LIns* obj_ins, JSObject*& obj2, jsuword& pcval) +{ + jsbytecode* pc = cx->fp->regs->pc; + JS_ASSERT(*pc != JSOP_INITPROP && *pc != JSOP_INITMETHOD && + *pc != JSOP_SETNAME && *pc != JSOP_SETPROP && *pc != JSOP_SETMETHOD); + + // Mimic the interpreter's special case for dense arrays by skipping up one + // hop along the proto chain when accessing a named (not indexed) property, + // typically to find Array.prototype methods. + JSObject* aobj = obj; + if (OBJ_IS_DENSE_ARRAY(cx, obj)) { + guardDenseArray(obj, obj_ins, BRANCH_EXIT); + aobj = OBJ_GET_PROTO(cx, obj); + obj_ins = stobj_get_proto(obj_ins); + } + + LIns* map_ins = map(obj_ins); + + CHECK_STATUS_A(guardNativePropertyOp(aobj, map_ins)); + + JSAtom* atom; + JSPropCacheEntry* entry; + PROPERTY_CACHE_TEST(cx, pc, aobj, obj2, entry, atom); + if (!atom) { + // Null atom means that obj2 is locked and must now be unlocked. + JS_UNLOCK_OBJ(cx, obj2); + } else { + // Miss: pre-fill the cache for the interpreter, as well as for our needs. + jsid id = ATOM_TO_JSID(atom); + JSProperty* prop; + if (JOF_OPMODE(*pc) == JOF_NAME) { + JS_ASSERT(aobj == obj); + + JSTraceMonitor &localtm = *traceMonitor; + entry = js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop); + + /* js_FindPropertyHelper can reenter the interpreter and kill |this|. */ + if (!localtm.recorder) + return ARECORD_ABORTED; + + if (!entry) + RETURN_ERROR_A("error in js_FindPropertyHelper"); + if (entry == JS_NO_PROP_CACHE_FILL) + RETURN_STOP_A("cannot cache name"); + } else { + JSTraceMonitor &localtm = *traceMonitor; + JSContext *localcx = cx; + int protoIndex = js_LookupPropertyWithFlags(cx, aobj, id, + cx->resolveFlags, + &obj2, &prop); + + /* js_LookupPropertyWithFlags can reenter the interpreter and kill |this|. */ + if (!localtm.recorder) { + if (prop) + obj2->dropProperty(localcx, prop); + return ARECORD_ABORTED; + } + + if (protoIndex < 0) + RETURN_ERROR_A("error in js_LookupPropertyWithFlags"); + + if (prop) { + if (!OBJ_IS_NATIVE(obj2)) { + obj2->dropProperty(cx, prop); + RETURN_STOP_A("property found on non-native object"); + } + entry = js_FillPropertyCache(cx, aobj, 0, protoIndex, obj2, + (JSScopeProperty*) prop, false); + JS_ASSERT(entry); + if (entry == JS_NO_PROP_CACHE_FILL) + entry = NULL; + } + + } + + if (!prop) { + // Propagate obj from js_FindPropertyHelper to record_JSOP_BINDNAME + // via our obj2 out-parameter. If we are recording JSOP_SETNAME and + // the global it's assigning does not yet exist, create it. + obj2 = obj; + + // Use PCVAL_NULL to return "no such property" to our caller. + pcval = PCVAL_NULL; + return ARECORD_CONTINUE; + } + + obj2->dropProperty(cx, prop); + if (!entry) + RETURN_STOP_A("failed to fill property cache"); + } + +#ifdef JS_THREADSAFE + // There's a potential race in any JS_THREADSAFE embedding that's nuts + // enough to share mutable objects on the scope or proto chain, but we + // don't care about such insane embeddings. Anyway, the (scope, proto) + // entry->vcap coordinates must reach obj2 from aobj at this point. + JS_ASSERT(cx->requestDepth); +#endif + + return InjectStatus(guardPropertyCacheHit(obj_ins, map_ins, aobj, obj2, entry, pcval)); +} + +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::guardPropertyCacheHit(LIns* obj_ins, + LIns* map_ins, + JSObject* aobj, + JSObject* obj2, + JSPropCacheEntry* entry, + jsuword& pcval) +{ + VMSideExit* exit = snapshot(BRANCH_EXIT); + + uint32 vshape = PCVCAP_SHAPE(entry->vcap); + + // Check for first-level cache hit and guard on kshape if possible. + // Otherwise guard on key object exact match. + if (PCVCAP_TAG(entry->vcap) <= 1) { + if (aobj != globalObj) + CHECK_STATUS(guardShape(obj_ins, aobj, entry->kshape, "guard_kshape", map_ins, exit)); + + if (entry->adding()) { + if (aobj == globalObj) + RETURN_STOP("adding a property to the global object"); + + LIns *vshape_ins = addName( + lir->insLoad(LIR_ld, + addName(lir->insLoad(LIR_ldcp, cx_ins, offsetof(JSContext, runtime)), + "runtime"), + offsetof(JSRuntime, protoHazardShape)), + "protoHazardShape"); + guard(true, + addName(lir->ins2i(LIR_eq, vshape_ins, vshape), "guard_protoHazardShape"), + MISMATCH_EXIT); + } + } else { +#ifdef DEBUG + JSOp op = js_GetOpcode(cx, cx->fp->script, cx->fp->regs->pc); + JSAtom *pcatom; + if (op == JSOP_LENGTH) { + pcatom = cx->runtime->atomState.lengthAtom; + } else { + ptrdiff_t pcoff = (JOF_TYPE(js_CodeSpec[op].format) == JOF_SLOTATOM) ? SLOTNO_LEN : 0; + GET_ATOM_FROM_BYTECODE(cx->fp->script, cx->fp->regs->pc, pcoff, pcatom); + } + JS_ASSERT(entry->kpc == (jsbytecode *) pcatom); + JS_ASSERT(entry->kshape == jsuword(aobj)); +#endif + if (aobj != globalObj && !obj_ins->isconstp()) { + guard(true, + addName(lir->ins2(LIR_peq, obj_ins, INS_CONSTOBJ(aobj)), "guard_kobj"), + exit); + } + } + + // For any hit that goes up the scope and/or proto chains, we will need to + // guard on the shape of the object containing the property. + if (PCVCAP_TAG(entry->vcap) >= 1) { + JS_ASSERT(OBJ_SHAPE(obj2) == vshape); + + LIns* obj2_ins; + if (PCVCAP_TAG(entry->vcap) == 1) { + // Duplicate the special case in PROPERTY_CACHE_TEST. + obj2_ins = addName(stobj_get_proto(obj_ins), "proto"); + guard(false, lir->ins_peq0(obj2_ins), exit); + } else { + obj2_ins = INS_CONSTOBJ(obj2); + } + CHECK_STATUS(guardShape(obj2_ins, obj2, vshape, "guard_vshape", map(obj2_ins), exit)); + } + + pcval = entry->vword; + return RECORD_CONTINUE; +} + +void +TraceRecorder::stobj_set_fslot(LIns *obj_ins, unsigned slot, LIns* v_ins) +{ + lir->insStorei(v_ins, obj_ins, offsetof(JSObject, fslots) + slot * sizeof(jsval)); +} + +void +TraceRecorder::stobj_set_dslot(LIns *obj_ins, unsigned slot, LIns*& dslots_ins, LIns* v_ins) +{ + if (!dslots_ins) + dslots_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, dslots)); + lir->insStorei(v_ins, dslots_ins, slot * sizeof(jsval)); +} + +void +TraceRecorder::stobj_set_slot(LIns* obj_ins, unsigned slot, LIns*& dslots_ins, LIns* v_ins) +{ + if (slot < JS_INITIAL_NSLOTS) { + stobj_set_fslot(obj_ins, slot, v_ins); + } else { + stobj_set_dslot(obj_ins, slot - JS_INITIAL_NSLOTS, dslots_ins, v_ins); + } +} + +LIns* +TraceRecorder::stobj_get_fslot(LIns* obj_ins, unsigned slot) +{ + JS_ASSERT(slot < JS_INITIAL_NSLOTS); + return lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, fslots) + slot * sizeof(jsval)); +} + +LIns* +TraceRecorder::stobj_get_dslot(LIns* obj_ins, unsigned index, LIns*& dslots_ins) +{ + if (!dslots_ins) + dslots_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, dslots)); + return lir->insLoad(LIR_ldp, dslots_ins, index * sizeof(jsval)); +} + +LIns* +TraceRecorder::stobj_get_slot(LIns* obj_ins, unsigned slot, LIns*& dslots_ins) +{ + if (slot < JS_INITIAL_NSLOTS) + return stobj_get_fslot(obj_ins, slot); + return stobj_get_dslot(obj_ins, slot - JS_INITIAL_NSLOTS, dslots_ins); +} + +JS_REQUIRES_STACK LIns* +TraceRecorder::box_jsval(jsval v, LIns* v_ins) +{ + if (isNumber(v)) { + LIns* args[] = { v_ins, cx_ins }; + v_ins = lir->insCall(&js_BoxDouble_ci, args); + guard(false, lir->ins2(LIR_peq, v_ins, INS_CONSTWORD(JSVAL_ERROR_COOKIE)), + OOM_EXIT); + return v_ins; + } + switch (JSVAL_TAG(v)) { + case JSVAL_SPECIAL: + return lir->ins2(LIR_pior, lir->ins2i(LIR_pilsh, lir->ins_u2p(v_ins), JSVAL_TAGBITS), + INS_CONSTWORD(JSVAL_SPECIAL)); + case JSVAL_OBJECT: + return v_ins; + default: + JS_ASSERT(JSVAL_TAG(v) == JSVAL_STRING); + return lir->ins2(LIR_pior, v_ins, INS_CONSTWORD(JSVAL_STRING)); + } +} + +JS_REQUIRES_STACK LIns* +TraceRecorder::unbox_jsval(jsval v, LIns* v_ins, VMSideExit* exit) +{ + if (isNumber(v)) { + // JSVAL_IS_NUMBER(v) + guard(false, + lir->ins_eq0(lir->ins2(LIR_or, + p2i(lir->ins2(LIR_piand, v_ins, INS_CONSTWORD(JSVAL_INT))), + lir->ins2(LIR_peq, + lir->ins2(LIR_piand, v_ins, + INS_CONSTWORD(JSVAL_TAGMASK)), + INS_CONSTWORD(JSVAL_DOUBLE)))), + exit); + LIns* args[] = { v_ins }; + return lir->insCall(&js_UnboxDouble_ci, args); + } + switch (JSVAL_TAG(v)) { + case JSVAL_SPECIAL: + guard(true, + lir->ins2(LIR_peq, + lir->ins2(LIR_piand, v_ins, INS_CONSTWORD(JSVAL_TAGMASK)), + INS_CONSTWORD(JSVAL_SPECIAL)), + exit); + return p2i(lir->ins2i(LIR_pursh, v_ins, JSVAL_TAGBITS)); + + case JSVAL_OBJECT: + if (JSVAL_IS_NULL(v)) { + // JSVAL_NULL maps to type TT_NULL, so insist that v_ins == 0 here. + guard(true, lir->ins_peq0(v_ins), exit); + } else { + guard(false, lir->ins_peq0(v_ins), exit); + guard(true, + lir->ins2(LIR_peq, + lir->ins2(LIR_piand, v_ins, INS_CONSTWORD(JSVAL_TAGMASK)), + INS_CONSTWORD(JSVAL_OBJECT)), + exit); + + /* + * LIR_ldcp is ok to use here even though Array classword can + * change, because no object's classword can ever change from + * &js_ArrayClass to &js_FunctionClass. + */ + guard(HAS_FUNCTION_CLASS(JSVAL_TO_OBJECT(v)), + lir->ins2(LIR_peq, + lir->ins2(LIR_piand, + lir->insLoad(LIR_ldcp, v_ins, offsetof(JSObject, classword)), + INS_CONSTWORD(~JSSLOT_CLASS_MASK_BITS)), + INS_CONSTPTR(&js_FunctionClass)), + exit); + } + return v_ins; + + default: + JS_ASSERT(JSVAL_TAG(v) == JSVAL_STRING); + guard(true, + lir->ins2(LIR_peq, + lir->ins2(LIR_piand, v_ins, INS_CONSTWORD(JSVAL_TAGMASK)), + INS_CONSTWORD(JSVAL_STRING)), + exit); + return lir->ins2(LIR_piand, v_ins, addName(lir->insImmWord(~JSVAL_TAGMASK), + "~JSVAL_TAGMASK")); + } +} + +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::getThis(LIns*& this_ins) +{ + /* + * js_ComputeThisForFrame updates cx->fp->argv[-1], so sample it into 'original' first. + */ + jsval original = JSVAL_NULL; + if (cx->fp->argv) { + original = cx->fp->argv[-1]; + if (!JSVAL_IS_PRIMITIVE(original) && + guardClass(JSVAL_TO_OBJECT(original), get(&cx->fp->argv[-1]), &js_WithClass, snapshot(MISMATCH_EXIT))) { + RETURN_STOP("can't trace getThis on With object"); + } + } + + JSObject* thisObj = js_ComputeThisForFrame(cx, cx->fp); + if (!thisObj) + RETURN_ERROR("js_ComputeThisForName failed"); + + /* In global code, bake in the global object as 'this' object. */ + if (!cx->fp->callee()) { + JS_ASSERT(callDepth == 0); + this_ins = INS_CONSTOBJ(thisObj); + + /* + * We don't have argv[-1] in global code, so we don't update the + * tracker here. + */ + return RECORD_CONTINUE; + } + + jsval& thisv = cx->fp->argv[-1]; + JS_ASSERT(JSVAL_IS_OBJECT(thisv)); + + /* + * Traces type-specialize between null and objects, so if we currently see + * a null value in argv[-1], this trace will only match if we see null at + * runtime as well. Bake in the global object as 'this' object, updating + * the tracker as well. We can only detect this condition prior to calling + * js_ComputeThisForFrame, since it updates the interpreter's copy of + * argv[-1]. + */ + JSClass* clasp = NULL;; + if (JSVAL_IS_NULL(original) || + (((clasp = STOBJ_GET_CLASS(JSVAL_TO_OBJECT(original))) == &js_CallClass) || + (clasp == &js_BlockClass))) { + if (clasp) + guardClass(JSVAL_TO_OBJECT(original), get(&thisv), clasp, snapshot(BRANCH_EXIT)); + JS_ASSERT(!JSVAL_IS_PRIMITIVE(thisv)); + if (thisObj != globalObj) + RETURN_STOP("global object was wrapped while recording"); + this_ins = INS_CONSTOBJ(thisObj); + set(&thisv, this_ins); + return RECORD_CONTINUE; + } + + this_ins = get(&thisv); + + JSObject* wrappedGlobal = globalObj->thisObject(cx); + if (!wrappedGlobal) + RETURN_ERROR("globalObj->thisObject hook threw in getThis"); + + /* + * The only unwrapped object that needs to be wrapped that we can get here + * is the global object obtained throught the scope chain. + */ + this_ins = lir->ins_choose(lir->ins_peq0(stobj_get_parent(this_ins)), + INS_CONSTOBJ(wrappedGlobal), + this_ins, avmplus::AvmCore::use_cmov()); + return RECORD_CONTINUE; +} + + +LIns* +TraceRecorder::getStringLength(LIns* str_ins) +{ + LIns* len_ins = lir->insLoad(LIR_ldp, str_ins, (int)offsetof(JSString, mLength)); + + LIns* masked_len_ins = lir->ins2(LIR_piand, + len_ins, + INS_CONSTWORD(JSString::LENGTH_MASK)); + + LIns* real_len = + lir->ins_choose(lir->ins_peq0(lir->ins2(LIR_piand, + len_ins, + INS_CONSTWORD(JSString::DEPENDENT))), + masked_len_ins, + lir->ins_choose(lir->ins_peq0(lir->ins2(LIR_piand, + len_ins, + INS_CONSTWORD(JSString::PREFIX))), + lir->ins2(LIR_piand, + len_ins, + INS_CONSTWORD(JSString::DEPENDENT_LENGTH_MASK)), + masked_len_ins, avmplus::AvmCore::use_cmov()), + avmplus::AvmCore::use_cmov()); + return p2i(real_len); +} + +JS_REQUIRES_STACK bool +TraceRecorder::guardClass(JSObject* obj, LIns* obj_ins, JSClass* clasp, VMSideExit* exit) +{ + bool cond = STOBJ_GET_CLASS(obj) == clasp; + + LIns* class_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, classword)); + class_ins = lir->ins2(LIR_piand, class_ins, INS_CONSTWORD(~JSSLOT_CLASS_MASK_BITS)); + + char namebuf[32]; + JS_snprintf(namebuf, sizeof namebuf, "guard(class is %s)", clasp->name); + guard(cond, addName(lir->ins2(LIR_peq, class_ins, INS_CONSTPTR(clasp)), namebuf), exit); + return cond; +} + +JS_REQUIRES_STACK bool +TraceRecorder::guardDenseArray(JSObject* obj, LIns* obj_ins, ExitType exitType) +{ + return guardClass(obj, obj_ins, &js_ArrayClass, snapshot(exitType)); +} + +JS_REQUIRES_STACK bool +TraceRecorder::guardDenseArray(JSObject* obj, LIns* obj_ins, VMSideExit* exit) +{ + return guardClass(obj, obj_ins, &js_ArrayClass, exit); +} + +JS_REQUIRES_STACK bool +TraceRecorder::guardHasPrototype(JSObject* obj, LIns* obj_ins, + JSObject** pobj, LIns** pobj_ins, + VMSideExit* exit) +{ + *pobj = obj->getProto(); + *pobj_ins = stobj_get_proto(obj_ins); + + bool cond = *pobj == NULL; + guard(cond, addName(lir->ins_peq0(*pobj_ins), "guard(proto-not-null)"), exit); + return !cond; +} + +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::guardPrototypeHasNoIndexedProperties(JSObject* obj, LIns* obj_ins, ExitType exitType) +{ + /* + * Guard that no object along the prototype chain has any indexed + * properties which might become visible through holes in the array. + */ + VMSideExit* exit = snapshot(exitType); + + if (js_PrototypeHasIndexedProperties(cx, obj)) + return RECORD_STOP; + + while (guardHasPrototype(obj, obj_ins, &obj, &obj_ins, exit)) + CHECK_STATUS(guardShape(obj_ins, obj, OBJ_SHAPE(obj), "guard(shape)", map(obj_ins), exit)); + return RECORD_CONTINUE; +} + +RecordingStatus +TraceRecorder::guardNotGlobalObject(JSObject* obj, LIns* obj_ins) +{ + if (obj == globalObj) + RETURN_STOP("reference aliases global object"); + guard(false, lir->ins2(LIR_peq, obj_ins, INS_CONSTOBJ(globalObj)), MISMATCH_EXIT); + return RECORD_CONTINUE; +} + +JS_REQUIRES_STACK void +TraceRecorder::clearFrameSlotsFromCache() +{ + /* + * Clear out all slots of this frame in the nativeFrameTracker. Different + * locations on the VM stack might map to different locations on the native + * stack depending on the number of arguments (i.e.) of the next call, so + * we have to make sure we map those in to the cache with the right + * offsets. + */ + JSStackFrame* fp = cx->fp; + jsval* vp; + jsval* vpstop; + + /* + * Duplicate native stack layout computation: see VisitFrameSlots header comment. + * This doesn't do layout arithmetic, but it must clear out all the slots defined as + * imported by VisitFrameSlots. + */ + if (fp->argv) { + vp = &fp->argv[-2]; + vpstop = &fp->argv[argSlots(fp)]; + while (vp < vpstop) + nativeFrameTracker.set(vp++, (LIns*)0); + nativeFrameTracker.set(&fp->argsobj, (LIns*)0); + } + vp = &fp->slots[0]; + vpstop = &fp->slots[fp->script->nslots]; + while (vp < vpstop) + nativeFrameTracker.set(vp++, (LIns*)0); +} + +/* + * If we have created an |arguments| object for the frame, we must copy the + * argument values into the object as properties in case it is used after + * this frame returns. + */ +JS_REQUIRES_STACK void +TraceRecorder::putArguments() +{ + if (cx->fp->argsobj && cx->fp->argc) { + LIns* argsobj_ins = get(&cx->fp->argsobj); + LIns* args_ins = lir->insAlloc(sizeof(jsval) * cx->fp->argc); + for (uintN i = 0; i < cx->fp->argc; ++i) { + LIns* arg_ins = box_jsval(cx->fp->argv[i], get(&cx->fp->argv[i])); + lir->insStorei(arg_ins, args_ins, i * sizeof(jsval)); + } + LIns* args[] = { args_ins, argsobj_ins, cx_ins }; + lir->insCall(&js_PutArguments_ci, args); + } +} + +static JS_REQUIRES_STACK inline bool +IsTraceableRecursion(JSContext *cx) +{ + JSStackFrame *fp = cx->fp; + JSStackFrame *down = cx->fp->down; + if (!down) + return false; + if (down->script != fp->script) + return false; + if (down->argc != fp->argc) + return false; + if (fp->argc != fp->fun->nargs) + return false; + if (fp->imacpc || down->imacpc) + return false; + if ((fp->flags & JSFRAME_CONSTRUCTING) || (down->flags & JSFRAME_CONSTRUCTING)) + return false; + if (*fp->script->code != JSOP_TRACE) + return false; + return true; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_EnterFrame(uintN& inlineCallCount) +{ + JSStackFrame* fp = cx->fp; + + if (++callDepth >= MAX_CALLDEPTH) + RETURN_STOP_A("exceeded maximum call depth"); + + debug_only_printf(LC_TMTracer, "EnterFrame %s, callDepth=%d\n", + js_AtomToPrintableString(cx, cx->fp->fun->atom), + callDepth); + debug_only_stmt( + if (js_LogController.lcbits & LC_TMRecorder) { + js_Disassemble(cx, cx->fp->script, JS_TRUE, stdout); + debug_only_print0(LC_TMTracer, "----\n"); + } + ) + LIns* void_ins = INS_VOID(); + + // Duplicate native stack layout computation: see VisitFrameSlots header comment. + // This doesn't do layout arithmetic, but it must initialize in the tracker all the + // slots defined as imported by VisitFrameSlots. + jsval* vp = &fp->argv[fp->argc]; + jsval* vpstop = vp + ptrdiff_t(fp->fun->nargs) - ptrdiff_t(fp->argc); + while (vp < vpstop) { + if (vp >= fp->down->regs->sp) + nativeFrameTracker.set(vp, (LIns*)0); + set(vp++, void_ins, true); + } + + vp = &fp->slots[0]; + vpstop = vp + fp->script->nfixed; + while (vp < vpstop) + set(vp++, void_ins, true); + set(&fp->argsobj, INS_NULL(), true); + + /* + * Check for recursion. This is a special check for recursive cases that can be + * a trace-tree, just like a loop. If recursion acts weird, for example + * differing argc or existence of an imacpc, it's not something this code is + * concerned about. That should pass through below to not regress pre-recursion + * functionality. + */ + if (IsTraceableRecursion(cx) && treeInfo->script == cx->fp->script) { + if (treeInfo->recursion == Recursion_Disallowed) + RETURN_STOP_A("recursion not allowed in this tree"); + if (treeInfo->script != cx->fp->script) + RETURN_STOP_A("recursion does not match original tree"); + return InjectStatus(downRecursion()); + } + + /* Try inlining one level in case this recursion doesn't go too deep. */ + if (fp->script == fp->down->script && + fp->down->down && fp->down->down->script == fp->script) { + RETURN_STOP_A("recursion started inlining"); + } + + TreeFragment* root = fragment->root; + TreeFragment* first = LookupLoop(&JS_TRACE_MONITOR(cx), fp->regs->pc, root->globalObj, + root->globalShape, fp->argc); + if (!first) + return ARECORD_CONTINUE; + TreeFragment* f = findNestedCompatiblePeer(first); + if (!f) { + /* + * If there were no compatible peers, but there were peers at all, then it is probable that + * an inner recursive function is type mismatching. Start a new recorder that must be + * recursive. + */ + for (f = first; f; f = f->peer) { + if (f->treeInfo && f->treeInfo->recursion == Recursion_Detected) { + /* Since this recorder is about to die, save its values. */ + if (++first->hits() <= HOTLOOP) + return ARECORD_STOP; + if (IsBlacklisted((jsbytecode*)f->ip)) + RETURN_STOP_A("inner recursive tree is blacklisted"); + JSContext* _cx = cx; + SlotList* globalSlots = treeInfo->globalSlots; + JSTraceMonitor* tm = traceMonitor; + js_AbortRecording(cx, "trying to compile inner recursive tree"); + if (RecordTree(_cx, tm, first, NULL, 0, first->globalObj, first->globalShape, + globalSlots, _cx->fp->argc, Record_EnterFrame)) { + JS_ASSERT(tm->recorder); + } + break; + } + } + return ARECORD_CONTINUE; + } else if (f) { + /* + * Make sure the shape of the global object still matches (this might + * flush the JIT cache). + */ + JSObject* globalObj = JS_GetGlobalForObject(cx, cx->fp->scopeChain); + uint32 globalShape = -1; + SlotList* globalSlots = NULL; + if (!CheckGlobalObjectShape(cx, traceMonitor, globalObj, &globalShape, &globalSlots)) + return ARECORD_ABORTED; + return attemptTreeCall(f, inlineCallCount); + } + + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_LeaveFrame() +{ + debug_only_stmt( + if (cx->fp->fun) + debug_only_printf(LC_TMTracer, + "LeaveFrame (back to %s), callDepth=%d\n", + js_AtomToPrintableString(cx, cx->fp->fun->atom), + callDepth); + ); + + JS_ASSERT(js_CodeSpec[js_GetOpcode(cx, cx->fp->script, + cx->fp->regs->pc)].length == JSOP_CALL_LENGTH); + + if (callDepth-- <= 0) + RETURN_STOP_A("returned out of a loop we started tracing"); + + // LeaveFrame gets called after the interpreter popped the frame and + // stored rval, so cx->fp not cx->fp->down, and -1 not 0. + atoms = FrameAtomBase(cx, cx->fp); + set(&stackval(-1), rval_ins, true); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_PUSH() +{ + stack(0, INS_CONST(JSVAL_TO_SPECIAL(JSVAL_VOID))); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_POPV() +{ + jsval& rval = stackval(-1); + LIns *rval_ins = box_jsval(rval, get(&rval)); + + // Store it in cx->fp->rval. NB: Tricky dependencies. cx->fp is the right + // frame because POPV appears only in global and eval code and we don't + // trace JSOP_EVAL or leaving the frame where tracing started. + LIns *fp_ins = lir->insLoad(LIR_ldp, cx_ins, offsetof(JSContext, fp)); + lir->insStorei(rval_ins, fp_ins, offsetof(JSStackFrame, rval)); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_ENTERWITH() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_LEAVEWITH() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_RETURN() +{ + /* A return from callDepth 0 terminates the current loop, except for recursion. */ + if (callDepth == 0) { + if (IsTraceableRecursion(cx) && treeInfo->recursion != Recursion_Disallowed && + treeInfo->script == cx->fp->script) { + return InjectStatus(upRecursion()); + } else { + AUDIT(returnLoopExits); + return endLoop(); + } + } + + putArguments(); + + /* If we inlined this function call, make the return value available to the caller code. */ + jsval& rval = stackval(-1); + JSStackFrame *fp = cx->fp; + if ((cx->fp->flags & JSFRAME_CONSTRUCTING) && JSVAL_IS_PRIMITIVE(rval)) { + JS_ASSERT(fp->thisv == fp->argv[-1]); + rval_ins = get(&fp->argv[-1]); + } else { + rval_ins = get(&rval); + } + debug_only_printf(LC_TMTracer, + "returning from %s\n", + js_AtomToPrintableString(cx, cx->fp->fun->atom)); + clearFrameSlotsFromCache(); + + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_GOTO() +{ + /* + * If we hit a break or a continue to an outer loop, end the loop and + * generate an always-taken loop exit guard. For other downward gotos + * (like if/else) continue recording. + */ + jssrcnote* sn = js_GetSrcNote(cx->fp->script, cx->fp->regs->pc); + + if (sn && (SN_TYPE(sn) == SRC_BREAK || SN_TYPE(sn) == SRC_CONT2LABEL)) { + AUDIT(breakLoopExits); + return endLoop(); + } + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_IFEQ() +{ + trackCfgMerges(cx->fp->regs->pc); + return ifop(); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_IFNE() +{ + return ifop(); +} + +LIns* +TraceRecorder::newArguments() +{ + LIns* global_ins = INS_CONSTOBJ(globalObj); + LIns* argc_ins = INS_CONST(cx->fp->argc); + LIns* callee_ins = get(&cx->fp->argv[-2]); + LIns* argv_ins = cx->fp->argc + ? lir->ins2(LIR_piadd, lirbuf->sp, + lir->insImmWord(nativespOffset(&cx->fp->argv[0]))) + : INS_CONSTPTR((void *) 2); + js_ArgsPrivateNative *apn = js_ArgsPrivateNative::create(traceAlloc(), cx->fp->argc); + for (uintN i = 0; i < cx->fp->argc; ++i) { + apn->typemap()[i] = determineSlotType(&cx->fp->argv[i]); + } + + LIns* args[] = { INS_CONSTPTR(apn), argv_ins, callee_ins, argc_ins, global_ins, cx_ins }; + LIns* call_ins = lir->insCall(&js_Arguments_ci, args); + guard(false, lir->ins_peq0(call_ins), OOM_EXIT); + return call_ins; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_ARGUMENTS() +{ + if (cx->fp->flags & JSFRAME_OVERRIDE_ARGS) + RETURN_STOP_A("Can't trace |arguments| if |arguments| is assigned to"); + + LIns* a_ins = get(&cx->fp->argsobj); + LIns* args_ins; + if (a_ins->opcode() == LIR_int) { + // |arguments| is set to 0 by EnterFrame on this trace, so call to create it. + args_ins = newArguments(); + } else { + // Generate LIR to create arguments only if it has not already been created. + + LIns* mem_ins = lir->insAlloc(sizeof(jsval)); + + LIns* br1 = lir->insBranch(LIR_jt, lir->ins_peq0(a_ins), NULL); + lir->insStorei(a_ins, mem_ins, 0); + LIns* br2 = lir->insBranch(LIR_j, NULL, NULL); + + LIns* label1 = lir->ins0(LIR_label); + br1->setTarget(label1); + + LIns* call_ins = newArguments(); + lir->insStorei(call_ins, mem_ins, 0); + + LIns* label2 = lir->ins0(LIR_label); + br2->setTarget(label2); + + args_ins = lir->insLoad(LIR_ldp, mem_ins, 0); + } + + stack(0, args_ins); + set(&cx->fp->argsobj, args_ins); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_DUP() +{ + stack(0, get(&stackval(-1))); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_DUP2() +{ + stack(0, get(&stackval(-2))); + stack(1, get(&stackval(-1))); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_SWAP() +{ + jsval& l = stackval(-2); + jsval& r = stackval(-1); + LIns* l_ins = get(&l); + LIns* r_ins = get(&r); + set(&r, l_ins); + set(&l, r_ins); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_PICK() +{ + jsval* sp = cx->fp->regs->sp; + jsint n = cx->fp->regs->pc[1]; + JS_ASSERT(sp - (n+1) >= StackBase(cx->fp)); + LIns* top = get(sp - (n+1)); + for (jsint i = 0; i < n; ++i) + set(sp - (n+1) + i, get(sp - n + i)); + set(&sp[-1], top); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_SETCONST() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_BITOR() +{ + return InjectStatus(binary(LIR_or)); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_BITXOR() +{ + return InjectStatus(binary(LIR_xor)); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_BITAND() +{ + return InjectStatus(binary(LIR_and)); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_EQ() +{ + return equality(false, true); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_NE() +{ + return equality(true, true); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_LT() +{ + return relational(LIR_flt, true); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_LE() +{ + return relational(LIR_fle, true); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_GT() +{ + return relational(LIR_fgt, true); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_GE() +{ + return relational(LIR_fge, true); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_LSH() +{ + return InjectStatus(binary(LIR_lsh)); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_RSH() +{ + return InjectStatus(binary(LIR_rsh)); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_URSH() +{ + return InjectStatus(binary(LIR_ush)); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_ADD() +{ + jsval& r = stackval(-1); + jsval& l = stackval(-2); + + if (!JSVAL_IS_PRIMITIVE(l)) { + RETURN_IF_XML_A(l); + if (!JSVAL_IS_PRIMITIVE(r)) { + RETURN_IF_XML_A(r); + return InjectStatus(call_imacro(add_imacros.obj_obj)); + } + return InjectStatus(call_imacro(add_imacros.obj_any)); + } + if (!JSVAL_IS_PRIMITIVE(r)) { + RETURN_IF_XML_A(r); + return InjectStatus(call_imacro(add_imacros.any_obj)); + } + + if (JSVAL_IS_STRING(l) || JSVAL_IS_STRING(r)) { + LIns* args[] = { stringify(r), stringify(l), cx_ins }; + LIns* concat = lir->insCall(&js_ConcatStrings_ci, args); + guard(false, lir->ins_peq0(concat), OOM_EXIT); + set(&l, concat); + return ARECORD_CONTINUE; + } + + return InjectStatus(binary(LIR_fadd)); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_SUB() +{ + return InjectStatus(binary(LIR_fsub)); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_MUL() +{ + return InjectStatus(binary(LIR_fmul)); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_DIV() +{ + return InjectStatus(binary(LIR_fdiv)); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_MOD() +{ + return InjectStatus(binary(LIR_fmod)); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_NOT() +{ + jsval& v = stackval(-1); + if (JSVAL_IS_SPECIAL(v)) { + set(&v, lir->ins_eq0(lir->ins2i(LIR_eq, get(&v), 1))); + return ARECORD_CONTINUE; + } + if (isNumber(v)) { + LIns* v_ins = get(&v); + set(&v, lir->ins2(LIR_or, lir->ins2(LIR_feq, v_ins, lir->insImmf(0)), + lir->ins_eq0(lir->ins2(LIR_feq, v_ins, v_ins)))); + return ARECORD_CONTINUE; + } + if (JSVAL_TAG(v) == JSVAL_OBJECT) { + set(&v, lir->ins_peq0(get(&v))); + return ARECORD_CONTINUE; + } + JS_ASSERT(JSVAL_IS_STRING(v)); + set(&v, lir->ins_peq0(lir->ins2(LIR_piand, + lir->insLoad(LIR_ldp, get(&v), (int)offsetof(JSString, mLength)), + INS_CONSTWORD(JSString::LENGTH_MASK)))); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_BITNOT() +{ + return InjectStatus(unary(LIR_not)); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_NEG() +{ + jsval& v = stackval(-1); + + if (!JSVAL_IS_PRIMITIVE(v)) { + RETURN_IF_XML_A(v); + return InjectStatus(call_imacro(unary_imacros.sign)); + } + + if (isNumber(v)) { + LIns* a = get(&v); + + /* + * If we're a promoted integer, we have to watch out for 0s since -0 is + * a double. Only follow this path if we're not an integer that's 0 and + * we're not a double that's zero. + */ + if (!oracle.isInstructionUndemotable(cx->fp->regs->pc) && + isPromoteInt(a) && + (!JSVAL_IS_INT(v) || JSVAL_TO_INT(v) != 0) && + (!JSVAL_IS_DOUBLE(v) || !JSDOUBLE_IS_NEGZERO(*JSVAL_TO_DOUBLE(v))) && + -asNumber(v) == (int)-asNumber(v)) { + a = lir->ins1(LIR_neg, ::demote(lir, a)); + if (!a->isconst()) { + VMSideExit* exit = snapshot(OVERFLOW_EXIT); + guard(false, lir->ins1(LIR_ov, a), exit); + guard(false, lir->ins2i(LIR_eq, a, 0), exit); + } + a = lir->ins1(LIR_i2f, a); + } else { + a = lir->ins1(LIR_fneg, a); + } + + set(&v, a); + return ARECORD_CONTINUE; + } + + if (JSVAL_IS_NULL(v)) { + set(&v, lir->insImmf(-0.0)); + return ARECORD_CONTINUE; + } + + JS_ASSERT(JSVAL_TAG(v) == JSVAL_STRING || JSVAL_IS_SPECIAL(v)); + + LIns* args[] = { get(&v), cx_ins }; + set(&v, lir->ins1(LIR_fneg, + lir->insCall(JSVAL_IS_STRING(v) + ? &js_StringToNumber_ci + : &js_BooleanOrUndefinedToNumber_ci, + args))); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_POS() +{ + jsval& v = stackval(-1); + + if (!JSVAL_IS_PRIMITIVE(v)) { + RETURN_IF_XML_A(v); + return InjectStatus(call_imacro(unary_imacros.sign)); + } + + if (isNumber(v)) + return ARECORD_CONTINUE; + + if (JSVAL_IS_NULL(v)) { + set(&v, lir->insImmf(0)); + return ARECORD_CONTINUE; + } + + JS_ASSERT(JSVAL_TAG(v) == JSVAL_STRING || JSVAL_IS_SPECIAL(v)); + + LIns* args[] = { get(&v), cx_ins }; + set(&v, lir->insCall(JSVAL_IS_STRING(v) + ? &js_StringToNumber_ci + : &js_BooleanOrUndefinedToNumber_ci, + args)); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_PRIMTOP() +{ + // Either this opcode does nothing or we couldn't have traced here, because + // we'd have thrown an exception -- so do nothing if we actually hit this. + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_OBJTOP() +{ + jsval& v = stackval(-1); + RETURN_IF_XML_A(v); + return ARECORD_CONTINUE; +} + +RecordingStatus +TraceRecorder::getClassPrototype(JSObject* ctor, LIns*& proto_ins) +{ + // ctor must be a function created via js_InitClass. +#ifdef DEBUG + JSClass *clasp = FUN_CLASP(GET_FUNCTION_PRIVATE(cx, ctor)); + JS_ASSERT(clasp); + + JSTraceMonitor &localtm = JS_TRACE_MONITOR(cx); +#endif + + jsval pval; + if (!ctor->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), &pval)) + RETURN_ERROR("error getting prototype from constructor"); + + // ctor.prototype is a permanent data property, so this lookup cannot have + // deep-aborted. + JS_ASSERT(localtm.recorder); + +#ifdef DEBUG + JSBool ok, found; + uintN attrs; + ok = JS_GetPropertyAttributes(cx, ctor, js_class_prototype_str, &attrs, &found); + JS_ASSERT(ok); + JS_ASSERT(found); + JS_ASSERT((~attrs & (JSPROP_READONLY | JSPROP_PERMANENT)) == 0); +#endif + + // Since ctor was built by js_InitClass, we can assert (rather than check) + // that pval is usable. + JS_ASSERT(!JSVAL_IS_PRIMITIVE(pval)); + JSObject *proto = JSVAL_TO_OBJECT(pval); + JS_ASSERT_IF(clasp != &js_ArrayClass, OBJ_SCOPE(proto)->emptyScope->clasp == clasp); + + proto_ins = INS_CONSTOBJ(proto); + return RECORD_CONTINUE; +} + +RecordingStatus +TraceRecorder::getClassPrototype(JSProtoKey key, LIns*& proto_ins) +{ +#ifdef DEBUG + JSTraceMonitor &localtm = JS_TRACE_MONITOR(cx); +#endif + + JSObject* proto; + if (!js_GetClassPrototype(cx, globalObj, INT_TO_JSID(key), &proto)) + RETURN_ERROR("error in js_GetClassPrototype"); + + // This should not have reentered. + JS_ASSERT(localtm.recorder); + + // If we might end up passing the proto to JSObject::initSharingEmptyScope, + // we must check here that proto has a matching emptyScope. We skip the + // check for Array.prototype because new arrays, being non-native, are + // never initialized using initSharingEmptyScope. + if (key != JSProto_Array) { + if (!OBJ_IS_NATIVE(proto)) + RETURN_STOP("non-native class prototype"); + JSEmptyScope *emptyScope = OBJ_SCOPE(proto)->emptyScope; + if (!emptyScope || JSCLASS_CACHED_PROTO_KEY(emptyScope->clasp) != key) + RETURN_STOP("class prototype is not the standard one"); + } + + proto_ins = INS_CONSTOBJ(proto); + return RECORD_CONTINUE; +} + +#define IGNORE_NATIVE_CALL_COMPLETE_CALLBACK ((JSSpecializedNative*)1) + +RecordingStatus +TraceRecorder::newString(JSObject* ctor, uint32 argc, jsval* argv, jsval* rval) +{ + JS_ASSERT(argc == 1); + + if (!JSVAL_IS_PRIMITIVE(argv[0])) { + RETURN_IF_XML(argv[0]); + return call_imacro(new_imacros.String); + } + + LIns* proto_ins; + CHECK_STATUS(getClassPrototype(ctor, proto_ins)); + + LIns* args[] = { stringify(argv[0]), proto_ins, cx_ins }; + LIns* obj_ins = lir->insCall(&js_String_tn_ci, args); + guard(false, lir->ins_peq0(obj_ins), OOM_EXIT); + + set(rval, obj_ins); + pendingSpecializedNative = IGNORE_NATIVE_CALL_COMPLETE_CALLBACK; + return RECORD_CONTINUE; +} + +RecordingStatus +TraceRecorder::newArray(JSObject* ctor, uint32 argc, jsval* argv, jsval* rval) +{ + LIns *proto_ins; + CHECK_STATUS(getClassPrototype(ctor, proto_ins)); + + LIns *arr_ins; + if (argc == 0) { + // arr_ins = js_NewEmptyArray(cx, Array.prototype) + LIns *args[] = { proto_ins, cx_ins }; + arr_ins = lir->insCall(&js_NewEmptyArray_ci, args); + guard(false, lir->ins_peq0(arr_ins), OOM_EXIT); + } else if (argc == 1 && JSVAL_IS_NUMBER(argv[0])) { + // arr_ins = js_NewEmptyArray(cx, Array.prototype, length) + LIns *args[] = { f2i(get(argv)), proto_ins, cx_ins }; // FIXME: is this 64-bit safe? + arr_ins = lir->insCall(&js_NewEmptyArrayWithLength_ci, args); + guard(false, lir->ins_peq0(arr_ins), OOM_EXIT); + } else { + // arr_ins = js_NewArrayWithSlots(cx, Array.prototype, argc) + LIns *args[] = { INS_CONST(argc), proto_ins, cx_ins }; + arr_ins = lir->insCall(&js_NewArrayWithSlots_ci, args); + guard(false, lir->ins_peq0(arr_ins), OOM_EXIT); + + // arr->dslots[i] = box_jsval(vp[i]); for i in 0..argc + LIns *dslots_ins = NULL; + for (uint32 i = 0; i < argc && !outOfMemory(); i++) { + LIns *elt_ins = box_jsval(argv[i], get(&argv[i])); + stobj_set_dslot(arr_ins, i, dslots_ins, elt_ins); + } + + if (argc > 0) + stobj_set_fslot(arr_ins, JSSLOT_ARRAY_COUNT, INS_CONST(argc)); + } + + set(rval, arr_ins); + pendingSpecializedNative = IGNORE_NATIVE_CALL_COMPLETE_CALLBACK; + return RECORD_CONTINUE; +} + +JS_REQUIRES_STACK void +TraceRecorder::propagateFailureToBuiltinStatus(LIns* ok_ins, LIns*& status_ins) +{ + /* + * Check the boolean return value (ok_ins) of a native JSNative, + * JSFastNative, or JSPropertyOp hook for failure. On failure, set the + * JSBUILTIN_ERROR bit of cx->builtinStatus. + * + * If the return value (ok_ins) is true, status' == status. Otherwise + * status' = status | JSBUILTIN_ERROR. We calculate (rval&1)^1, which is 1 + * if rval is JS_FALSE (error), and then shift that by 1, which is the log2 + * of JSBUILTIN_ERROR. + */ + JS_STATIC_ASSERT(((JS_TRUE & 1) ^ 1) << 1 == 0); + JS_STATIC_ASSERT(((JS_FALSE & 1) ^ 1) << 1 == JSBUILTIN_ERROR); + status_ins = lir->ins2(LIR_or, + status_ins, + lir->ins2i(LIR_lsh, + lir->ins2i(LIR_xor, + lir->ins2i(LIR_and, ok_ins, 1), + 1), + 1)); + lir->insStorei(status_ins, lirbuf->state, (int) offsetof(InterpState, builtinStatus)); +} + +JS_REQUIRES_STACK void +TraceRecorder::emitNativePropertyOp(JSScope* scope, JSScopeProperty* sprop, LIns* obj_ins, + bool setflag, LIns* boxed_ins) +{ + JS_ASSERT(!(sprop->attrs & (setflag ? JSPROP_SETTER : JSPROP_GETTER))); + JS_ASSERT(setflag ? !SPROP_HAS_STUB_SETTER(sprop) : !SPROP_HAS_STUB_GETTER_OR_IS_METHOD(sprop)); + + enterDeepBailCall(); + + // It is unsafe to pass the address of an object slot as the out parameter, + // because the getter or setter could end up resizing the object's dslots. + // Instead, use a word of stack and root it in nativeVp. + LIns* vp_ins = lir->insAlloc(sizeof(jsval)); + lir->insStorei(vp_ins, lirbuf->state, offsetof(InterpState, nativeVp)); + lir->insStorei(INS_CONST(1), lirbuf->state, offsetof(InterpState, nativeVpLen)); + if (setflag) + lir->insStorei(boxed_ins, vp_ins, 0); + + CallInfo* ci = new (traceAlloc()) CallInfo(); + ci->_address = uintptr_t(setflag ? sprop->setter : sprop->getter); + ci->_argtypes = ARGSIZE_I << (0*ARGSIZE_SHIFT) | + ARGSIZE_P << (1*ARGSIZE_SHIFT) | + ARGSIZE_P << (2*ARGSIZE_SHIFT) | + ARGSIZE_P << (3*ARGSIZE_SHIFT) | + ARGSIZE_P << (4*ARGSIZE_SHIFT); + ci->_cse = ci->_fold = 0; + ci->_abi = ABI_CDECL; +#ifdef DEBUG + ci->_name = "JSPropertyOp"; +#endif + LIns* args[] = { vp_ins, INS_CONSTVAL(SPROP_USERID(sprop)), obj_ins, cx_ins }; + LIns* ok_ins = lir->insCall(ci, args); + + // Cleanup. Immediately clear nativeVp before we might deep bail. + lir->insStorei(INS_NULL(), lirbuf->state, offsetof(InterpState, nativeVp)); + leaveDeepBailCall(); + + // Guard that the call succeeded and builtinStatus is still 0. + // If the native op succeeds but we deep-bail here, the result value is + // lost! Therefore this can only be used for setters of shared properties. + // In that case we ignore the result value anyway. + LIns* status_ins = lir->insLoad(LIR_ld, + lirbuf->state, + (int) offsetof(InterpState, builtinStatus)); + propagateFailureToBuiltinStatus(ok_ins, status_ins); + guard(true, lir->ins_eq0(status_ins), STATUS_EXIT); + + // Re-load the value--but this is currently unused, so commented out. + //boxed_ins = lir->insLoad(LIR_ldp, vp_ins, 0); +} + +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::emitNativeCall(JSSpecializedNative* sn, uintN argc, LIns* args[], bool rooted) +{ + bool constructing = !!(sn->flags & JSTN_CONSTRUCTOR); + + if (JSTN_ERRTYPE(sn) == FAIL_STATUS) { + // This needs to capture the pre-call state of the stack. So do not set + // pendingSpecializedNative before taking this snapshot. + JS_ASSERT(!pendingSpecializedNative); + + // Take snapshot for js_DeepBail and store it in cx->bailExit. + // If we are calling a slow native, add information to the side exit + // for SynthesizeSlowNativeFrame. + VMSideExit* exit = enterDeepBailCall(); + JSObject* funobj = JSVAL_TO_OBJECT(stackval(0 - (2 + argc))); + if (FUN_SLOW_NATIVE(GET_FUNCTION_PRIVATE(cx, funobj))) { + exit->setNativeCallee(funobj, constructing); + treeInfo->gcthings.addUnique(OBJECT_TO_JSVAL(funobj)); + } + } + + LIns* res_ins = lir->insCall(sn->builtin, args); + + // Immediately unroot the vp as soon we return since we might deep bail next. + if (rooted) + lir->insStorei(INS_NULL(), lirbuf->state, offsetof(InterpState, nativeVp)); + + rval_ins = res_ins; + switch (JSTN_ERRTYPE(sn)) { + case FAIL_NULL: + guard(false, lir->ins_peq0(res_ins), OOM_EXIT); + break; + case FAIL_NEG: + res_ins = lir->ins1(LIR_i2f, res_ins); + guard(false, lir->ins2(LIR_flt, res_ins, lir->insImmf(0)), OOM_EXIT); + break; + case FAIL_VOID: + guard(false, lir->ins2i(LIR_eq, res_ins, JSVAL_TO_SPECIAL(JSVAL_VOID)), OOM_EXIT); + break; + case FAIL_COOKIE: + guard(false, lir->ins2(LIR_peq, res_ins, INS_CONSTWORD(JSVAL_ERROR_COOKIE)), OOM_EXIT); + break; + default:; + } + + set(&stackval(0 - (2 + argc)), res_ins); + + /* + * The return value will be processed by NativeCallComplete since + * we have to know the actual return value type for calls that return + * jsval (like Array_p_pop). + */ + pendingSpecializedNative = sn; + + return RECORD_CONTINUE; +} + +/* + * Check whether we have a specialized implementation for this native + * invocation. + */ +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::callSpecializedNative(JSNativeTraceInfo *trcinfo, uintN argc, + bool constructing) +{ + JSStackFrame* fp = cx->fp; + jsbytecode *pc = fp->regs->pc; + + jsval& fval = stackval(0 - (2 + argc)); + jsval& tval = stackval(0 - (1 + argc)); + + LIns* this_ins = get(&tval); + + LIns* args[nanojit::MAXARGS]; + JSSpecializedNative *sn = trcinfo->specializations; + JS_ASSERT(sn); + do { + if (((sn->flags & JSTN_CONSTRUCTOR) != 0) != constructing) + continue; + + uintN knownargc = strlen(sn->argtypes); + if (argc != knownargc) + continue; + + intN prefixc = strlen(sn->prefix); + JS_ASSERT(prefixc <= 3); + LIns** argp = &args[argc + prefixc - 1]; + char argtype; + +#if defined DEBUG + memset(args, 0xCD, sizeof(args)); +#endif + + uintN i; + for (i = prefixc; i--; ) { + argtype = sn->prefix[i]; + if (argtype == 'C') { + *argp = cx_ins; + } else if (argtype == 'T') { /* this, as an object */ + if (JSVAL_IS_PRIMITIVE(tval)) + goto next_specialization; + *argp = this_ins; + } else if (argtype == 'S') { /* this, as a string */ + if (!JSVAL_IS_STRING(tval)) + goto next_specialization; + *argp = this_ins; + } else if (argtype == 'f') { + *argp = INS_CONSTOBJ(JSVAL_TO_OBJECT(fval)); + } else if (argtype == 'p') { + CHECK_STATUS(getClassPrototype(JSVAL_TO_OBJECT(fval), *argp)); + } else if (argtype == 'R') { + *argp = INS_CONSTPTR(cx->runtime); + } else if (argtype == 'P') { + // FIXME: Set pc to imacpc when recording JSOP_CALL inside the + // JSOP_GETELEM imacro (bug 476559). + if ((*pc == JSOP_CALL) && + fp->imacpc && *fp->imacpc == JSOP_GETELEM) + *argp = INS_CONSTPTR(fp->imacpc); + else + *argp = INS_CONSTPTR(pc); + } else if (argtype == 'D') { /* this, as a number */ + if (!isNumber(tval)) + goto next_specialization; + *argp = this_ins; + } else { + JS_NOT_REACHED("unknown prefix arg type"); + } + argp--; + } + + for (i = knownargc; i--; ) { + jsval& arg = stackval(0 - (i + 1)); + *argp = get(&arg); + + argtype = sn->argtypes[i]; + if (argtype == 'd' || argtype == 'i') { + if (!isNumber(arg)) + goto next_specialization; + if (argtype == 'i') + *argp = f2i(*argp); + } else if (argtype == 'o') { + if (JSVAL_IS_PRIMITIVE(arg)) + goto next_specialization; + } else if (argtype == 's') { + if (!JSVAL_IS_STRING(arg)) + goto next_specialization; + } else if (argtype == 'r') { + if (!VALUE_IS_REGEXP(cx, arg)) + goto next_specialization; + } else if (argtype == 'f') { + if (!VALUE_IS_FUNCTION(cx, arg)) + goto next_specialization; + } else if (argtype == 'v') { + *argp = box_jsval(arg, *argp); + } else { + goto next_specialization; + } + argp--; + } +#if defined DEBUG + JS_ASSERT(args[0] != (LIns *)0xcdcdcdcd); +#endif + return emitNativeCall(sn, argc, args, false); + +next_specialization:; + } while ((sn++)->flags & JSTN_MORE); + + return RECORD_STOP; +} + +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::callNative(uintN argc, JSOp mode) +{ + LIns* args[5]; + + JS_ASSERT(mode == JSOP_CALL || mode == JSOP_NEW || mode == JSOP_APPLY); + + jsval* vp = &stackval(0 - (2 + argc)); + JSObject* funobj = JSVAL_TO_OBJECT(vp[0]); + JSFunction* fun = GET_FUNCTION_PRIVATE(cx, funobj); + JSFastNative native = (JSFastNative)fun->u.n.native; + + switch (argc) { + case 1: + if (isNumber(vp[2]) && + (native == js_math_ceil || native == js_math_floor || native == js_math_round)) { + LIns* a = get(&vp[2]); + if (isPromote(a)) { + set(&vp[0], a); + pendingSpecializedNative = IGNORE_NATIVE_CALL_COMPLETE_CALLBACK; + return RECORD_CONTINUE; + } + } + break; + + case 2: + if (isNumber(vp[2]) && isNumber(vp[3]) && + (native == js_math_min || native == js_math_max)) { + LIns* a = get(&vp[2]); + LIns* b = get(&vp[3]); + if (isPromote(a) && isPromote(b)) { + a = ::demote(lir, a); + b = ::demote(lir, b); + set(&vp[0], + lir->ins1(LIR_i2f, + lir->ins_choose(lir->ins2((native == js_math_min) + ? LIR_lt + : LIR_gt, a, b), + a, b, avmplus::AvmCore::use_cmov()))); + pendingSpecializedNative = IGNORE_NATIVE_CALL_COMPLETE_CALLBACK; + return RECORD_CONTINUE; + } + } + break; + } + + if (fun->flags & JSFUN_TRCINFO) { + JSNativeTraceInfo *trcinfo = FUN_TRCINFO(fun); + JS_ASSERT(trcinfo && (JSFastNative)fun->u.n.native == trcinfo->native); + + /* Try to call a type specialized version of the native. */ + if (trcinfo->specializations) { + RecordingStatus status = callSpecializedNative(trcinfo, argc, mode == JSOP_NEW); + if (status != RECORD_STOP) + return status; + } + } + + if (native == js_fun_apply || native == js_fun_call) + RETURN_STOP("trying to call native apply or call"); + + // Allocate the vp vector and emit code to root it. + uintN vplen = 2 + JS_MAX(argc, unsigned(FUN_MINARGS(fun))) + fun->u.n.extra; + if (!(fun->flags & JSFUN_FAST_NATIVE)) + vplen++; // slow native return value slot + LIns* invokevp_ins = lir->insAlloc(vplen * sizeof(jsval)); + + // vp[0] is the callee. + lir->insStorei(INS_CONSTVAL(OBJECT_TO_JSVAL(funobj)), invokevp_ins, 0); + + // Calculate |this|. + LIns* this_ins; + if (mode == JSOP_NEW) { + JSClass* clasp = fun->u.n.clasp; + JS_ASSERT(clasp != &js_SlowArrayClass); + if (!clasp) + clasp = &js_ObjectClass; + JS_ASSERT(((jsuword) clasp & 3) == 0); + + // Abort on |new Function|. js_NewInstance would allocate a regular- + // sized JSObject, not a Function-sized one. (The Function ctor would + // deep-bail anyway but let's not go there.) + if (clasp == &js_FunctionClass) + RETURN_STOP("new Function"); + + if (clasp->getObjectOps) + RETURN_STOP("new with non-native ops"); + + args[0] = INS_CONSTOBJ(funobj); + args[1] = INS_CONSTPTR(clasp); + args[2] = cx_ins; + newobj_ins = lir->insCall(&js_NewInstance_ci, args); + guard(false, lir->ins_peq0(newobj_ins), OOM_EXIT); + this_ins = newobj_ins; /* boxing an object is a no-op */ + } else if (JSFUN_BOUND_METHOD_TEST(fun->flags)) { + /* |funobj| was rooted above already. */ + this_ins = INS_CONSTWORD(OBJECT_TO_JSVAL(OBJ_GET_PARENT(cx, funobj))); + } else { + this_ins = get(&vp[1]); + + /* + * For fast natives, 'null' or primitives are fine as as 'this' value. + * For slow natives we have to ensure the object is substituted for the + * appropriate global object or boxed object value. JSOP_NEW allocates its + * own object so it's guaranteed to have a valid 'this' value. + */ + if (!(fun->flags & JSFUN_FAST_NATIVE)) { + if (JSVAL_IS_NULL(vp[1])) { + JSObject* thisObj = js_ComputeThis(cx, JS_FALSE, vp + 2); + if (!thisObj) + RETURN_ERROR("error in js_ComputeGlobalThis"); + this_ins = INS_CONSTOBJ(thisObj); + } else if (!JSVAL_IS_OBJECT(vp[1])) { + RETURN_STOP("slow native(primitive, args)"); + } else { + if (guardClass(JSVAL_TO_OBJECT(vp[1]), this_ins, &js_WithClass, snapshot(MISMATCH_EXIT))) + RETURN_STOP("can't trace slow native invocation on With object"); + + this_ins = lir->ins_choose(lir->ins_peq0(stobj_get_parent(this_ins)), + INS_CONSTOBJ(globalObj), + this_ins, avmplus::AvmCore::use_cmov()); + } + } + this_ins = box_jsval(vp[1], this_ins); + } + lir->insStorei(this_ins, invokevp_ins, 1 * sizeof(jsval)); + + // Populate argv. + for (uintN n = 2; n < 2 + argc; n++) { + LIns* i = box_jsval(vp[n], get(&vp[n])); + lir->insStorei(i, invokevp_ins, n * sizeof(jsval)); + + // For a very long argument list we might run out of LIR space, so + // check inside the loop. + if (outOfMemory()) + RETURN_STOP("out of memory in argument list"); + } + + // Populate extra slots, including the return value slot for a slow native. + if (2 + argc < vplen) { + LIns* undef_ins = INS_CONSTWORD(JSVAL_VOID); + for (uintN n = 2 + argc; n < vplen; n++) { + lir->insStorei(undef_ins, invokevp_ins, n * sizeof(jsval)); + + if (outOfMemory()) + RETURN_STOP("out of memory in extra slots"); + } + } + + // Set up arguments for the JSNative or JSFastNative. + uint32 types; + if (fun->flags & JSFUN_FAST_NATIVE) { + if (mode == JSOP_NEW) + RETURN_STOP("untraceable fast native constructor"); + native_rval_ins = invokevp_ins; + args[0] = invokevp_ins; + args[1] = lir->insImm(argc); + args[2] = cx_ins; + types = ARGSIZE_I << (0*ARGSIZE_SHIFT) | + ARGSIZE_P << (1*ARGSIZE_SHIFT) | + ARGSIZE_I << (2*ARGSIZE_SHIFT) | + ARGSIZE_P << (3*ARGSIZE_SHIFT); + } else { + int32_t offset = (vplen - 1) * sizeof(jsval); + native_rval_ins = lir->ins2(LIR_piadd, invokevp_ins, INS_CONSTWORD(offset)); + args[0] = native_rval_ins; + args[1] = lir->ins2(LIR_piadd, invokevp_ins, INS_CONSTWORD(2 * sizeof(jsval))); + args[2] = lir->insImm(argc); + args[3] = this_ins; + args[4] = cx_ins; + types = ARGSIZE_I << (0*ARGSIZE_SHIFT) | + ARGSIZE_P << (1*ARGSIZE_SHIFT) | + ARGSIZE_P << (2*ARGSIZE_SHIFT) | + ARGSIZE_I << (3*ARGSIZE_SHIFT) | + ARGSIZE_P << (4*ARGSIZE_SHIFT) | + ARGSIZE_P << (5*ARGSIZE_SHIFT); + } + + // Generate CallInfo and a JSSpecializedNative structure on the fly. + // Do not use JSTN_UNBOX_AFTER for mode JSOP_NEW because + // record_NativeCallComplete unboxes the result specially. + + CallInfo* ci = new (traceAlloc()) CallInfo(); + ci->_address = uintptr_t(fun->u.n.native); + ci->_cse = ci->_fold = 0; + ci->_abi = ABI_CDECL; + ci->_argtypes = types; +#ifdef DEBUG + ci->_name = JS_GetFunctionName(fun); + #endif + + // Generate a JSSpecializedNative structure on the fly. + generatedSpecializedNative.builtin = ci; + generatedSpecializedNative.flags = FAIL_STATUS | ((mode == JSOP_NEW) + ? JSTN_CONSTRUCTOR + : JSTN_UNBOX_AFTER); + generatedSpecializedNative.prefix = NULL; + generatedSpecializedNative.argtypes = NULL; + + // We only have to ensure that the values we wrote into the stack buffer + // are rooted if we actually make it to the call, so only set nativeVp and + // nativeVpLen immediately before emitting the call code. This way we avoid + // leaving trace with a bogus nativeVp because we fall off trace while unboxing + // values into the stack buffer. + lir->insStorei(INS_CONST(vplen), lirbuf->state, offsetof(InterpState, nativeVpLen)); + lir->insStorei(invokevp_ins, lirbuf->state, offsetof(InterpState, nativeVp)); + + // argc is the original argc here. It is used to calculate where to place + // the return value. + return emitNativeCall(&generatedSpecializedNative, argc, args, true); +} + +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::functionCall(uintN argc, JSOp mode) +{ + jsval& fval = stackval(0 - (2 + argc)); + JS_ASSERT(&fval >= StackBase(cx->fp)); + + if (!VALUE_IS_FUNCTION(cx, fval)) + RETURN_STOP("callee is not a function"); + + jsval& tval = stackval(0 - (1 + argc)); + + /* + * If callee is not constant, it's a shapeless call and we have to guard + * explicitly that we will get this callee again at runtime. + */ + if (!get(&fval)->isconstp()) + CHECK_STATUS(guardCallee(fval)); + + /* + * Require that the callee be a function object, to avoid guarding on its + * class here. We know if the callee and this were pushed by JSOP_CALLNAME + * or JSOP_CALLPROP that callee is a *particular* function, since these hit + * the property cache and guard on the object (this) in which the callee + * was found. So it's sufficient to test here that the particular function + * is interpreted, not guard on that condition. + * + * Bytecode sequences that push shapeless callees must guard on the callee + * class being Function and the function being interpreted. + */ + JSFunction* fun = GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(fval)); + + if (FUN_INTERPRETED(fun)) { + if (mode == JSOP_NEW) { + LIns* args[] = { get(&fval), INS_CONSTPTR(&js_ObjectClass), cx_ins }; + LIns* tv_ins = lir->insCall(&js_NewInstance_ci, args); + guard(false, lir->ins_peq0(tv_ins), OOM_EXIT); + set(&tval, tv_ins); + } + return interpretedFunctionCall(fval, fun, argc, mode == JSOP_NEW); + } + + if (FUN_SLOW_NATIVE(fun)) { + JSNative native = fun->u.n.native; + jsval* argv = &tval + 1; + if (native == js_Array) + return newArray(JSVAL_TO_OBJECT(fval), argc, argv, &fval); + if (native == js_String && argc == 1) { + if (mode == JSOP_NEW) + return newString(JSVAL_TO_OBJECT(fval), 1, argv, &fval); + if (!JSVAL_IS_PRIMITIVE(argv[0])) { + RETURN_IF_XML(argv[0]); + return call_imacro(call_imacros.String); + } + set(&fval, stringify(argv[0])); + pendingSpecializedNative = IGNORE_NATIVE_CALL_COMPLETE_CALLBACK; + return RECORD_CONTINUE; + } + } + + return callNative(argc, mode); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_NEW() +{ + uintN argc = GET_ARGC(cx->fp->regs->pc); + cx->fp->assertValidStackDepth(argc + 2); + return InjectStatus(functionCall(argc, JSOP_NEW)); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_DELNAME() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_DELPROP() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_DELELEM() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_TYPEOF() +{ + jsval& r = stackval(-1); + LIns* type; + if (JSVAL_IS_STRING(r)) { + type = INS_ATOM(cx->runtime->atomState.typeAtoms[JSTYPE_STRING]); + } else if (isNumber(r)) { + type = INS_ATOM(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]); + } else if (VALUE_IS_FUNCTION(cx, r)) { + type = INS_ATOM(cx->runtime->atomState.typeAtoms[JSTYPE_FUNCTION]); + } else { + LIns* args[] = { get(&r), cx_ins }; + if (JSVAL_IS_SPECIAL(r)) { + // We specialize identically for boolean and undefined. We must not have a hole here. + // Pass the unboxed type here, since TypeOfBoolean knows how to handle it. + JS_ASSERT(r == JSVAL_TRUE || r == JSVAL_FALSE || r == JSVAL_VOID); + type = lir->insCall(&js_TypeOfBoolean_ci, args); + } else { + JS_ASSERT(JSVAL_TAG(r) == JSVAL_OBJECT); + type = lir->insCall(&js_TypeOfObject_ci, args); + } + } + set(&r, type); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_VOID() +{ + stack(-1, INS_CONST(JSVAL_TO_SPECIAL(JSVAL_VOID))); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_INCNAME() +{ + return incName(1); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_INCPROP() +{ + return incProp(1); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_INCELEM() +{ + return InjectStatus(incElem(1)); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_DECNAME() +{ + return incName(-1); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_DECPROP() +{ + return incProp(-1); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_DECELEM() +{ + return InjectStatus(incElem(-1)); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::incName(jsint incr, bool pre) +{ + jsval* vp; + LIns* v_ins; + LIns* v_after; + NameResult nr; + + CHECK_STATUS_A(name(vp, v_ins, nr)); + jsval v = nr.tracked ? *vp : nr.v; + CHECK_STATUS_A(incHelper(v, v_ins, v_after, incr)); + LIns* v_result = pre ? v_after : v_ins; + if (nr.tracked) { + set(vp, v_after); + stack(0, v_result); + return ARECORD_CONTINUE; + } + + if (OBJ_GET_CLASS(cx, nr.obj) != &js_CallClass) + RETURN_STOP_A("incName on unsupported object class"); + + CHECK_STATUS_A(setCallProp(nr.obj, nr.obj_ins, nr.sprop, v_after, v)); + stack(0, v_result); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_NAMEINC() +{ + return incName(1, false); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_PROPINC() +{ + return incProp(1, false); +} + +// XXX consolidate with record_JSOP_GETELEM code... +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_ELEMINC() +{ + return InjectStatus(incElem(1, false)); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_NAMEDEC() +{ + return incName(-1, false); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_PROPDEC() +{ + return incProp(-1, false); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_ELEMDEC() +{ + return InjectStatus(incElem(-1, false)); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_GETPROP() +{ + return getProp(stackval(-1)); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_SETPROP() +{ + jsval& l = stackval(-2); + if (JSVAL_IS_PRIMITIVE(l)) + RETURN_STOP_A("primitive this for SETPROP"); + + JSObject* obj = JSVAL_TO_OBJECT(l); + if (obj->map->ops->setProperty != js_SetProperty) + RETURN_STOP_A("non-native JSObjectOps::setProperty"); + return ARECORD_CONTINUE; +} + +/* Emit a specialized, inlined copy of js_NativeSet. */ +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::nativeSet(JSObject* obj, LIns* obj_ins, JSScopeProperty* sprop, + jsval v, LIns* v_ins) +{ + JSScope* scope = OBJ_SCOPE(obj); + uint32 slot = sprop->slot; + + /* + * We do not trace assignment to properties that have both a nonstub setter + * and a slot, for several reasons. + * + * First, that would require sampling rt->propertyRemovals before and after + * (see js_NativeSet), and even more code to handle the case where the two + * samples differ. A mere guard is not enough, because you can't just bail + * off trace in the middle of a property assignment without storing the + * value and making the stack right. + * + * If obj is the global object, there are two additional problems. We would + * have to emit still more code to store the result in the object (not the + * native global frame) if the setter returned successfully after + * deep-bailing. And we would have to cope if the run-time type of the + * setter's return value differed from the record-time type of v, in which + * case unboxing would fail and, having called a native setter, we could + * not just retry the instruction in the interpreter. + */ + JS_ASSERT(SPROP_HAS_STUB_SETTER(sprop) || slot == SPROP_INVALID_SLOT); + + // Box the value to be stored, if necessary. + LIns* boxed_ins = NULL; + if (!SPROP_HAS_STUB_SETTER(sprop) || (slot != SPROP_INVALID_SLOT && obj != globalObj)) + boxed_ins = box_jsval(v, v_ins); + + // Call the setter, if any. + if (!SPROP_HAS_STUB_SETTER(sprop)) + emitNativePropertyOp(scope, sprop, obj_ins, true, boxed_ins); + + // Store the value, if this property has a slot. + if (slot != SPROP_INVALID_SLOT) { + JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, scope)); + JS_ASSERT(!(sprop->attrs & JSPROP_SHARED)); + if (obj == globalObj) { + if (!lazilyImportGlobalSlot(slot)) + RETURN_STOP("lazy import of global slot failed"); + set(&STOBJ_GET_SLOT(obj, slot), v_ins); + } else { + LIns* dslots_ins = NULL; + stobj_set_slot(obj_ins, slot, dslots_ins, boxed_ins); + } + } + + return RECORD_CONTINUE; +} + +static JSBool FASTCALL +MethodWriteBarrier(JSContext* cx, JSObject* obj, JSScopeProperty* sprop, JSObject* funobj) +{ + JSAutoTempValueRooter tvr(cx, funobj); + + return OBJ_SCOPE(obj)->methodWriteBarrier(cx, sprop, tvr.value()); +} +JS_DEFINE_CALLINFO_4(static, BOOL_FAIL, MethodWriteBarrier, CONTEXT, OBJECT, SCOPEPROP, OBJECT, + 0, 0) + +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::setProp(jsval &l, JSPropCacheEntry* entry, JSScopeProperty* sprop, + jsval &v, LIns*& v_ins) +{ + if (entry == JS_NO_PROP_CACHE_FILL) + RETURN_STOP("can't trace uncacheable property set"); + JS_ASSERT_IF(PCVCAP_TAG(entry->vcap) >= 1, sprop->attrs & JSPROP_SHARED); + if (!SPROP_HAS_STUB_SETTER(sprop) && sprop->slot != SPROP_INVALID_SLOT) + RETURN_STOP("can't trace set of property with setter and slot"); + if (sprop->attrs & JSPROP_SETTER) + RETURN_STOP("can't trace JavaScript function setter"); + + // These two cases are errors and can't be traced. + if (sprop->attrs & JSPROP_GETTER) + RETURN_STOP("can't assign to property with script getter but no setter"); + if (sprop->attrs & JSPROP_READONLY) + RETURN_STOP("can't assign to readonly property"); + + JS_ASSERT(!JSVAL_IS_PRIMITIVE(l)); + JSObject* obj = JSVAL_TO_OBJECT(l); + LIns* obj_ins = get(&l); + JSScope* scope = OBJ_SCOPE(obj); + + JS_ASSERT_IF(entry->vcap == PCVCAP_MAKE(entry->kshape, 0, 0), scope->hasProperty(sprop)); + + // Fast path for CallClass. This is about 20% faster than the general case. + v_ins = get(&v); + if (OBJ_GET_CLASS(cx, obj) == &js_CallClass) + return setCallProp(obj, obj_ins, sprop, v_ins, v); + + // Find obj2. If entry->adding(), the TAG bits are all 0. + JSObject* obj2 = obj; + for (jsuword i = PCVCAP_TAG(entry->vcap) >> PCVCAP_PROTOBITS; i; i--) + obj2 = OBJ_GET_PARENT(cx, obj2); + for (jsuword j = PCVCAP_TAG(entry->vcap) & PCVCAP_PROTOMASK; j; j--) + obj2 = OBJ_GET_PROTO(cx, obj2); + scope = OBJ_SCOPE(obj2); + JS_ASSERT_IF(entry->adding(), obj2 == obj); + + // Guard before anything else. + LIns* map_ins = map(obj_ins); + CHECK_STATUS(guardNativePropertyOp(obj, map_ins)); + jsuword pcval; + CHECK_STATUS(guardPropertyCacheHit(obj_ins, map_ins, obj, obj2, entry, pcval)); + JS_ASSERT(scope->object == obj2); + JS_ASSERT(scope->hasProperty(sprop)); + JS_ASSERT_IF(obj2 != obj, sprop->attrs & JSPROP_SHARED); + + /* + * Setting a function-valued property might need to rebrand the object, so + * we emit a call to the method write barrier. There's no need to guard on + * this, because functions have distinct trace-type from other values and + * branded-ness is implied by the shape, which we've already guarded on. + */ + if (scope->brandedOrHasMethodBarrier() && VALUE_IS_FUNCTION(cx, v) && entry->directHit()) { + if (obj == globalObj) + RETURN_STOP("can't trace function-valued property set in branded global scope"); + + enterDeepBailCall(); + LIns* args[] = { v_ins, INS_CONSTSPROP(sprop), obj_ins, cx_ins }; + LIns* ok_ins = lir->insCall(&MethodWriteBarrier_ci, args); + guard(false, lir->ins_eq0(ok_ins), OOM_EXIT); + leaveDeepBailCall(); + } + + // Add a property to the object if necessary. + if (entry->adding()) { + JS_ASSERT(!(sprop->attrs & JSPROP_SHARED)); + if (obj == globalObj) + RETURN_STOP("adding a property to the global object"); + + LIns* args[] = { INS_CONSTSPROP(sprop), obj_ins, cx_ins }; + LIns* ok_ins = lir->insCall(&js_AddProperty_ci, args); + guard(false, lir->ins_eq0(ok_ins), OOM_EXIT); + } + + return nativeSet(obj, obj_ins, sprop, v, v_ins); +} + +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::setCallProp(JSObject *callobj, LIns *callobj_ins, JSScopeProperty *sprop, + LIns *v_ins, jsval v) +{ + // Set variables in on-trace-stack call objects by updating the tracker. + JSStackFrame *fp = frameIfInRange(callobj); + if (fp) { + if (sprop->setter == SetCallArg) { + jsint slot = JSVAL_TO_INT(SPROP_USERID(sprop)); + jsval *vp2 = &fp->argv[slot]; + set(vp2, v_ins); + return RECORD_CONTINUE; + } + if (sprop->setter == SetCallVar) { + jsint slot = JSVAL_TO_INT(SPROP_USERID(sprop)); + jsval *vp2 = &fp->slots[slot]; + set(vp2, v_ins); + return RECORD_CONTINUE; + } + RETURN_STOP("can't trace special CallClass setter"); + } + + // Set variables in off-trace-stack call objects by calling standard builtins. + const CallInfo* ci = NULL; + if (sprop->setter == SetCallArg) + ci = &js_SetCallArg_ci; + else if (sprop->setter == SetCallVar) + ci = &js_SetCallVar_ci; + else + RETURN_STOP("can't trace special CallClass setter"); + + LIns* args[] = { + box_jsval(v, v_ins), + INS_CONST(SPROP_USERID(sprop)), + callobj_ins, + cx_ins + }; + LIns* call_ins = lir->insCall(ci, args); + guard(false, addName(lir->ins_eq0(call_ins), "guard(set upvar)"), STATUS_EXIT); + return RECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_SetPropHit(JSPropCacheEntry* entry, JSScopeProperty* sprop) +{ + jsval& r = stackval(-1); + jsval& l = stackval(-2); + LIns* v_ins; + CHECK_STATUS_A(setProp(l, entry, sprop, r, v_ins)); + + jsbytecode* pc = cx->fp->regs->pc; + switch (*pc) { + case JSOP_SETPROP: + case JSOP_SETNAME: + case JSOP_SETMETHOD: + if (pc[JSOP_SETPROP_LENGTH] != JSOP_POP) + set(&l, v_ins); + break; + + default:; + } + + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK VMSideExit* +TraceRecorder::enterDeepBailCall() +{ + // Take snapshot for js_DeepBail and store it in cx->bailExit. + VMSideExit* exit = snapshot(DEEP_BAIL_EXIT); + lir->insStorei(INS_CONSTPTR(exit), cx_ins, offsetof(JSContext, bailExit)); + + // Tell nanojit not to discard or defer stack writes before this call. + GuardRecord* guardRec = createGuardRecord(exit); + lir->insGuard(LIR_xbarrier, NULL, guardRec); + + // Forget about guarded shapes, since deep bailers can reshape the world. + forgetGuardedShapes(); + return exit; +} + +JS_REQUIRES_STACK void +TraceRecorder::leaveDeepBailCall() +{ + // Keep cx->bailExit null when it's invalid. + lir->insStorei(INS_NULL(), cx_ins, offsetof(JSContext, bailExit)); +} + +JS_REQUIRES_STACK void +TraceRecorder::finishGetProp(LIns* obj_ins, LIns* vp_ins, LIns* ok_ins, jsval* outp) +{ + // Store the boxed result (and this-object, if JOF_CALLOP) before the + // guard. The deep-bail case requires this. If the property get fails, + // these slots will be ignored anyway. + LIns* result_ins = lir->insLoad(LIR_ldp, vp_ins, 0); + set(outp, result_ins, true); + if (js_CodeSpec[*cx->fp->regs->pc].format & JOF_CALLOP) + set(outp + 1, obj_ins, true); + + // We need to guard on ok_ins, but this requires a snapshot of the state + // after this op. monitorRecording will do it for us. + pendingGuardCondition = ok_ins; + + // Note there is a boxed result sitting on the stack. The caller must leave + // it there for the time being, since the return type is not yet + // known. monitorRecording will emit the code to unbox it. + pendingUnboxSlot = outp; +} + +static inline bool +RootedStringToId(JSContext* cx, JSString** namep, jsid* idp) +{ + JSString* name = *namep; + if (name->isAtomized()) { + *idp = ATOM_TO_JSID((JSAtom*) STRING_TO_JSVAL(name)); + return true; + } + + JSAtom* atom = js_AtomizeString(cx, name, 0); + if (!atom) + return false; + *namep = ATOM_TO_STRING(atom); /* write back to GC root */ + *idp = ATOM_TO_JSID(atom); + return true; +} + +static JSBool FASTCALL +GetPropertyByName(JSContext* cx, JSObject* obj, JSString** namep, jsval* vp) +{ + js_LeaveTraceIfGlobalObject(cx, obj); + + jsid id; + if (!RootedStringToId(cx, namep, &id) || !obj->getProperty(cx, id, vp)) { + js_SetBuiltinError(cx); + return false; + } + return cx->interpState->builtinStatus == 0; +} +JS_DEFINE_CALLINFO_4(static, BOOL_FAIL, GetPropertyByName, CONTEXT, OBJECT, STRINGPTR, JSVALPTR, + 0, 0) + +// Convert the value in a slot to a string and store the resulting string back +// in the slot (typically in order to root it). +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::primitiveToStringInPlace(jsval* vp) +{ + jsval v = *vp; + JS_ASSERT(JSVAL_IS_PRIMITIVE(v)); + + if (!JSVAL_IS_STRING(v)) { + // v is not a string. Turn it into one. js_ValueToString is safe + // because v is not an object. + JSString *str = js_ValueToString(cx, v); + if (!str) + RETURN_ERROR("failed to stringify element id"); + v = STRING_TO_JSVAL(str); + set(vp, stringify(*vp)); + + // Write the string back to the stack to save the interpreter some work + // and to ensure snapshots get the correct type for this slot. + *vp = v; + } + return RECORD_CONTINUE; +} + +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::getPropertyByName(LIns* obj_ins, jsval* idvalp, jsval* outp) +{ + CHECK_STATUS(primitiveToStringInPlace(idvalp)); + enterDeepBailCall(); + + // Call GetPropertyByName. The vp parameter points to stack because this is + // what the interpreter currently does. obj and id are rooted on the + // interpreter stack, but the slot at vp is not a root. + LIns* vp_ins = addName(lir->insAlloc(sizeof(jsval)), "vp"); + LIns* idvalp_ins = addName(addr(idvalp), "idvalp"); + LIns* args[] = {vp_ins, idvalp_ins, obj_ins, cx_ins}; + LIns* ok_ins = lir->insCall(&GetPropertyByName_ci, args); + + // GetPropertyByName can assign to *idvalp, so the tracker has an incorrect + // entry for that address. Correct it. (If the value in the address is + // never used again, the usual case, Nanojit will kill this load.) + tracker.set(idvalp, lir->insLoad(LIR_ldp, idvalp_ins, 0)); + + finishGetProp(obj_ins, vp_ins, ok_ins, outp); + leaveDeepBailCall(); + return RECORD_CONTINUE; +} + +static JSBool FASTCALL +GetPropertyByIndex(JSContext* cx, JSObject* obj, int32 index, jsval* vp) +{ + js_LeaveTraceIfGlobalObject(cx, obj); + + JSAutoTempIdRooter idr(cx); + if (!js_Int32ToId(cx, index, idr.addr()) || !obj->getProperty(cx, idr.id(), vp)) { + js_SetBuiltinError(cx); + return JS_FALSE; + } + return cx->interpState->builtinStatus == 0; +} +JS_DEFINE_CALLINFO_4(static, BOOL_FAIL, GetPropertyByIndex, CONTEXT, OBJECT, INT32, JSVALPTR, 0, 0) + +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::getPropertyByIndex(LIns* obj_ins, LIns* index_ins, jsval* outp) +{ + index_ins = makeNumberInt32(index_ins); + + // See note in getPropertyByName about vp. + enterDeepBailCall(); + LIns* vp_ins = addName(lir->insAlloc(sizeof(jsval)), "vp"); + LIns* args[] = {vp_ins, index_ins, obj_ins, cx_ins}; + LIns* ok_ins = lir->insCall(&GetPropertyByIndex_ci, args); + finishGetProp(obj_ins, vp_ins, ok_ins, outp); + leaveDeepBailCall(); + return RECORD_CONTINUE; +} + +static JSBool FASTCALL +GetPropertyById(JSContext* cx, JSObject* obj, jsid id, jsval* vp) +{ + js_LeaveTraceIfGlobalObject(cx, obj); + if (!obj->getProperty(cx, id, vp)) { + js_SetBuiltinError(cx); + return JS_FALSE; + } + return cx->interpState->builtinStatus == 0; +} +JS_DEFINE_CALLINFO_4(static, BOOL_FAIL, GetPropertyById, + CONTEXT, OBJECT, JSVAL, JSVALPTR, 0, 0) + +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::getPropertyById(LIns* obj_ins, jsval* outp) +{ + // Find the atom. + JSAtom* atom; + jsbytecode* pc = cx->fp->regs->pc; + const JSCodeSpec& cs = js_CodeSpec[*pc]; + if (*pc == JSOP_LENGTH) { + atom = cx->runtime->atomState.lengthAtom; + } else if (JOF_TYPE(cs.format) == JOF_ATOM) { + atom = atoms[GET_INDEX(pc)]; + } else { + JS_ASSERT(JOF_TYPE(cs.format) == JOF_SLOTATOM); + atom = atoms[GET_INDEX(pc + SLOTNO_LEN)]; + } + + // Call GetPropertyById. See note in getPropertyByName about vp. + enterDeepBailCall(); + jsid id = ATOM_TO_JSID(atom); + LIns* vp_ins = addName(lir->insAlloc(sizeof(jsval)), "vp"); + LIns* args[] = {vp_ins, INS_CONSTWORD(id), obj_ins, cx_ins}; + LIns* ok_ins = lir->insCall(&GetPropertyById_ci, args); + finishGetProp(obj_ins, vp_ins, ok_ins, outp); + leaveDeepBailCall(); + return RECORD_CONTINUE; +} + +/* Manually inlined, specialized copy of js_NativeGet. */ +static JSBool FASTCALL +GetPropertyWithNativeGetter(JSContext* cx, JSObject* obj, JSScopeProperty* sprop, jsval* vp) +{ + js_LeaveTraceIfGlobalObject(cx, obj); + +#ifdef DEBUG + JSProperty* prop; + JSObject* pobj; + JS_ASSERT(obj->lookupProperty(cx, sprop->id, &pobj, &prop)); + JS_ASSERT(prop == (JSProperty*) sprop); + obj->dropProperty(cx, prop); +#endif + + // JSScopeProperty::get contains a special case for With objects. We can + // elide it here because With objects are, we claim, never on the operand + // stack while recording. + JS_ASSERT(STOBJ_GET_CLASS(obj) != &js_WithClass); + + *vp = JSVAL_VOID; + if (!sprop->getter(cx, obj, SPROP_USERID(sprop), vp)) { + js_SetBuiltinError(cx); + return JS_FALSE; + } + return cx->interpState->builtinStatus == 0; +} +JS_DEFINE_CALLINFO_4(static, BOOL_FAIL, GetPropertyWithNativeGetter, + CONTEXT, OBJECT, SCOPEPROP, JSVALPTR, 0, 0) + +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::getPropertyWithNativeGetter(LIns* obj_ins, JSScopeProperty* sprop, jsval* outp) +{ + JS_ASSERT(!(sprop->attrs & JSPROP_GETTER)); + JS_ASSERT(sprop->slot == SPROP_INVALID_SLOT); + JS_ASSERT(!SPROP_HAS_STUB_GETTER_OR_IS_METHOD(sprop)); + + // Call GetPropertyWithNativeGetter. See note in getPropertyByName about vp. + // FIXME - We should call the getter directly. Using a builtin function for + // now because it buys some extra asserts. See bug 508310. + enterDeepBailCall(); + LIns* vp_ins = addName(lir->insAlloc(sizeof(jsval)), "vp"); + LIns* args[] = {vp_ins, INS_CONSTPTR(sprop), obj_ins, cx_ins}; + LIns* ok_ins = lir->insCall(&GetPropertyWithNativeGetter_ci, args); + finishGetProp(obj_ins, vp_ins, ok_ins, outp); + leaveDeepBailCall(); + return RECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_GETELEM() +{ + bool call = *cx->fp->regs->pc == JSOP_CALLELEM; + + jsval& idx = stackval(-1); + jsval& lval = stackval(-2); + + LIns* obj_ins = get(&lval); + LIns* idx_ins = get(&idx); + + // Special case for array-like access of strings. + if (JSVAL_IS_STRING(lval) && isInt32(idx)) { + if (call) + RETURN_STOP_A("JSOP_CALLELEM on a string"); + int i = asInt32(idx); + if (size_t(i) >= JSVAL_TO_STRING(lval)->length()) + RETURN_STOP_A("Invalid string index in JSOP_GETELEM"); + idx_ins = makeNumberInt32(idx_ins); + LIns* args[] = { idx_ins, obj_ins, cx_ins }; + LIns* unitstr_ins = lir->insCall(&js_String_getelem_ci, args); + guard(false, lir->ins_peq0(unitstr_ins), MISMATCH_EXIT); + set(&lval, unitstr_ins); + return ARECORD_CONTINUE; + } + + if (JSVAL_IS_PRIMITIVE(lval)) + RETURN_STOP_A("JSOP_GETLEM on a primitive"); + RETURN_IF_XML_A(lval); + + JSObject* obj = JSVAL_TO_OBJECT(lval); + if (obj == globalObj) + RETURN_STOP_A("JSOP_GETELEM on global"); + LIns* v_ins; + + /* Property access using a string name or something we have to stringify. */ + if (!JSVAL_IS_INT(idx)) { + if (!JSVAL_IS_PRIMITIVE(idx)) + RETURN_STOP_A("object used as index"); + + return InjectStatus(getPropertyByName(obj_ins, &idx, &lval)); + } + + if (STOBJ_GET_CLASS(obj) == &js_ArgumentsClass) { + unsigned depth; + JSStackFrame *afp = guardArguments(obj, obj_ins, &depth); + if (afp) { + uintN int_idx = JSVAL_TO_INT(idx); + jsval* vp = &afp->argv[int_idx]; + if (idx_ins->isconstf()) { + if (int_idx >= 0 && int_idx < afp->argc) + v_ins = get(vp); + else + v_ins = INS_VOID(); + } else { + // If the index is not a constant expression, we generate LIR to load the value from + // the native stack area. The guard on js_ArgumentClass above ensures the up-to-date + // value has been written back to the native stack area. + idx_ins = makeNumberInt32(idx_ins); + if (int_idx >= 0 && int_idx < afp->argc) { + JSTraceType type = getCoercedType(*vp); + + // Guard that the argument has the same type on trace as during recording. + LIns* typemap_ins; + if (callDepth == depth) { + // In this case, we are in the same frame where the arguments object was created. + // The entry type map is not necessarily up-to-date, so we capture a new type map + // for this point in the code. + unsigned stackSlots = NativeStackSlots(cx, 0 /* callDepth */); + JSTraceType* typemap = new (traceAlloc()) JSTraceType[stackSlots]; + DetermineTypesVisitor detVisitor(*this, typemap); + VisitStackSlots(detVisitor, cx, 0); + typemap_ins = INS_CONSTPTR(typemap + 2 /* callee, this */); + } else { + // In this case, we are in a deeper frame from where the arguments object was + // created. The type map at the point of the call out from the creation frame + // is accurate. + // Note: this relies on the assumption that we abort on setting an element of + // an arguments object in any deeper frame. + LIns* fip_ins = lir->insLoad(LIR_ldp, lirbuf->rp, (callDepth-depth)*sizeof(FrameInfo*)); + typemap_ins = lir->ins2(LIR_add, fip_ins, INS_CONST(sizeof(FrameInfo) + 2/*callee,this*/ * sizeof(JSTraceType))); + } + + LIns* typep_ins = lir->ins2(LIR_piadd, typemap_ins, + lir->ins_u2p(lir->ins2(LIR_mul, + idx_ins, + INS_CONST(sizeof(JSTraceType))))); + LIns* type_ins = lir->insLoad(LIR_ldcb, typep_ins, 0); + guard(true, + addName(lir->ins2(LIR_eq, type_ins, lir->insImm(type)), + "guard(type-stable upvar)"), + BRANCH_EXIT); + + // Read the value out of the native stack area. + guard(true, lir->ins2(LIR_ult, idx_ins, INS_CONST(afp->argc)), + snapshot(BRANCH_EXIT)); + size_t stackOffset = nativespOffset(&afp->argv[0]); + LIns* args_addr_ins = lir->ins2(LIR_piadd, lirbuf->sp, INS_CONSTWORD(stackOffset)); + LIns* argi_addr_ins = lir->ins2(LIR_piadd, + args_addr_ins, + lir->ins_u2p(lir->ins2(LIR_mul, + idx_ins, + INS_CONST(sizeof(double))))); + v_ins = stackLoad(argi_addr_ins, type); + } else { + guard(false, lir->ins2(LIR_ult, idx_ins, INS_CONST(afp->argc)), + snapshot(BRANCH_EXIT)); + v_ins = INS_VOID(); + } + } + JS_ASSERT(v_ins); + set(&lval, v_ins); + return ARECORD_CONTINUE; + } + RETURN_STOP_A("can't reach arguments object's frame"); + } + if (js_IsDenseArray(obj)) { + // Fast path for dense arrays accessed with a integer index. + jsval* vp; + LIns* addr_ins; + + guardDenseArray(obj, obj_ins, BRANCH_EXIT); + CHECK_STATUS_A(denseArrayElement(lval, idx, vp, v_ins, addr_ins)); + set(&lval, v_ins); + if (call) + set(&idx, obj_ins); + return ARECORD_CONTINUE; + } + + return InjectStatus(getPropertyByIndex(obj_ins, idx_ins, &lval)); +} + +/* Functions used by JSOP_SETELEM */ + +static JSBool FASTCALL +SetPropertyByName(JSContext* cx, JSObject* obj, JSString** namep, jsval* vp) +{ + js_LeaveTraceIfGlobalObject(cx, obj); + + jsid id; + if (!RootedStringToId(cx, namep, &id) || !obj->setProperty(cx, id, vp)) { + js_SetBuiltinError(cx); + return JS_FALSE; + } + return cx->interpState->builtinStatus == 0; +} +JS_DEFINE_CALLINFO_4(static, BOOL_FAIL, SetPropertyByName, CONTEXT, OBJECT, STRINGPTR, JSVALPTR, + 0, 0) + +static JSBool FASTCALL +InitPropertyByName(JSContext* cx, JSObject* obj, JSString** namep, jsval val) +{ + js_LeaveTraceIfGlobalObject(cx, obj); + + jsid id; + if (!RootedStringToId(cx, namep, &id) || + !obj->defineProperty(cx, id, val, NULL, NULL, JSPROP_ENUMERATE)) { + js_SetBuiltinError(cx); + return JS_FALSE; + } + return cx->interpState->builtinStatus == 0; +} +JS_DEFINE_CALLINFO_4(static, BOOL_FAIL, InitPropertyByName, CONTEXT, OBJECT, STRINGPTR, JSVAL, + 0, 0) + +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::initOrSetPropertyByName(LIns* obj_ins, jsval* idvalp, jsval* rvalp, bool init) +{ + CHECK_STATUS(primitiveToStringInPlace(idvalp)); + + LIns* rval_ins = box_jsval(*rvalp, get(rvalp)); + + enterDeepBailCall(); + + LIns* ok_ins; + LIns* idvalp_ins = addName(addr(idvalp), "idvalp"); + if (init) { + LIns* args[] = {rval_ins, idvalp_ins, obj_ins, cx_ins}; + ok_ins = lir->insCall(&InitPropertyByName_ci, args); + } else { + // See note in getPropertyByName about vp. + LIns* vp_ins = addName(lir->insAlloc(sizeof(jsval)), "vp"); + lir->insStorei(rval_ins, vp_ins, 0); + LIns* args[] = {vp_ins, idvalp_ins, obj_ins, cx_ins}; + ok_ins = lir->insCall(&SetPropertyByName_ci, args); + } + pendingGuardCondition = ok_ins; + + leaveDeepBailCall(); + return RECORD_CONTINUE; +} + +static JSBool FASTCALL +SetPropertyByIndex(JSContext* cx, JSObject* obj, int32 index, jsval* vp) +{ + js_LeaveTraceIfGlobalObject(cx, obj); + + JSAutoTempIdRooter idr(cx); + if (!js_Int32ToId(cx, index, idr.addr()) || !obj->setProperty(cx, idr.id(), vp)) { + js_SetBuiltinError(cx); + return JS_FALSE; + } + return cx->interpState->builtinStatus == 0; +} +JS_DEFINE_CALLINFO_4(static, BOOL_FAIL, SetPropertyByIndex, CONTEXT, OBJECT, INT32, JSVALPTR, 0, 0) + +static JSBool FASTCALL +InitPropertyByIndex(JSContext* cx, JSObject* obj, int32 index, jsval val) +{ + js_LeaveTraceIfGlobalObject(cx, obj); + + JSAutoTempIdRooter idr(cx); + if (!js_Int32ToId(cx, index, idr.addr()) || + !obj->defineProperty(cx, idr.id(), val, NULL, NULL, JSPROP_ENUMERATE)) { + js_SetBuiltinError(cx); + return JS_FALSE; + } + return cx->interpState->builtinStatus == 0; +} +JS_DEFINE_CALLINFO_4(static, BOOL_FAIL, InitPropertyByIndex, CONTEXT, OBJECT, INT32, JSVAL, 0, 0) + +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::initOrSetPropertyByIndex(LIns* obj_ins, LIns* index_ins, jsval* rvalp, bool init) +{ + index_ins = makeNumberInt32(index_ins); + + LIns* rval_ins = box_jsval(*rvalp, get(rvalp)); + + enterDeepBailCall(); + + LIns* ok_ins; + if (init) { + LIns* args[] = {rval_ins, index_ins, obj_ins, cx_ins}; + ok_ins = lir->insCall(&InitPropertyByIndex_ci, args); + } else { + // See note in getPropertyByName about vp. + LIns* vp_ins = addName(lir->insAlloc(sizeof(jsval)), "vp"); + lir->insStorei(rval_ins, vp_ins, 0); + LIns* args[] = {vp_ins, index_ins, obj_ins, cx_ins}; + ok_ins = lir->insCall(&SetPropertyByIndex_ci, args); + } + pendingGuardCondition = ok_ins; + + leaveDeepBailCall(); + return RECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_SETELEM() +{ + jsval& v = stackval(-1); + jsval& idx = stackval(-2); + jsval& lval = stackval(-3); + + if (JSVAL_IS_PRIMITIVE(lval)) + RETURN_STOP_A("left JSOP_SETELEM operand is not an object"); + RETURN_IF_XML_A(lval); + + JSObject* obj = JSVAL_TO_OBJECT(lval); + LIns* obj_ins = get(&lval); + LIns* idx_ins = get(&idx); + LIns* v_ins = get(&v); + + if (JS_InstanceOf(cx, obj, &js_ArgumentsClass, NULL)) + RETURN_STOP_A("can't trace setting elements of the |arguments| object"); + + if (!JSVAL_IS_INT(idx)) { + if (!JSVAL_IS_PRIMITIVE(idx)) + RETURN_STOP_A("non-primitive index"); + CHECK_STATUS_A(initOrSetPropertyByName(obj_ins, &idx, &v, + *cx->fp->regs->pc == JSOP_INITELEM)); + } else if (JSVAL_TO_INT(idx) < 0 || !OBJ_IS_DENSE_ARRAY(cx, obj)) { + CHECK_STATUS_A(initOrSetPropertyByIndex(obj_ins, idx_ins, &v, + *cx->fp->regs->pc == JSOP_INITELEM)); + } else { + // Fast path: assigning to element of dense array. + + // Make sure the array is actually dense. + if (!guardDenseArray(obj, obj_ins, BRANCH_EXIT)) + return ARECORD_STOP; + + // The index was on the stack and is therefore a LIR float. Force it to + // be an integer. + idx_ins = makeNumberInt32(idx_ins); + + // Box the value so we can use one builtin instead of having to add one + // builtin for every storage type. Special case for integers though, + // since they are so common. + LIns* res_ins; + LIns* args[] = { NULL, idx_ins, obj_ins, cx_ins }; + if (isNumber(v)) { + if (isPromoteInt(v_ins)) { + args[0] = ::demote(lir, v_ins); + res_ins = lir->insCall(&js_Array_dense_setelem_int_ci, args); + } else { + args[0] = v_ins; + res_ins = lir->insCall(&js_Array_dense_setelem_double_ci, args); + } + } else { + LIns* args[] = { box_jsval(v, v_ins), idx_ins, obj_ins, cx_ins }; + res_ins = lir->insCall(&js_Array_dense_setelem_ci, args); + } + guard(false, lir->ins_eq0(res_ins), MISMATCH_EXIT); + } + + jsbytecode* pc = cx->fp->regs->pc; + if (*pc == JSOP_SETELEM && pc[JSOP_SETELEM_LENGTH] != JSOP_POP) + set(&lval, v_ins); + + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_CALLNAME() +{ + JSObject* obj = cx->fp->scopeChain; + if (obj != globalObj) { + jsval* vp; + LIns* ins; + NameResult nr; + CHECK_STATUS_A(scopeChainProp(obj, vp, ins, nr)); + stack(0, ins); + stack(1, INS_CONSTOBJ(globalObj)); + return ARECORD_CONTINUE; + } + + LIns* obj_ins = scopeChain(); + JSObject* obj2; + jsuword pcval; + + CHECK_STATUS_A(test_property_cache(obj, obj_ins, obj2, pcval)); + + if (PCVAL_IS_NULL(pcval) || !PCVAL_IS_OBJECT(pcval)) + RETURN_STOP_A("callee is not an object"); + + JS_ASSERT(HAS_FUNCTION_CLASS(PCVAL_TO_OBJECT(pcval))); + + stack(0, INS_CONSTOBJ(PCVAL_TO_OBJECT(pcval))); + stack(1, obj_ins); + return ARECORD_CONTINUE; +} + +JS_DEFINE_CALLINFO_5(extern, UINT32, GetUpvarArgOnTrace, CONTEXT, UINT32, INT32, UINT32, + DOUBLEPTR, 0, 0) +JS_DEFINE_CALLINFO_5(extern, UINT32, GetUpvarVarOnTrace, CONTEXT, UINT32, INT32, UINT32, + DOUBLEPTR, 0, 0) +JS_DEFINE_CALLINFO_5(extern, UINT32, GetUpvarStackOnTrace, CONTEXT, UINT32, INT32, UINT32, + DOUBLEPTR, 0, 0) + +/* + * Record LIR to get the given upvar. Return the LIR instruction for the upvar + * value. NULL is returned only on a can't-happen condition with an invalid + * typemap. The value of the upvar is returned as v. + */ +JS_REQUIRES_STACK LIns* +TraceRecorder::upvar(JSScript* script, JSUpvarArray* uva, uintN index, jsval& v) +{ + /* + * Try to find the upvar in the current trace's tracker. For &vr to be + * the address of the jsval found in js_GetUpvar, we must initialize + * vr directly with the result, so it is a reference to the same location. + * It does not work to assign the result to v, because v is an already + * existing reference that points to something else. + */ + uint32 cookie = uva->vector[index]; + jsval& vr = js_GetUpvar(cx, script->staticLevel, cookie); + v = vr; + + if (known(&vr)) + return get(&vr); + + /* + * The upvar is not in the current trace, so get the upvar value exactly as + * the interpreter does and unbox. + */ + uint32 level = script->staticLevel - UPVAR_FRAME_SKIP(cookie); + uint32 cookieSlot = UPVAR_FRAME_SLOT(cookie); + JSStackFrame* fp = cx->display[level]; + const CallInfo* ci; + int32 slot; + if (!fp->fun) { + ci = &GetUpvarStackOnTrace_ci; + slot = cookieSlot; + } else if (cookieSlot < fp->fun->nargs) { + ci = &GetUpvarArgOnTrace_ci; + slot = cookieSlot; + } else if (cookieSlot == CALLEE_UPVAR_SLOT) { + ci = &GetUpvarArgOnTrace_ci; + slot = -2; + } else { + ci = &GetUpvarVarOnTrace_ci; + slot = cookieSlot - fp->fun->nargs; + } + + LIns* outp = lir->insAlloc(sizeof(double)); + LIns* args[] = { + outp, + INS_CONST(callDepth), + INS_CONST(slot), + INS_CONST(level), + cx_ins + }; + LIns* call_ins = lir->insCall(ci, args); + JSTraceType type = getCoercedType(v); + guard(true, + addName(lir->ins2(LIR_eq, call_ins, lir->insImm(type)), + "guard(type-stable upvar)"), + BRANCH_EXIT); + return stackLoad(outp, type); +} + +/* + * Generate LIR to load a value from the native stack. This method ensures that + * the correct LIR load operator is used. + */ +LIns* TraceRecorder::stackLoad(LIns* base, uint8 type) +{ + LOpcode loadOp; + switch (type) { + case TT_DOUBLE: + loadOp = LIR_ldq; + break; + case TT_OBJECT: + case TT_STRING: + case TT_FUNCTION: + case TT_NULL: + loadOp = LIR_ldp; + break; + case TT_INT32: + case TT_PSEUDOBOOLEAN: + loadOp = LIR_ld; + break; + case TT_JSVAL: + default: + JS_NOT_REACHED("found jsval type in an upvar type map entry"); + return NULL; + } + + LIns* result = lir->insLoad(loadOp, base, 0); + if (type == TT_INT32) + result = lir->ins1(LIR_i2f, result); + return result; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_GETUPVAR() +{ + uintN index = GET_UINT16(cx->fp->regs->pc); + JSScript *script = cx->fp->script; + JSUpvarArray* uva = script->upvars(); + JS_ASSERT(index < uva->length); + + jsval v; + LIns* upvar_ins = upvar(script, uva, index, v); + if (!upvar_ins) + return ARECORD_STOP; + stack(0, upvar_ins); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_CALLUPVAR() +{ + CHECK_STATUS_A(record_JSOP_GETUPVAR()); + stack(1, INS_NULL()); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_GETDSLOT() +{ + JSObject* callee = cx->fp->calleeObject(); + LIns* callee_ins = get(&cx->fp->argv[-2]); + + unsigned index = GET_UINT16(cx->fp->regs->pc); + LIns* dslots_ins = NULL; + LIns* v_ins = stobj_get_dslot(callee_ins, index, dslots_ins); + + stack(0, unbox_jsval(callee->dslots[index], v_ins, snapshot(BRANCH_EXIT))); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_CALLDSLOT() +{ + CHECK_STATUS_A(record_JSOP_GETDSLOT()); + stack(1, INS_NULL()); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::guardCallee(jsval& callee) +{ + JS_ASSERT(VALUE_IS_FUNCTION(cx, callee)); + + VMSideExit* branchExit = snapshot(BRANCH_EXIT); + JSObject* callee_obj = JSVAL_TO_OBJECT(callee); + LIns* callee_ins = get(&callee); + + treeInfo->gcthings.addUnique(callee); + guard(true, + lir->ins2(LIR_peq, + stobj_get_private(callee_ins), + INS_CONSTPTR(callee_obj->getPrivate())), + branchExit); + guard(true, + lir->ins2(LIR_peq, + stobj_get_parent(callee_ins), + INS_CONSTOBJ(OBJ_GET_PARENT(cx, callee_obj))), + branchExit); + return RECORD_CONTINUE; +} + +/* + * Prepare the given |arguments| object to be accessed on trace. If the return + * value is non-NULL, then the given |arguments| object refers to a frame on + * the current trace and is guaranteed to refer to the same frame on trace for + * all later executions. + */ +JS_REQUIRES_STACK JSStackFrame * +TraceRecorder::guardArguments(JSObject *obj, LIns* obj_ins, unsigned *depthp) +{ + JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_ArgumentsClass); + + JSStackFrame *afp = frameIfInRange(obj, depthp); + if (!afp) + return NULL; + + VMSideExit *exit = snapshot(MISMATCH_EXIT); + guardClass(obj, obj_ins, &js_ArgumentsClass, exit); + + LIns* args_ins = get(&afp->argsobj); + LIns* cmp = lir->ins2(LIR_peq, args_ins, obj_ins); + lir->insGuard(LIR_xf, cmp, createGuardRecord(exit)); + return afp; +} + +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::interpretedFunctionCall(jsval& fval, JSFunction* fun, uintN argc, bool constructing) +{ + /* + * The function's identity (JSFunction and therefore JSScript) is guarded, + * so we can optimize for the empty script singleton right away. No need to + * worry about crossing globals or relocating argv, even, in this case! + * + * Note that the interpreter shortcuts empty-script call and construct too, + * and does not call any TR::record_*CallComplete hook. + */ + if (fun->u.i.script->isEmpty()) { + LIns* rval_ins = constructing ? stack(-1 - argc) : INS_CONST(JSVAL_TO_SPECIAL(JSVAL_VOID)); + stack(-2 - argc, rval_ins); + return RECORD_CONTINUE; + } + + if (JS_GetGlobalForObject(cx, JSVAL_TO_OBJECT(fval)) != globalObj) + RETURN_STOP("JSOP_CALL or JSOP_NEW crosses global scopes"); + + JSStackFrame* fp = cx->fp; + + // TODO: track the copying via the tracker... + if (argc < fun->nargs && + jsuword(fp->regs->sp + (fun->nargs - argc)) > cx->stackPool.current->limit) { + RETURN_STOP("can't trace calls with too few args requiring argv move"); + } + + // Generate a type map for the outgoing frame and stash it in the LIR + unsigned stackSlots = NativeStackSlots(cx, 0 /* callDepth */); + FrameInfo* fi = (FrameInfo*) + tempAlloc().alloc(sizeof(FrameInfo) + stackSlots * sizeof(JSTraceType)); + JSTraceType* typemap = (JSTraceType*)(fi + 1); + + DetermineTypesVisitor detVisitor(*this, typemap); + VisitStackSlots(detVisitor, cx, 0); + + JS_ASSERT(argc < FrameInfo::CONSTRUCTING_FLAG); + + treeInfo->gcthings.addUnique(fval); + fi->block = fp->blockChain; + if (fp->blockChain) + treeInfo->gcthings.addUnique(OBJECT_TO_JSVAL(fp->blockChain)); + fi->pc = fp->regs->pc; + fi->imacpc = fp->imacpc; + fi->spdist = fp->regs->sp - fp->slots; + fi->set_argc(argc, constructing); + fi->callerHeight = stackSlots - (2 + argc); + fi->callerArgc = fp->argc; + + if (callDepth >= treeInfo->maxCallDepth) + treeInfo->maxCallDepth = callDepth + 1; + + fi = traceMonitor->frameCache->memoize(fi); + if (!fi) + RETURN_STOP("out of memory"); + lir->insStorei(INS_CONSTPTR(fi), lirbuf->rp, callDepth * sizeof(FrameInfo*)); + +#if defined JS_JIT_SPEW + debug_only_printf(LC_TMTracer, "iFC frameinfo=%p, stack=%d, map=", (void*)fi, + fi->callerHeight); + for (unsigned i = 0; i < fi->callerHeight; i++) + debug_only_printf(LC_TMTracer, "%c", typeChar[fi->get_typemap()[i]]); + debug_only_print0(LC_TMTracer, "\n"); +#endif + + atoms = fun->u.i.script->atomMap.vector; + return RECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_CALL() +{ + uintN argc = GET_ARGC(cx->fp->regs->pc); + cx->fp->assertValidStackDepth(argc + 2); + return InjectStatus(functionCall(argc, + (cx->fp->imacpc && *cx->fp->imacpc == JSOP_APPLY) + ? JSOP_APPLY + : JSOP_CALL)); +} + +static jsbytecode* apply_imacro_table[] = { + apply_imacros.apply0, + apply_imacros.apply1, + apply_imacros.apply2, + apply_imacros.apply3, + apply_imacros.apply4, + apply_imacros.apply5, + apply_imacros.apply6, + apply_imacros.apply7, + apply_imacros.apply8 +}; + +static jsbytecode* call_imacro_table[] = { + apply_imacros.call0, + apply_imacros.call1, + apply_imacros.call2, + apply_imacros.call3, + apply_imacros.call4, + apply_imacros.call5, + apply_imacros.call6, + apply_imacros.call7, + apply_imacros.call8 +}; + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_APPLY() +{ + JSStackFrame* fp = cx->fp; + jsbytecode *pc = fp->regs->pc; + uintN argc = GET_ARGC(pc); + cx->fp->assertValidStackDepth(argc + 2); + + jsval* vp = fp->regs->sp - (argc + 2); + jsuint length = 0; + JSObject* aobj = NULL; + LIns* aobj_ins = NULL; + + JS_ASSERT(!fp->imacpc); + + if (!VALUE_IS_FUNCTION(cx, vp[0])) + return record_JSOP_CALL(); + RETURN_IF_XML_A(vp[0]); + + JSObject* obj = JSVAL_TO_OBJECT(vp[0]); + JSFunction* fun = GET_FUNCTION_PRIVATE(cx, obj); + if (FUN_INTERPRETED(fun)) + return record_JSOP_CALL(); + + bool apply = (JSFastNative)fun->u.n.native == js_fun_apply; + if (!apply && (JSFastNative)fun->u.n.native != js_fun_call) + return record_JSOP_CALL(); + + /* + * We don't trace apply and call with a primitive 'this', which is the + * first positional parameter. + */ + if (argc > 0 && !JSVAL_IS_OBJECT(vp[2])) + return record_JSOP_CALL(); + + /* + * Guard on the identity of this, which is the function we are applying. + */ + if (!VALUE_IS_FUNCTION(cx, vp[1])) + RETURN_STOP_A("callee is not a function"); + CHECK_STATUS_A(guardCallee(vp[1])); + + if (apply && argc >= 2) { + if (argc != 2) + RETURN_STOP_A("apply with excess arguments"); + if (JSVAL_IS_PRIMITIVE(vp[3])) + RETURN_STOP_A("arguments parameter of apply is primitive"); + aobj = JSVAL_TO_OBJECT(vp[3]); + aobj_ins = get(&vp[3]); + + /* + * We trace dense arrays and arguments objects. The code we generate + * for apply uses imacros to handle a specific number of arguments. + */ + if (OBJ_IS_DENSE_ARRAY(cx, aobj)) { + guardDenseArray(aobj, aobj_ins); + length = jsuint(aobj->fslots[JSSLOT_ARRAY_LENGTH]); + guard(true, + lir->ins2i(LIR_eq, + p2i(stobj_get_fslot(aobj_ins, JSSLOT_ARRAY_LENGTH)), + length), + BRANCH_EXIT); + } else if (OBJ_GET_CLASS(cx, aobj) == &js_ArgumentsClass) { + unsigned depth; + JSStackFrame *afp = guardArguments(aobj, aobj_ins, &depth); + if (!afp) + RETURN_STOP_A("can't reach arguments object's frame"); + length = afp->argc; + } else { + RETURN_STOP_A("arguments parameter of apply is not a dense array or argments object"); + } + + if (length >= JS_ARRAY_LENGTH(apply_imacro_table)) + RETURN_STOP_A("too many arguments to apply"); + + return InjectStatus(call_imacro(apply_imacro_table[length])); + } + + if (argc >= JS_ARRAY_LENGTH(call_imacro_table)) + RETURN_STOP_A("too many arguments to call"); + + return InjectStatus(call_imacro(call_imacro_table[argc])); +} + +static JSBool FASTCALL +CatchStopIteration_tn(JSContext* cx, JSBool ok, jsval* vp) +{ + if (!ok && cx->throwing && js_ValueIsStopIteration(cx->exception)) { + cx->throwing = JS_FALSE; + cx->exception = JSVAL_VOID; + *vp = JSVAL_HOLE; + return JS_TRUE; + } + return ok; +} + +JS_DEFINE_TRCINFO_1(CatchStopIteration_tn, + (3, (static, BOOL, CatchStopIteration_tn, CONTEXT, BOOL, JSVALPTR, 0, 0))) + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_NativeCallComplete() +{ + if (pendingSpecializedNative == IGNORE_NATIVE_CALL_COMPLETE_CALLBACK) + return ARECORD_CONTINUE; + + jsbytecode* pc = cx->fp->regs->pc; + + JS_ASSERT(pendingSpecializedNative); + JS_ASSERT(*pc == JSOP_CALL || *pc == JSOP_APPLY || *pc == JSOP_NEW || *pc == JSOP_SETPROP); + + jsval& v = stackval(-1); + LIns* v_ins = get(&v); + + /* + * At this point the generated code has already called the native function + * and we can no longer fail back to the original pc location (JSOP_CALL) + * because that would cause the interpreter to re-execute the native + * function, which might have side effects. + * + * Instead, the snapshot() call below sees that we are currently parked on + * a traceable native's JSOP_CALL instruction, and it will advance the pc + * to restore by the length of the current opcode. If the native's return + * type is jsval, snapshot() will also indicate in the type map that the + * element on top of the stack is a boxed value which doesn't need to be + * boxed if the type guard generated by unbox_jsval() fails. + */ + + if (JSTN_ERRTYPE(pendingSpecializedNative) == FAIL_STATUS) { + /* Keep cx->bailExit null when it's invalid. */ + lir->insStorei(INS_NULL(), cx_ins, (int) offsetof(JSContext, bailExit)); + + LIns* status = lir->insLoad(LIR_ld, lirbuf->state, (int) offsetof(InterpState, builtinStatus)); + if (pendingSpecializedNative == &generatedSpecializedNative) { + LIns* ok_ins = v_ins; + + /* + * Custom implementations of Iterator.next() throw a StopIteration exception. + * Catch and clear it and set the return value to JSVAL_HOLE in this case. + */ + if (uintptr_t(pc - nextiter_imacros.custom_iter_next) < + sizeof(nextiter_imacros.custom_iter_next)) { + LIns* args[] = { native_rval_ins, ok_ins, cx_ins }; /* reverse order */ + ok_ins = lir->insCall(&CatchStopIteration_tn_ci, args); + } + + /* + * If we run a generic traceable native, the return value is in the argument + * vector for native function calls. The actual return value of the native is a JSBool + * indicating the error status. + */ + v_ins = lir->insLoad(LIR_ldp, native_rval_ins, 0); + if (*pc == JSOP_NEW) { + LIns* x = lir->ins_peq0(lir->ins2(LIR_piand, v_ins, INS_CONSTWORD(JSVAL_TAGMASK))); + x = lir->ins_choose(x, v_ins, INS_CONSTWORD(0), avmplus::AvmCore::use_cmov()); + v_ins = lir->ins_choose(lir->ins_peq0(x), newobj_ins, x, avmplus::AvmCore::use_cmov()); + } + set(&v, v_ins); + + propagateFailureToBuiltinStatus(ok_ins, status); + } + guard(true, lir->ins_eq0(status), STATUS_EXIT); + } + + if (pendingSpecializedNative->flags & JSTN_UNBOX_AFTER) { + /* + * If we side exit on the unboxing code due to a type change, make sure that the boxed + * value is actually currently associated with that location, and that we are talking + * about the top of the stack here, which is where we expected boxed values. + */ + JS_ASSERT(&v == &cx->fp->regs->sp[-1] && get(&v) == v_ins); + set(&v, unbox_jsval(v, v_ins, snapshot(BRANCH_EXIT))); + } else if (JSTN_ERRTYPE(pendingSpecializedNative) == FAIL_NEG) { + /* Already added i2f in functionCall. */ + JS_ASSERT(JSVAL_IS_NUMBER(v)); + } else { + /* Convert the result to double if the builtin returns int32. */ + if (JSVAL_IS_NUMBER(v) && + (pendingSpecializedNative->builtin->_argtypes & ARGSIZE_MASK_ANY) == ARGSIZE_I) { + set(&v, lir->ins1(LIR_i2f, v_ins)); + } + } + + // We'll null pendingSpecializedNative in monitorRecording, on the next op + // cycle. There must be a next op since the stack is non-empty. + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::name(jsval*& vp, LIns*& ins, NameResult& nr) +{ + JSObject* obj = cx->fp->scopeChain; + if (obj != globalObj) + return scopeChainProp(obj, vp, ins, nr); + + /* Can't use prop here, because we don't want unboxing from global slots. */ + LIns* obj_ins = scopeChain(); + uint32 slot; + + JSObject* obj2; + jsuword pcval; + + /* + * Property cache ensures that we are dealing with an existing property, + * and guards the shape for us. + */ + CHECK_STATUS_A(test_property_cache(obj, obj_ins, obj2, pcval)); + + /* Abort if property doesn't exist (interpreter will report an error.) */ + if (PCVAL_IS_NULL(pcval)) + RETURN_STOP_A("named property not found"); + + /* Insist on obj being the directly addressed object. */ + if (obj2 != obj) + RETURN_STOP_A("name() hit prototype chain"); + + /* Don't trace getter or setter calls, our caller wants a direct slot. */ + if (PCVAL_IS_SPROP(pcval)) { + JSScopeProperty* sprop = PCVAL_TO_SPROP(pcval); + if (!isValidSlot(OBJ_SCOPE(obj), sprop)) + RETURN_STOP_A("name() not accessing a valid slot"); + slot = sprop->slot; + } else { + if (!PCVAL_IS_SLOT(pcval)) + RETURN_STOP_A("PCE is not a slot"); + slot = PCVAL_TO_SLOT(pcval); + } + + if (!lazilyImportGlobalSlot(slot)) + RETURN_STOP_A("lazy import of global slot failed"); + + vp = &STOBJ_GET_SLOT(obj, slot); + ins = get(vp); + nr.tracked = true; + return ARECORD_CONTINUE; +} + +static JSObject* FASTCALL +MethodReadBarrier(JSContext* cx, JSObject* obj, JSScopeProperty* sprop, JSObject* funobj) +{ + JSAutoTempValueRooter tvr(cx, funobj); + + if (!OBJ_SCOPE(obj)->methodReadBarrier(cx, sprop, tvr.addr())) + return NULL; + JS_ASSERT(VALUE_IS_FUNCTION(cx, tvr.value())); + return JSVAL_TO_OBJECT(tvr.value()); +} +JS_DEFINE_CALLINFO_4(static, OBJECT_FAIL, MethodReadBarrier, CONTEXT, OBJECT, SCOPEPROP, OBJECT, + 0, 0) + +/* + * Get a property. The current opcode has JOF_ATOM. + * + * There are two modes. The caller must pass nonnull pointers for either outp + * or both slotp and v_insp. In the latter case, we require a plain old + * property with a slot; if the property turns out to be anything else, abort + * tracing (rather than emit a call to a native getter or GetAnyProperty). + */ +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::prop(JSObject* obj, LIns* obj_ins, uint32 *slotp, LIns** v_insp, jsval *outp) +{ + JS_ASSERT((slotp && v_insp && !outp) || (!slotp && !v_insp && outp)); + + /* + * Can't specialize to assert obj != global, must guard to avoid aliasing + * stale homes of stacked global variables. + */ + CHECK_STATUS_A(guardNotGlobalObject(obj, obj_ins)); + + /* + * Property cache ensures that we are dealing with an existing property, + * and guards the shape for us. + */ + JSObject* obj2; + jsuword pcval; + CHECK_STATUS_A(test_property_cache(obj, obj_ins, obj2, pcval)); + + /* Check for non-existent property reference, which results in undefined. */ + const JSCodeSpec& cs = js_CodeSpec[*cx->fp->regs->pc]; + if (PCVAL_IS_NULL(pcval)) { + if (slotp) + RETURN_STOP_A("property not found"); + + /* + * We could specialize to guard on just JSClass.getProperty, but a mere + * class guard is simpler and slightly faster. + */ + if (OBJ_GET_CLASS(cx, obj)->getProperty != JS_PropertyStub) { + RETURN_STOP_A("can't trace through access to undefined property if " + "JSClass.getProperty hook isn't stubbed"); + } + guardClass(obj, obj_ins, OBJ_GET_CLASS(cx, obj), snapshot(MISMATCH_EXIT)); + + /* + * This trace will be valid as long as neither the object nor any object + * on its prototype chain changes shape. + * + * FIXME: This loop can become a single shape guard once bug 497789 has + * been fixed. + */ + VMSideExit* exit = snapshot(BRANCH_EXIT); + do { + LIns* map_ins = map(obj_ins); + LIns* ops_ins; + if (map_is_native(obj->map, map_ins, ops_ins)) { + CHECK_STATUS_A(InjectStatus(guardShape(obj_ins, obj, OBJ_SHAPE(obj), "guard(shape)", + map_ins, exit))); + } else if (!guardDenseArray(obj, obj_ins, exit)) { + RETURN_STOP_A("non-native object involved in undefined property access"); + } + } while (guardHasPrototype(obj, obj_ins, &obj, &obj_ins, exit)); + + set(outp, INS_CONST(JSVAL_TO_SPECIAL(JSVAL_VOID)), true); + return ARECORD_CONTINUE; + } + + uint32 setflags = (cs.format & (JOF_INCDEC | JOF_FOR)); + JS_ASSERT(!(cs.format & JOF_SET)); + + JSScopeProperty* sprop; + uint32 slot; + bool isMethod; + + if (PCVAL_IS_SPROP(pcval)) { + sprop = PCVAL_TO_SPROP(pcval); + JS_ASSERT(OBJ_SCOPE(obj2)->hasProperty(sprop)); + + if (setflags && !SPROP_HAS_STUB_SETTER(sprop)) + RETURN_STOP_A("non-stub setter"); + if (setflags && (sprop->attrs & JSPROP_READONLY)) + RETURN_STOP_A("writing to a readonly property"); + if (!SPROP_HAS_STUB_GETTER_OR_IS_METHOD(sprop)) { + if (slotp) + RETURN_STOP_A("can't trace non-stub getter for this opcode"); + if (sprop->attrs & JSPROP_GETTER) + RETURN_STOP_A("script getter"); + if (sprop->slot == SPROP_INVALID_SLOT) + return InjectStatus(getPropertyWithNativeGetter(obj_ins, sprop, outp)); + return InjectStatus(getPropertyById(obj_ins, outp)); + } + if (!SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj2))) + RETURN_STOP_A("no valid slot"); + slot = sprop->slot; + isMethod = sprop->isMethod(); + JS_ASSERT_IF(isMethod, OBJ_SCOPE(obj2)->hasMethodBarrier()); + } else { + if (!PCVAL_IS_SLOT(pcval)) + RETURN_STOP_A("PCE is not a slot"); + slot = PCVAL_TO_SLOT(pcval); + sprop = NULL; + isMethod = false; + } + + /* We have a slot. Check whether it is direct or in a prototype. */ + if (obj2 != obj) { + if (setflags) + RETURN_STOP_A("JOF_INCDEC|JOF_FOR opcode hit prototype chain"); + + /* + * We're getting a proto-property. Walk up the prototype chain emitting + * proto slot loads, updating obj as we go, leaving obj set to obj2 with + * obj_ins the last proto-load. + */ + do { + obj_ins = stobj_get_proto(obj_ins); + obj = STOBJ_GET_PROTO(obj); + } while (obj != obj2); + } + + LIns* dslots_ins = NULL; + LIns* v_ins = unbox_jsval(STOBJ_GET_SLOT(obj, slot), + stobj_get_slot(obj_ins, slot, dslots_ins), + snapshot(BRANCH_EXIT)); + + /* + * Joined function object stored as a method must be cloned when extracted + * as a property value other than a callee. Note that shapes cover method + * value as well as other property attributes and order, so this condition + * is trace-invariant. + * + * We do not impose the method read barrier if in an imacro, assuming any + * property gets it does (e.g., for 'toString' from JSOP_NEW) will not be + * leaked to the calling script. + */ + if (isMethod && !cx->fp->imacpc) { + enterDeepBailCall(); + LIns* args[] = { v_ins, INS_CONSTSPROP(sprop), obj_ins, cx_ins }; + v_ins = lir->insCall(&MethodReadBarrier_ci, args); + leaveDeepBailCall(); + } + + if (slotp) { + *slotp = slot; + *v_insp = v_ins; + } + if (outp) + set(outp, v_ins, true); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::denseArrayElement(jsval& oval, jsval& ival, jsval*& vp, LIns*& v_ins, + LIns*& addr_ins) +{ + JS_ASSERT(JSVAL_IS_OBJECT(oval) && JSVAL_IS_INT(ival)); + + JSObject* obj = JSVAL_TO_OBJECT(oval); + LIns* obj_ins = get(&oval); + jsint idx = JSVAL_TO_INT(ival); + LIns* idx_ins = makeNumberInt32(get(&ival)); + LIns* pidx_ins = lir->ins_u2p(idx_ins); + + VMSideExit* exit = snapshot(BRANCH_EXIT); + + /* check that the index is within bounds */ + LIns* dslots_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, dslots)); + jsuint capacity = js_DenseArrayCapacity(obj); + bool within = (jsuint(idx) < jsuint(obj->fslots[JSSLOT_ARRAY_LENGTH]) && jsuint(idx) < capacity); + if (!within) { + /* If idx < 0, stay on trace (and read value as undefined, since this is a dense array). */ + LIns* br1 = NULL; + if (MAX_DSLOTS_LENGTH > MAX_DSLOTS_LENGTH32 && !idx_ins->isconst()) { + /* Only 64-bit machines support large enough arrays for this. */ + JS_ASSERT(sizeof(jsval) == 8); + br1 = lir->insBranch(LIR_jt, + lir->ins2i(LIR_lt, idx_ins, 0), + NULL); + } + + /* If not idx < length, stay on trace (and read value as undefined). */ + LIns* br2 = lir->insBranch(LIR_jf, + lir->ins2(LIR_pult, + pidx_ins, + stobj_get_fslot(obj_ins, JSSLOT_ARRAY_LENGTH)), + NULL); + + /* If dslots is NULL, stay on trace (and read value as undefined). */ + LIns* br3 = lir->insBranch(LIR_jt, lir->ins_peq0(dslots_ins), NULL); + + /* If not idx < capacity, stay on trace (and read value as undefined). */ + LIns* br4 = lir->insBranch(LIR_jf, + lir->ins2(LIR_pult, + pidx_ins, + lir->insLoad(LIR_ldp, + dslots_ins, + -(int)sizeof(jsval))), + NULL); + lir->insGuard(LIR_x, NULL, createGuardRecord(exit)); + LIns* label = lir->ins0(LIR_label); + if (br1) + br1->setTarget(label); + br2->setTarget(label); + br3->setTarget(label); + br4->setTarget(label); + + CHECK_STATUS(guardPrototypeHasNoIndexedProperties(obj, obj_ins, MISMATCH_EXIT)); + + // Return undefined and indicate that we didn't actually read this (addr_ins). + v_ins = lir->insImm(JSVAL_TO_SPECIAL(JSVAL_VOID)); + addr_ins = NULL; + return RECORD_CONTINUE; + } + + /* Guard against negative index */ + if (MAX_DSLOTS_LENGTH > MAX_DSLOTS_LENGTH32 && !idx_ins->isconst()) { + /* Only 64-bit machines support large enough arrays for this. */ + JS_ASSERT(sizeof(jsval) == 8); + guard(false, + lir->ins2i(LIR_lt, idx_ins, 0), + exit); + } + + /* Guard array length */ + guard(true, + lir->ins2(LIR_pult, pidx_ins, stobj_get_fslot(obj_ins, JSSLOT_ARRAY_LENGTH)), + exit); + + /* dslots must not be NULL */ + guard(false, + lir->ins_peq0(dslots_ins), + exit); + + /* Guard array capacity */ + guard(true, + lir->ins2(LIR_pult, + pidx_ins, + lir->insLoad(LIR_ldp, dslots_ins, 0 - (int)sizeof(jsval))), + exit); + + /* Load the value and guard on its type to unbox it. */ + vp = &obj->dslots[jsuint(idx)]; + addr_ins = lir->ins2(LIR_piadd, dslots_ins, + lir->ins2i(LIR_pilsh, pidx_ins, (sizeof(jsval) == 4) ? 2 : 3)); + v_ins = unbox_jsval(*vp, lir->insLoad(LIR_ldp, addr_ins, 0), exit); + + if (JSVAL_IS_SPECIAL(*vp)) { + /* + * If we read a hole from the array, convert it to undefined and guard + * that there are no indexed properties along the prototype chain. + */ + LIns* br = lir->insBranch(LIR_jf, + lir->ins2i(LIR_eq, v_ins, JSVAL_TO_SPECIAL(JSVAL_HOLE)), + NULL); + CHECK_STATUS(guardPrototypeHasNoIndexedProperties(obj, obj_ins, MISMATCH_EXIT)); + br->setTarget(lir->ins0(LIR_label)); + + /* Don't let the hole value escape. Turn it into an undefined. */ + v_ins = lir->ins2i(LIR_and, v_ins, ~(JSVAL_HOLE_FLAG >> JSVAL_TAGBITS)); + } + return RECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::getProp(JSObject* obj, LIns* obj_ins) +{ + const JSCodeSpec& cs = js_CodeSpec[*cx->fp->regs->pc]; + JS_ASSERT(cs.ndefs == 1); + return prop(obj, obj_ins, NULL, NULL, &stackval(-cs.nuses)); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::getProp(jsval& v) +{ + if (JSVAL_IS_PRIMITIVE(v)) + RETURN_STOP_A("primitive lhs"); + + return getProp(JSVAL_TO_OBJECT(v), get(&v)); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_NAME() +{ + jsval* vp; + LIns* v_ins; + NameResult nr; + CHECK_STATUS_A(name(vp, v_ins, nr)); + stack(0, v_ins); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_DOUBLE() +{ + jsval v = jsval(atoms[GET_INDEX(cx->fp->regs->pc)]); + stack(0, lir->insImmf(*JSVAL_TO_DOUBLE(v))); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_STRING() +{ + JSAtom* atom = atoms[GET_INDEX(cx->fp->regs->pc)]; + JS_ASSERT(ATOM_IS_STRING(atom)); + stack(0, INS_ATOM(atom)); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_ZERO() +{ + stack(0, lir->insImmf(0)); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_ONE() +{ + stack(0, lir->insImmf(1)); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_NULL() +{ + stack(0, INS_NULL()); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_THIS() +{ + LIns* this_ins; + CHECK_STATUS_A(getThis(this_ins)); + stack(0, this_ins); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_FALSE() +{ + stack(0, lir->insImm(0)); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_TRUE() +{ + stack(0, lir->insImm(1)); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_OR() +{ + return ifop(); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_AND() +{ + return ifop(); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_TABLESWITCH() +{ +#ifdef NANOJIT_IA32 + /* Handle tableswitches specially -- prepare a jump table if needed. */ + return tableswitch(); +#else + return InjectStatus(switchop()); +#endif +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_LOOKUPSWITCH() +{ + return InjectStatus(switchop()); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_STRICTEQ() +{ + strictEquality(true, false); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_STRICTNE() +{ + strictEquality(false, false); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_OBJECT() +{ + JSStackFrame* fp = cx->fp; + JSScript* script = fp->script; + unsigned index = atoms - script->atomMap.vector + GET_INDEX(fp->regs->pc); + + JSObject* obj; + obj = script->getObject(index); + stack(0, INS_CONSTOBJ(obj)); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_POP() +{ + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_TRAP() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_GETARG() +{ + stack(0, arg(GET_ARGNO(cx->fp->regs->pc))); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_SETARG() +{ + arg(GET_ARGNO(cx->fp->regs->pc), stack(-1)); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_GETLOCAL() +{ + stack(0, var(GET_SLOTNO(cx->fp->regs->pc))); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_SETLOCAL() +{ + var(GET_SLOTNO(cx->fp->regs->pc), stack(-1)); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_UINT16() +{ + stack(0, lir->insImmf(GET_UINT16(cx->fp->regs->pc))); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_NEWINIT() +{ + JSProtoKey key = JSProtoKey(GET_INT8(cx->fp->regs->pc)); + LIns* proto_ins; + CHECK_STATUS_A(getClassPrototype(key, proto_ins)); + + LIns* args[] = { proto_ins, cx_ins }; + const CallInfo *ci = (key == JSProto_Array) ? &js_NewEmptyArray_ci : &js_Object_tn_ci; + LIns* v_ins = lir->insCall(ci, args); + guard(false, lir->ins_peq0(v_ins), OOM_EXIT); + stack(0, v_ins); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_ENDINIT() +{ +#ifdef DEBUG + jsval& v = stackval(-1); + JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); +#endif + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_INITPROP() +{ + // All the action is in record_SetPropHit. + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_INITELEM() +{ + return record_JSOP_SETELEM(); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_DEFSHARP() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_USESHARP() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_INCARG() +{ + return InjectStatus(inc(argval(GET_ARGNO(cx->fp->regs->pc)), 1)); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_INCLOCAL() +{ + return InjectStatus(inc(varval(GET_SLOTNO(cx->fp->regs->pc)), 1)); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_DECARG() +{ + return InjectStatus(inc(argval(GET_ARGNO(cx->fp->regs->pc)), -1)); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_DECLOCAL() +{ + return InjectStatus(inc(varval(GET_SLOTNO(cx->fp->regs->pc)), -1)); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_ARGINC() +{ + return InjectStatus(inc(argval(GET_ARGNO(cx->fp->regs->pc)), 1, false)); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_LOCALINC() +{ + return InjectStatus(inc(varval(GET_SLOTNO(cx->fp->regs->pc)), 1, false)); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_ARGDEC() +{ + return InjectStatus(inc(argval(GET_ARGNO(cx->fp->regs->pc)), -1, false)); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_LOCALDEC() +{ + return InjectStatus(inc(varval(GET_SLOTNO(cx->fp->regs->pc)), -1, false)); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_IMACOP() +{ + JS_ASSERT(cx->fp->imacpc); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_ITER() +{ + jsval& v = stackval(-1); + if (JSVAL_IS_PRIMITIVE(v)) + RETURN_STOP_A("for-in on a primitive value"); + RETURN_IF_XML_A(v); + + jsuint flags = cx->fp->regs->pc[1]; + + if (hasIteratorMethod(JSVAL_TO_OBJECT(v))) { + if (flags == JSITER_ENUMERATE) + return InjectStatus(call_imacro(iter_imacros.for_in)); + if (flags == (JSITER_ENUMERATE | JSITER_FOREACH)) + return InjectStatus(call_imacro(iter_imacros.for_each)); + } else { + if (flags == JSITER_ENUMERATE) + return InjectStatus(call_imacro(iter_imacros.for_in_native)); + if (flags == (JSITER_ENUMERATE | JSITER_FOREACH)) + return InjectStatus(call_imacro(iter_imacros.for_each_native)); + } + RETURN_STOP_A("unimplemented JSITER_* flags"); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_NEXTITER() +{ + jsval& iterobj_val = stackval(-2); + if (JSVAL_IS_PRIMITIVE(iterobj_val)) + RETURN_STOP_A("for-in on a primitive value"); + RETURN_IF_XML_A(iterobj_val); + JSObject* iterobj = JSVAL_TO_OBJECT(iterobj_val); + JSClass* clasp = STOBJ_GET_CLASS(iterobj); + LIns* iterobj_ins = get(&iterobj_val); + guardClass(iterobj, iterobj_ins, clasp, snapshot(BRANCH_EXIT)); + if (clasp == &js_IteratorClass || clasp == &js_GeneratorClass) + return InjectStatus(call_imacro(nextiter_imacros.native_iter_next)); + return InjectStatus(call_imacro(nextiter_imacros.custom_iter_next)); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_ENDITER() +{ + LIns* args[] = { stack(-2), cx_ins }; + LIns* ok_ins = lir->insCall(&js_CloseIterator_ci, args); + guard(false, lir->ins_eq0(ok_ins), MISMATCH_EXIT); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_FORNAME() +{ + jsval* vp; + LIns* x_ins; + NameResult nr; + CHECK_STATUS_A(name(vp, x_ins, nr)); + if (!nr.tracked) + RETURN_STOP_A("forname on non-tracked value not supported"); + set(vp, stack(-1)); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_FORPROP() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_FORELEM() +{ + return record_JSOP_DUP(); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_FORARG() +{ + return record_JSOP_SETARG(); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_FORLOCAL() +{ + return record_JSOP_SETLOCAL(); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_POPN() +{ + return ARECORD_CONTINUE; +} + +/* + * Generate LIR to reach |obj2| from |obj| by traversing the scope chain. The generated code + * also ensures that any call objects found have not changed shape. + * + * obj starting object + * obj_ins LIR instruction representing obj + * obj2 end object for traversal + * obj2_ins [out] LIR instruction representing obj2 + */ +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::traverseScopeChain(JSObject *obj, LIns *obj_ins, JSObject *obj2, LIns *&obj2_ins) +{ + VMSideExit* exit = NULL; + for (;;) { + if (obj != globalObj) { + if (!js_IsCacheableNonGlobalScope(obj)) + RETURN_STOP("scope chain lookup crosses non-cacheable object"); + + // We must guard on the shape of all call objects for heavyweight functions + // that we traverse on the scope chain: if the shape changes, a variable with + // the same name may have been inserted in the scope chain. + if (STOBJ_GET_CLASS(obj) == &js_CallClass && + JSFUN_HEAVYWEIGHT_TEST(js_GetCallObjectFunction(obj)->flags)) { + LIns* map_ins = map(obj_ins); + LIns* shape_ins = addName(lir->insLoad(LIR_ld, map_ins, offsetof(JSScope, shape)), + "obj_shape"); + if (!exit) + exit = snapshot(BRANCH_EXIT); + guard(true, + addName(lir->ins2i(LIR_eq, shape_ins, OBJ_SHAPE(obj)), "guard_shape"), + exit); + } + } + + if (obj == obj2) + break; + + obj = STOBJ_GET_PARENT(obj); + if (!obj) + RETURN_STOP("target object not reached on scope chain"); + obj_ins = stobj_get_parent(obj_ins); + } + + obj2_ins = obj_ins; + return RECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_BINDNAME() +{ + JSStackFrame *fp = cx->fp; + JSObject *obj; + + if (!fp->fun) { + obj = fp->scopeChain; + + // In global code, fp->scopeChain can only contain blocks whose values + // are still on the stack. We never use BINDNAME to refer to these. + while (OBJ_GET_CLASS(cx, obj) == &js_BlockClass) { + // The block's values are still on the stack. + JS_ASSERT(obj->getPrivate() == fp); + obj = OBJ_GET_PARENT(cx, obj); + // Blocks always have parents. + JS_ASSERT(obj); + } + + if (obj != globalObj) + RETURN_STOP_A("BINDNAME in global code resolved to non-global object"); + + /* + * The trace is specialized to this global object. Furthermore, we know it + * is the sole 'global' object on the scope chain: we set globalObj to the + * scope chain element with no parent, and we reached it starting from the + * function closure or the current scopeChain, so there is nothing inner to + * it. Therefore this must be the right base object. + */ + stack(0, INS_CONSTOBJ(obj)); + return ARECORD_CONTINUE; + } + + // We can't trace BINDNAME in functions that contain direct calls to eval, + // as they might add bindings which previously-traced references would have + // to see. + if (JSFUN_HEAVYWEIGHT_TEST(fp->fun->flags)) + RETURN_STOP_A("BINDNAME in heavyweight function."); + + // We don't have the scope chain on trace, so instead we get a start object + // that is on the scope chain and doesn't skip the target object (the one + // that contains the property). + jsval *callee = &cx->fp->argv[-2]; + obj = STOBJ_GET_PARENT(JSVAL_TO_OBJECT(*callee)); + if (obj == globalObj) { + stack(0, INS_CONSTOBJ(obj)); + return ARECORD_CONTINUE; + } + LIns *obj_ins = stobj_get_parent(get(callee)); + + // Find the target object. + JSAtom *atom = atoms[GET_INDEX(cx->fp->regs->pc)]; + jsid id = ATOM_TO_JSID(atom); + JSObject *obj2 = js_FindIdentifierBase(cx, fp->scopeChain, id); + if (obj2 != globalObj && STOBJ_GET_CLASS(obj2) != &js_CallClass) + RETURN_STOP_A("BINDNAME on non-global, non-call object"); + + // Generate LIR to get to the target object from the start object. + LIns *obj2_ins; + CHECK_STATUS_A(traverseScopeChain(obj, obj_ins, obj2, obj2_ins)); + + // If |obj2| is the global object, we can refer to it directly instead of walking up + // the scope chain. There may still be guards on intervening call objects. + stack(0, obj2 == globalObj ? INS_CONSTOBJ(obj2) : obj2_ins); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_SETNAME() +{ + jsval& l = stackval(-2); + JS_ASSERT(!JSVAL_IS_PRIMITIVE(l)); + + /* + * Trace only cases that are global code, in lightweight functions + * scoped by the global object only, or in call objects. + */ + JSObject* obj = JSVAL_TO_OBJECT(l); + if (OBJ_GET_CLASS(cx, obj) == &js_CallClass) + return ARECORD_CONTINUE; + if (obj != cx->fp->scopeChain || obj != globalObj) + RETURN_STOP_A("JSOP_SETNAME left operand is not the global object"); + + // The rest of the work is in record_SetPropHit. + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_THROW() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_IN() +{ + jsval& rval = stackval(-1); + jsval& lval = stackval(-2); + + if (JSVAL_IS_PRIMITIVE(rval)) + RETURN_STOP_A("JSOP_IN on non-object right operand"); + JSObject* obj = JSVAL_TO_OBJECT(rval); + LIns* obj_ins = get(&rval); + + jsid id; + LIns* x; + if (JSVAL_IS_INT(lval)) { + id = INT_JSVAL_TO_JSID(lval); + LIns* args[] = { makeNumberInt32(get(&lval)), obj_ins, cx_ins }; + x = lir->insCall(&js_HasNamedPropertyInt32_ci, args); + } else if (JSVAL_IS_STRING(lval)) { + if (!js_ValueToStringId(cx, lval, &id)) + RETURN_ERROR_A("left operand of JSOP_IN didn't convert to a string-id"); + LIns* args[] = { get(&lval), obj_ins, cx_ins }; + x = lir->insCall(&js_HasNamedProperty_ci, args); + } else { + RETURN_STOP_A("string or integer expected"); + } + + guard(false, lir->ins2i(LIR_eq, x, JSVAL_TO_SPECIAL(JSVAL_VOID)), OOM_EXIT); + x = lir->ins2i(LIR_eq, x, 1); + + JSTraceMonitor &localtm = *traceMonitor; + JSContext *localcx = cx; + + JSObject* obj2; + JSProperty* prop; + bool ok = obj->lookupProperty(cx, id, &obj2, &prop); + + /* lookupProperty can reenter the interpreter and kill |this|. */ + if (!localtm.recorder) { + if (prop) + obj2->dropProperty(localcx, prop); + return ARECORD_STOP; + } + + if (!ok) + RETURN_ERROR_A("obj->lookupProperty failed in JSOP_IN"); + bool cond = prop != NULL; + if (prop) + obj2->dropProperty(cx, prop); + + /* + * The interpreter fuses comparisons and the following branch, so we have + * to do that here as well. + */ + fuseIf(cx->fp->regs->pc + 1, cond, x); + + /* + * We update the stack after the guard. This is safe since the guard bails + * out at the comparison and the interpreter will therefore re-execute the + * comparison. This way the value of the condition doesn't have to be + * calculated and saved on the stack in most cases. + */ + set(&lval, x); + return ARECORD_CONTINUE; +} + +static JSBool FASTCALL +HasInstance(JSContext* cx, JSObject* ctor, jsval val) +{ + JSBool result = JS_FALSE; + if (!ctor->map->ops->hasInstance(cx, ctor, val, &result)) + js_SetBuiltinError(cx); + return result; +} +JS_DEFINE_CALLINFO_3(static, BOOL_FAIL, HasInstance, CONTEXT, OBJECT, JSVAL, 0, 0) + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_INSTANCEOF() +{ + // If the rhs isn't an object, we are headed for a TypeError. + jsval& ctor = stackval(-1); + if (JSVAL_IS_PRIMITIVE(ctor)) + RETURN_STOP_A("non-object on rhs of instanceof"); + + jsval& val = stackval(-2); + LIns* val_ins = box_jsval(val, get(&val)); + + enterDeepBailCall(); + LIns* args[] = {val_ins, get(&ctor), cx_ins}; + stack(-2, lir->insCall(&HasInstance_ci, args)); + LIns* status_ins = lir->insLoad(LIR_ld, + lirbuf->state, + offsetof(InterpState, builtinStatus)); + pendingGuardCondition = lir->ins_eq0(status_ins); + leaveDeepBailCall(); + + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_DEBUGGER() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_GOSUB() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_RETSUB() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_EXCEPTION() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_LINENO() +{ + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_CONDSWITCH() +{ + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_CASE() +{ + strictEquality(true, true); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_DEFAULT() +{ + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_EVAL() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_ENUMELEM() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_GETTER() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_SETTER() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_DEFFUN() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_DEFFUN_FC() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_DEFCONST() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_DEFVAR() +{ + return ARECORD_STOP; +} + +jsatomid +TraceRecorder::getFullIndex(ptrdiff_t pcoff) +{ + jsatomid index = GET_INDEX(cx->fp->regs->pc + pcoff); + index += atoms - cx->fp->script->atomMap.vector; + return index; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_LAMBDA() +{ + JSFunction* fun; + fun = cx->fp->script->getFunction(getFullIndex()); + + /* + * Emit code to clone a null closure parented by this recorder's global + * object, in order to preserve function object evaluation rules observable + * via identity and mutation. But don't clone if our result is consumed by + * JSOP_SETMETHOD or JSOP_INITMETHOD, since we optimize away the clone for + * these combinations and clone only if the "method value" escapes. + * + * See jsops.cpp, the JSOP_LAMBDA null closure case. The JSOP_SETMETHOD and + * JSOP_INITMETHOD logic governing the early ARECORD_CONTINUE returns below + * must agree with the corresponding break-from-do-while(0) logic there. + */ + if (FUN_NULL_CLOSURE(fun) && OBJ_GET_PARENT(cx, FUN_OBJECT(fun)) == globalObj) { + JSOp op2 = JSOp(cx->fp->regs->pc[JSOP_LAMBDA_LENGTH]); + + if (op2 == JSOP_SETMETHOD) { + jsval lval = stackval(-1); + + if (!JSVAL_IS_PRIMITIVE(lval) && + OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(lval)) == &js_ObjectClass) { + stack(0, INS_CONSTOBJ(FUN_OBJECT(fun))); + return ARECORD_CONTINUE; + } + } else if (op2 == JSOP_INITMETHOD) { + stack(0, INS_CONSTOBJ(FUN_OBJECT(fun))); + return ARECORD_CONTINUE; + } + + LIns *proto_ins; + CHECK_STATUS_A(getClassPrototype(JSProto_Function, proto_ins)); + + LIns* args[] = { INS_CONSTOBJ(globalObj), proto_ins, INS_CONSTFUN(fun), cx_ins }; + LIns* x = lir->insCall(&js_NewNullClosure_ci, args); + stack(0, x); + return ARECORD_CONTINUE; + } + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_LAMBDA_FC() +{ + JSFunction* fun; + fun = cx->fp->script->getFunction(getFullIndex()); + + if (OBJ_GET_PARENT(cx, FUN_OBJECT(fun)) != globalObj) + return ARECORD_STOP; + + LIns* args[] = { + INS_CONSTOBJ(globalObj), + INS_CONSTFUN(fun), + cx_ins + }; + LIns* call_ins = lir->insCall(&js_AllocFlatClosure_ci, args); + guard(false, + addName(lir->ins2(LIR_peq, call_ins, INS_NULL()), + "guard(js_AllocFlatClosure)"), + OOM_EXIT); + + if (fun->u.i.nupvars) { + JSUpvarArray *uva = fun->u.i.script->upvars(); + for (uint32 i = 0, n = uva->length; i < n; i++) { + jsval v; + LIns* upvar_ins = upvar(fun->u.i.script, uva, i, v); + if (!upvar_ins) + return ARECORD_STOP; + LIns* dslots_ins = NULL; + stobj_set_dslot(call_ins, i, dslots_ins, box_jsval(v, upvar_ins)); + } + } + + stack(0, call_ins); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_CALLEE() +{ + stack(0, get(&cx->fp->argv[-2])); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_SETLOCALPOP() +{ + var(GET_SLOTNO(cx->fp->regs->pc), stack(-1)); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_IFPRIMTOP() +{ + // Traces are type-specialized, including null vs. object, so we need do + // nothing here. The upstream unbox_jsval called after valueOf or toString + // from an imacro (e.g.) will fork the trace for us, allowing us to just + // follow along mindlessly :-). + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_SETCALL() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_TRY() +{ + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_FINALLY() +{ + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_NOP() +{ + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_ARGSUB() +{ + JSStackFrame* fp = cx->fp; + if (!(fp->fun->flags & JSFUN_HEAVYWEIGHT)) { + uintN slot = GET_ARGNO(fp->regs->pc); + if (slot < fp->argc) + stack(0, get(&cx->fp->argv[slot])); + else + stack(0, INS_VOID()); + return ARECORD_CONTINUE; + } + RETURN_STOP_A("can't trace JSOP_ARGSUB hard case"); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_ARGCNT() +{ + if (cx->fp->fun->flags & JSFUN_HEAVYWEIGHT) + RETURN_STOP_A("can't trace heavyweight JSOP_ARGCNT"); + + // argc is fixed on trace, so ideally we would simply generate LIR for + // constant argc. But the user can mutate arguments.length in the + // interpreter, so we have to check for that in the trace entry frame. + // We also have to check that arguments.length has not been mutated + // at record time, because if so we will generate incorrect constant + // LIR, which will assert in alu(). + if (cx->fp->argsobj && js_IsOverriddenArgsLength(JSVAL_TO_OBJECT(cx->fp->argsobj))) + RETURN_STOP_A("can't trace JSOP_ARGCNT if arguments.length has been modified"); + LIns *a_ins = get(&cx->fp->argsobj); + if (callDepth == 0) { + LIns *br = lir->insBranch(LIR_jt, lir->ins_peq0(a_ins), NULL); + + // The following implements js_IsOverriddenArgsLength on trace. + // The '2' bit is set if length was overridden. + LIns *len_ins = stobj_get_fslot(a_ins, JSSLOT_ARGS_LENGTH); + LIns *ovr_ins = lir->ins2(LIR_piand, len_ins, INS_CONSTWORD(2)); + + guard(true, lir->ins_peq0(ovr_ins), snapshot(BRANCH_EXIT)); + LIns *label = lir->ins0(LIR_label); + br->setTarget(label); + } + stack(0, lir->insImmf(cx->fp->argc)); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_DefLocalFunSetSlot(uint32 slot, JSObject* obj) +{ + JSFunction* fun = GET_FUNCTION_PRIVATE(cx, obj); + + if (FUN_NULL_CLOSURE(fun) && OBJ_GET_PARENT(cx, FUN_OBJECT(fun)) == globalObj) { + LIns *proto_ins; + CHECK_STATUS_A(getClassPrototype(JSProto_Function, proto_ins)); + + LIns* args[] = { INS_CONSTOBJ(globalObj), proto_ins, INS_CONSTFUN(fun), cx_ins }; + LIns* x = lir->insCall(&js_NewNullClosure_ci, args); + var(slot, x); + return ARECORD_CONTINUE; + } + + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_DEFLOCALFUN() +{ + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_DEFLOCALFUN_FC() +{ + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_GOTOX() +{ + return record_JSOP_GOTO(); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_IFEQX() +{ + return record_JSOP_IFEQ(); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_IFNEX() +{ + return record_JSOP_IFNE(); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_ORX() +{ + return record_JSOP_OR(); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_ANDX() +{ + return record_JSOP_AND(); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_GOSUBX() +{ + return record_JSOP_GOSUB(); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_CASEX() +{ + strictEquality(true, true); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_DEFAULTX() +{ + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_TABLESWITCHX() +{ + return record_JSOP_TABLESWITCH(); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_LOOKUPSWITCHX() +{ + return InjectStatus(switchop()); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_BACKPATCH() +{ + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_BACKPATCH_POP() +{ + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_THROWING() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_SETRVAL() +{ + // If we implement this, we need to update JSOP_STOP. + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_RETRVAL() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_GETGVAR() +{ + jsval slotval = cx->fp->slots[GET_SLOTNO(cx->fp->regs->pc)]; + if (JSVAL_IS_NULL(slotval)) + return ARECORD_CONTINUE; // We will see JSOP_NAME from the interpreter's jump, so no-op here. + + uint32 slot = JSVAL_TO_INT(slotval); + + if (!lazilyImportGlobalSlot(slot)) + RETURN_STOP_A("lazy import of global slot failed"); + + stack(0, get(&STOBJ_GET_SLOT(globalObj, slot))); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_SETGVAR() +{ + jsval slotval = cx->fp->slots[GET_SLOTNO(cx->fp->regs->pc)]; + if (JSVAL_IS_NULL(slotval)) + return ARECORD_CONTINUE; // We will see JSOP_NAME from the interpreter's jump, so no-op here. + + uint32 slot = JSVAL_TO_INT(slotval); + + if (!lazilyImportGlobalSlot(slot)) + RETURN_STOP_A("lazy import of global slot failed"); + + set(&STOBJ_GET_SLOT(globalObj, slot), stack(-1)); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_INCGVAR() +{ + jsval slotval = cx->fp->slots[GET_SLOTNO(cx->fp->regs->pc)]; + if (JSVAL_IS_NULL(slotval)) + // We will see JSOP_INCNAME from the interpreter's jump, so no-op here. + return ARECORD_CONTINUE; + + uint32 slot = JSVAL_TO_INT(slotval); + + if (!lazilyImportGlobalSlot(slot)) + RETURN_STOP_A("lazy import of global slot failed"); + + return InjectStatus(inc(STOBJ_GET_SLOT(globalObj, slot), 1)); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_DECGVAR() +{ + jsval slotval = cx->fp->slots[GET_SLOTNO(cx->fp->regs->pc)]; + if (JSVAL_IS_NULL(slotval)) + // We will see JSOP_INCNAME from the interpreter's jump, so no-op here. + return ARECORD_CONTINUE; + + uint32 slot = JSVAL_TO_INT(slotval); + + if (!lazilyImportGlobalSlot(slot)) + RETURN_STOP_A("lazy import of global slot failed"); + + return InjectStatus(inc(STOBJ_GET_SLOT(globalObj, slot), -1)); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_GVARINC() +{ + jsval slotval = cx->fp->slots[GET_SLOTNO(cx->fp->regs->pc)]; + if (JSVAL_IS_NULL(slotval)) + // We will see JSOP_INCNAME from the interpreter's jump, so no-op here. + return ARECORD_CONTINUE; + + uint32 slot = JSVAL_TO_INT(slotval); + + if (!lazilyImportGlobalSlot(slot)) + RETURN_STOP_A("lazy import of global slot failed"); + + return InjectStatus(inc(STOBJ_GET_SLOT(globalObj, slot), 1, false)); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_GVARDEC() +{ + jsval slotval = cx->fp->slots[GET_SLOTNO(cx->fp->regs->pc)]; + if (JSVAL_IS_NULL(slotval)) + // We will see JSOP_INCNAME from the interpreter's jump, so no-op here. + return ARECORD_CONTINUE; + + uint32 slot = JSVAL_TO_INT(slotval); + + if (!lazilyImportGlobalSlot(slot)) + RETURN_STOP_A("lazy import of global slot failed"); + + return InjectStatus(inc(STOBJ_GET_SLOT(globalObj, slot), -1, false)); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_REGEXP() +{ + return ARECORD_STOP; +} + +// begin JS_HAS_XML_SUPPORT + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_DEFXMLNS() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_ANYNAME() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_QNAMEPART() +{ + return record_JSOP_STRING(); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_QNAMECONST() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_QNAME() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_TOATTRNAME() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_TOATTRVAL() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_ADDATTRNAME() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_ADDATTRVAL() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_BINDXMLNAME() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_SETXMLNAME() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_XMLNAME() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_DESCENDANTS() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_FILTER() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_ENDFILTER() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_TOXML() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_TOXMLLIST() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_XMLTAGEXPR() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_XMLELTEXPR() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_XMLOBJECT() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_XMLCDATA() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_XMLCOMMENT() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_XMLPI() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_GETFUNNS() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_STARTXML() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_STARTXMLEXPR() +{ + return ARECORD_STOP; +} + +// end JS_HAS_XML_SUPPORT + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_CALLPROP() +{ + jsval& l = stackval(-1); + JSObject* obj; + LIns* obj_ins; + LIns* this_ins; + if (!JSVAL_IS_PRIMITIVE(l)) { + obj = JSVAL_TO_OBJECT(l); + obj_ins = get(&l); + this_ins = obj_ins; // |this| for subsequent call + } else { + jsint i; + debug_only_stmt(const char* protoname = NULL;) + if (JSVAL_IS_STRING(l)) { + i = JSProto_String; + debug_only_stmt(protoname = "String.prototype";) + } else if (JSVAL_IS_NUMBER(l)) { + i = JSProto_Number; + debug_only_stmt(protoname = "Number.prototype";) + } else if (JSVAL_IS_SPECIAL(l)) { + if (l == JSVAL_VOID) + RETURN_STOP_A("callprop on void"); + guard(false, lir->ins2i(LIR_eq, get(&l), JSVAL_TO_SPECIAL(JSVAL_VOID)), MISMATCH_EXIT); + i = JSProto_Boolean; + debug_only_stmt(protoname = "Boolean.prototype";) + } else { + JS_ASSERT(JSVAL_IS_NULL(l) || JSVAL_IS_VOID(l)); + RETURN_STOP_A("callprop on null or void"); + } + + if (!js_GetClassPrototype(cx, NULL, INT_TO_JSID(i), &obj)) + RETURN_ERROR_A("GetClassPrototype failed!"); + + obj_ins = INS_CONSTOBJ(obj); + debug_only_stmt(obj_ins = addName(obj_ins, protoname);) + this_ins = get(&l); // use primitive as |this| + } + + JSObject* obj2; + jsuword pcval; + CHECK_STATUS_A(test_property_cache(obj, obj_ins, obj2, pcval)); + + if (PCVAL_IS_NULL(pcval) || !PCVAL_IS_OBJECT(pcval)) + RETURN_STOP_A("callee is not an object"); + JS_ASSERT(HAS_FUNCTION_CLASS(PCVAL_TO_OBJECT(pcval))); + + if (JSVAL_IS_PRIMITIVE(l)) { + JSFunction* fun = GET_FUNCTION_PRIVATE(cx, PCVAL_TO_OBJECT(pcval)); + if (!PRIMITIVE_THIS_TEST(fun, l)) + RETURN_STOP_A("callee does not accept primitive |this|"); + } + + stack(0, this_ins); + stack(-1, INS_CONSTOBJ(PCVAL_TO_OBJECT(pcval))); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_DELDESC() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_UINT24() +{ + stack(0, lir->insImmf(GET_UINT24(cx->fp->regs->pc))); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_INDEXBASE() +{ + atoms += GET_INDEXBASE(cx->fp->regs->pc); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_RESETBASE() +{ + atoms = cx->fp->script->atomMap.vector; + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_RESETBASE0() +{ + atoms = cx->fp->script->atomMap.vector; + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_CALLELEM() +{ + return record_JSOP_GETELEM(); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_STOP() +{ + JSStackFrame *fp = cx->fp; + + if (fp->imacpc) { + /* + * End of imacro, so return true to the interpreter immediately. The + * interpreter's JSOP_STOP case will return from the imacro, back to + * the pc after the calling op, still in the same JSStackFrame. + */ + atoms = fp->script->atomMap.vector; + return ARECORD_CONTINUE; + } + + putArguments(); + + /* + * We know falling off the end of a constructor returns the new object that + * was passed in via fp->argv[-1], while falling off the end of a function + * returns undefined. + * + * NB: we do not support script rval (eval, API users who want the result + * of the last expression-statement, debugger API calls). + */ + if (fp->flags & JSFRAME_CONSTRUCTING) { + JS_ASSERT(fp->thisv == fp->argv[-1]); + rval_ins = get(&fp->argv[-1]); + } else { + rval_ins = INS_CONST(JSVAL_TO_SPECIAL(JSVAL_VOID)); + } + clearFrameSlotsFromCache(); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_GETXPROP() +{ + jsval& l = stackval(-1); + if (JSVAL_IS_PRIMITIVE(l)) + RETURN_STOP_A("primitive-this for GETXPROP?"); + + jsval* vp; + LIns* v_ins; + NameResult nr; + CHECK_STATUS_A(name(vp, v_ins, nr)); + stack(-1, v_ins); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_CALLXMLNAME() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_TYPEOFEXPR() +{ + return record_JSOP_TYPEOF(); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_ENTERBLOCK() +{ + JSObject* obj; + obj = cx->fp->script->getObject(getFullIndex(0)); + + LIns* void_ins = INS_CONST(JSVAL_TO_SPECIAL(JSVAL_VOID)); + for (int i = 0, n = OBJ_BLOCK_COUNT(cx, obj); i < n; i++) + stack(i, void_ins); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_LEAVEBLOCK() +{ + /* We mustn't exit the lexical block we began recording in. */ + if (cx->fp->blockChain != lexicalBlock) + return ARECORD_CONTINUE; + else + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_GENERATOR() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_YIELD() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_ARRAYPUSH() +{ + uint32_t slot = GET_UINT16(cx->fp->regs->pc); + JS_ASSERT(cx->fp->script->nfixed <= slot); + JS_ASSERT(cx->fp->slots + slot < cx->fp->regs->sp - 1); + jsval &arrayval = cx->fp->slots[slot]; + JS_ASSERT(JSVAL_IS_OBJECT(arrayval)); + JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, JSVAL_TO_OBJECT(arrayval))); + LIns *array_ins = get(&arrayval); + jsval &elt = stackval(-1); + LIns *elt_ins = box_jsval(elt, get(&elt)); + + LIns *args[] = { elt_ins, array_ins, cx_ins }; + LIns *ok_ins = lir->insCall(&js_ArrayCompPush_ci, args); + guard(false, lir->ins_eq0(ok_ins), OOM_EXIT); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_ENUMCONSTELEM() +{ + return ARECORD_STOP; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_LEAVEBLOCKEXPR() +{ + LIns* v_ins = stack(-1); + int n = -1 - GET_UINT16(cx->fp->regs->pc); + stack(n, v_ins); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_GETTHISPROP() +{ + LIns* this_ins; + + CHECK_STATUS_A(getThis(this_ins)); + + /* + * It's safe to just use cx->fp->thisv here because getThis() returns + * ARECORD_STOP if thisv is not available. + */ + JS_ASSERT(cx->fp->flags & JSFRAME_COMPUTED_THIS); + CHECK_STATUS_A(getProp(JSVAL_TO_OBJECT(cx->fp->thisv), this_ins)); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_GETARGPROP() +{ + return getProp(argval(GET_ARGNO(cx->fp->regs->pc))); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_GETLOCALPROP() +{ + return getProp(varval(GET_SLOTNO(cx->fp->regs->pc))); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_INDEXBASE1() +{ + atoms += 1 << 16; + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_INDEXBASE2() +{ + atoms += 2 << 16; + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_INDEXBASE3() +{ + atoms += 3 << 16; + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_CALLGVAR() +{ + jsval slotval = cx->fp->slots[GET_SLOTNO(cx->fp->regs->pc)]; + if (JSVAL_IS_NULL(slotval)) + // We will see JSOP_CALLNAME from the interpreter's jump, so no-op here. + return ARECORD_CONTINUE; + + uint32 slot = JSVAL_TO_INT(slotval); + + if (!lazilyImportGlobalSlot(slot)) + RETURN_STOP_A("lazy import of global slot failed"); + + jsval& v = STOBJ_GET_SLOT(globalObj, slot); + stack(0, get(&v)); + stack(1, INS_NULL()); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_CALLLOCAL() +{ + uintN slot = GET_SLOTNO(cx->fp->regs->pc); + stack(0, var(slot)); + stack(1, INS_NULL()); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_CALLARG() +{ + uintN slot = GET_ARGNO(cx->fp->regs->pc); + stack(0, arg(slot)); + stack(1, INS_NULL()); + return ARECORD_CONTINUE; +} + +/* Functions for use with JSOP_CALLBUILTIN. */ + +static JSBool +ObjectToIterator(JSContext *cx, uintN argc, jsval *vp) +{ + jsval *argv = JS_ARGV(cx, vp); + JS_ASSERT(JSVAL_IS_INT(argv[0])); + JS_SET_RVAL(cx, vp, JS_THIS(cx, vp)); + return js_ValueToIterator(cx, JSVAL_TO_INT(argv[0]), &JS_RVAL(cx, vp)); +} + +static JSObject* FASTCALL +ObjectToIterator_tn(JSContext* cx, jsbytecode* pc, JSObject *obj, int32 flags) +{ + jsval v = OBJECT_TO_JSVAL(obj); + JSBool ok = js_ValueToIterator(cx, flags, &v); + + if (!ok) { + js_SetBuiltinError(cx); + return NULL; + } + return JSVAL_TO_OBJECT(v); +} + +static JSBool +CallIteratorNext(JSContext *cx, uintN argc, jsval *vp) +{ + return js_CallIteratorNext(cx, JS_THIS_OBJECT(cx, vp), &JS_RVAL(cx, vp)); +} + +static jsval FASTCALL +CallIteratorNext_tn(JSContext* cx, jsbytecode* pc, JSObject* iterobj) +{ + JSAutoTempValueRooter tvr(cx); + JSBool ok = js_CallIteratorNext(cx, iterobj, tvr.addr()); + + if (!ok) { + js_SetBuiltinError(cx); + return JSVAL_ERROR_COOKIE; + } + return tvr.value(); +} + +JS_DEFINE_TRCINFO_1(ObjectToIterator, + (4, (static, OBJECT_FAIL, ObjectToIterator_tn, CONTEXT, PC, THIS, INT32, 0, 0))) +JS_DEFINE_TRCINFO_1(CallIteratorNext, + (3, (static, JSVAL_FAIL, CallIteratorNext_tn, CONTEXT, PC, THIS, 0, 0))) + +static const struct BuiltinFunctionInfo { + JSNativeTraceInfo *ti; + int nargs; +} builtinFunctionInfo[JSBUILTIN_LIMIT] = { + {&ObjectToIterator_trcinfo, 1}, + {&CallIteratorNext_trcinfo, 0}, +}; + +JSObject * +js_GetBuiltinFunction(JSContext *cx, uintN index) +{ + JSRuntime *rt = cx->runtime; + JSObject *funobj = rt->builtinFunctions[index]; + + if (!funobj) { + /* Use NULL parent and atom. Builtin functions never escape to scripts. */ + JS_ASSERT(index < JS_ARRAY_LENGTH(builtinFunctionInfo)); + const BuiltinFunctionInfo *bfi = &builtinFunctionInfo[index]; + JSFunction *fun = js_NewFunction(cx, + NULL, + JS_DATA_TO_FUNC_PTR(JSNative, bfi->ti), + bfi->nargs, + JSFUN_FAST_NATIVE | JSFUN_TRCINFO, + NULL, + NULL); + if (fun) { + funobj = FUN_OBJECT(fun); + STOBJ_CLEAR_PROTO(funobj); + STOBJ_CLEAR_PARENT(funobj); + + JS_LOCK_GC(rt); + if (!rt->builtinFunctions[index]) /* retest now that the lock is held */ + rt->builtinFunctions[index] = funobj; + else + funobj = rt->builtinFunctions[index]; + JS_UNLOCK_GC(rt); + } + } + return funobj; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_CALLBUILTIN() +{ + JSObject *obj = js_GetBuiltinFunction(cx, GET_INDEX(cx->fp->regs->pc)); + if (!obj) + RETURN_ERROR_A("error in js_GetBuiltinFunction"); + + stack(0, get(&stackval(-1))); + stack(-1, INS_CONSTOBJ(obj)); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_INT8() +{ + stack(0, lir->insImmf(GET_INT8(cx->fp->regs->pc))); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_INT32() +{ + stack(0, lir->insImmf(GET_INT32(cx->fp->regs->pc))); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_LENGTH() +{ + jsval& l = stackval(-1); + if (JSVAL_IS_PRIMITIVE(l)) { + if (!JSVAL_IS_STRING(l)) + RETURN_STOP_A("non-string primitive JSOP_LENGTH unsupported"); + set(&l, lir->ins1(LIR_i2f, getStringLength(get(&l)))); + return ARECORD_CONTINUE; + } + + JSObject* obj = JSVAL_TO_OBJECT(l); + LIns* obj_ins = get(&l); + + if (STOBJ_GET_CLASS(obj) == &js_ArgumentsClass) { + unsigned depth; + JSStackFrame *afp = guardArguments(obj, obj_ins, &depth); + if (!afp) + RETURN_STOP_A("can't reach arguments object's frame"); + + LIns* v_ins = lir->ins1(LIR_i2f, INS_CONST(afp->argc)); + set(&l, v_ins); + return ARECORD_CONTINUE; + } + + LIns* v_ins; + if (OBJ_IS_ARRAY(cx, obj)) { + if (OBJ_IS_DENSE_ARRAY(cx, obj)) { + if (!guardDenseArray(obj, obj_ins, BRANCH_EXIT)) { + JS_NOT_REACHED("OBJ_IS_DENSE_ARRAY but not?!?"); + return ARECORD_STOP; + } + } else { + if (!guardClass(obj, obj_ins, &js_SlowArrayClass, snapshot(BRANCH_EXIT))) + RETURN_STOP_A("can't trace length property access on non-array"); + } + v_ins = lir->ins1(LIR_i2f, p2i(stobj_get_fslot(obj_ins, JSSLOT_ARRAY_LENGTH))); + } else { + if (!OBJ_IS_NATIVE(obj)) + RETURN_STOP_A("can't trace length property access on non-array, non-native object"); + return getProp(obj, obj_ins); + } + set(&l, v_ins); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_NEWARRAY() +{ + LIns *proto_ins; + CHECK_STATUS_A(getClassPrototype(JSProto_Array, proto_ins)); + + uint32 len = GET_UINT16(cx->fp->regs->pc); + cx->fp->assertValidStackDepth(len); + + LIns* args[] = { lir->insImm(len), proto_ins, cx_ins }; + LIns* v_ins = lir->insCall(&js_NewArrayWithSlots_ci, args); + guard(false, lir->ins_peq0(v_ins), OOM_EXIT); + + LIns* dslots_ins = NULL; + uint32 count = 0; + for (uint32 i = 0; i < len; i++) { + jsval& v = stackval(int(i) - int(len)); + if (v != JSVAL_HOLE) + count++; + LIns* elt_ins = box_jsval(v, get(&v)); + stobj_set_dslot(v_ins, i, dslots_ins, elt_ins); + } + + if (count > 0) + stobj_set_fslot(v_ins, JSSLOT_ARRAY_COUNT, INS_CONST(count)); + + stack(-int(len), v_ins); + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_HOLE() +{ + stack(0, INS_CONST(JSVAL_TO_SPECIAL(JSVAL_HOLE))); + return ARECORD_CONTINUE; +} + +AbortableRecordingStatus +TraceRecorder::record_JSOP_TRACE() +{ + return ARECORD_CONTINUE; +} + +static const uint32 sMaxConcatNSize = 32; + +/* + * Copy the result of defvalue.string back into concatn's arguments, clean the + * stack, and return a pointer to the argument that was just overwritten. + */ +JS_REQUIRES_STACK jsval * +js_ConcatPostImacroStackCleanup(uint32 argc, JSFrameRegs ®s, + TraceRecorder *recorder) +{ + JS_ASSERT(*regs.pc == JSOP_IMACOP); + + /* Pop the argument offset and imacro return value. */ + jsint offset = JSVAL_TO_INT(*--regs.sp); + jsval *imacroResult = --regs.sp; + + /* Replace non-primitive argument with new primitive argument. */ + jsval *vp = regs.sp - offset; + JS_ASSERT(regs.sp - argc <= vp && vp < regs.sp); + if (recorder) + recorder->set(vp, recorder->get(imacroResult)); + *vp = *imacroResult; + + return vp; +} + +/* + * Initially, concatn takes N arguments on the stack, where N is immediate + * operand. To convert these arguments to primitives, we must repeatedly call + * the defvalue.string imacro. To achieve this iteration, defvalue.string ends + * with imacop. Hence, this function is called multiple times, each time with + * one less non-primitive. To keep track of where we are in the loop, we must + * push an additional index value on the stack. Hence, on all subsequent + * entries, the stack is organized as follows (bottom to top): + * + * prim[1] + * ... + * prim[i-1] + * nonprim[i] argument to imacro + * arg[i+1] + * ... + * arg[N] + * primarg[i] nonprim[i] converted to primitive + * i + * + * Hence, the stack setup on entry to this function (and JSOP_CONCATN in the + * interpreter, on trace abort) is dependent on whether an imacro is in + * progress. When all of concatn's arguments are primitive, it emits a builtin + * call and allows the actual JSOP_CONCATN to be executed by the interpreter. + */ +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_CONCATN() +{ + JSStackFrame *fp = cx->fp; + JSFrameRegs ®s = *fp->regs; + + /* + * If we are in an imacro, we must have just finished a call to + * defvalue.string. Continue where we left off last time. + */ + uint32 argc; + jsval *loopStart; + if (fp->imacpc) { + JS_ASSERT(*fp->imacpc == JSOP_CONCATN); + argc = GET_ARGC(fp->imacpc); + loopStart = js_ConcatPostImacroStackCleanup(argc, regs, this) + 1; + } else { + argc = GET_ARGC(regs.pc); + JS_ASSERT(argc > 0); + loopStart = regs.sp - argc; + + /* Prevent code/alloca explosion. */ + if (argc > sMaxConcatNSize) + return ARECORD_STOP; + } + + /* Convert non-primitives to primitives using defvalue.string. */ + for (jsval *vp = loopStart; vp != regs.sp; ++vp) { + if (!JSVAL_IS_PRIMITIVE(*vp)) { + /* + * In addition to the jsval we want the imacro to convert to + * primitive, pass through the offset of the argument on the stack. + */ + jsint offset = regs.sp - vp; + + /* Push the non-primitive to convert. */ + set(regs.sp, get(vp), true); + *regs.sp++ = *vp; + + /* Push the argument index. */ + set(regs.sp, lir->insImm(offset), true); + *regs.sp++ = INT_TO_JSVAL(offset); + + /* Nested imacro call OK because this is a tail call. */ + return InjectStatus(call_imacro(defvalue_imacros.string)); + } + } + + /* Build an array of the stringified primitives. */ + int32_t bufSize = argc * sizeof(JSString *); + LIns *buf_ins = lir->insAlloc(bufSize); + int32_t d = 0; + for (jsval *vp = regs.sp - argc; vp != regs.sp; ++vp, d += sizeof(void *)) + lir->insStorei(stringify(*vp), buf_ins, d); + + /* Perform concatenation using a builtin. */ + LIns *args[] = { lir->insImm(argc), buf_ins, cx_ins }; + LIns *concat = lir->insCall(&js_ConcatN_ci, args); + guard(false, lir->ins_peq0(concat), OOM_EXIT); + + /* Update tracker with result. */ + jsval *afterPop = regs.sp - (argc - 1); + set(afterPop - 1, concat); + + return ARECORD_CONTINUE; +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_SETMETHOD() +{ + return record_JSOP_SETPROP(); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_INITMETHOD() +{ + return record_JSOP_INITPROP(); +} + +JS_REQUIRES_STACK AbortableRecordingStatus +TraceRecorder::record_JSOP_SHARPINIT() +{ + return ARECORD_STOP; +} + +#define DBG_STUB(OP) \ + JS_REQUIRES_STACK AbortableRecordingStatus \ + TraceRecorder::record_##OP() \ + { \ + RETURN_STOP_A("can't trace " #OP); \ + } + +DBG_STUB(JSOP_GETUPVAR_DBG) +DBG_STUB(JSOP_CALLUPVAR_DBG) +DBG_STUB(JSOP_DEFFUN_DBGFC) +DBG_STUB(JSOP_DEFLOCALFUN_DBGFC) +DBG_STUB(JSOP_LAMBDA_DBGFC) + +#ifdef JS_JIT_SPEW +/* + * Print information about entry typemaps and unstable exits for all peers + * at a PC. + */ +void +DumpPeerStability(JSTraceMonitor* tm, const void* ip, JSObject* globalObj, uint32 globalShape, + uint32 argc) +{ + TreeFragment* f; + TreeInfo* ti; + bool looped = false; + unsigned length = 0; + + for (f = LookupLoop(tm, ip, globalObj, globalShape, argc); f != NULL; f = f->peer) { + if (!f->treeInfo) + continue; + debug_only_printf(LC_TMRecorder, "Stability of fragment %p:\nENTRY STACK=", (void*)f); + ti = f->treeInfo; + if (looped) + JS_ASSERT(ti->nStackTypes == length); + for (unsigned i = 0; i < ti->nStackTypes; i++) + debug_only_printf(LC_TMRecorder, "%c", typeChar[ti->stackTypeMap()[i]]); + debug_only_print0(LC_TMRecorder, " GLOBALS="); + for (unsigned i = 0; i < ti->nGlobalTypes(); i++) + debug_only_printf(LC_TMRecorder, "%c", typeChar[ti->globalTypeMap()[i]]); + debug_only_print0(LC_TMRecorder, "\n"); + UnstableExit* uexit = ti->unstableExits; + while (uexit != NULL) { + debug_only_print0(LC_TMRecorder, "EXIT "); + JSTraceType* m = uexit->exit->fullTypeMap(); + debug_only_print0(LC_TMRecorder, "STACK="); + for (unsigned i = 0; i < uexit->exit->numStackSlots; i++) + debug_only_printf(LC_TMRecorder, "%c", typeChar[m[i]]); + debug_only_print0(LC_TMRecorder, " GLOBALS="); + for (unsigned i = 0; i < uexit->exit->numGlobalSlots; i++) { + debug_only_printf(LC_TMRecorder, "%c", + typeChar[m[uexit->exit->numStackSlots + i]]); + } + debug_only_print0(LC_TMRecorder, "\n"); + uexit = uexit->next; + } + length = ti->nStackTypes; + looped = true; + } +} +#endif + +#ifdef MOZ_TRACEVIS + +FILE* traceVisLogFile = NULL; +JSHashTable *traceVisScriptTable = NULL; + +JS_FRIEND_API(bool) +JS_StartTraceVis(const char* filename = "tracevis.dat") +{ + if (traceVisLogFile) { + // If we're currently recording, first we must stop. + JS_StopTraceVis(); + } + + traceVisLogFile = fopen(filename, "wb"); + if (!traceVisLogFile) + return false; + + return true; +} + +JS_FRIEND_API(JSBool) +js_StartTraceVis(JSContext *cx, JSObject *obj, + uintN argc, jsval *argv, jsval *rval) +{ + JSBool ok; + + if (argc > 0 && JSVAL_IS_STRING(argv[0])) { + JSString *str = JSVAL_TO_STRING(argv[0]); + char *filename = js_DeflateString(cx, str->chars(), str->length()); + if (!filename) + goto error; + ok = JS_StartTraceVis(filename); + cx->free(filename); + } else { + ok = JS_StartTraceVis(); + } + + if (ok) { + fprintf(stderr, "started TraceVis recording\n"); + return JS_TRUE; + } + + error: + JS_ReportError(cx, "failed to start TraceVis recording"); + return JS_FALSE; +} + +JS_FRIEND_API(bool) +JS_StopTraceVis() +{ + if (!traceVisLogFile) + return false; + + fclose(traceVisLogFile); // not worth checking the result + traceVisLogFile = NULL; + + return true; +} + +JS_FRIEND_API(JSBool) +js_StopTraceVis(JSContext *cx, JSObject *obj, + uintN argc, jsval *argv, jsval *rval) +{ + JSBool ok = JS_StopTraceVis(); + + if (ok) + fprintf(stderr, "stopped TraceVis recording\n"); + else + JS_ReportError(cx, "TraceVis isn't running"); + + return ok; +} + +#endif /* MOZ_TRACEVIS */ + +JS_REQUIRES_STACK void +js_CaptureStackTypes(JSContext* cx, unsigned callDepth, JSTraceType* typeMap) +{ + CaptureTypesVisitor capVisitor(cx, typeMap); + VisitStackSlots(capVisitor, cx, callDepth); +} + +JS_REQUIRES_STACK void +TraceRecorder::determineGlobalTypes(JSTraceType* typeMap) +{ + DetermineTypesVisitor detVisitor(*this, typeMap); + VisitGlobalSlots(detVisitor, cx, *treeInfo->globalSlots); +} + +#include "jsrecursion.cpp" + diff --git a/ape-server/deps/js/src/jstracer.h b/ape-server/deps/js/src/jstracer.h new file mode 100755 index 0000000..285bf71 --- /dev/null +++ b/ape-server/deps/js/src/jstracer.h @@ -0,0 +1,1639 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=99 ft=cpp: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released + * May 28, 2008. + * + * The Initial Developer of the Original Code is + * Brendan Eich + * + * Contributor(s): + * Andreas Gal + * Mike Shaver + * David Anderson + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jstracer_h___ +#define jstracer_h___ + +#ifdef JS_TRACER + +#include "jstypes.h" +#include "jsbuiltins.h" +#include "jscntxt.h" +#include "jsdhash.h" +#include "jsinterp.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsvector.h" + +#if defined(DEBUG) && !defined(JS_JIT_SPEW) +#define JS_JIT_SPEW +#endif + +template +class Queue { + T* _data; + unsigned _len; + unsigned _max; + nanojit::Allocator* alloc; + +public: + void ensure(unsigned size) { + if (!_max) + _max = 16; + while (_max < size) + _max <<= 1; + if (alloc) { + T* tmp = new (*alloc) T[_max]; + memcpy(tmp, _data, _len * sizeof(T)); + _data = tmp; + } else { + _data = (T*)realloc(_data, _max * sizeof(T)); + } +#if defined(DEBUG) + memset(&_data[_len], 0xcd, _max - _len); +#endif + } + + Queue(nanojit::Allocator* alloc) + : alloc(alloc) + { + this->_max = + this->_len = 0; + this->_data = NULL; + } + + ~Queue() { + if (!alloc) + free(_data); + } + + bool contains(T a) { + for (unsigned n = 0; n < _len; ++n) { + if (_data[n] == a) + return true; + } + return false; + } + + void add(T a) { + ensure(_len + 1); + JS_ASSERT(_len <= _max); + _data[_len++] = a; + } + + void add(T* chunk, unsigned size) { + ensure(_len + size); + JS_ASSERT(_len <= _max); + memcpy(&_data[_len], chunk, size * sizeof(T)); + _len += size; + } + + void addUnique(T a) { + if (!contains(a)) + add(a); + } + + void setLength(unsigned len) { + ensure(len + 1); + _len = len; + } + + void clear() { + _len = 0; + } + + T & get(unsigned i) { + JS_ASSERT(i < length()); + return _data[i]; + } + + const T & get(unsigned i) const { + JS_ASSERT(i < length()); + return _data[i]; + } + + T & operator [](unsigned i) { + return get(i); + } + + const T & operator [](unsigned i) const { + return get(i); + } + + unsigned length() const { + return _len; + } + + T* data() const { + return _data; + } +}; + +/* + * Tracker is used to keep track of values being manipulated by the interpreter + * during trace recording. It maps opaque, 4-byte aligned address to LIns pointers. + * pointers. To do this efficiently, we observe that the addresses of jsvals + * living in the interpreter tend to be aggregated close to each other - + * usually on the same page (where a tracker page doesn't have to be the same + * size as the OS page size, but it's typically similar). The Tracker + * consists of a linked-list of structures representing a memory page, which + * are created on-demand as memory locations are used. + * + * For every address, first we split it into two parts: upper bits which + * represent the "base", and lower bits which represent an offset against the + * base. For the offset, we then right-shift it by two because the bottom two + * bits of a 4-byte aligned address are always zero. The mapping then + * becomes: + * + * page = page in pagelist such that Base(address) == page->base, + * page->map[Offset(address)] + */ +class Tracker { + #define TRACKER_PAGE_SZB 4096 + #define TRACKER_PAGE_ENTRIES (TRACKER_PAGE_SZB >> 2) // each slot is 4 bytes + #define TRACKER_PAGE_MASK jsuword(TRACKER_PAGE_SZB - 1) + + struct TrackerPage { + struct TrackerPage* next; + jsuword base; + nanojit::LIns* map[TRACKER_PAGE_ENTRIES]; + }; + struct TrackerPage* pagelist; + + jsuword getTrackerPageBase(const void* v) const; + jsuword getTrackerPageOffset(const void* v) const; + struct TrackerPage* findTrackerPage(const void* v) const; + struct TrackerPage* addTrackerPage(const void* v); +public: + Tracker(); + ~Tracker(); + + bool has(const void* v) const; + nanojit::LIns* get(const void* v) const; + void set(const void* v, nanojit::LIns* ins); + void clear(); +}; + +class VMFragment : public nanojit::Fragment { +public: + VMFragment(const void* _ip verbose_only(, uint32_t profFragID)) + : Fragment(_ip verbose_only(, profFragID)) + {} + + /* + * If this is anchored off a TreeFragment, this points to that tree fragment. + * Otherwise, it is |this|. + */ + TreeFragment* root; + + TreeFragment* toTreeFragment(); +}; + +struct LinkableFragment : public VMFragment +{ + LinkableFragment(const void* _ip verbose_only(, uint32_t profFragID)) + : VMFragment(_ip verbose_only(, profFragID)) + { } + + uint32 branchCount; +}; + +/* + * argc is cx->fp->argc at the trace loop header, i.e., the number of arguments + * pushed for the innermost JS frame. This is required as part of the fragment + * key because the fragment will write those arguments back to the interpreter + * stack when it exits, using its typemap, which implicitly incorporates a + * given value of argc. Without this feature, a fragment could be called as an + * inner tree with two different values of argc, and entry type checking or + * exit frame synthesis could crash. + */ +struct TreeFragment : public LinkableFragment +{ + TreeFragment(const void* _ip, JSObject* _globalObj, uint32 _globalShape, uint32 _argc + verbose_only(, uint32_t profFragID)) : + LinkableFragment(_ip verbose_only(, profFragID)), + treeInfo(NULL), + first(NULL), + next(NULL), + peer(NULL), + globalObj(_globalObj), + globalShape(_globalShape), + argc(_argc) + { } + + TreeInfo *treeInfo; + TreeFragment* first; + TreeFragment* next; + TreeFragment* peer; + JSObject* globalObj; + uint32 globalShape; + uint32 argc; +}; + +inline TreeFragment* +VMFragment::toTreeFragment() +{ + JS_ASSERT(root == this); + return static_cast(this); +} + +#if defined(JS_JIT_SPEW) || defined(NJ_NO_VARIADIC_MACROS) + +enum LC_TMBits { + /* + * Output control bits for all non-Nanojit code. Only use bits 16 and + * above, since Nanojit uses 0 .. 15 itself. + */ + LC_TMMinimal = 1<<16, + LC_TMTracer = 1<<17, + LC_TMRecorder = 1<<18, + LC_TMAbort = 1<<19, + LC_TMStats = 1<<20, + LC_TMRegexp = 1<<21, + LC_TMTreeVis = 1<<22 +}; + +#endif + +#ifdef NJ_NO_VARIADIC_MACROS + +#define debug_only_stmt(action) /* */ +static void debug_only_printf(int mask, const char *fmt, ...) {} +#define debug_only_print0(mask, str) /* */ + +#elif defined(JS_JIT_SPEW) + +// Top level logging controller object. +extern nanojit::LogControl js_LogController; + +// Top level profiling hook, needed to harvest profile info from Fragments +// whose logical lifetime is about to finish +extern void js_FragProfiling_FragFinalizer(nanojit::Fragment* f, JSTraceMonitor*); + +#define debug_only_stmt(stmt) \ + stmt + +#define debug_only_printf(mask, fmt, ...) \ + JS_BEGIN_MACRO \ + if ((js_LogController.lcbits & (mask)) > 0) { \ + js_LogController.printf(fmt, __VA_ARGS__); \ + fflush(stdout); \ + } \ + JS_END_MACRO + +#define debug_only_print0(mask, str) \ + JS_BEGIN_MACRO \ + if ((js_LogController.lcbits & (mask)) > 0) { \ + js_LogController.printf("%s", str); \ + fflush(stdout); \ + } \ + JS_END_MACRO + +#else + +#define debug_only_stmt(action) /* */ +#define debug_only_printf(mask, fmt, ...) /* */ +#define debug_only_print0(mask, str) /* */ + +#endif + +/* + * The oracle keeps track of hit counts for program counter locations, as + * well as slots that should not be demoted to int because we know them to + * overflow or they result in type-unstable traces. We are using simple + * hash tables. Collisions lead to loss of optimization (demotable slots + * are not demoted, etc.) but have no correctness implications. + */ +#define ORACLE_SIZE 4096 + +class Oracle { + avmplus::BitSet _stackDontDemote; + avmplus::BitSet _globalDontDemote; + avmplus::BitSet _pcDontDemote; +public: + Oracle(); + + JS_REQUIRES_STACK void markGlobalSlotUndemotable(JSContext* cx, unsigned slot); + JS_REQUIRES_STACK bool isGlobalSlotUndemotable(JSContext* cx, unsigned slot) const; + JS_REQUIRES_STACK void markStackSlotUndemotable(JSContext* cx, unsigned slot); + JS_REQUIRES_STACK void markStackSlotUndemotable(JSContext* cx, unsigned slot, const void* pc); + JS_REQUIRES_STACK bool isStackSlotUndemotable(JSContext* cx, unsigned slot) const; + JS_REQUIRES_STACK bool isStackSlotUndemotable(JSContext* cx, unsigned slot, const void* pc) const; + void markInstructionUndemotable(jsbytecode* pc); + bool isInstructionUndemotable(jsbytecode* pc) const; + + void clearDemotability(); + void clear() { + clearDemotability(); + } +}; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 || (defined(__GNUC__) && __GNUC__ >= 4) +#define USE_TRACE_TYPE_ENUM +#endif + +/* + * The types of values calculated during tracing, used to specialize operations + * to the types of those values. These loosely correspond to the values of the + * JSVAL_* language types, but we add a few further divisions to enable further + * optimization at execution time. Do not rely on this loose correspondence for + * correctness without adding static assertions! + * + * The ifdefs enforce that this enum occupies only one byte of memory, where + * possible. If it doesn't, type maps will occupy more space but should + * otherwise work correctly. A static assertion in jstracer.cpp verifies that + * this requirement is correctly enforced by these compilers. + */ +enum JSTraceType_ +#if defined(_MSC_VER) && _MSC_VER >= 1400 +: int8_t +#endif +{ + TT_OBJECT = 0, /* pointer to JSObject whose class is not js_FunctionClass */ + TT_INT32 = 1, /* 32-bit signed integer */ + TT_DOUBLE = 2, /* pointer to jsdouble */ + TT_JSVAL = 3, /* arbitrary jsval */ + TT_STRING = 4, /* pointer to JSString */ + TT_NULL = 5, /* null */ + TT_PSEUDOBOOLEAN = 6, /* true, false, or undefined (0, 1, or 2) */ + TT_FUNCTION = 7 /* pointer to JSObject whose class is js_FunctionClass */ +} +#if defined(__GNUC__) && defined(USE_TRACE_TYPE_ENUM) +__attribute__((packed)) +#endif +; + +#ifdef USE_TRACE_TYPE_ENUM +typedef JSTraceType_ JSTraceType; +#else +typedef int8_t JSTraceType; +#endif + +/* + * This indicates an invalid type or error. Note that it should not be used in typemaps, + * because it is the wrong size. It can only be used as a uint32, for example as the + * return value from a function that returns a type as a uint32. + */ +const uint32 TT_INVALID = uint32(-1); + +typedef Queue SlotList; + +class TypeMap : public Queue { +public: + TypeMap(nanojit::Allocator* alloc) : Queue(alloc) {} + JS_REQUIRES_STACK void captureTypes(JSContext* cx, JSObject* globalObj, SlotList& slots, unsigned callDepth); + JS_REQUIRES_STACK void captureMissingGlobalTypes(JSContext* cx, JSObject* globalObj, SlotList& slots, + unsigned stackSlots); + bool matches(TypeMap& other) const; + void fromRaw(JSTraceType* other, unsigned numSlots); +}; + +#define JS_TM_EXITCODES(_) \ + /* \ + * An exit at a possible branch-point in the trace at which to attach a \ + * future secondary trace. Therefore the recorder must generate different \ + * code to handle the other outcome of the branch condition from the \ + * primary trace's outcome. \ + */ \ + _(BRANCH) \ + /* \ + * Exit at a tableswitch via a numbered case. \ + */ \ + _(CASE) \ + /* \ + * Exit at a tableswitch via the default case. \ + */ \ + _(DEFAULT) \ + _(LOOP) \ + _(NESTED) \ + /* \ + * An exit from a trace because a condition relied upon at recording time \ + * no longer holds, where the alternate path of execution is so rare or \ + * difficult to address in native code that it is not traced at all, e.g. \ + * negative array index accesses, which differ from positive indexes in \ + * that they require a string-based property lookup rather than a simple \ + * memory access. \ + */ \ + _(MISMATCH) \ + /* \ + * A specialization of MISMATCH_EXIT to handle allocation failures. \ + */ \ + _(OOM) \ + _(OVERFLOW) \ + _(UNSTABLE_LOOP) \ + _(TIMEOUT) \ + _(DEEP_BAIL) \ + _(STATUS) \ + /* Exit is almost recursive and wants a peer at recursive_pc */ \ + _(RECURSIVE_UNLINKED) \ + /* Exit is recursive, and there are no more frames */ \ + _(RECURSIVE_LOOP) \ + /* Exit is recursive, but type-mismatched guarding on a down frame */ \ + _(RECURSIVE_MISMATCH) \ + /* Exit is recursive, and the JIT wants to try slurping interp frames */ \ + _(RECURSIVE_EMPTY_RP) \ + /* Slurping interp frames in up-recursion failed */ \ + _(RECURSIVE_SLURP_FAIL) \ + /* Tried to slurp an interp frame, but the pc or argc mismatched */ \ + _(RECURSIVE_SLURP_MISMATCH) + +enum ExitType { + #define MAKE_EXIT_CODE(x) x##_EXIT, + JS_TM_EXITCODES(MAKE_EXIT_CODE) + #undef MAKE_EXIT_CODE + TOTAL_EXIT_TYPES +}; + +struct FrameInfo; + +struct VMSideExit : public nanojit::SideExit +{ + JSObject* block; + jsbytecode* pc; + jsbytecode* imacpc; + intptr_t sp_adj; + intptr_t rp_adj; + int32_t calldepth; + uint32 numGlobalSlots; + uint32 numStackSlots; + uint32 numStackSlotsBelowCurrentFrame; + ExitType exitType; + uintN lookupFlags; + void* recursive_pc; + FrameInfo* recursive_down; + unsigned hitcount; + unsigned slurpFailSlot; + JSTraceType slurpType; + + /* + * Ordinarily 0. If a slow native function is atop the stack, the 1 bit is + * set if constructing and the other bits are a pointer to the funobj. + */ + uintptr_t nativeCalleeWord; + + JSObject * nativeCallee() { + return (JSObject *) (nativeCalleeWord & ~1); + } + + bool constructing() { + return bool(nativeCalleeWord & 1); + } + + void setNativeCallee(JSObject *callee, bool constructing) { + nativeCalleeWord = uintptr_t(callee) | (constructing ? 1 : 0); + } + + inline JSTraceType* stackTypeMap() { + return (JSTraceType*)(this + 1); + } + + inline JSTraceType& stackType(unsigned i) { + JS_ASSERT(i < numStackSlots); + return stackTypeMap()[i]; + } + + inline JSTraceType* globalTypeMap() { + return (JSTraceType*)(this + 1) + this->numStackSlots; + } + + inline JSTraceType* fullTypeMap() { + return stackTypeMap(); + } + + inline VMFragment* fromFrag() { + return (VMFragment*)from; + } + + inline TreeFragment* root() { + return fromFrag()->root; + } +}; + +class VMAllocator : public nanojit::Allocator +{ + +public: + VMAllocator() : mOutOfMemory(false), mSize(0) + {} + + size_t size() { + return mSize; + } + + bool outOfMemory() { + return mOutOfMemory; + } + + struct Mark + { + VMAllocator& vma; + bool committed; + nanojit::Allocator::Chunk* saved_chunk; + char* saved_top; + char* saved_limit; + size_t saved_size; + + Mark(VMAllocator& vma) : + vma(vma), + committed(false), + saved_chunk(vma.current_chunk), + saved_top(vma.current_top), + saved_limit(vma.current_limit), + saved_size(vma.mSize) + {} + + ~Mark() + { + if (!committed) + vma.rewind(*this); + } + + void commit() { committed = true; } + }; + + void rewind(const Mark& m) { + while (current_chunk != m.saved_chunk) { + Chunk *prev = current_chunk->prev; + freeChunk(current_chunk); + current_chunk = prev; + } + current_top = m.saved_top; + current_limit = m.saved_limit; + mSize = m.saved_size; + memset(current_top, 0, current_limit - current_top); + } + + bool mOutOfMemory; + size_t mSize; + + /* + * FIXME: Area the LIR spills into if we encounter an OOM mid-way + * through compilation; we must check mOutOfMemory before we run out + * of mReserve, otherwise we're in undefined territory. This area + * used to be one page, now 16 to be "safer". This is a temporary + * and quite unsatisfactory approach to handling OOM in Nanojit. + */ + uintptr_t mReserve[0x10000]; +}; + +struct REHashKey { + size_t re_length; + uint16 re_flags; + const jschar* re_chars; + + REHashKey(size_t re_length, uint16 re_flags, const jschar *re_chars) + : re_length(re_length) + , re_flags(re_flags) + , re_chars(re_chars) + {} + + bool operator==(const REHashKey& other) const + { + return ((this->re_length == other.re_length) && + (this->re_flags == other.re_flags) && + !memcmp(this->re_chars, other.re_chars, + this->re_length * sizeof(jschar))); + } +}; + +struct REHashFn { + static size_t hash(const REHashKey& k) { + return + k.re_length + + k.re_flags + + nanojit::murmurhash(k.re_chars, k.re_length * sizeof(jschar)); + } +}; + +class TreeInfo; + +struct FrameInfo { + JSObject* block; // caller block chain head + jsbytecode* pc; // caller fp->regs->pc + jsbytecode* imacpc; // caller fp->imacpc + uint32 spdist; // distance from fp->slots to fp->regs->sp at JSOP_CALL + + /* + * Bit 15 (0x8000) is a flag that is set if constructing (called through new). + * Bits 0-14 are the actual argument count. This may be less than fun->nargs. + * NB: This is argc for the callee, not the caller. + */ + uint32 argc; + + /* + * Number of stack slots in the caller, not counting slots pushed when + * invoking the callee. That is, slots after JSOP_CALL completes but + * without the return value. This is also equal to the number of slots + * between fp->down->argv[-2] (calleR fp->callee) and fp->argv[-2] + * (calleE fp->callee). + */ + uint32 callerHeight; + + /* argc of the caller */ + uint32 callerArgc; + + // Safer accessors for argc. + enum { CONSTRUCTING_FLAG = 0x10000 }; + void set_argc(uint16 argc, bool constructing) { + this->argc = uint32(argc) | (constructing ? CONSTRUCTING_FLAG: 0); + } + uint16 get_argc() const { return uint16(argc & ~CONSTRUCTING_FLAG); } + bool is_constructing() const { return (argc & CONSTRUCTING_FLAG) != 0; } + + // The typemap just before the callee is called. + JSTraceType* get_typemap() { return (JSTraceType*) (this+1); } + const JSTraceType* get_typemap() const { return (JSTraceType*) (this+1); } +}; + +struct UnstableExit +{ + VMFragment* fragment; + VMSideExit* exit; + UnstableExit* next; +}; + +enum RecordReason +{ + Record_Branch, + Record_EnterFrame, + Record_LeaveFrame +}; + +enum RecursionStatus +{ + Recursion_None, /* No recursion has been compiled yet. */ + Recursion_Disallowed, /* This tree cannot be recursive. */ + Recursion_Unwinds, /* Tree is up-recursive only. */ + Recursion_Detected /* Tree has down recursion and maybe up recursion. */ +}; + +class TreeInfo { +public: + TreeFragment* const rootFragment; + JSScript* script; + unsigned maxNativeStackSlots; + ptrdiff_t nativeStackBase; + unsigned maxCallDepth; + TypeMap typeMap; + unsigned nStackTypes; + SlotList* globalSlots; + /* Dependent trees must be trashed if this tree dies, and updated on missing global types */ + Queue dependentTrees; + /* Linked trees must be updated on missing global types, but are not dependent */ + Queue linkedTrees; + Queue sideExits; + UnstableExit* unstableExits; + /* All embedded GC things are registered here so the GC can scan them. */ + Queue gcthings; + Queue sprops; +#ifdef DEBUG + const char* treeFileName; + uintN treeLineNumber; + uintN treePCOffset; +#endif + RecursionStatus recursion; + + TreeInfo(nanojit::Allocator* alloc, + TreeFragment* fragment, + SlotList* globalSlots) + : rootFragment(fragment), + script(NULL), + maxNativeStackSlots(0), + nativeStackBase(0), + maxCallDepth(0), + typeMap(alloc), + nStackTypes(0), + globalSlots(globalSlots), + dependentTrees(alloc), + linkedTrees(alloc), + sideExits(alloc), + unstableExits(NULL), + gcthings(alloc), + sprops(alloc), + recursion(Recursion_None) + {} + + inline unsigned nGlobalTypes() { + return typeMap.length() - nStackTypes; + } + inline JSTraceType* globalTypeMap() { + return typeMap.data() + nStackTypes; + } + inline JSTraceType* stackTypeMap() { + return typeMap.data(); + } + inline JSObject* globalObj() { + return rootFragment->globalObj; + } + + UnstableExit* removeUnstableExit(VMSideExit* exit); +}; + +typedef enum JSBuiltinStatus { + JSBUILTIN_BAILED = 1, + JSBUILTIN_ERROR = 2 +} JSBuiltinStatus; + +// Arguments objects created on trace have a private value that points to an +// instance of this struct. The struct includes a typemap that is allocated +// as part of the object. +struct js_ArgsPrivateNative { + double *argv; + + static js_ArgsPrivateNative *create(VMAllocator &alloc, unsigned argc) + { + return (js_ArgsPrivateNative*) new (alloc) char[sizeof(js_ArgsPrivateNative) + argc]; + } + + JSTraceType *typemap() + { + return (JSTraceType*) (this+1); + } +}; + +static JS_INLINE void +js_SetBuiltinError(JSContext *cx) +{ + cx->interpState->builtinStatus |= JSBUILTIN_ERROR; +} + +#ifdef DEBUG_RECORDING_STATUS_NOT_BOOL +/* #define DEBUG_RECORDING_STATUS_NOT_BOOL to detect misuses of RecordingStatus */ +struct RecordingStatus { + int code; + bool operator==(RecordingStatus &s) { return this->code == s.code; }; + bool operator!=(RecordingStatus &s) { return this->code != s.code; }; +}; +enum RecordingStatusCodes { + RECORD_ERROR_code = 0, + RECORD_STOP_code = 1, + + RECORD_CONTINUE_code = 3, + RECORD_IMACRO_code = 4 +}; +RecordingStatus RECORD_CONTINUE = { RECORD_CONTINUE_code }; +RecordingStatus RECORD_STOP = { RECORD_STOP_code }; +RecordingStatus RECORD_IMACRO = { RECORD_IMACRO_code }; +RecordingStatus RECORD_ERROR = { RECORD_ERROR_code }; + +struct AbortableRecordingStatus { + int code; + bool operator==(AbortableRecordingStatus &s) { return this->code == s.code; }; + bool operator!=(AbortableRecordingStatus &s) { return this->code != s.code; }; +}; +enum AbortableRecordingStatusCodes { + ARECORD_ERROR_code = 0, + ARECORD_STOP_code = 1, + ARECORD_ABORTED_code = 2, + ARECORD_CONTINUE_code = 3, + ARECORD_IMACRO_code = 4, + ARECORD_COMPLETED_code = 5 +}; +AbortableRecordingStatus ARECORD_ERROR = { ARECORD_ERROR_code }; +AbortableRecordingStatus ARECORD_STOP = { ARECORD_STOP_code }; +AbortableRecordingStatus ARECORD_CONTINUE = { ARECORD_CONTINUE_code }; +AbortableRecordingStatus ARECORD_IMACRO = { ARECORD_IMACRO_code }; +AbortableRecordingStatus ARECORD_ABORTED = { ARECORD_ABORTED_code }; +AbortableRecordingStatus ARECORD_COMPLETED = { ARECORD_COMPLETED_code }; + +static inline AbortableRecordingStatus +InjectStatus(RecordingStatus rs) +{ + AbortableRecordingStatus ars = { rs.code }; + return ars; +} +static inline AbortableRecordingStatus +InjectStatus(AbortableRecordingStatus ars) +{ + return ars; +} + +static inline bool +StatusAbortsRecording(AbortableRecordingStatus ars) +{ + return ars == ARECORD_ERROR || ars == ARECORD_STOP; +} +#else + +/* + * Normally, during recording, when the recorder cannot continue, it returns + * ARECORD_STOP to indicate that recording should be aborted by the top-level + * recording function. However, if the recorder reenters the interpreter (e.g., + * when executing an inner loop), there will be an immediate abort. This + * condition must be carefully detected and propagated out of all nested + * recorder calls lest the now-invalid TraceRecorder object be accessed + * accidentally. This condition is indicated by the ARECORD_ABORTED value. + * + * The AbortableRecordingStatus enumeration represents the general set of + * possible results of calling a recorder function. Functions that cannot + * possibly return ARECORD_ABORTED may statically guarantee this to the caller + * using the RecordingStatus enumeration. Ideally, C++ would allow subtyping + * of enumerations, but it doesn't. To simulate subtype conversion manually, + * code should call InjectStatus to inject a value of the restricted set into a + * value of the general set. + */ + +enum RecordingStatus { + RECORD_ERROR = 0, // Error; propagate to interpreter. + RECORD_STOP = 1, // Recording should be aborted at the top-level + // call to the recorder. + // (value reserved for ARECORD_ABORTED) + RECORD_CONTINUE = 3, // Continue recording. + RECORD_IMACRO = 4 // Entered imacro; continue recording. + // Only JSOP_IS_IMACOP opcodes may return this. +}; + +enum AbortableRecordingStatus { + ARECORD_ERROR = 0, + ARECORD_STOP = 1, + ARECORD_ABORTED = 2, // Recording has already been aborted; the recorder + // has been deleted. + ARECORD_CONTINUE = 3, + ARECORD_IMACRO = 4, + ARECORD_COMPLETED = 5 // Recording of the current trace recorder completed +}; + +static JS_ALWAYS_INLINE AbortableRecordingStatus +InjectStatus(RecordingStatus rs) +{ + return static_cast(rs); +} + +static JS_ALWAYS_INLINE AbortableRecordingStatus +InjectStatus(AbortableRecordingStatus ars) +{ + return ars; +} + +/* + * Return whether the recording status requires the current recording session + * to be deleted. ABORTED and COMPLETED indicate the recording session is + * already deleted, so they return 'false'. + */ +static JS_ALWAYS_INLINE bool +StatusAbortsRecording(AbortableRecordingStatus ars) +{ + return ars <= ARECORD_STOP; +} +#endif + +class SlotMap; +class SlurpInfo; + +/* Results of trying to compare two typemaps together */ +enum TypeConsensus +{ + TypeConsensus_Okay, /* Two typemaps are compatible */ + TypeConsensus_Undemotes, /* Not compatible now, but would be with pending undemotes. */ + TypeConsensus_Bad /* Typemaps are not compatible */ +}; + +#ifdef DEBUG +# define js_AbortRecording(cx, reason) js_AbortRecordingImpl(cx, reason) +#else +# define js_AbortRecording(cx, reason) js_AbortRecordingImpl(cx) +#endif + +class TraceRecorder +{ + /*************************************************************** Recording session constants */ + + /* The context in which recording started. */ + JSContext* const cx; + + /* Cached value of JS_TRACE_MONITOR(cx). */ + JSTraceMonitor* const traceMonitor; + + /* The Fragment being recorded by this recording session. */ + VMFragment* const fragment; + + /* The tree to which this |fragment| will belong when finished. */ + TreeInfo* const treeInfo; + + /* The reason we started recording. */ + RecordReason const recordReason; + + /* The global object from the start of recording until now. */ + JSObject* const globalObj; + + /* If non-null, the (pc of the) outer loop aborted to start recording this loop. */ + jsbytecode* const outer; + + /* If |outer|, the argc to use when looking up |outer| in the fragments table. */ + uint32 const outerArgc; + + /* The current frame's lexical block when recording started. */ + JSObject* const lexicalBlock; + + /* If non-null, the side exit from which we are growing. */ + VMSideExit* const anchor; + + /* The LIR-generation pipeline used to build |fragment|. */ + nanojit::LirWriter* const lir; + + /* Instructions yielding the corresponding trace-const members of InterpState. */ + nanojit::LIns* const cx_ins; + nanojit::LIns* const eos_ins; + nanojit::LIns* const eor_ins; + nanojit::LIns* const loopLabel; + + /* + * The LirBuffer used to supply memory to our LirWriter pipeline. Also contains the most recent + * instruction for {sp, rp, state}. Also contains names for debug JIT spew. Should be split. + */ + nanojit::LirBuffer* const lirbuf; + + /* + * Remembers traceAlloc state before recording started; automatically rewinds when mark is + * destroyed on a failed compilation. + */ + VMAllocator::Mark mark; + + /* Remembers the number of sideExits in treeInfo before recording started. */ + const unsigned numSideExitsBefore; + + /*********************************************************** Recording session mutable state */ + + /* Maps interpreter stack values to the instruction generating that value. */ + Tracker tracker; + + /* Maps interpreter stack values to the instruction writing back to the native stack. */ + Tracker nativeFrameTracker; + + /* The start of the global object's dslots we assume for the trackers. */ + jsval* global_dslots; + + /* The number of interpreted calls entered (and not yet left) since recording began. */ + unsigned callDepth; + + /* The current atom table, mirroring the interpreter loop's variable of the same name. */ + JSAtom** atoms; + + /* FIXME: Dead, but soon to be used for something or other. */ + Queue cfgMerges; + + /* Indicates whether the current tree should be trashed when the recording session ends. */ + bool trashSelf; + + /* A list of trees to trash at the end of the recording session. */ + Queue whichTreesToTrash; + + /***************************************** Temporal state hoisted into the recording session */ + + /* Carry the return value from a STOP/RETURN to the subsequent record_LeaveFrame. */ + nanojit::LIns* rval_ins; + + /* Carry the return value from a native call to the record_NativeCallComplete. */ + nanojit::LIns* native_rval_ins; + + /* Carry the return value of js_NewInstance to record_NativeCallComplete. */ + nanojit::LIns* newobj_ins; + + /* Carry the JSSpecializedNative used to generate a call to record_NativeCallComplete. */ + JSSpecializedNative* pendingSpecializedNative; + + /* Carry whether this is a jsval on the native stack from finishGetProp to monitorRecording. */ + jsval* pendingUnboxSlot; + + /* Carry a guard condition to the beginning of the next monitorRecording. */ + nanojit::LIns* pendingGuardCondition; + + /* Carry whether we have an always-exit from emitIf to checkTraceEnd. */ + bool pendingLoop; + + /* Temporary JSSpecializedNative used to describe non-specialized fast natives. */ + JSSpecializedNative generatedSpecializedNative; + + /* Temporary JSTraceType array used to construct temporary typemaps. */ + js::Vector tempTypeMap; + + /************************************************************* 10 bajillion member functions */ + + nanojit::LIns* insImmVal(jsval val); + nanojit::LIns* insImmObj(JSObject* obj); + nanojit::LIns* insImmFun(JSFunction* fun); + nanojit::LIns* insImmStr(JSString* str); + nanojit::LIns* insImmSprop(JSScopeProperty* sprop); + nanojit::LIns* p2i(nanojit::LIns* ins); + + /* + * Examines current interpreter state to record information suitable for returning to the + * interpreter through a side exit of the given type. + */ + JS_REQUIRES_STACK VMSideExit* snapshot(ExitType exitType); + + /* + * Creates a separate but identical copy of the given side exit, allowing the guards associated + * with each to be entirely separate even after subsequent patching. + */ + JS_REQUIRES_STACK VMSideExit* copy(VMSideExit* exit); + + /* + * Creates an instruction whose payload is a GuardRecord for the given exit. The instruction + * is suitable for use as the final argument of a single call to LirBuffer::insGuard; do not + * reuse the returned value. + */ + JS_REQUIRES_STACK nanojit::GuardRecord* createGuardRecord(VMSideExit* exit); + + bool isGlobal(jsval* p) const; + ptrdiff_t nativeGlobalOffset(jsval* p) const; + JS_REQUIRES_STACK ptrdiff_t nativeStackOffset(jsval* p) const; + JS_REQUIRES_STACK ptrdiff_t nativespOffset(jsval* p) const; + JS_REQUIRES_STACK void import(nanojit::LIns* base, ptrdiff_t offset, jsval* p, JSTraceType t, + const char *prefix, uintN index, JSStackFrame *fp); + JS_REQUIRES_STACK void import(TreeInfo* treeInfo, nanojit::LIns* sp, unsigned stackSlots, + unsigned callDepth, unsigned ngslots, JSTraceType* typeMap); + void trackNativeStackUse(unsigned slots); + + JS_REQUIRES_STACK bool isValidSlot(JSScope* scope, JSScopeProperty* sprop); + JS_REQUIRES_STACK bool lazilyImportGlobalSlot(unsigned slot); + + JS_REQUIRES_STACK void guard(bool expected, nanojit::LIns* cond, ExitType exitType); + JS_REQUIRES_STACK void guard(bool expected, nanojit::LIns* cond, VMSideExit* exit); + JS_REQUIRES_STACK nanojit::LIns* slurpInt32Slot(nanojit::LIns* val_ins, jsval* vp, + VMSideExit* exit); + JS_REQUIRES_STACK nanojit::LIns* slurpDoubleSlot(nanojit::LIns* val_ins, jsval* vp, + VMSideExit* exit); + JS_REQUIRES_STACK nanojit::LIns* slurpStringSlot(nanojit::LIns* val_ins, jsval* vp, + VMSideExit* exit); + JS_REQUIRES_STACK nanojit::LIns* slurpObjectSlot(nanojit::LIns* val_ins, jsval* vp, + VMSideExit* exit); + JS_REQUIRES_STACK nanojit::LIns* slurpFunctionSlot(nanojit::LIns* val_ins, jsval* vp, + VMSideExit* exit); + JS_REQUIRES_STACK nanojit::LIns* slurpNullSlot(nanojit::LIns* val_ins, jsval* vp, + VMSideExit* exit); + JS_REQUIRES_STACK nanojit::LIns* slurpBoolSlot(nanojit::LIns* val_ins, jsval* vp, + VMSideExit* exit); + JS_REQUIRES_STACK nanojit::LIns* slurpSlot(nanojit::LIns* val_ins, jsval* vp, + VMSideExit* exit); + JS_REQUIRES_STACK void slurpSlot(nanojit::LIns* val_ins, jsval* vp, SlurpInfo* info); + JS_REQUIRES_STACK AbortableRecordingStatus slurpDownFrames(jsbytecode* return_pc); + JS_REQUIRES_STACK AbortableRecordingStatus upRecursion(); + JS_REQUIRES_STACK AbortableRecordingStatus downRecursion(); + + nanojit::LIns* addName(nanojit::LIns* ins, const char* name); + + nanojit::LIns* writeBack(nanojit::LIns* i, nanojit::LIns* base, ptrdiff_t offset, + bool demote); + JS_REQUIRES_STACK void set(jsval* p, nanojit::LIns* l, bool initializing = false, + bool demote = true); + JS_REQUIRES_STACK nanojit::LIns* get(jsval* p); + JS_REQUIRES_STACK nanojit::LIns* addr(jsval* p); + + JS_REQUIRES_STACK bool known(jsval* p); + JS_REQUIRES_STACK void checkForGlobalObjectReallocation(); + + JS_REQUIRES_STACK TypeConsensus selfTypeStability(SlotMap& smap); + JS_REQUIRES_STACK TypeConsensus peerTypeStability(SlotMap& smap, const void* ip, + TreeFragment** peer); + + JS_REQUIRES_STACK jsval& argval(unsigned n) const; + JS_REQUIRES_STACK jsval& varval(unsigned n) const; + JS_REQUIRES_STACK jsval& stackval(int n) const; + + struct NameResult { + // |tracked| is true iff the result of the name lookup is a variable that + // is already in the tracker. The rest of the fields are set only if + // |tracked| is false. + bool tracked; + jsval v; // current property value + JSObject *obj; // Call object where name was found + nanojit::LIns *obj_ins; // LIR value for obj + JSScopeProperty *sprop; // sprop name was resolved to + }; + + JS_REQUIRES_STACK nanojit::LIns* scopeChain() const; + JS_REQUIRES_STACK JSStackFrame* frameIfInRange(JSObject* obj, unsigned* depthp = NULL) const; + JS_REQUIRES_STACK RecordingStatus traverseScopeChain(JSObject *obj, nanojit::LIns *obj_ins, JSObject *obj2, nanojit::LIns *&obj2_ins); + JS_REQUIRES_STACK AbortableRecordingStatus scopeChainProp(JSObject* obj, jsval*& vp, nanojit::LIns*& ins, NameResult& nr); + JS_REQUIRES_STACK RecordingStatus callProp(JSObject* obj, JSProperty* sprop, jsid id, jsval*& vp, nanojit::LIns*& ins, NameResult& nr); + + JS_REQUIRES_STACK nanojit::LIns* arg(unsigned n); + JS_REQUIRES_STACK void arg(unsigned n, nanojit::LIns* i); + JS_REQUIRES_STACK nanojit::LIns* var(unsigned n); + JS_REQUIRES_STACK void var(unsigned n, nanojit::LIns* i); + JS_REQUIRES_STACK nanojit::LIns* upvar(JSScript* script, JSUpvarArray* uva, uintN index, jsval& v); + nanojit::LIns* stackLoad(nanojit::LIns* addr, uint8 type); + JS_REQUIRES_STACK nanojit::LIns* stack(int n); + JS_REQUIRES_STACK void stack(int n, nanojit::LIns* i); + + JS_REQUIRES_STACK nanojit::LIns* alu(nanojit::LOpcode op, jsdouble v0, jsdouble v1, + nanojit::LIns* s0, nanojit::LIns* s1); + nanojit::LIns* f2i(nanojit::LIns* f); + JS_REQUIRES_STACK nanojit::LIns* makeNumberInt32(nanojit::LIns* f); + JS_REQUIRES_STACK nanojit::LIns* stringify(jsval& v); + + JS_REQUIRES_STACK nanojit::LIns* newArguments(); + + JS_REQUIRES_STACK RecordingStatus call_imacro(jsbytecode* imacro); + + JS_REQUIRES_STACK AbortableRecordingStatus ifop(); + JS_REQUIRES_STACK RecordingStatus switchop(); +#ifdef NANOJIT_IA32 + JS_REQUIRES_STACK AbortableRecordingStatus tableswitch(); +#endif + JS_REQUIRES_STACK RecordingStatus inc(jsval& v, jsint incr, bool pre = true); + JS_REQUIRES_STACK RecordingStatus inc(jsval v, nanojit::LIns*& v_ins, jsint incr, + bool pre = true); + JS_REQUIRES_STACK RecordingStatus incHelper(jsval v, nanojit::LIns* v_ins, + nanojit::LIns*& v_after, jsint incr); + JS_REQUIRES_STACK AbortableRecordingStatus incProp(jsint incr, bool pre = true); + JS_REQUIRES_STACK RecordingStatus incElem(jsint incr, bool pre = true); + JS_REQUIRES_STACK AbortableRecordingStatus incName(jsint incr, bool pre = true); + + JS_REQUIRES_STACK void strictEquality(bool equal, bool cmpCase); + JS_REQUIRES_STACK AbortableRecordingStatus equality(bool negate, bool tryBranchAfterCond); + JS_REQUIRES_STACK AbortableRecordingStatus equalityHelper(jsval l, jsval r, + nanojit::LIns* l_ins, nanojit::LIns* r_ins, + bool negate, bool tryBranchAfterCond, + jsval& rval); + JS_REQUIRES_STACK AbortableRecordingStatus relational(nanojit::LOpcode op, bool tryBranchAfterCond); + + JS_REQUIRES_STACK RecordingStatus unary(nanojit::LOpcode op); + JS_REQUIRES_STACK RecordingStatus binary(nanojit::LOpcode op); + + JS_REQUIRES_STACK RecordingStatus guardShape(nanojit::LIns* obj_ins, JSObject* obj, + uint32 shape, const char* name, + nanojit::LIns* map_ins, VMSideExit* exit); + + JSDHashTable guardedShapeTable; + +#if defined DEBUG_notme && defined XP_UNIX + void dumpGuardedShapes(const char* prefix); +#endif + + void forgetGuardedShapes(); + + inline nanojit::LIns* map(nanojit::LIns *obj_ins); + JS_REQUIRES_STACK bool map_is_native(JSObjectMap* map, nanojit::LIns* map_ins, + nanojit::LIns*& ops_ins, size_t op_offset = 0); + JS_REQUIRES_STACK AbortableRecordingStatus test_property_cache(JSObject* obj, nanojit::LIns* obj_ins, + JSObject*& obj2, jsuword& pcval); + JS_REQUIRES_STACK RecordingStatus guardNativePropertyOp(JSObject* aobj, + nanojit::LIns* map_ins); + JS_REQUIRES_STACK RecordingStatus guardPropertyCacheHit(nanojit::LIns* obj_ins, + nanojit::LIns* map_ins, + JSObject* aobj, + JSObject* obj2, + JSPropCacheEntry* entry, + jsuword& pcval); + + void stobj_set_fslot(nanojit::LIns *obj_ins, unsigned slot, + nanojit::LIns* v_ins); + void stobj_set_dslot(nanojit::LIns *obj_ins, unsigned slot, nanojit::LIns*& dslots_ins, + nanojit::LIns* v_ins); + void stobj_set_slot(nanojit::LIns* obj_ins, unsigned slot, nanojit::LIns*& dslots_ins, + nanojit::LIns* v_ins); + + nanojit::LIns* stobj_get_fslot(nanojit::LIns* obj_ins, unsigned slot); + nanojit::LIns* stobj_get_dslot(nanojit::LIns* obj_ins, unsigned index, + nanojit::LIns*& dslots_ins); + nanojit::LIns* stobj_get_slot(nanojit::LIns* obj_ins, unsigned slot, + nanojit::LIns*& dslots_ins); + + nanojit::LIns* stobj_get_private(nanojit::LIns* obj_ins) { + return stobj_get_fslot(obj_ins, JSSLOT_PRIVATE); + } + + nanojit::LIns* stobj_get_proto(nanojit::LIns* obj_ins) { + return stobj_get_fslot(obj_ins, JSSLOT_PROTO); + } + + nanojit::LIns* stobj_get_parent(nanojit::LIns* obj_ins) { + return stobj_get_fslot(obj_ins, JSSLOT_PARENT); + } + + nanojit::LIns* getStringLength(nanojit::LIns* str_ins); + + JS_REQUIRES_STACK AbortableRecordingStatus name(jsval*& vp, nanojit::LIns*& ins, NameResult& nr); + JS_REQUIRES_STACK AbortableRecordingStatus prop(JSObject* obj, nanojit::LIns* obj_ins, uint32 *slotp, + nanojit::LIns** v_insp, jsval* outp); + JS_REQUIRES_STACK RecordingStatus denseArrayElement(jsval& oval, jsval& idx, jsval*& vp, + nanojit::LIns*& v_ins, + nanojit::LIns*& addr_ins); + JS_REQUIRES_STACK AbortableRecordingStatus getProp(JSObject* obj, nanojit::LIns* obj_ins); + JS_REQUIRES_STACK AbortableRecordingStatus getProp(jsval& v); + JS_REQUIRES_STACK RecordingStatus getThis(nanojit::LIns*& this_ins); + + JS_REQUIRES_STACK VMSideExit* enterDeepBailCall(); + JS_REQUIRES_STACK void leaveDeepBailCall(); + + JS_REQUIRES_STACK RecordingStatus primitiveToStringInPlace(jsval* vp); + JS_REQUIRES_STACK void finishGetProp(nanojit::LIns* obj_ins, nanojit::LIns* vp_ins, + nanojit::LIns* ok_ins, jsval* outp); + JS_REQUIRES_STACK RecordingStatus getPropertyByName(nanojit::LIns* obj_ins, jsval* idvalp, + jsval* outp); + JS_REQUIRES_STACK RecordingStatus getPropertyByIndex(nanojit::LIns* obj_ins, + nanojit::LIns* index_ins, jsval* outp); + JS_REQUIRES_STACK RecordingStatus getPropertyById(nanojit::LIns* obj_ins, jsval* outp); + JS_REQUIRES_STACK RecordingStatus getPropertyWithNativeGetter(nanojit::LIns* obj_ins, + JSScopeProperty* sprop, + jsval* outp); + + JS_REQUIRES_STACK RecordingStatus nativeSet(JSObject* obj, nanojit::LIns* obj_ins, + JSScopeProperty* sprop, + jsval v, nanojit::LIns* v_ins); + JS_REQUIRES_STACK RecordingStatus setProp(jsval &l, JSPropCacheEntry* entry, + JSScopeProperty* sprop, + jsval &v, nanojit::LIns*& v_ins); + JS_REQUIRES_STACK RecordingStatus setCallProp(JSObject *callobj, nanojit::LIns *callobj_ins, + JSScopeProperty *sprop, nanojit::LIns *v_ins, + jsval v); + JS_REQUIRES_STACK RecordingStatus initOrSetPropertyByName(nanojit::LIns* obj_ins, + jsval* idvalp, jsval* rvalp, + bool init); + JS_REQUIRES_STACK RecordingStatus initOrSetPropertyByIndex(nanojit::LIns* obj_ins, + nanojit::LIns* index_ins, + jsval* rvalp, bool init); + + JS_REQUIRES_STACK nanojit::LIns* box_jsval(jsval v, nanojit::LIns* v_ins); + JS_REQUIRES_STACK nanojit::LIns* unbox_jsval(jsval v, nanojit::LIns* v_ins, VMSideExit* exit); + JS_REQUIRES_STACK bool guardClass(JSObject* obj, nanojit::LIns* obj_ins, JSClass* clasp, + VMSideExit* exit); + JS_REQUIRES_STACK bool guardDenseArray(JSObject* obj, nanojit::LIns* obj_ins, + ExitType exitType = MISMATCH_EXIT); + JS_REQUIRES_STACK bool guardDenseArray(JSObject* obj, nanojit::LIns* obj_ins, + VMSideExit* exit); + JS_REQUIRES_STACK bool guardHasPrototype(JSObject* obj, nanojit::LIns* obj_ins, + JSObject** pobj, nanojit::LIns** pobj_ins, + VMSideExit* exit); + JS_REQUIRES_STACK RecordingStatus guardPrototypeHasNoIndexedProperties(JSObject* obj, + nanojit::LIns* obj_ins, + ExitType exitType); + JS_REQUIRES_STACK RecordingStatus guardNotGlobalObject(JSObject* obj, + nanojit::LIns* obj_ins); + void clearFrameSlotsFromCache(); + JS_REQUIRES_STACK void putArguments(); + JS_REQUIRES_STACK RecordingStatus guardCallee(jsval& callee); + JS_REQUIRES_STACK JSStackFrame *guardArguments(JSObject *obj, nanojit::LIns* obj_ins, + unsigned *depthp); + JS_REQUIRES_STACK RecordingStatus getClassPrototype(JSObject* ctor, + nanojit::LIns*& proto_ins); + JS_REQUIRES_STACK RecordingStatus getClassPrototype(JSProtoKey key, + nanojit::LIns*& proto_ins); + JS_REQUIRES_STACK RecordingStatus newArray(JSObject* ctor, uint32 argc, jsval* argv, + jsval* rval); + JS_REQUIRES_STACK RecordingStatus newString(JSObject* ctor, uint32 argc, jsval* argv, + jsval* rval); + JS_REQUIRES_STACK RecordingStatus interpretedFunctionCall(jsval& fval, JSFunction* fun, + uintN argc, bool constructing); + JS_REQUIRES_STACK void propagateFailureToBuiltinStatus(nanojit::LIns *ok_ins, + nanojit::LIns *&status_ins); + JS_REQUIRES_STACK RecordingStatus emitNativeCall(JSSpecializedNative* sn, uintN argc, + nanojit::LIns* args[], bool rooted); + JS_REQUIRES_STACK void emitNativePropertyOp(JSScope* scope, + JSScopeProperty* sprop, + nanojit::LIns* obj_ins, + bool setflag, + nanojit::LIns* boxed_ins); + JS_REQUIRES_STACK RecordingStatus callSpecializedNative(JSNativeTraceInfo* trcinfo, uintN argc, + bool constructing); + JS_REQUIRES_STACK RecordingStatus callNative(uintN argc, JSOp mode); + JS_REQUIRES_STACK RecordingStatus functionCall(uintN argc, JSOp mode); + + JS_REQUIRES_STACK void trackCfgMerges(jsbytecode* pc); + JS_REQUIRES_STACK void emitIf(jsbytecode* pc, bool cond, nanojit::LIns* x); + JS_REQUIRES_STACK void fuseIf(jsbytecode* pc, bool cond, nanojit::LIns* x); + JS_REQUIRES_STACK AbortableRecordingStatus checkTraceEnd(jsbytecode* pc); + + bool hasMethod(JSObject* obj, jsid id); + JS_REQUIRES_STACK bool hasIteratorMethod(JSObject* obj); + + JS_REQUIRES_STACK jsatomid getFullIndex(ptrdiff_t pcoff = 0); + + JS_REQUIRES_STACK JSTraceType determineSlotType(jsval* vp); + + JS_REQUIRES_STACK AbortableRecordingStatus compile(); + JS_REQUIRES_STACK AbortableRecordingStatus closeLoop(); + JS_REQUIRES_STACK AbortableRecordingStatus closeLoop(VMSideExit* exit); + JS_REQUIRES_STACK AbortableRecordingStatus closeLoop(SlotMap& slotMap, VMSideExit* exit); + JS_REQUIRES_STACK AbortableRecordingStatus endLoop(); + JS_REQUIRES_STACK AbortableRecordingStatus endLoop(VMSideExit* exit); + JS_REQUIRES_STACK void joinEdgesToEntry(TreeFragment* peer_root); + JS_REQUIRES_STACK void adjustCallerTypes(TreeFragment* f); + JS_REQUIRES_STACK void prepareTreeCall(TreeFragment* inner, nanojit::LIns*& inner_sp_ins); + JS_REQUIRES_STACK void emitTreeCall(TreeFragment* inner, VMSideExit* exit, nanojit::LIns* inner_sp_ins); + JS_REQUIRES_STACK void determineGlobalTypes(JSTraceType* typeMap); + JS_REQUIRES_STACK VMSideExit* downSnapshot(FrameInfo* downFrame); + JS_REQUIRES_STACK TreeFragment* findNestedCompatiblePeer(TreeFragment* f); + JS_REQUIRES_STACK AbortableRecordingStatus attemptTreeCall(TreeFragment* inner, + uintN& inlineCallCount); + + static JS_REQUIRES_STACK bool recordLoopEdge(JSContext* cx, TraceRecorder* r, + uintN& inlineCallCount); + + /* Allocators associated with this recording session. */ + VMAllocator& tempAlloc() const { return *traceMonitor->tempAlloc; } + VMAllocator& traceAlloc() const { return *traceMonitor->traceAlloc; } + VMAllocator& dataAlloc() const { return *traceMonitor->dataAlloc; } + + /* Member declarations for each opcode, to be called before interpreting the opcode. */ +#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ + JS_REQUIRES_STACK AbortableRecordingStatus record_##op(); +# include "jsopcode.tbl" +#undef OPDEF + + inline void* operator new(size_t size) { return calloc(1, size); } + inline void operator delete(void *p) { free(p); } + + JS_REQUIRES_STACK + TraceRecorder(JSContext* cx, VMSideExit*, VMFragment*, TreeInfo*, + unsigned stackSlots, unsigned ngslots, JSTraceType* typeMap, + VMSideExit* expectedInnerExit, jsbytecode* outerTree, + uint32 outerArgc, RecordReason reason); + + /* The destructor should only be called through finish*, not directly. */ + ~TraceRecorder(); + JS_REQUIRES_STACK AbortableRecordingStatus finishSuccessfully(); + JS_REQUIRES_STACK AbortableRecordingStatus finishAbort(const char* reason); + + friend class ImportBoxedStackSlotVisitor; + friend class ImportUnboxedStackSlotVisitor; + friend class ImportGlobalSlotVisitor; + friend class AdjustCallerGlobalTypesVisitor; + friend class AdjustCallerStackTypesVisitor; + friend class TypeCompatibilityVisitor; + friend class SlotMap; + friend class DefaultSlotMap; + friend class DetermineTypesVisitor; + friend class RecursiveSlotMap; + friend class UpRecursiveSlotMap; + friend jsval* js_ConcatPostImacroStackCleanup(uint32, JSFrameRegs &, TraceRecorder *); + friend bool js_MonitorLoopEdge(JSContext*, uintN&, RecordReason); + friend void js_AbortRecording(JSContext*, const char*); + +public: + static bool JS_REQUIRES_STACK + startRecorder(JSContext*, VMSideExit*, VMFragment*, TreeInfo*, + unsigned stackSlots, unsigned ngslots, JSTraceType* typeMap, + VMSideExit* expectedInnerExit, jsbytecode* outerTree, + uint32 outerArgc, RecordReason reason); + + /* Accessors. */ + VMFragment* getFragment() const { return fragment; } + TreeInfo* getTreeInfo() const { return treeInfo; } + bool outOfMemory() const { return traceMonitor->outOfMemory(); } + + /* Entry points / callbacks from the interpreter. */ + JS_REQUIRES_STACK AbortableRecordingStatus monitorRecording(JSOp op); + JS_REQUIRES_STACK AbortableRecordingStatus record_EnterFrame(uintN& inlineCallCount); + JS_REQUIRES_STACK AbortableRecordingStatus record_LeaveFrame(); + JS_REQUIRES_STACK AbortableRecordingStatus record_SetPropHit(JSPropCacheEntry* entry, + JSScopeProperty* sprop); + JS_REQUIRES_STACK AbortableRecordingStatus record_DefLocalFunSetSlot(uint32 slot, JSObject* obj); + JS_REQUIRES_STACK AbortableRecordingStatus record_NativeCallComplete(); + void forgetGuardedShapesForObject(JSObject* obj); + +#ifdef DEBUG + /* Debug printing functionality to emit printf() on trace. */ + JS_REQUIRES_STACK void tprint(const char *format, int count, nanojit::LIns *insa[]); + JS_REQUIRES_STACK void tprint(const char *format); + JS_REQUIRES_STACK void tprint(const char *format, nanojit::LIns *ins); + JS_REQUIRES_STACK void tprint(const char *format, nanojit::LIns *ins1, + nanojit::LIns *ins2); + JS_REQUIRES_STACK void tprint(const char *format, nanojit::LIns *ins1, + nanojit::LIns *ins2, nanojit::LIns *ins3); + JS_REQUIRES_STACK void tprint(const char *format, nanojit::LIns *ins1, + nanojit::LIns *ins2, nanojit::LIns *ins3, + nanojit::LIns *ins4); + JS_REQUIRES_STACK void tprint(const char *format, nanojit::LIns *ins1, + nanojit::LIns *ins2, nanojit::LIns *ins3, + nanojit::LIns *ins4, nanojit::LIns *ins5); + JS_REQUIRES_STACK void tprint(const char *format, nanojit::LIns *ins1, + nanojit::LIns *ins2, nanojit::LIns *ins3, + nanojit::LIns *ins4, nanojit::LIns *ins5, + nanojit::LIns *ins6); +#endif +}; + +#define TRACING_ENABLED(cx) ((cx)->jitEnabled) +#define TRACE_RECORDER(cx) (JS_TRACE_MONITOR(cx).recorder) +#define SET_TRACE_RECORDER(cx,tr) (JS_TRACE_MONITOR(cx).recorder = (tr)) + +#define JSOP_IN_RANGE(op,lo,hi) (uintN((op) - (lo)) <= uintN((hi) - (lo))) +#define JSOP_IS_BINARY(op) JSOP_IN_RANGE(op, JSOP_BITOR, JSOP_MOD) +#define JSOP_IS_UNARY(op) JSOP_IN_RANGE(op, JSOP_NEG, JSOP_POS) +#define JSOP_IS_EQUALITY(op) JSOP_IN_RANGE(op, JSOP_EQ, JSOP_NE) + +#define TRACE_ARGS_(x,args) \ + JS_BEGIN_MACRO \ + if (TraceRecorder* tr_ = TRACE_RECORDER(cx)) { \ + AbortableRecordingStatus status = tr_->record_##x args; \ + if (StatusAbortsRecording(status)) { \ + js_AbortRecording(cx, #x); \ + if (status == ARECORD_ERROR) \ + goto error; \ + } \ + JS_ASSERT(status != ARECORD_IMACRO); \ + } \ + JS_END_MACRO + +#define TRACE_ARGS(x,args) TRACE_ARGS_(x, args) +#define TRACE_0(x) TRACE_ARGS(x, ()) +#define TRACE_1(x,a) TRACE_ARGS(x, (a)) +#define TRACE_2(x,a,b) TRACE_ARGS(x, (a, b)) + +extern JS_REQUIRES_STACK bool +js_MonitorLoopEdge(JSContext* cx, uintN& inlineCallCount, RecordReason reason); + +extern JS_REQUIRES_STACK void +js_AbortRecording(JSContext* cx, const char* reason); + +extern void +js_InitJIT(JSTraceMonitor *tm); + +extern void +js_FinishJIT(JSTraceMonitor *tm); + +extern void +js_PurgeScriptFragments(JSContext* cx, JSScript* script); + +extern bool +js_OverfullJITCache(JSTraceMonitor* tm); + +extern void +js_ResetJIT(JSContext* cx); + +extern void +js_PurgeJITOracle(); + +extern JSObject * +js_GetBuiltinFunction(JSContext *cx, uintN index); + +extern void +js_SetMaxCodeCacheBytes(JSContext* cx, uint32 bytes); + +extern bool +js_NativeToValue(JSContext* cx, jsval& v, JSTraceType type, double* slot); + +#ifdef MOZ_TRACEVIS + +extern JS_FRIEND_API(bool) +JS_StartTraceVis(const char* filename); + +extern JS_FRIEND_API(JSBool) +js_StartTraceVis(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +extern JS_FRIEND_API(bool) +JS_StopTraceVis(); + +extern JS_FRIEND_API(JSBool) +js_StopTraceVis(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +/* Must contain no more than 16 items. */ +enum TraceVisState { + // Special: means we returned from current activity to last + S_EXITLAST, + // Activities + S_INTERP, + S_MONITOR, + S_RECORD, + S_COMPILE, + S_EXECUTE, + S_NATIVE, + // Events: these all have (bit 3) == 1. + S_RESET = 8 +}; + +/* Reason for an exit to the interpreter. */ +enum TraceVisExitReason { + R_NONE, + R_ABORT, + /* Reasons in js_MonitorLoopEdge */ + R_INNER_SIDE_EXIT, + R_DOUBLES, + R_CALLBACK_PENDING, + R_OOM_GETANCHOR, + R_BACKED_OFF, + R_COLD, + R_FAIL_RECORD_TREE, + R_MAX_PEERS, + R_FAIL_EXECUTE_TREE, + R_FAIL_STABILIZE, + R_FAIL_EXTEND_FLUSH, + R_FAIL_EXTEND_MAX_BRANCHES, + R_FAIL_EXTEND_START, + R_FAIL_EXTEND_COLD, + R_NO_EXTEND_OUTER, + R_MISMATCH_EXIT, + R_OOM_EXIT, + R_TIMEOUT_EXIT, + R_DEEP_BAIL_EXIT, + R_STATUS_EXIT, + R_OTHER_EXIT +}; + +enum TraceVisFlushReason { + FR_DEEP_BAIL, + FR_OOM, + FR_GLOBAL_SHAPE_MISMATCH, + FR_GLOBALS_FULL +}; + +const unsigned long long MS64_MASK = 0xfull << 60; +const unsigned long long MR64_MASK = 0x1full << 55; +const unsigned long long MT64_MASK = ~(MS64_MASK | MR64_MASK); + +extern FILE* traceVisLogFile; +extern JSHashTable *traceVisScriptTable; + +extern JS_FRIEND_API(void) +js_StoreTraceVisState(JSContext *cx, TraceVisState s, TraceVisExitReason r); + +static inline void +js_LogTraceVisState(JSContext *cx, TraceVisState s, TraceVisExitReason r) +{ + if (traceVisLogFile) { + unsigned long long sllu = s; + unsigned long long rllu = r; + unsigned long long d = (sllu << 60) | (rllu << 55) | (rdtsc() & MT64_MASK); + fwrite(&d, sizeof(d), 1, traceVisLogFile); + } + if (traceVisScriptTable) { + js_StoreTraceVisState(cx, s, r); + } +} + +/* + * Although this runs the same code as js_LogTraceVisState, it is a separate + * function because the meaning of the log entry is different. Also, the entry + * formats may diverge someday. + */ +static inline void +js_LogTraceVisEvent(JSContext *cx, TraceVisState s, TraceVisFlushReason r) +{ + js_LogTraceVisState(cx, s, (TraceVisExitReason) r); +} + +static inline void +js_EnterTraceVisState(JSContext *cx, TraceVisState s, TraceVisExitReason r) +{ + js_LogTraceVisState(cx, s, r); +} + +static inline void +js_ExitTraceVisState(JSContext *cx, TraceVisExitReason r) +{ + js_LogTraceVisState(cx, S_EXITLAST, r); +} + +struct TraceVisStateObj { + TraceVisExitReason r; + JSContext *mCx; + + inline TraceVisStateObj(JSContext *cx, TraceVisState s) : r(R_NONE) + { + js_EnterTraceVisState(cx, s, R_NONE); + mCx = cx; + } + inline ~TraceVisStateObj() + { + js_ExitTraceVisState(mCx, r); + } +}; + +#endif /* MOZ_TRACEVIS */ + +extern jsval * +js_ConcatPostImacroStackCleanup(uint32 argc, JSFrameRegs ®s, + TraceRecorder *recorder); + +#else /* !JS_TRACER */ + +#define TRACE_0(x) ((void)0) +#define TRACE_1(x,a) ((void)0) +#define TRACE_2(x,a,b) ((void)0) + +#endif /* !JS_TRACER */ + +#endif /* jstracer_h___ */ diff --git a/ape-server/deps/js/src/jstypes.h b/ape-server/deps/js/src/jstypes.h new file mode 100755 index 0000000..f6b49f4 --- /dev/null +++ b/ape-server/deps/js/src/jstypes.h @@ -0,0 +1,469 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * IBM Corp. + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* +** File: jstypes.h +** Description: Definitions of NSPR's basic types +** +** Prototypes and macros used to make up for deficiencies in ANSI environments +** that we have found. +** +** Since we do not wrap and all the other standard headers, authors +** of portable code will not know in general that they need these definitions. +** Instead of requiring these authors to find the dependent uses in their code +** and take the following steps only in those C files, we take steps once here +** for all C files. +**/ + +#ifndef jstypes_h___ +#define jstypes_h___ + +#include +#include "js-config.h" + +/*********************************************************************** +** MACROS: JS_EXTERN_API +** JS_EXPORT_API +** DESCRIPTION: +** These are only for externally visible routines and globals. For +** internal routines, just use "extern" for type checking and that +** will not export internal cross-file or forward-declared symbols. +** Define a macro for declaring procedures return types. We use this to +** deal with windoze specific type hackery for DLL definitions. Use +** JS_EXTERN_API when the prototype for the method is declared. Use +** JS_EXPORT_API for the implementation of the method. +** +** Example: +** in dowhim.h +** JS_EXTERN_API( void ) DoWhatIMean( void ); +** in dowhim.c +** JS_EXPORT_API( void ) DoWhatIMean( void ) { return; } +** +** +***********************************************************************/ +#ifdef WIN32 + +/* These also work for __MWERKS__ */ +# define JS_EXTERN_API(__type) extern __declspec(dllexport) __type +# define JS_EXPORT_API(__type) __declspec(dllexport) __type +# define JS_EXTERN_DATA(__type) extern __declspec(dllexport) __type +# define JS_EXPORT_DATA(__type) __declspec(dllexport) __type + +#elif defined(XP_OS2) && defined(__declspec) + +# define JS_EXTERN_API(__type) extern __declspec(dllexport) __type +# define JS_EXPORT_API(__type) __declspec(dllexport) __type +# define JS_EXTERN_DATA(__type) extern __declspec(dllexport) __type +# define JS_EXPORT_DATA(__type) __declspec(dllexport) __type + +#elif defined(__SYMBIAN32__) + +# define JS_EXTERN_API(__type) extern EXPORT_C __type +# define JS_EXPORT_API(__type) EXPORT_C __type +# define JS_EXTERN_DATA(__type) extern EXPORT_C __type +# define JS_EXPORT_DATA(__type) EXPORT_C __type + +#else /* Unix */ + +# ifdef HAVE_VISIBILITY_ATTRIBUTE +# define JS_EXTERNAL_VIS __attribute__((visibility ("default"))) +# elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) +# define JS_EXTERNAL_VIS __global +# else +# define JS_EXTERNAL_VIS +# endif + +# define JS_EXTERN_API(__type) extern JS_EXTERNAL_VIS __type +# define JS_EXPORT_API(__type) JS_EXTERNAL_VIS __type +# define JS_EXTERN_DATA(__type) extern JS_EXTERNAL_VIS __type +# define JS_EXPORT_DATA(__type) JS_EXTERNAL_VIS __type + +#endif + +#ifdef _WIN32 +# if defined(__MWERKS__) || defined(__GNUC__) +# define JS_IMPORT_API(__x) __x +# else +# define JS_IMPORT_API(__x) __declspec(dllimport) __x +# endif +#elif defined(XP_OS2) && defined(__declspec) +# define JS_IMPORT_API(__x) __declspec(dllimport) __x +#elif defined(__SYMBIAN32__) +# define JS_IMPORT_API(__x) IMPORT_C __x +#else +# define JS_IMPORT_API(__x) JS_EXPORT_API (__x) +#endif + +#if defined(_WIN32) && !defined(__MWERKS__) +# define JS_IMPORT_DATA(__x) __declspec(dllimport) __x +#elif defined(XP_OS2) && defined(__declspec) +# define JS_IMPORT_DATA(__x) __declspec(dllimport) __x +#elif defined(__SYMBIAN32__) +# if defined(__CW32__) +# define JS_IMPORT_DATA(__x) __declspec(dllimport) __x +# else +# define JS_IMPORT_DATA(__x) IMPORT_C __x +# endif +#else +# define JS_IMPORT_DATA(__x) JS_EXPORT_DATA (__x) +#endif + +/* + * The linkage of JS API functions differs depending on whether the file is + * used within the JS library or not. Any source file within the JS + * interpreter should define EXPORT_JS_API whereas any client of the library + * should not. STATIC_JS_API is used to build JS as a static library. + */ +#if defined(STATIC_JS_API) + +# define JS_PUBLIC_API(t) t +# define JS_PUBLIC_DATA(t) t + +#elif defined(EXPORT_JS_API) + +# define JS_PUBLIC_API(t) JS_EXPORT_API(t) +# define JS_PUBLIC_DATA(t) JS_EXPORT_DATA(t) + +#else + +# define JS_PUBLIC_API(t) JS_IMPORT_API(t) +# define JS_PUBLIC_DATA(t) JS_IMPORT_DATA(t) + +#endif + +#define JS_FRIEND_API(t) JS_PUBLIC_API(t) +#define JS_FRIEND_DATA(t) JS_PUBLIC_DATA(t) + +#if defined(_MSC_VER) && defined(_M_IX86) +#define JS_FASTCALL __fastcall +#elif defined(__GNUC__) && defined(__i386__) && \ + ((__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) +#define JS_FASTCALL __attribute__((fastcall)) +#else +#define JS_FASTCALL +#define JS_NO_FASTCALL +#endif + +#ifndef JS_INLINE +# if defined __cplusplus +# define JS_INLINE inline +# elif defined _MSC_VER +# define JS_INLINE __inline +# elif defined __GNUC__ +# define JS_INLINE __inline__ +# else +# define JS_INLINE inline +# endif +#endif + +#ifndef JS_ALWAYS_INLINE +# if defined DEBUG +# define JS_ALWAYS_INLINE JS_INLINE +# elif defined _MSC_VER +# define JS_ALWAYS_INLINE __forceinline +# elif defined __GNUC__ +# define JS_ALWAYS_INLINE __attribute__((always_inline)) JS_INLINE +# else +# define JS_ALWAYS_INLINE JS_INLINE +# endif +#endif + +#ifdef NS_STATIC_CHECKING +/* + * Attributes for static analysis. Functions declared with JS_REQUIRES_STACK + * always have a valid cx->fp and can access it freely. Other functions can + * access cx->fp only after calling a function that "forces" the stack + * (i.e. lazily instantiates it as needed). + */ +# define JS_REQUIRES_STACK __attribute__((user("JS_REQUIRES_STACK"))) +# define JS_FORCES_STACK __attribute__((user("JS_FORCES_STACK"))) +/* + * Skip the JS_REQUIRES_STACK analysis within functions with this annotation. + */ +# define JS_IGNORE_STACK __attribute__((user("JS_IGNORE_STACK"))) +#else +# define JS_REQUIRES_STACK +# define JS_FORCES_STACK +# define JS_IGNORE_STACK +#endif + +/*********************************************************************** +** MACROS: JS_BEGIN_MACRO +** JS_END_MACRO +** DESCRIPTION: +** Macro body brackets so that macros with compound statement definitions +** behave syntactically more like functions when called. +***********************************************************************/ +#define JS_BEGIN_MACRO do { + +#if defined(_MSC_VER) && _MSC_VER >= 1400 +# define JS_END_MACRO \ + } __pragma(warning(push)) __pragma(warning(disable:4127)) \ + while (0) __pragma(warning(pop)) +#else +# define JS_END_MACRO } while (0) +#endif + +/*********************************************************************** +** MACROS: JS_BEGIN_EXTERN_C +** JS_END_EXTERN_C +** DESCRIPTION: +** Macro shorthands for conditional C++ extern block delimiters. +***********************************************************************/ +#ifdef __cplusplus + +# define JS_BEGIN_EXTERN_C extern "C" { +# define JS_END_EXTERN_C } + +#else + +# define JS_BEGIN_EXTERN_C +# define JS_END_EXTERN_C + +#endif + +/*********************************************************************** +** MACROS: JS_BIT +** JS_BITMASK +** DESCRIPTION: +** Bit masking macros. XXX n must be <= 31 to be portable +***********************************************************************/ +#define JS_BIT(n) ((JSUint32)1 << (n)) +#define JS_BITMASK(n) (JS_BIT(n) - 1) + +/*********************************************************************** +** MACROS: JS_PTR_TO_INT32 +** JS_PTR_TO_UINT32 +** JS_INT32_TO_PTR +** JS_UINT32_TO_PTR +** DESCRIPTION: +** Integer to pointer and pointer to integer conversion macros. +***********************************************************************/ +#define JS_PTR_TO_INT32(x) ((jsint)((char *)(x) - (char *)0)) +#define JS_PTR_TO_UINT32(x) ((jsuint)((char *)(x) - (char *)0)) +#define JS_INT32_TO_PTR(x) ((void *)((char *)0 + (jsint)(x))) +#define JS_UINT32_TO_PTR(x) ((void *)((char *)0 + (jsuint)(x))) + +/*********************************************************************** +** MACROS: JS_HOWMANY +** JS_ROUNDUP +** JS_MIN +** JS_MAX +** DESCRIPTION: +** Commonly used macros for operations on compatible types. +***********************************************************************/ +#define JS_HOWMANY(x,y) (((x)+(y)-1)/(y)) +#define JS_ROUNDUP(x,y) (JS_HOWMANY(x,y)*(y)) +#define JS_MIN(x,y) ((x)<(y)?(x):(y)) +#define JS_MAX(x,y) ((x)>(y)?(x):(y)) + +#ifdef _MSC_VER +# include "jscpucfg.h" /* We can't auto-detect MSVC configuration */ +# if _MSC_VER < 1400 +# define NJ_NO_VARIADIC_MACROS +# endif +#else +# include "jsautocfg.h" /* Use auto-detected configuration */ +#endif + +#include "jsinttypes.h" + +JS_BEGIN_EXTERN_C + +/************************************************************************ +** TYPES: JSUintn +** JSIntn +** DESCRIPTION: +** The JSIntn types are most appropriate for automatic variables. They are +** guaranteed to be at least 16 bits, though various architectures may +** define them to be wider (e.g., 32 or even 64 bits). These types are +** never valid for fields of a structure. +************************************************************************/ + +typedef int JSIntn; +typedef unsigned int JSUintn; + +/************************************************************************ +** TYPES: JSFloat64 +** DESCRIPTION: +** NSPR's floating point type is always 64 bits. +************************************************************************/ +typedef double JSFloat64; + +/************************************************************************ +** TYPES: JSSize +** DESCRIPTION: +** A type for representing the size of objects. +************************************************************************/ +typedef size_t JSSize; + +/************************************************************************ +** TYPES: JSPtrDiff +** DESCRIPTION: +** A type for pointer difference. Variables of this type are suitable +** for storing a pointer or pointer sutraction. +************************************************************************/ +typedef ptrdiff_t JSPtrdiff; + +/************************************************************************ +** TYPES: JSUptrdiff +** DESCRIPTION: +** A type for pointer difference. Variables of this type are suitable +** for storing a pointer or pointer sutraction. +************************************************************************/ +typedef JSUintPtr JSUptrdiff; + +/************************************************************************ +** TYPES: JSBool +** DESCRIPTION: +** Use JSBool for variables and parameter types. Use JS_FALSE and JS_TRUE +** for clarity of target type in assignments and actual arguments. Use +** 'if (bool)', 'while (!bool)', '(bool) ? x : y' etc., to test booleans +** just as you would C int-valued conditions. +************************************************************************/ +typedef JSIntn JSBool; +#define JS_TRUE (JSIntn)1 +#define JS_FALSE (JSIntn)0 + +/************************************************************************ +** TYPES: JSPackedBool +** DESCRIPTION: +** Use JSPackedBool within structs where bitfields are not desireable +** but minimum and consistent overhead matters. +************************************************************************/ +typedef JSUint8 JSPackedBool; + +/* +** A JSWord is an integer that is the same size as a void* +*/ +typedef JSIntPtr JSWord; +typedef JSUintPtr JSUword; + +#include "jsotypes.h" + +/*********************************************************************** +** MACROS: JS_LIKELY +** JS_UNLIKELY +** DESCRIPTION: +** These macros allow you to give a hint to the compiler about branch +** probability so that it can better optimize. Use them like this: +** +** if (JS_LIKELY(v == 1)) { +** ... expected code path ... +** } +** +** if (JS_UNLIKELY(v == 0)) { +** ... non-expected code path ... +** } +** +***********************************************************************/ +#if defined(__GNUC__) && (__GNUC__ > 2) + +# define JS_LIKELY(x) (__builtin_expect((x), 1)) +# define JS_UNLIKELY(x) (__builtin_expect((x), 0)) + +#else + +# define JS_LIKELY(x) (x) +# define JS_UNLIKELY(x) (x) + +#endif + +/*********************************************************************** +** MACROS: JS_ARRAY_LENGTH +** JS_ARRAY_END +** DESCRIPTION: +** Macros to get the number of elements and the pointer to one past the +** last element of a C array. Use them like this: +** +** jschar buf[10], *s; +** JSString *str; +** ... +** for (s = buf; s != JS_ARRAY_END(buf); ++s) *s = ...; +** ... +** str = JS_NewStringCopyN(cx, buf, JS_ARRAY_LENGTH(buf)); +** ... +** +***********************************************************************/ + +#define JS_ARRAY_LENGTH(array) (sizeof (array) / sizeof (array)[0]) +#define JS_ARRAY_END(array) ((array) + JS_ARRAY_LENGTH(array)) + +#define JS_BITS_PER_BYTE 8 +#define JS_BITS_PER_BYTE_LOG2 3 + +#define JS_BITS_PER_WORD (JS_BITS_PER_BYTE * JS_BYTES_PER_WORD) +#define JS_BITS_PER_DOUBLE (JS_BITS_PER_BYTE * JS_BYTES_PER_DOUBLE) + +/*********************************************************************** +** MACROS: JS_FUNC_TO_DATA_PTR +** JS_DATA_TO_FUNC_PTR +** DESCRIPTION: +** Macros to convert between function and data pointers assuming that +** they have the same size. Use them like this: +** +** JSPropertyOp nativeGetter; +** JSObject *scriptedGetter; +** ... +** scriptedGetter = JS_FUNC_TO_DATA_PTR(JSObject *, nativeGetter); +** ... +** nativeGetter = JS_DATA_TO_FUNC_PTR(JSPropertyOp, scriptedGetter); +** +***********************************************************************/ + +#ifdef __GNUC__ +# define JS_FUNC_TO_DATA_PTR(type, fun) (__extension__ (type) (fun)) +# define JS_DATA_TO_FUNC_PTR(type, ptr) (__extension__ (type) (ptr)) +#else +/* Use an extra (void *) cast for MSVC. */ +# define JS_FUNC_TO_DATA_PTR(type, fun) ((type) (void *) (fun)) +# define JS_DATA_TO_FUNC_PTR(type, ptr) ((type) (void *) (ptr)) +#endif + +#ifdef __GNUC__ +# define JS_EXTENSION __extension__ +# define JS_EXTENSION_(s) __extension__ ({ s; }) +#else +# define JS_EXTENSION +# define JS_EXTENSION_(s) s +#endif + +JS_END_EXTERN_C + +#endif /* jstypes_h___ */ diff --git a/ape-server/deps/js/src/jsutil.cpp b/ape-server/deps/js/src/jsutil.cpp new file mode 100755 index 0000000..0394996 --- /dev/null +++ b/ape-server/deps/js/src/jsutil.cpp @@ -0,0 +1,363 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * IBM Corp. + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * PR assertion checker. + */ +#include +#include +#include "jstypes.h" +#include "jsstdint.h" +#include "jsutil.h" + +#ifdef WIN32 +# include +#else +# include +#endif + +/* + * Checks the assumption that JS_FUNC_TO_DATA_PTR and JS_DATA_TO_FUNC_PTR + * macros uses to implement casts between function and data pointers. + */ +JS_STATIC_ASSERT(sizeof(void *) == sizeof(void (*)())); + +JS_PUBLIC_API(void) JS_Assert(const char *s, const char *file, JSIntn ln) +{ + fprintf(stderr, "Assertion failure: %s, at %s:%d\n", s, file, ln); +#if defined(WIN32) + DebugBreak(); + exit(3); +#else + /* In GDB, you can continue from here with the command "signal 0". */ + raise(SIGABRT); +#endif +} + +#ifdef JS_BASIC_STATS + +#include +#include +#include "jscompat.h" +#include "jsbit.h" + +/* + * Histogram bins count occurrences of values <= the bin label, as follows: + * + * linear: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 or more + * 2**x: 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512 or more + * 10**x: 0, 1, 10, 100, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9 or more + * + * We wish to count occurrences of 0 and 1 values separately, always. + */ +static uint32 +BinToVal(uintN logscale, uintN bin) +{ + JS_ASSERT(bin <= 10); + if (bin <= 1 || logscale == 0) + return bin; + --bin; + if (logscale == 2) + return JS_BIT(bin); + JS_ASSERT(logscale == 10); + return (uint32) pow(10.0, (double) bin); +} + +static uintN +ValToBin(uintN logscale, uint32 val) +{ + uintN bin; + + if (val <= 1) + return val; + bin = (logscale == 10) + ? (uintN) ceil(log10((double) val)) + : (logscale == 2) + ? (uintN) JS_CeilingLog2(val) + : val; + return JS_MIN(bin, 10); +} + +void +JS_BasicStatsAccum(JSBasicStats *bs, uint32 val) +{ + uintN oldscale, newscale, bin; + double mean; + + ++bs->num; + if (bs->max < val) + bs->max = val; + bs->sum += val; + bs->sqsum += (double)val * val; + + oldscale = bs->logscale; + if (oldscale != 10) { + mean = bs->sum / bs->num; + if (bs->max > 16 && mean > 8) { + newscale = (bs->max > 1e6 && mean > 1000) ? 10 : 2; + if (newscale != oldscale) { + uint32 newhist[11], newbin; + + memset(newhist, 0, sizeof newhist); + for (bin = 0; bin <= 10; bin++) { + newbin = ValToBin(newscale, BinToVal(oldscale, bin)); + newhist[newbin] += bs->hist[bin]; + } + memcpy(bs->hist, newhist, sizeof bs->hist); + bs->logscale = newscale; + } + } + } + + bin = ValToBin(bs->logscale, val); + ++bs->hist[bin]; +} + +double +JS_MeanAndStdDev(uint32 num, double sum, double sqsum, double *sigma) +{ + double var; + + if (num == 0 || sum == 0) { + *sigma = 0; + return 0; + } + + var = num * sqsum - sum * sum; + if (var < 0 || num == 1) + var = 0; + else + var /= (double)num * (num - 1); + + /* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */ + *sigma = (var != 0) ? sqrt(var) : 0; + return sum / num; +} + +void +JS_DumpBasicStats(JSBasicStats *bs, const char *title, FILE *fp) +{ + double mean, sigma; + + mean = JS_MeanAndStdDevBS(bs, &sigma); + fprintf(fp, "\nmean %s %g, std. deviation %g, max %lu\n", + title, mean, sigma, (unsigned long) bs->max); + JS_DumpHistogram(bs, fp); +} + +void +JS_DumpHistogram(JSBasicStats *bs, FILE *fp) +{ + uintN bin; + uint32 cnt, max, prev, val, i; + double sum, mean; + + for (bin = 0, max = 0, sum = 0; bin <= 10; bin++) { + cnt = bs->hist[bin]; + if (max < cnt) + max = cnt; + sum += cnt; + } + mean = sum / cnt; + for (bin = 0, prev = 0; bin <= 10; bin++, prev = val) { + val = BinToVal(bs->logscale, bin); + cnt = bs->hist[bin]; + if (prev + 1 >= val) + fprintf(fp, " [%6u]", val); + else + fprintf(fp, "[%6u, %6u]", prev + 1, val); + fprintf(fp, "%s %8u ", (bin == 10) ? "+" : ":", cnt); + if (cnt != 0) { + if (max > 1e6 && mean > 1e3) + cnt = (uint32) ceil(log10((double) cnt)); + else if (max > 16 && mean > 8) + cnt = JS_CeilingLog2(cnt); + for (i = 0; i < cnt; i++) + putc('*', fp); + } + putc('\n', fp); + } +} + +#endif /* JS_BASIC_STATS */ + +#if defined(DEBUG_notme) && defined(XP_UNIX) + +#define __USE_GNU 1 +#include +#include +#include "jshash.h" +#include "jsprf.h" + +JSCallsite js_calltree_root = {0, NULL, NULL, 0, NULL, NULL, NULL, NULL}; + +static JSCallsite * +CallTree(void **bp) +{ + void **bpup, **bpdown, *pc; + JSCallsite *parent, *site, **csp; + Dl_info info; + int ok, offset; + const char *symbol; + char *method; + + /* Reverse the stack frame list to avoid recursion. */ + bpup = NULL; + for (;;) { + bpdown = (void**) bp[0]; + bp[0] = (void*) bpup; + if ((void**) bpdown[0] < bpdown) + break; + bpup = bp; + bp = bpdown; + } + + /* Reverse the stack again, finding and building a path in the tree. */ + parent = &js_calltree_root; + do { + bpup = (void**) bp[0]; + bp[0] = (void*) bpdown; + pc = bp[1]; + + csp = &parent->kids; + while ((site = *csp) != NULL) { + if (site->pc == (uint32)pc) { + /* Put the most recently used site at the front of siblings. */ + *csp = site->siblings; + site->siblings = parent->kids; + parent->kids = site; + + /* Site already built -- go up the stack. */ + goto upward; + } + csp = &site->siblings; + } + + /* Check for recursion: see if pc is on our ancestor line. */ + for (site = parent; site; site = site->parent) { + if (site->pc == (uint32)pc) + goto upward; + } + + /* + * Not in tree at all: let's find our symbolic callsite info. + * XXX static syms are masked by nearest lower global + */ + info.dli_fname = info.dli_sname = NULL; + ok = dladdr(pc, &info); + if (ok < 0) { + fprintf(stderr, "dladdr failed!\n"); + return NULL; + } + +/* XXXbe sub 0x08040000? or something, see dbaron bug with tenthumbs comment */ + symbol = info.dli_sname; + offset = (char*)pc - (char*)info.dli_fbase; + method = symbol + ? strdup(symbol) + : JS_smprintf("%s+%X", + info.dli_fname ? info.dli_fname : "main", + offset); + if (!method) + return NULL; + + /* Create a new callsite record. */ + site = (JSCallsite *) js_malloc(sizeof(JSCallsite)); + if (!site) + return NULL; + + /* Insert the new site into the tree. */ + site->pc = (uint32)pc; + site->name = method; + site->library = info.dli_fname; + site->offset = offset; + site->parent = parent; + site->siblings = parent->kids; + parent->kids = site; + site->kids = NULL; + + upward: + parent = site; + bpdown = bp; + bp = bpup; + } while (bp); + + return site; +} + +JS_FRIEND_API(JSCallsite *) +JS_Backtrace(int skip) +{ + void **bp, **bpdown; + + /* Stack walking code adapted from Kipp's "leaky". */ +#if defined(__i386) + __asm__( "movl %%ebp, %0" : "=g"(bp)); +#elif defined(__x86_64__) + __asm__( "movq %%rbp, %0" : "=g"(bp)); +#else + /* + * It would be nice if this worked uniformly, but at least on i386 and + * x86_64, it stopped working with gcc 4.1, because it points to the + * end of the saved registers instead of the start. + */ + bp = (void**) __builtin_frame_address(0); +#endif + while (--skip >= 0) { + bpdown = (void**) *bp++; + if (bpdown < bp) + break; + bp = bpdown; + } + + return CallTree(bp); +} + +JS_FRIEND_API(void) +JS_DumpBacktrace(JSCallsite *trace) +{ + while (trace) { + fprintf(stdout, "%s [%s +0x%X]\n", trace->name, trace->library, + trace->offset); + trace = trace->parent; + } +} + +#endif /* defined(DEBUG_notme) && defined(XP_UNIX) */ diff --git a/ape-server/deps/js/src/jsutil.h b/ape-server/deps/js/src/jsutil.h new file mode 100755 index 0000000..08a31ee --- /dev/null +++ b/ape-server/deps/js/src/jsutil.h @@ -0,0 +1,304 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * PR assertion checker. + */ + +#ifndef jsutil_h___ +#define jsutil_h___ + +#include + +JS_BEGIN_EXTERN_C + +/* + * JS_Assert is present even in release builds, for the benefit of applications + * that build DEBUG and link against a non-DEBUG SpiderMonkey library. + */ +extern JS_PUBLIC_API(void) +JS_Assert(const char *s, const char *file, JSIntn ln); + +#ifdef DEBUG + +#define JS_ASSERT(expr) \ + ((expr) ? (void)0 : JS_Assert(#expr, __FILE__, __LINE__)) + +#define JS_ASSERT_IF(cond, expr) \ + ((!(cond) || (expr)) ? (void)0 : JS_Assert(#expr, __FILE__, __LINE__)) + +#define JS_NOT_REACHED(reason) \ + JS_Assert(reason, __FILE__, __LINE__) + +#else + +#define JS_ASSERT(expr) ((void) 0) +#define JS_ASSERT_IF(cond,expr) ((void) 0) +#define JS_NOT_REACHED(reason) + +#endif /* defined(DEBUG) */ + +/* + * Compile-time assert. "cond" must be a constant expression. + * The macro can be used only in places where an "extern" declaration is + * allowed. + */ + +/* + * Sun Studio C++ compiler has a bug + * "sizeof expression not accepted as size of array parameter" + * The bug number is 6688515. It is not public yet. + * Turn off this assert for Sun Studio until this bug is fixed. + */ +#ifdef __SUNPRO_CC +#define JS_STATIC_ASSERT(cond) +#else +#ifdef __COUNTER__ + #define JS_STATIC_ASSERT_GLUE1(x,y) x##y + #define JS_STATIC_ASSERT_GLUE(x,y) JS_STATIC_ASSERT_GLUE1(x,y) + #define JS_STATIC_ASSERT(cond) \ + typedef int JS_STATIC_ASSERT_GLUE(js_static_assert, __COUNTER__)[(cond) ? 1 : -1] +#else + #define JS_STATIC_ASSERT(cond) extern void js_static_assert(int arg[(cond) ? 1 : -1]) +#endif +#endif + +#define JS_STATIC_ASSERT_IF(cond, expr) JS_STATIC_ASSERT(!(cond) || (expr)) + +/* + * Abort the process in a non-graceful manner. This will cause a core file, + * call to the debugger or other moral equivalent as well as causing the + * entire process to stop. + */ +extern JS_PUBLIC_API(void) JS_Abort(void); + +#if 0 +# define JS_BASIC_STATS 1 +# define JS_SCOPE_DEPTH_METER 1 +#endif + +#if defined DEBUG && !defined JS_BASIC_STATS +# define JS_BASIC_STATS 1 +#endif + +#ifdef JS_BASIC_STATS + +#include + +typedef struct JSBasicStats { + uint32 num; + uint32 max; + double sum; + double sqsum; + uint32 logscale; /* logarithmic scale: 0 (linear), 2, 10 */ + uint32 hist[11]; +} JSBasicStats; + +#define JS_INIT_STATIC_BASIC_STATS {0,0,0,0,0,{0,0,0,0,0,0,0,0,0,0,0}} +#define JS_BASIC_STATS_INIT(bs) memset((bs), 0, sizeof(JSBasicStats)) + +#define JS_BASIC_STATS_ACCUM(bs,val) \ + JS_BasicStatsAccum(bs, val) + +#define JS_MeanAndStdDevBS(bs,sigma) \ + JS_MeanAndStdDev((bs)->num, (bs)->sum, (bs)->sqsum, sigma) + +extern void +JS_BasicStatsAccum(JSBasicStats *bs, uint32 val); + +extern double +JS_MeanAndStdDev(uint32 num, double sum, double sqsum, double *sigma); + +extern void +JS_DumpBasicStats(JSBasicStats *bs, const char *title, FILE *fp); + +extern void +JS_DumpHistogram(JSBasicStats *bs, FILE *fp); + +#else + +#define JS_BASIC_STATS_ACCUM(bs,val) /* nothing */ + +#endif /* JS_BASIC_STATS */ + + +#if defined(DEBUG_notme) && defined(XP_UNIX) + +typedef struct JSCallsite JSCallsite; + +struct JSCallsite { + uint32 pc; + char *name; + const char *library; + int offset; + JSCallsite *parent; + JSCallsite *siblings; + JSCallsite *kids; + void *handy; +}; + +extern JS_FRIEND_API(JSCallsite *) +JS_Backtrace(int skip); + +extern JS_FRIEND_API(void) +JS_DumpBacktrace(JSCallsite *trace); +#endif + +static JS_INLINE void* js_malloc(size_t bytes) { + if (bytes < sizeof(void*)) /* for asyncFree */ + bytes = sizeof(void*); + return malloc(bytes); +} + +static JS_INLINE void* js_calloc(size_t bytes) { + if (bytes < sizeof(void*)) /* for asyncFree */ + bytes = sizeof(void*); + return calloc(bytes, 1); +} + +static JS_INLINE void* js_realloc(void* p, size_t bytes) { + if (bytes < sizeof(void*)) /* for asyncFree */ + bytes = sizeof(void*); + return realloc(p, bytes); +} + +static JS_INLINE void js_free(void* p) { + free(p); +} + +JS_END_EXTERN_C + +#ifdef __cplusplus + +/** + * The following classes are designed to cause assertions to detect + * inadvertent use of guard objects as temporaries. In other words, + * when we have a guard object whose only purpose is its constructor and + * destructor (and is never otherwise referenced), the intended use + * might be: + * JSAutoTempValueRooter tvr(cx, 1, &val); + * but is is easy to accidentally write: + * JSAutoTempValueRooter(cx, 1, &val); + * which compiles just fine, but runs the destructor well before the + * intended time. + * + * They work by adding (#ifdef DEBUG) an additional parameter to the + * guard object's constructor, with a default value, so that users of + * the guard object's API do not need to do anything. The default value + * of this parameter is a temporary object. C++ (ISO/IEC 14882:1998), + * section 12.2 [class.temporary], clauses 4 and 5 seem to assume a + * guarantee that temporaries are destroyed in the reverse of their + * construction order, but I actually can't find a statement that that + * is true in the general case (beyond the two specific cases mentioned + * there). However, it seems to be true. + * + * These classes are intended to be used only via the macros immediately + * below them: + * JS_DECL_USE_GUARD_OBJECT_NOTIFIER declares (ifdef DEBUG) a member + * variable, and should be put where a declaration of a private + * member variable would be placed. + * JS_GUARD_OBJECT_NOTIFIER_PARAM should be placed at the end of the + * parameters to each constructor of the guard object; it declares + * (ifdef DEBUG) an additional parameter. + * JS_GUARD_OBJECT_NOTIFIER_INIT is a statement that belongs in each + * constructor. It uses the parameter declared by + * JS_GUARD_OBJECT_NOTIFIER_PARAM. + */ +#ifdef DEBUG +class JSGuardObjectNotifier +{ +private: + bool* mStatementDone; +public: + JSGuardObjectNotifier() : mStatementDone(NULL) {} + + ~JSGuardObjectNotifier() { + *mStatementDone = true; + } + + void SetStatementDone(bool *aStatementDone) { + mStatementDone = aStatementDone; + } +}; + +class JSGuardObjectNotificationReceiver +{ +private: + bool mStatementDone; +public: + JSGuardObjectNotificationReceiver() : mStatementDone(false) {} + + ~JSGuardObjectNotificationReceiver() { + /* + * Assert that the guard object was not used as a temporary. + * (Note that this assert might also fire if Init is not called + * because the guard object's implementation is not using the + * above macros correctly.) + */ + JS_ASSERT(mStatementDone); + } + + void Init(const JSGuardObjectNotifier &aNotifier) { + /* + * aNotifier is passed as a const reference so that we can pass a + * temporary, but we really intend it as non-const + */ + const_cast(aNotifier). + SetStatementDone(&mStatementDone); + } +}; + +#define JS_DECL_USE_GUARD_OBJECT_NOTIFIER \ + JSGuardObjectNotificationReceiver _mCheckNotUsedAsTemporary; +#define JS_GUARD_OBJECT_NOTIFIER_PARAM \ + , const JSGuardObjectNotifier& _notifier = JSGuardObjectNotifier() +#define JS_GUARD_OBJECT_NOTIFIER_INIT \ + JS_BEGIN_MACRO _mCheckNotUsedAsTemporary.Init(_notifier); JS_END_MACRO + +#else /* defined(DEBUG) */ + +#define JS_DECL_USE_GUARD_OBJECT_NOTIFIER +#define JS_GUARD_OBJECT_NOTIFIER_PARAM +#define JS_GUARD_OBJECT_NOTIFIER_INIT JS_BEGIN_MACRO JS_END_MACRO + +#endif /* !defined(DEBUG) */ + +#endif /* defined(__cplusplus) */ + +#endif /* jsutil_h___ */ diff --git a/ape-server/deps/js/src/jsvector.h b/ape-server/deps/js/src/jsvector.h new file mode 100755 index 0000000..eafb11c --- /dev/null +++ b/ape-server/deps/js/src/jsvector.h @@ -0,0 +1,775 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=99 ft=cpp: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released + * June 12, 2009. + * + * The Initial Developer of the Original Code is + * the Mozilla Corporation. + * + * Contributor(s): + * Luke Wagner + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsvector_h_ +#define jsvector_h_ + +#include + +#include "jstl.h" + +namespace js { + +/* + * This template class provides a default implementation for vector operations + * when the element type is not known to be a POD, as judged by IsPodType. + */ +template +struct VectorImpl +{ + /* Destroys constructed objects in the range [begin, end). */ + static inline void destroy(T *begin, T *end) { + for (T *p = begin; p != end; ++p) + p->~T(); + } + + /* Constructs objects in the uninitialized range [begin, end). */ + static inline void initialize(T *begin, T *end) { + for (T *p = begin; p != end; ++p) + new(p) T(); + } + + /* + * Copy-constructs objects in the uninitialized range + * [dst, dst+(srcend-srcbeg)) from the range [srcbeg, srcend). + */ + template + static inline void copyConstruct(T *dst, const U *srcbeg, const U *srcend) { + for (const U *p = srcbeg; p != srcend; ++p, ++dst) + new(dst) T(*p); + } + + /* + * Copy-constructs objects in the uninitialized range [dst, dst+n) from the + * same object u. + */ + template + static inline void copyConstructN(T *dst, size_t n, const U &u) { + for (T *end = dst + n; dst != end; ++dst) + new(dst) T(u); + } + + /* + * Grows the given buffer to have capacity newcap, preserving the objects + * constructed in the range [begin, end) and updating v. Assumes that (1) + * newcap has not overflowed, and (2) multiplying newcap by sizeof(T) will + * not overflow. + */ + static inline bool growTo(Vector &v, size_t newcap) { + JS_ASSERT(!v.usingInlineStorage()); + T *newbuf = reinterpret_cast(v.malloc(newcap * sizeof(T))); + if (!newbuf) + return false; + for (T *dst = newbuf, *src = v.heapBegin(); src != v.heapEnd(); ++dst, ++src) + new(dst) T(*src); + VectorImpl::destroy(v.heapBegin(), v.heapEnd()); + v.free(v.heapBegin()); + v.heapEnd() = newbuf + v.heapLength(); + v.heapBegin() = newbuf; + v.heapCapacity() = newcap; + return true; + } +}; + +/* + * This partial template specialization provides a default implementation for + * vector operations when the element type is known to be a POD, as judged by + * IsPodType. + */ +template +struct VectorImpl +{ + static inline void destroy(T *, T *) {} + + static inline void initialize(T *begin, T *end) { + /* + * You would think that memset would be a big win (or even break even) + * when we know T is a POD. But currently it's not. This is probably + * because |append| tends to be given small ranges and memset requires + * a function call that doesn't get inlined. + * + * memset(begin, 0, sizeof(T) * (end-begin)); + */ + for (T *p = begin; p != end; ++p) + new(p) T(); + } + + template + static inline void copyConstruct(T *dst, const U *srcbeg, const U *srcend) { + /* + * See above memset comment. Also, notice that copyConstruct is + * currently templated (T != U), so memcpy won't work without + * requiring T == U. + * + * memcpy(dst, srcbeg, sizeof(T) * (srcend - srcbeg)); + */ + for (const U *p = srcbeg; p != srcend; ++p, ++dst) + *dst = *p; + } + + static inline void copyConstructN(T *dst, size_t n, const T &t) { + for (T *p = dst, *end = dst + n; p != end; ++p) + *p = t; + } + + static inline bool growTo(Vector &v, size_t newcap) { + JS_ASSERT(!v.usingInlineStorage()); + size_t bytes = sizeof(T) * newcap; + T *newbuf = reinterpret_cast(v.realloc(v.heapBegin(), bytes)); + if (!newbuf) + return false; + v.heapEnd() = newbuf + v.heapLength(); + v.heapBegin() = newbuf; + v.heapCapacity() = newcap; + return true; + } +}; + +/* + * JS-friendly, STL-like container providing a short-lived, dynamic buffer. + * Vector calls the constructors/destructors of all elements stored in + * its internal buffer, so non-PODs may be safely used. Additionally, + * Vector will store the first N elements in-place before resorting to + * dynamic allocation. + * + * T requirements: + * - default and copy constructible, assignable, destructible + * - operations do not throw + * N requirements: + * - any value, however, N is clamped to min/max values + * AllocPolicy: + * - see "Allocation policies" in jstl.h (default ContextAllocPolicy) + * + * N.B: Vector is not reentrant: T member functions called during Vector member + * functions must not call back into the same object. + */ +template +class Vector : AllocPolicy +{ + /* utilities */ + + static const bool sElemIsPod = tl::IsPodType::result; + typedef VectorImpl Impl; + friend struct VectorImpl; + + bool calculateNewCapacity(size_t curLength, size_t lengthInc, size_t &newCap); + bool growHeapStorageBy(size_t lengthInc); + bool convertToHeapStorage(size_t lengthInc); + + /* magic constants */ + + static const int sMaxInlineBytes = 1024; + + /* compute constants */ + + /* + * Pointers to the heap-allocated buffer. Only [heapBegin(), heapEnd()) + * hold valid constructed T objects. The range [heapEnd(), heapBegin() + + * heapCapacity()) holds uninitialized memory. + */ + struct BufferPtrs { + T *mBegin, *mEnd; + }; + + /* + * Since a vector either stores elements inline or in a heap-allocated + * buffer, reuse the storage. mLengthOrCapacity serves as the union + * discriminator. In inline mode (when elements are stored in u.mBuf), + * mLengthOrCapacity holds the vector's length. In heap mode (when elements + * are stored in [u.ptrs.mBegin, u.ptrs.mEnd)), mLengthOrCapacity holds the + * vector's capacity. + */ + static const size_t sInlineCapacity = + tl::Clamp::result; + + /* Calculate inline buffer size; avoid 0-sized array. */ + static const size_t sInlineBytes = + tl::Max<1, sInlineCapacity * sizeof(T)>::result; + + /* member data */ + + size_t mLengthOrCapacity; + bool usingInlineStorage() const { return mLengthOrCapacity <= sInlineCapacity; } + + union { + BufferPtrs ptrs; + char mBuf[sInlineBytes]; + } u; + + /* Only valid when usingInlineStorage() */ + size_t &inlineLength() { + JS_ASSERT(usingInlineStorage()); + return mLengthOrCapacity; + } + + size_t inlineLength() const { + JS_ASSERT(usingInlineStorage()); + return mLengthOrCapacity; + } + + T *inlineBegin() const { + JS_ASSERT(usingInlineStorage()); + return (T *)u.mBuf; + } + + T *inlineEnd() const { + JS_ASSERT(usingInlineStorage()); + return (T *)u.mBuf + mLengthOrCapacity; + } + + /* Only valid when !usingInlineStorage() */ + size_t heapLength() const { + JS_ASSERT(!usingInlineStorage()); + /* Guaranteed by calculateNewCapacity. */ + JS_ASSERT(size_t(u.ptrs.mEnd - u.ptrs.mBegin) == + ((size_t(u.ptrs.mEnd) - size_t(u.ptrs.mBegin)) / sizeof(T))); + return u.ptrs.mEnd - u.ptrs.mBegin; + } + + size_t &heapCapacity() { + JS_ASSERT(!usingInlineStorage()); + return mLengthOrCapacity; + } + + T *&heapBegin() { + JS_ASSERT(!usingInlineStorage()); + return u.ptrs.mBegin; + } + + T *&heapEnd() { + JS_ASSERT(!usingInlineStorage()); + return u.ptrs.mEnd; + } + + size_t heapCapacity() const { + JS_ASSERT(!usingInlineStorage()); + return mLengthOrCapacity; + } + + T *heapBegin() const { + JS_ASSERT(!usingInlineStorage()); + return u.ptrs.mBegin; + } + + T *heapEnd() const { + JS_ASSERT(!usingInlineStorage()); + return u.ptrs.mEnd; + } + +#ifdef DEBUG + friend class ReentrancyGuard; + bool mEntered; +#endif + + Vector(const Vector &); + Vector &operator=(const Vector &); + + public: + Vector(AllocPolicy = AllocPolicy()); + ~Vector(); + + /* accessors */ + + size_t length() const { + return usingInlineStorage() ? inlineLength() : heapLength(); + } + + bool empty() const { + return usingInlineStorage() ? inlineLength() == 0 : heapBegin() == heapEnd(); + } + + size_t capacity() const { + return usingInlineStorage() ? sInlineCapacity : heapCapacity(); + } + + T *begin() { + JS_ASSERT(!mEntered); + return usingInlineStorage() ? inlineBegin() : heapBegin(); + } + + const T *begin() const { + JS_ASSERT(!mEntered); + return usingInlineStorage() ? inlineBegin() : heapBegin(); + } + + T *end() { + JS_ASSERT(!mEntered); + return usingInlineStorage() ? inlineEnd() : heapEnd(); + } + + const T *end() const { + JS_ASSERT(!mEntered); + return usingInlineStorage() ? inlineEnd() : heapEnd(); + } + + T &operator[](size_t i) { + JS_ASSERT(!mEntered && i < length()); + return begin()[i]; + } + + const T &operator[](size_t i) const { + JS_ASSERT(!mEntered && i < length()); + return begin()[i]; + } + + T &back() { + JS_ASSERT(!mEntered && !empty()); + return *(end() - 1); + } + + const T &back() const { + JS_ASSERT(!mEntered && !empty()); + return *(end() - 1); + } + + /* mutators */ + + /* If reserve(N) succeeds, the N next appends are guaranteed to succeed. */ + bool reserve(size_t capacity); + + /* Destroy elements in the range [begin() + incr, end()). */ + void shrinkBy(size_t incr); + + /* + * Grow the vector by incr elements. If T is a POD (as judged by + * tl::IsPodType), leave as uninitialized memory. Otherwise, default + * construct each element. + */ + bool growBy(size_t incr); + + /* Call shrinkBy or growBy based on whether newSize > length(). */ + bool resize(size_t newLength); + + void clear(); + + bool append(const T &t); + bool appendN(const T &t, size_t n); + template bool append(const U *begin, const U *end); + template bool append(const U *begin, size_t length); + + void popBack(); + + /* + * Transfers ownership of the internal buffer used by Vector to the caller. + * After this call, the Vector is empty. Since the returned buffer may need + * to be allocated (if the elements are currently stored in-place), the + * call can fail, returning NULL. + * + * N.B. Although a T*, only the range [0, length()) is constructed. + */ + T *extractRawBuffer(); + + /* + * Transfer ownership of an array of objects into the Vector. + * N.B. This call assumes that there are no uninitialized elements in the + * passed array. + */ + void replaceRawBuffer(T *p, size_t length); +}; + +/* Helper functions */ + +/* + * This helper function is specialized for appending the characters of a string + * literal to a vector. This could not be done generically since one must take + * care not to append the terminating '\0'. + */ +template +bool +js_AppendLiteral(Vector &v, const char (&array)[ArrayLength]) +{ + return v.append(array, array + ArrayLength - 1); +} + + +/* Vector Implementation */ + +template +inline +Vector::Vector(AP ap) + : AP(ap), mLengthOrCapacity(0) +#ifdef DEBUG + , mEntered(false) +#endif +{} + +template +inline +Vector::~Vector() +{ + ReentrancyGuard g(*this); + if (usingInlineStorage()) { + Impl::destroy(inlineBegin(), inlineEnd()); + } else { + Impl::destroy(heapBegin(), heapEnd()); + this->free(heapBegin()); + } +} + +/* + * Calculate a new capacity that is at least lengthInc greater than + * curLength and check for overflow. + */ +template +inline bool +Vector::calculateNewCapacity(size_t curLength, size_t lengthInc, + size_t &newCap) +{ + size_t newMinCap = curLength + lengthInc; + + /* + * Check for overflow in the above addition, below CEILING_LOG2, and later + * multiplication by sizeof(T). + */ + if (newMinCap < curLength || + newMinCap & tl::MulOverflowMask<2 * sizeof(T)>::result) { + this->reportAllocOverflow(); + return false; + } + + /* Round up to next power of 2. */ + newCap = RoundUpPow2(newMinCap); + + /* + * Do not allow a buffer large enough that the expression ((char *)end() - + * (char *)begin()) overflows ptrdiff_t. See Bug 510319. + */ + if (newCap & tl::UnsafeRangeSizeMask::result) { + this->reportAllocOverflow(); + return false; + } + return true; +} + +/* + * This function will grow the current heap capacity to have capacity + * (heapLength() + lengthInc) and fail on OOM or integer overflow. + */ +template +inline bool +Vector::growHeapStorageBy(size_t lengthInc) +{ + size_t newCap; + return calculateNewCapacity(heapLength(), lengthInc, newCap) && + Impl::growTo(*this, newCap); +} + +/* + * This function will create a new heap buffer with capacity (inlineLength() + + * lengthInc()), move all elements in the inline buffer to this new buffer, + * and fail on OOM or integer overflow. + */ +template +inline bool +Vector::convertToHeapStorage(size_t lengthInc) +{ + size_t newCap; + if (!calculateNewCapacity(inlineLength(), lengthInc, newCap)) + return false; + + /* Allocate buffer. */ + T *newBuf = reinterpret_cast(this->malloc(newCap * sizeof(T))); + if (!newBuf) + return false; + + /* Copy inline elements into heap buffer. */ + size_t length = inlineLength(); + Impl::copyConstruct(newBuf, inlineBegin(), inlineEnd()); + Impl::destroy(inlineBegin(), inlineEnd()); + + /* Switch in heap buffer. */ + mLengthOrCapacity = newCap; /* marks us as !usingInlineStorage() */ + heapBegin() = newBuf; + heapEnd() = newBuf + length; + return true; +} + +template +inline bool +Vector::reserve(size_t request) +{ + ReentrancyGuard g(*this); + if (usingInlineStorage()) { + if (request > sInlineCapacity) + return convertToHeapStorage(request - inlineLength()); + } else { + if (request > heapCapacity()) + return growHeapStorageBy(request - heapLength()); + } + return true; +} + +template +inline void +Vector::shrinkBy(size_t incr) +{ + ReentrancyGuard g(*this); + JS_ASSERT(incr <= length()); + if (usingInlineStorage()) { + Impl::destroy(inlineEnd() - incr, inlineEnd()); + inlineLength() -= incr; + } else { + Impl::destroy(heapEnd() - incr, heapEnd()); + heapEnd() -= incr; + } +} + +template +inline bool +Vector::growBy(size_t incr) +{ + ReentrancyGuard g(*this); + if (usingInlineStorage()) { + size_t freespace = sInlineCapacity - inlineLength(); + if (incr <= freespace) { + T *newend = inlineEnd() + incr; + if (!tl::IsPodType::result) + Impl::initialize(inlineEnd(), newend); + inlineLength() += incr; + JS_ASSERT(usingInlineStorage()); + return true; + } + if (!convertToHeapStorage(incr)) + return false; + } + else { + /* grow if needed */ + size_t freespace = heapCapacity() - heapLength(); + if (incr > freespace) { + if (!growHeapStorageBy(incr)) + return false; + } + } + + /* We are !usingInlineStorage(). Initialize new elements. */ + JS_ASSERT(heapCapacity() - heapLength() >= incr); + T *newend = heapEnd() + incr; + if (!tl::IsPodType::result) + Impl::initialize(heapEnd(), newend); + heapEnd() = newend; + return true; +} + +template +inline bool +Vector::resize(size_t newLength) +{ + size_t curLength = length(); + if (newLength > curLength) + return growBy(newLength - curLength); + shrinkBy(curLength - newLength); + return true; +} + +template +inline void +Vector::clear() +{ + ReentrancyGuard g(*this); + if (usingInlineStorage()) { + Impl::destroy(inlineBegin(), inlineEnd()); + inlineLength() = 0; + } + else { + Impl::destroy(heapBegin(), heapEnd()); + heapEnd() = heapBegin(); + } +} + +template +inline bool +Vector::append(const T &t) +{ + ReentrancyGuard g(*this); + if (usingInlineStorage()) { + if (inlineLength() < sInlineCapacity) { + new(inlineEnd()) T(t); + ++inlineLength(); + JS_ASSERT(usingInlineStorage()); + return true; + } + if (!convertToHeapStorage(1)) + return false; + } else { + if (heapLength() == heapCapacity() && !growHeapStorageBy(1)) + return false; + } + + /* We are !usingInlineStorage(). Initialize new elements. */ + JS_ASSERT(heapLength() <= heapCapacity() && heapCapacity() - heapLength() >= 1); + new(heapEnd()++) T(t); + return true; +} + +template +inline bool +Vector::appendN(const T &t, size_t needed) +{ + ReentrancyGuard g(*this); + if (usingInlineStorage()) { + size_t freespace = sInlineCapacity - inlineLength(); + if (needed <= freespace) { + Impl::copyConstructN(inlineEnd(), needed, t); + inlineLength() += needed; + JS_ASSERT(usingInlineStorage()); + return true; + } + if (!convertToHeapStorage(needed)) + return false; + } else { + size_t freespace = heapCapacity() - heapLength(); + if (needed > freespace && !growHeapStorageBy(needed)) + return false; + } + + /* We are !usingInlineStorage(). Initialize new elements. */ + JS_ASSERT(heapLength() <= heapCapacity() && heapCapacity() - heapLength() >= needed); + Impl::copyConstructN(heapEnd(), needed, t); + heapEnd() += needed; + return true; +} + +template +template +inline bool +Vector::append(const U *insBegin, const U *insEnd) +{ + ReentrancyGuard g(*this); + size_t needed = PointerRangeSize(insBegin, insEnd); + if (usingInlineStorage()) { + size_t freespace = sInlineCapacity - inlineLength(); + if (needed <= freespace) { + Impl::copyConstruct(inlineEnd(), insBegin, insEnd); + inlineLength() += needed; + JS_ASSERT(usingInlineStorage()); + return true; + } + if (!convertToHeapStorage(needed)) + return false; + } else { + size_t freespace = heapCapacity() - heapLength(); + if (needed > freespace && !growHeapStorageBy(needed)) + return false; + } + + /* We are !usingInlineStorage(). Initialize new elements. */ + JS_ASSERT(heapLength() <= heapCapacity() && heapCapacity() - heapLength() >= needed); + Impl::copyConstruct(heapEnd(), insBegin, insEnd); + heapEnd() += needed; + return true; +} + +template +template +inline bool +Vector::append(const U *insBegin, size_t length) +{ + return this->append(insBegin, insBegin + length); +} + +template +inline void +Vector::popBack() +{ + ReentrancyGuard g(*this); + JS_ASSERT(!empty()); + if (usingInlineStorage()) { + --inlineLength(); + inlineEnd()->~T(); + } else { + --heapEnd(); + heapEnd()->~T(); + } +} + +template +inline T * +Vector::extractRawBuffer() +{ + if (usingInlineStorage()) { + T *ret = reinterpret_cast(this->malloc(inlineLength() * sizeof(T))); + if (!ret) + return NULL; + Impl::copyConstruct(ret, inlineBegin(), inlineEnd()); + Impl::destroy(inlineBegin(), inlineEnd()); + inlineLength() = 0; + return ret; + } + + T *ret = heapBegin(); + mLengthOrCapacity = 0; /* marks us as !usingInlineStorage() */ + return ret; +} + +template +inline void +Vector::replaceRawBuffer(T *p, size_t length) +{ + ReentrancyGuard g(*this); + + /* Destroy what we have. */ + if (usingInlineStorage()) { + Impl::destroy(inlineBegin(), inlineEnd()); + inlineLength() = 0; + } else { + Impl::destroy(heapBegin(), heapEnd()); + this->free(heapBegin()); + } + + /* Take in the new buffer. */ + if (length <= sInlineCapacity) { + /* + * (mLengthOrCapacity <= sInlineCapacity) means inline storage, so we + * MUST use inline storage, even though p might otherwise be acceptable. + */ + mLengthOrCapacity = length; /* marks us as usingInlineStorage() */ + Impl::copyConstruct(inlineBegin(), p, p + length); + Impl::destroy(p, p + length); + this->free(p); + } else { + mLengthOrCapacity = length; /* marks us as !usingInlineStorage() */ + heapBegin() = p; + heapEnd() = heapBegin() + length; + } +} + +} /* namespace js */ + +#endif /* jsvector_h_ */ diff --git a/ape-server/deps/js/src/jsversion.h b/ape-server/deps/js/src/jsversion.h new file mode 100755 index 0000000..56c0aa7 --- /dev/null +++ b/ape-server/deps/js/src/jsversion.h @@ -0,0 +1,238 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * JS configuration macros. + */ +#ifndef JS_VERSION +#define JS_VERSION 185 +#endif + +/* + * Compile-time JS version configuration. The JS version numbers lie on the + * number line like so: + * + * 1.0 1.1 1.2 1.3 1.4 ECMAv3 1.5 1.6 1.7 1.8 ECMAv5 + * ^ ^ + * | | + * basis for ECMAv1 close to ECMAv2 + * + * where ECMAv3 stands for ECMA-262 Edition 3, and ECMAv5 stands for Edition 5. + * See the runtime version enum JSVersion in jspubtd.h. Code in the engine can + * therefore count on version <= JSVERSION_1_4 to mean "before the Third + * Edition of ECMA-262" and version > JSVERSION_1_4 to mean "at or after the + * Third Edition". + * + * In the (likely?) event that SpiderMonkey grows to implement JavaScript 2.0, + * the version number to use would be near 200, or greater. + * + * The JS_VERSION_ECMA_3 version is the minimal configuration conforming to + * the ECMA-262 Edition 3 specification. Use it for minimal embeddings, where + * you're sure you don't need any of the extensions disabled in this version. + * In order to facilitate testing, JS_HAS_OBJ_PROTO_PROP is defined as part of + * the JS_VERSION_ECMA_3_TEST version. + * + * To keep things sane in the modern age, where we need exceptions in order to + * implement, e.g., iterators and generators, we are dropping support for all + * versions <= 1.4. + */ +#define JS_VERSION_ECMA_3 148 +#define JS_VERSION_ECMA_3_TEST 149 + +#if JS_VERSION == JS_VERSION_ECMA_3 || \ + JS_VERSION == JS_VERSION_ECMA_3_TEST + +#define JS_HAS_STR_HTML_HELPERS 0 /* has str.anchor, str.bold, etc. */ +#define JS_HAS_PERL_SUBSTR 0 /* has str.substr */ +#if JS_VERSION == JS_VERSION_ECMA_3_TEST +#define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ +#else +#define JS_HAS_OBJ_PROTO_PROP 0 /* has o.__proto__ etc. */ +#endif +#define JS_HAS_OBJ_WATCHPOINT 0 /* has o.watch and o.unwatch */ +#define JS_HAS_EVAL_THIS_SCOPE 0 /* Math.eval is same as with (Math) */ +#define JS_HAS_SHARP_VARS 0 /* has #n=, #n# for object literals */ +#define JS_HAS_SCRIPT_OBJECT 0 /* has (new Script("x++")).exec() */ +#define JS_HAS_XDR 0 /* has XDR API and internal support */ +#define JS_HAS_XDR_FREEZE_THAW 0 /* has XDR freeze/thaw script methods */ +#define JS_HAS_TOSOURCE 0 /* has Object/Array toSource method */ +#define JS_HAS_DEBUGGER_KEYWORD 0 /* has hook for debugger keyword */ +#define JS_HAS_CATCH_GUARD 0 /* has exception handling catch guard */ +#define JS_HAS_SPARSE_ARRAYS 0 /* array methods preserve empty elems */ +#define JS_HAS_GETTER_SETTER 0 /* has JS2 getter/setter functions */ +#define JS_HAS_UNEVAL 0 /* has uneval() top-level function */ +#define JS_HAS_CONST 0 /* has JS2 const as alternative var */ +#define JS_HAS_FUN_EXPR_STMT 0 /* has function expression statement */ +#define JS_HAS_NO_SUCH_METHOD 0 /* has o.__noSuchMethod__ handler */ +#define JS_HAS_XML_SUPPORT 0 /* has ECMAScript for XML support */ +#define JS_HAS_ARRAY_EXTRAS 0 /* has indexOf and Lispy extras */ +#define JS_HAS_GENERATORS 0 /* has yield in generator function */ +#define JS_HAS_BLOCK_SCOPE 0 /* has block scope via let/arraycomp */ +#define JS_HAS_DESTRUCTURING 0 /* has [a,b] = ... or {p:a,q:b} = ... */ +#define JS_HAS_GENERATOR_EXPRS 0 /* has (expr for (lhs in iterable)) */ +#define JS_HAS_EXPR_CLOSURES 0 /* has function (formals) listexpr */ + +#elif JS_VERSION < 150 + +#error "unsupported JS_VERSION" + +#elif JS_VERSION == 150 + +#define JS_HAS_STR_HTML_HELPERS 1 /* has str.anchor, str.bold, etc. */ +#define JS_HAS_PERL_SUBSTR 1 /* has str.substr */ +#define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ +#define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */ +#define JS_HAS_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */ +#define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ +#define JS_HAS_SCRIPT_OBJECT 1 /* has (new Script("x++")).exec() */ +#define JS_HAS_XDR 1 /* has XDR API and internal support */ +#define JS_HAS_XDR_FREEZE_THAW 0 /* has XDR freeze/thaw script methods */ +#define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ +#define JS_HAS_DEBUGGER_KEYWORD 1 /* has hook for debugger keyword */ +#define JS_HAS_CATCH_GUARD 1 /* has exception handling catch guard */ +#define JS_HAS_SPARSE_ARRAYS 0 /* array methods preserve empty elems */ +#define JS_HAS_GETTER_SETTER 1 /* has JS2 getter/setter functions */ +#define JS_HAS_UNEVAL 1 /* has uneval() top-level function */ +#define JS_HAS_CONST 1 /* has JS2 const as alternative var */ +#define JS_HAS_FUN_EXPR_STMT 1 /* has function expression statement */ +#define JS_HAS_NO_SUCH_METHOD 1 /* has o.__noSuchMethod__ handler */ +#define JS_HAS_XML_SUPPORT 0 /* has ECMAScript for XML support */ +#define JS_HAS_ARRAY_EXTRAS 0 /* has indexOf and Lispy extras */ +#define JS_HAS_GENERATORS 0 /* has yield in generator function */ +#define JS_HAS_BLOCK_SCOPE 0 /* has block scope via let/arraycomp */ +#define JS_HAS_DESTRUCTURING 0 /* has [a,b] = ... or {p:a,q:b} = ... */ +#define JS_HAS_GENERATOR_EXPRS 0 /* has (expr for (lhs in iterable)) */ +#define JS_HAS_EXPR_CLOSURES 0 /* has function (formals) listexpr */ + +#elif JS_VERSION == 160 + +#define JS_HAS_STR_HTML_HELPERS 1 /* has str.anchor, str.bold, etc. */ +#define JS_HAS_PERL_SUBSTR 1 /* has str.substr */ +#define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ +#define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */ +#define JS_HAS_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */ +#define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ +#define JS_HAS_SCRIPT_OBJECT 1 /* has (new Script("x++")).exec() */ +#define JS_HAS_XDR 1 /* has XDR API and internal support */ +#define JS_HAS_XDR_FREEZE_THAW 0 /* has XDR freeze/thaw script methods */ +#define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ +#define JS_HAS_DEBUGGER_KEYWORD 1 /* has hook for debugger keyword */ +#define JS_HAS_CATCH_GUARD 1 /* has exception handling catch guard */ +#define JS_HAS_SPARSE_ARRAYS 0 /* array methods preserve empty elems */ +#define JS_HAS_GETTER_SETTER 1 /* has JS2 getter/setter functions */ +#define JS_HAS_UNEVAL 1 /* has uneval() top-level function */ +#define JS_HAS_CONST 1 /* has JS2 const as alternative var */ +#define JS_HAS_FUN_EXPR_STMT 1 /* has function expression statement */ +#define JS_HAS_NO_SUCH_METHOD 1 /* has o.__noSuchMethod__ handler */ +#define JS_HAS_XML_SUPPORT 1 /* has ECMAScript for XML support */ +#define JS_HAS_ARRAY_EXTRAS 1 /* has indexOf and Lispy extras */ +#define JS_HAS_GENERATORS 0 /* has yield in generator function */ +#define JS_HAS_BLOCK_SCOPE 0 /* has block scope via let/arraycomp */ +#define JS_HAS_DESTRUCTURING 0 /* has [a,b] = ... or {p:a,q:b} = ... */ +#define JS_HAS_GENERATOR_EXPRS 0 /* has (expr for (lhs in iterable)) */ +#define JS_HAS_EXPR_CLOSURES 0 /* has function (formals) listexpr */ + +#elif JS_VERSION == 170 + +#define JS_HAS_STR_HTML_HELPERS 1 /* has str.anchor, str.bold, etc. */ +#define JS_HAS_PERL_SUBSTR 1 /* has str.substr */ +#define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ +#define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */ +#define JS_HAS_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */ +#define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ +#define JS_HAS_SCRIPT_OBJECT 0 /* has (new Script("x++")).exec() */ +#define JS_HAS_XDR 1 /* has XDR API and internal support */ +#define JS_HAS_XDR_FREEZE_THAW 0 /* has XDR freeze/thaw script methods */ +#define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ +#define JS_HAS_DEBUGGER_KEYWORD 1 /* has hook for debugger keyword */ +#define JS_HAS_CATCH_GUARD 1 /* has exception handling catch guard */ +#define JS_HAS_SPARSE_ARRAYS 0 /* array methods preserve empty elems */ +#define JS_HAS_GETTER_SETTER 1 /* has JS2 getter/setter functions */ +#define JS_HAS_UNEVAL 1 /* has uneval() top-level function */ +#define JS_HAS_CONST 1 /* has JS2 const as alternative var */ +#define JS_HAS_FUN_EXPR_STMT 1 /* has function expression statement */ +#define JS_HAS_NO_SUCH_METHOD 1 /* has o.__noSuchMethod__ handler */ +#define JS_HAS_XML_SUPPORT 1 /* has ECMAScript for XML support */ +#define JS_HAS_ARRAY_EXTRAS 1 /* has indexOf and Lispy extras */ +#define JS_HAS_GENERATORS 1 /* has yield in generator function */ +#define JS_HAS_BLOCK_SCOPE 1 /* has block scope via let/arraycomp */ +#define JS_HAS_DESTRUCTURING 1 /* has [a,b] = ... or {p:a,q:b} = ... */ +#define JS_HAS_GENERATOR_EXPRS 0 /* has (expr for (lhs in iterable)) */ +#define JS_HAS_EXPR_CLOSURES 0 /* has function (formals) listexpr */ + +#elif 180 <= JS_VERSION && JS_VERSION <= 185 + +#define JS_HAS_STR_HTML_HELPERS 1 /* has str.anchor, str.bold, etc. */ +#define JS_HAS_PERL_SUBSTR 1 /* has str.substr */ +#define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ +#define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */ +#define JS_HAS_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */ +#define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ +#define JS_HAS_SCRIPT_OBJECT 0 /* has (new Script("x++")).exec() */ +#define JS_HAS_XDR 1 /* has XDR API and internal support */ +#define JS_HAS_XDR_FREEZE_THAW 0 /* has XDR freeze/thaw script methods */ +#define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ +#define JS_HAS_DEBUGGER_KEYWORD 1 /* has hook for debugger keyword */ +#define JS_HAS_CATCH_GUARD 1 /* has exception handling catch guard */ +#define JS_HAS_SPARSE_ARRAYS 0 /* array methods preserve empty elems */ +#define JS_HAS_GETTER_SETTER 1 /* has JS2 getter/setter functions */ +#define JS_HAS_UNEVAL 1 /* has uneval() top-level function */ +#define JS_HAS_CONST 1 /* has JS2 const as alternative var */ +#define JS_HAS_FUN_EXPR_STMT 1 /* has function expression statement */ +#define JS_HAS_NO_SUCH_METHOD 1 /* has o.__noSuchMethod__ handler */ +#define JS_HAS_XML_SUPPORT 1 /* has ECMAScript for XML support */ +#define JS_HAS_ARRAY_EXTRAS 1 /* has indexOf and Lispy extras */ +#define JS_HAS_GENERATORS 1 /* has yield in generator function */ +#define JS_HAS_BLOCK_SCOPE 1 /* has block scope via let/arraycomp */ +#define JS_HAS_DESTRUCTURING 2 /* has [a,b] = ... or {p:a,q:b} = ... */ +#define JS_HAS_GENERATOR_EXPRS 1 /* has (expr for (lhs in iterable)) */ +#define JS_HAS_EXPR_CLOSURES 1 /* has function (formals) listexpr */ + +#else + +#error "unknown JS_VERSION" + +#endif + +/* Features that are present in all versions. */ +#define JS_HAS_RESERVED_JAVA_KEYWORDS 1 +#define JS_HAS_RESERVED_ECMA_KEYWORDS 1 + +/* Feature-test macro for evolving destructuring support. */ +#define JS_HAS_DESTRUCTURING_SHORTHAND (JS_HAS_DESTRUCTURING == 2) diff --git a/ape-server/deps/js/src/jswince.asm b/ape-server/deps/js/src/jswince.asm new file mode 100755 index 0000000..6a2e1eb --- /dev/null +++ b/ape-server/deps/js/src/jswince.asm @@ -0,0 +1,82 @@ + INCLUDE kxarm.h + + area js_msvc, code, readonly + + MACRO + FUNC_HEADER $Name +FuncName SETS VBar:CC:"$Name":CC:VBar +PrologName SETS VBar:CC:"$Name":CC:"_Prolog":CC:VBar +FuncEndName SETS VBar:CC:"$Name":CC:"_end":CC:VBar + + AREA |.pdata|,ALIGN=2,PDATA + DCD $FuncName + DCD (($PrologName-$FuncName)/4) :OR: ((($FuncEndName-$FuncName)/4):SHL:8) :OR: 0x40000000 + AREA $AreaName,CODE,READONLY + ALIGN 2 + GLOBAL $FuncName + EXPORT $FuncName +$FuncName + ROUT +$PrologName + MEND + + ;; -------- Functions to test processor features. + export js_arm_try_thumb_op + export js_arm_try_armv6t2_op + export js_arm_try_armv7_op + export js_arm_try_armv6_op + export js_arm_try_armv5_op + export js_arm_try_vfp_op + + ;; Test for Thumb support. + FUNC_HEADER js_arm_try_thumb_op + bx lr + mov pc, lr + ENTRY_END + endp + + ;; I'm not smart enough to figure out which flags to pass to armasm to get it + ;; to understand movt and fmdrr/vmov; the disassembler figures them out just fine! + + ;; Test for Thumb2 support. + FUNC_HEADER js_arm_try_armv6t2_op + ;; movt r0,#0xFFFF + DCD 0xE34F0FFF + mov pc,lr + ENTRY_END + endp + + ;; Test for VFP support. + FUNC_HEADER js_arm_try_vfp_op + ;; fmdrr d0, r0, r1 + DCD 0xEC410B10 + mov pc,lr + ENTRY_END + endp + + ;; Tests for each architecture version. + + FUNC_HEADER js_arm_try_armv7_op + ;; pli pc, #0 + DCD 0xF45FF000 + mov pc, lr + ENTRY_END + endp + + FUNC_HEADER js_arm_try_armv6_op + ;; rev ip, ip + DCD 0xE6BFCF3C + mov pc, lr + ENTRY_END + endp + + FUNC_HEADER js_arm_try_armv5_op + ;; clz ip, ip + DCD 0xE16FCF1C + mov pc, lr + ENTRY_END + endp + + ;; -------- + + end diff --git a/ape-server/deps/js/src/jsxdrapi.cpp b/ape-server/deps/js/src/jsxdrapi.cpp new file mode 100755 index 0000000..5f5707f --- /dev/null +++ b/ape-server/deps/js/src/jsxdrapi.cpp @@ -0,0 +1,794 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +#include "jsversion.h" + +#if JS_HAS_XDR + +#include +#include "jstypes.h" +#include "jsstdint.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jsdhash.h" +#include "jsprf.h" +#include "jsapi.h" +#include "jscntxt.h" +#include "jsnum.h" +#include "jsobj.h" /* js_XDRObject */ +#include "jsscript.h" /* js_XDRScript */ +#include "jsstr.h" +#include "jsxdrapi.h" + +#ifdef DEBUG +#define DBG(x) x +#else +#define DBG(x) ((void)0) +#endif + +typedef struct JSXDRMemState { + JSXDRState state; + char *base; + uint32 count; + uint32 limit; +} JSXDRMemState; + +#define MEM_BLOCK 8192 +#define MEM_PRIV(xdr) ((JSXDRMemState *)(xdr)) + +#define MEM_BASE(xdr) (MEM_PRIV(xdr)->base) +#define MEM_COUNT(xdr) (MEM_PRIV(xdr)->count) +#define MEM_LIMIT(xdr) (MEM_PRIV(xdr)->limit) + +#define MEM_LEFT(xdr, bytes) \ + JS_BEGIN_MACRO \ + if ((xdr)->mode == JSXDR_DECODE && \ + MEM_COUNT(xdr) + bytes > MEM_LIMIT(xdr)) { \ + JS_ReportErrorNumber((xdr)->cx, js_GetErrorMessage, NULL, \ + JSMSG_END_OF_DATA); \ + return 0; \ + } \ + JS_END_MACRO + +#define MEM_NEED(xdr, bytes) \ + JS_BEGIN_MACRO \ + if ((xdr)->mode == JSXDR_ENCODE) { \ + if (MEM_LIMIT(xdr) && \ + MEM_COUNT(xdr) + bytes > MEM_LIMIT(xdr)) { \ + uint32 limit_ = JS_ROUNDUP(MEM_COUNT(xdr) + bytes, MEM_BLOCK);\ + void *data_ = (xdr)->cx->realloc(MEM_BASE(xdr), limit_); \ + if (!data_) \ + return 0; \ + MEM_BASE(xdr) = (char *) data_; \ + MEM_LIMIT(xdr) = limit_; \ + } \ + } else { \ + MEM_LEFT(xdr, bytes); \ + } \ + JS_END_MACRO + +#define MEM_DATA(xdr) ((void *)(MEM_BASE(xdr) + MEM_COUNT(xdr))) +#define MEM_INCR(xdr,bytes) (MEM_COUNT(xdr) += (bytes)) + +static JSBool +mem_get32(JSXDRState *xdr, uint32 *lp) +{ + MEM_LEFT(xdr, 4); + *lp = *(uint32 *)MEM_DATA(xdr); + MEM_INCR(xdr, 4); + return JS_TRUE; +} + +static JSBool +mem_set32(JSXDRState *xdr, uint32 *lp) +{ + MEM_NEED(xdr, 4); + *(uint32 *)MEM_DATA(xdr) = *lp; + MEM_INCR(xdr, 4); + return JS_TRUE; +} + +static JSBool +mem_getbytes(JSXDRState *xdr, char *bytes, uint32 len) +{ + MEM_LEFT(xdr, len); + memcpy(bytes, MEM_DATA(xdr), len); + MEM_INCR(xdr, len); + return JS_TRUE; +} + +static JSBool +mem_setbytes(JSXDRState *xdr, char *bytes, uint32 len) +{ + MEM_NEED(xdr, len); + memcpy(MEM_DATA(xdr), bytes, len); + MEM_INCR(xdr, len); + return JS_TRUE; +} + +static void * +mem_raw(JSXDRState *xdr, uint32 len) +{ + void *data; + if (xdr->mode == JSXDR_ENCODE) { + MEM_NEED(xdr, len); + } else if (xdr->mode == JSXDR_DECODE) { + MEM_LEFT(xdr, len); + } + data = MEM_DATA(xdr); + MEM_INCR(xdr, len); + return data; +} + +static JSBool +mem_seek(JSXDRState *xdr, int32 offset, JSXDRWhence whence) +{ + switch (whence) { + case JSXDR_SEEK_CUR: + if ((int32)MEM_COUNT(xdr) + offset < 0) { + JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, + JSMSG_SEEK_BEYOND_START); + return JS_FALSE; + } + if (offset > 0) + MEM_NEED(xdr, offset); + MEM_COUNT(xdr) += offset; + return JS_TRUE; + case JSXDR_SEEK_SET: + if (offset < 0) { + JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, + JSMSG_SEEK_BEYOND_START); + return JS_FALSE; + } + if (xdr->mode == JSXDR_ENCODE) { + if ((uint32)offset > MEM_COUNT(xdr)) + MEM_NEED(xdr, offset - MEM_COUNT(xdr)); + MEM_COUNT(xdr) = offset; + } else { + if ((uint32)offset > MEM_LIMIT(xdr)) { + JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, + JSMSG_SEEK_BEYOND_END); + return JS_FALSE; + } + MEM_COUNT(xdr) = offset; + } + return JS_TRUE; + case JSXDR_SEEK_END: + if (offset >= 0 || + xdr->mode == JSXDR_ENCODE || + (int32)MEM_LIMIT(xdr) + offset < 0) { + JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, + JSMSG_END_SEEK); + return JS_FALSE; + } + MEM_COUNT(xdr) = MEM_LIMIT(xdr) + offset; + return JS_TRUE; + default: { + char numBuf[12]; + JS_snprintf(numBuf, sizeof numBuf, "%d", whence); + JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, + JSMSG_WHITHER_WHENCE, numBuf); + return JS_FALSE; + } + } +} + +static uint32 +mem_tell(JSXDRState *xdr) +{ + return MEM_COUNT(xdr); +} + +static void +mem_finalize(JSXDRState *xdr) +{ + xdr->cx->free(MEM_BASE(xdr)); +} + +static JSXDROps xdrmem_ops = { + mem_get32, mem_set32, mem_getbytes, mem_setbytes, + mem_raw, mem_seek, mem_tell, mem_finalize +}; + +JS_PUBLIC_API(void) +JS_XDRInitBase(JSXDRState *xdr, JSXDRMode mode, JSContext *cx) +{ + xdr->mode = mode; + xdr->cx = cx; + xdr->registry = NULL; + xdr->numclasses = xdr->maxclasses = 0; + xdr->reghash = NULL; + xdr->userdata = NULL; + xdr->script = NULL; +} + +JS_PUBLIC_API(JSXDRState *) +JS_XDRNewMem(JSContext *cx, JSXDRMode mode) +{ + JSXDRState *xdr = (JSXDRState *) cx->malloc(sizeof(JSXDRMemState)); + if (!xdr) + return NULL; + JS_XDRInitBase(xdr, mode, cx); + if (mode == JSXDR_ENCODE) { + if (!(MEM_BASE(xdr) = (char *) cx->malloc(MEM_BLOCK))) { + cx->free(xdr); + return NULL; + } + } else { + /* XXXbe ok, so better not deref MEM_BASE(xdr) if not ENCODE */ + MEM_BASE(xdr) = NULL; + } + xdr->ops = &xdrmem_ops; + MEM_COUNT(xdr) = 0; + MEM_LIMIT(xdr) = MEM_BLOCK; + return xdr; +} + +JS_PUBLIC_API(void *) +JS_XDRMemGetData(JSXDRState *xdr, uint32 *lp) +{ + if (xdr->ops != &xdrmem_ops) + return NULL; + *lp = MEM_COUNT(xdr); + return MEM_BASE(xdr); +} + +JS_PUBLIC_API(void) +JS_XDRMemSetData(JSXDRState *xdr, void *data, uint32 len) +{ + if (xdr->ops != &xdrmem_ops) + return; + MEM_LIMIT(xdr) = len; + MEM_BASE(xdr) = (char *) data; + MEM_COUNT(xdr) = 0; +} + +JS_PUBLIC_API(uint32) +JS_XDRMemDataLeft(JSXDRState *xdr) +{ + if (xdr->ops != &xdrmem_ops) + return 0; + return MEM_LIMIT(xdr) - MEM_COUNT(xdr); +} + +JS_PUBLIC_API(void) +JS_XDRMemResetData(JSXDRState *xdr) +{ + if (xdr->ops != &xdrmem_ops) + return; + MEM_COUNT(xdr) = 0; +} + +JS_PUBLIC_API(void) +JS_XDRDestroy(JSXDRState *xdr) +{ + JSContext *cx = xdr->cx; + xdr->ops->finalize(xdr); + if (xdr->registry) { + cx->free(xdr->registry); + if (xdr->reghash) + JS_DHashTableDestroy((JSDHashTable *) xdr->reghash); + } + cx->free(xdr); +} + +JS_PUBLIC_API(JSBool) +JS_XDRUint8(JSXDRState *xdr, uint8 *b) +{ + uint32 l = *b; + if (!JS_XDRUint32(xdr, &l)) + return JS_FALSE; + *b = (uint8) l; + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_XDRUint16(JSXDRState *xdr, uint16 *s) +{ + uint32 l = *s; + if (!JS_XDRUint32(xdr, &l)) + return JS_FALSE; + *s = (uint16) l; + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_XDRUint32(JSXDRState *xdr, uint32 *lp) +{ + JSBool ok = JS_TRUE; + if (xdr->mode == JSXDR_ENCODE) { + uint32 xl = JSXDR_SWAB32(*lp); + ok = xdr->ops->set32(xdr, &xl); + } else if (xdr->mode == JSXDR_DECODE) { + ok = xdr->ops->get32(xdr, lp); + *lp = JSXDR_SWAB32(*lp); + } + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_XDRBytes(JSXDRState *xdr, char *bytes, uint32 len) +{ + uint32 padlen; + static char padbuf[JSXDR_ALIGN-1]; + + if (xdr->mode == JSXDR_ENCODE) { + if (!xdr->ops->setbytes(xdr, bytes, len)) + return JS_FALSE; + } else { + if (!xdr->ops->getbytes(xdr, bytes, len)) + return JS_FALSE; + } + len = xdr->ops->tell(xdr); + if (len % JSXDR_ALIGN) { + padlen = JSXDR_ALIGN - (len % JSXDR_ALIGN); + if (xdr->mode == JSXDR_ENCODE) { + if (!xdr->ops->setbytes(xdr, padbuf, padlen)) + return JS_FALSE; + } else { + if (!xdr->ops->seek(xdr, padlen, JSXDR_SEEK_CUR)) + return JS_FALSE; + } + } + return JS_TRUE; +} + +/** + * Convert between a C string and the XDR representation: + * leading 32-bit count, then counted vector of chars, + * then possibly \0 padding to multiple of 4. + */ +JS_PUBLIC_API(JSBool) +JS_XDRCString(JSXDRState *xdr, char **sp) +{ + uint32 len; + + if (xdr->mode == JSXDR_ENCODE) + len = strlen(*sp); + JS_XDRUint32(xdr, &len); + if (xdr->mode == JSXDR_DECODE) { + if (!(*sp = (char *) xdr->cx->malloc(len + 1))) + return JS_FALSE; + } + if (!JS_XDRBytes(xdr, *sp, len)) { + if (xdr->mode == JSXDR_DECODE) + xdr->cx->free(*sp); + return JS_FALSE; + } + if (xdr->mode == JSXDR_DECODE) { + (*sp)[len] = '\0'; + } else if (xdr->mode == JSXDR_FREE) { + xdr->cx->free(*sp); + *sp = NULL; + } + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_XDRCStringOrNull(JSXDRState *xdr, char **sp) +{ + uint32 null = (*sp == NULL); + if (!JS_XDRUint32(xdr, &null)) + return JS_FALSE; + if (null) { + *sp = NULL; + return JS_TRUE; + } + return JS_XDRCString(xdr, sp); +} + +static JSBool +XDRChars(JSXDRState *xdr, jschar *chars, uint32 nchars) +{ + uint32 i, padlen, nbytes; + jschar *raw; + + nbytes = nchars * sizeof(jschar); + padlen = nbytes % JSXDR_ALIGN; + if (padlen) { + padlen = JSXDR_ALIGN - padlen; + nbytes += padlen; + } + if (!(raw = (jschar *) xdr->ops->raw(xdr, nbytes))) + return JS_FALSE; + if (xdr->mode == JSXDR_ENCODE) { + for (i = 0; i != nchars; i++) + raw[i] = JSXDR_SWAB16(chars[i]); + if (padlen) + memset((char *)raw + nbytes - padlen, 0, padlen); + } else if (xdr->mode == JSXDR_DECODE) { + for (i = 0; i != nchars; i++) + chars[i] = JSXDR_SWAB16(raw[i]); + } + return JS_TRUE; +} + +/* + * Convert between a JS (Unicode) string and the XDR representation. + */ +JS_PUBLIC_API(JSBool) +JS_XDRString(JSXDRState *xdr, JSString **strp) +{ + uint32 nchars; + jschar *chars; + + if (xdr->mode == JSXDR_ENCODE) + nchars = (*strp)->length(); + if (!JS_XDRUint32(xdr, &nchars)) + return JS_FALSE; + + if (xdr->mode == JSXDR_DECODE) { + chars = (jschar *) xdr->cx->malloc((nchars + 1) * sizeof(jschar)); + if (!chars) + return JS_FALSE; + } else { + chars = (*strp)->chars(); + } + + if (!XDRChars(xdr, chars, nchars)) + goto bad; + if (xdr->mode == JSXDR_DECODE) { + chars[nchars] = 0; + *strp = JS_NewUCString(xdr->cx, chars, nchars); + if (!*strp) + goto bad; + } + return JS_TRUE; + +bad: + if (xdr->mode == JSXDR_DECODE) + xdr->cx->free(chars); + return JS_FALSE; +} + +JS_PUBLIC_API(JSBool) +JS_XDRStringOrNull(JSXDRState *xdr, JSString **strp) +{ + uint32 null = (*strp == NULL); + if (!JS_XDRUint32(xdr, &null)) + return JS_FALSE; + if (null) { + *strp = NULL; + return JS_TRUE; + } + return JS_XDRString(xdr, strp); +} + +static JSBool +XDRDoubleValue(JSXDRState *xdr, jsdouble *dp) +{ + jsdpun u; + + u.d = (xdr->mode == JSXDR_ENCODE) ? *dp : 0.0; + if (!JS_XDRUint32(xdr, &u.s.lo) || !JS_XDRUint32(xdr, &u.s.hi)) + return JS_FALSE; + if (xdr->mode == JSXDR_DECODE) + *dp = u.d; + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_XDRDouble(JSXDRState *xdr, jsdouble **dpp) +{ + jsdouble d = (xdr->mode == JSXDR_ENCODE) ? **dpp : 0.0; + if (!XDRDoubleValue(xdr, &d)) + return JS_FALSE; + if (xdr->mode == JSXDR_DECODE) { + *dpp = JS_NewDouble(xdr->cx, d); + if (!*dpp) + return JS_FALSE; + } + return JS_TRUE; +} + +/* These are magic pseudo-tags: see jsapi.h, near the top, for real tags. */ +#define JSVAL_XDRNULL 0x8 +#define JSVAL_XDRVOID 0xA + +static JSBool +XDRValueBody(JSXDRState *xdr, uint32 type, jsval *vp) +{ + switch (type) { + case JSVAL_XDRNULL: + *vp = JSVAL_NULL; + break; + case JSVAL_XDRVOID: + *vp = JSVAL_VOID; + break; + case JSVAL_STRING: { + JSString *str; + if (xdr->mode == JSXDR_ENCODE) + str = JSVAL_TO_STRING(*vp); + if (!JS_XDRString(xdr, &str)) + return JS_FALSE; + if (xdr->mode == JSXDR_DECODE) + *vp = STRING_TO_JSVAL(str); + break; + } + case JSVAL_DOUBLE: { + jsdouble *dp = (xdr->mode == JSXDR_ENCODE) ? JSVAL_TO_DOUBLE(*vp) : NULL; + if (!JS_XDRDouble(xdr, &dp)) + return JS_FALSE; + if (xdr->mode == JSXDR_DECODE) + *vp = DOUBLE_TO_JSVAL(dp); + break; + } + case JSVAL_OBJECT: { + JSObject *obj; + if (xdr->mode == JSXDR_ENCODE) + obj = JSVAL_TO_OBJECT(*vp); + if (!js_XDRObject(xdr, &obj)) + return JS_FALSE; + if (xdr->mode == JSXDR_DECODE) + *vp = OBJECT_TO_JSVAL(obj); + break; + } + case JSVAL_SPECIAL: { + uint32 b; + if (xdr->mode == JSXDR_ENCODE) + b = (uint32) JSVAL_TO_BOOLEAN(*vp); + if (!JS_XDRUint32(xdr, &b)) + return JS_FALSE; + if (xdr->mode == JSXDR_DECODE) + *vp = BOOLEAN_TO_JSVAL(!!b); + break; + } + default: { + uint32 i; + + JS_ASSERT(type & JSVAL_INT); + if (xdr->mode == JSXDR_ENCODE) + i = (uint32) JSVAL_TO_INT(*vp); + if (!JS_XDRUint32(xdr, &i)) + return JS_FALSE; + if (xdr->mode == JSXDR_DECODE) + *vp = INT_TO_JSVAL((int32) i); + break; + } + } + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_XDRValue(JSXDRState *xdr, jsval *vp) +{ + uint32 type; + + if (xdr->mode == JSXDR_ENCODE) { + if (JSVAL_IS_NULL(*vp)) + type = JSVAL_XDRNULL; + else if (JSVAL_IS_VOID(*vp)) + type = JSVAL_XDRVOID; + else + type = JSVAL_TAG(*vp); + } + return JS_XDRUint32(xdr, &type) && XDRValueBody(xdr, type, vp); +} + +JSBool +js_XDRAtom(JSXDRState *xdr, JSAtom **atomp) +{ + jsval v; + uint32 type; + + if (xdr->mode == JSXDR_ENCODE) { + v = ATOM_KEY(*atomp); + return JS_XDRValue(xdr, &v); + } + + /* + * Inline JS_XDRValue when decoding to avoid ceation of GC things when + * then corresponding atom already exists. See bug 321985. + */ + if (!JS_XDRUint32(xdr, &type)) + return JS_FALSE; + if (type == JSVAL_STRING) + return js_XDRStringAtom(xdr, atomp); + + if (type == JSVAL_DOUBLE) { + jsdouble d = 0; + if (!XDRDoubleValue(xdr, &d)) + return JS_FALSE; + *atomp = js_AtomizeDouble(xdr->cx, d); + return *atomp != NULL; + } + + return XDRValueBody(xdr, type, &v) && + js_AtomizePrimitiveValue(xdr->cx, v, atomp); +} + +extern JSBool +js_XDRStringAtom(JSXDRState *xdr, JSAtom **atomp) +{ + JSString *str; + uint32 nchars; + JSAtom *atom; + JSContext *cx; + jschar *chars; + jschar stackChars[256]; + + if (xdr->mode == JSXDR_ENCODE) { + JS_ASSERT(ATOM_IS_STRING(*atomp)); + str = ATOM_TO_STRING(*atomp); + return JS_XDRString(xdr, &str); + } + + /* + * Inline JS_XDRString when decoding to avoid JSString allocation + * for already existing atoms. See bug 321985. + */ + if (!JS_XDRUint32(xdr, &nchars)) + return JS_FALSE; + atom = NULL; + cx = xdr->cx; + if (nchars <= JS_ARRAY_LENGTH(stackChars)) { + chars = stackChars; + } else { + /* + * This is very uncommon. Don't use the tempPool arena for this as + * most allocations here will be bigger than tempPool's arenasize. + */ + chars = (jschar *) cx->malloc(nchars * sizeof(jschar)); + if (!chars) + return JS_FALSE; + } + + if (XDRChars(xdr, chars, nchars)) + atom = js_AtomizeChars(cx, chars, nchars, 0); + if (chars != stackChars) + cx->free(chars); + + if (!atom) + return JS_FALSE; + *atomp = atom; + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_XDRScript(JSXDRState *xdr, JSScript **scriptp) +{ + if (!js_XDRScript(xdr, scriptp, true, NULL)) + return JS_FALSE; + if (xdr->mode == JSXDR_DECODE) + js_CallNewScriptHook(xdr->cx, *scriptp, NULL); + return JS_TRUE; +} + +#define CLASS_REGISTRY_MIN 8 +#define CLASS_INDEX_TO_ID(i) ((i)+1) +#define CLASS_ID_TO_INDEX(id) ((id)-1) + +typedef struct JSRegHashEntry { + JSDHashEntryHdr hdr; + const char *name; + uint32 index; +} JSRegHashEntry; + +JS_PUBLIC_API(JSBool) +JS_XDRRegisterClass(JSXDRState *xdr, JSClass *clasp, uint32 *idp) +{ + uintN numclasses, maxclasses; + JSClass **registry; + + numclasses = xdr->numclasses; + maxclasses = xdr->maxclasses; + if (numclasses == maxclasses) { + maxclasses = (maxclasses == 0) ? CLASS_REGISTRY_MIN : maxclasses << 1; + registry = (JSClass **) + xdr->cx->realloc(xdr->registry, maxclasses * sizeof(JSClass *)); + if (!registry) + return JS_FALSE; + xdr->registry = registry; + xdr->maxclasses = maxclasses; + } else { + JS_ASSERT(numclasses && numclasses < maxclasses); + registry = xdr->registry; + } + + registry[numclasses] = clasp; + if (xdr->reghash) { + JSRegHashEntry *entry = (JSRegHashEntry *) + JS_DHashTableOperate((JSDHashTable *) xdr->reghash, + clasp->name, JS_DHASH_ADD); + if (!entry) { + JS_ReportOutOfMemory(xdr->cx); + return JS_FALSE; + } + entry->name = clasp->name; + entry->index = numclasses; + } + *idp = CLASS_INDEX_TO_ID(numclasses); + xdr->numclasses = ++numclasses; + return JS_TRUE; +} + +JS_PUBLIC_API(uint32) +JS_XDRFindClassIdByName(JSXDRState *xdr, const char *name) +{ + uintN i, numclasses; + + numclasses = xdr->numclasses; + if (numclasses >= 10) { + JSRegHashEntry *entry; + + /* Bootstrap reghash from registry on first overpopulated Find. */ + if (!xdr->reghash) { + xdr->reghash = + JS_NewDHashTable(JS_DHashGetStubOps(), NULL, + sizeof(JSRegHashEntry), + JS_DHASH_DEFAULT_CAPACITY(numclasses)); + if (xdr->reghash) { + for (i = 0; i < numclasses; i++) { + JSClass *clasp = xdr->registry[i]; + entry = (JSRegHashEntry *) + JS_DHashTableOperate((JSDHashTable *) xdr->reghash, + clasp->name, JS_DHASH_ADD); + entry->name = clasp->name; + entry->index = i; + } + } + } + + /* If we managed to create reghash, use it for O(1) Find. */ + if (xdr->reghash) { + entry = (JSRegHashEntry *) + JS_DHashTableOperate((JSDHashTable *) xdr->reghash, + name, JS_DHASH_LOOKUP); + if (JS_DHASH_ENTRY_IS_BUSY(&entry->hdr)) + return CLASS_INDEX_TO_ID(entry->index); + } + } + + /* Only a few classes, or we couldn't malloc reghash: use linear search. */ + for (i = 0; i < numclasses; i++) { + if (!strcmp(name, xdr->registry[i]->name)) + return CLASS_INDEX_TO_ID(i); + } + return 0; +} + +JS_PUBLIC_API(JSClass *) +JS_XDRFindClassById(JSXDRState *xdr, uint32 id) +{ + uintN i = CLASS_ID_TO_INDEX(id); + + if (i >= xdr->numclasses) + return NULL; + return xdr->registry[i]; +} + +#endif /* JS_HAS_XDR */ diff --git a/ape-server/deps/js/src/jsxdrapi.h b/ape-server/deps/js/src/jsxdrapi.h new file mode 100755 index 0000000..14063b0 --- /dev/null +++ b/ape-server/deps/js/src/jsxdrapi.h @@ -0,0 +1,221 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsxdrapi_h___ +#define jsxdrapi_h___ + +/* + * JS external data representation interface API. + * + * The XDR system is comprised of three major parts: + * + * - the state serialization/deserialization APIs, which allow consumers + * of the API to serialize JS runtime state (script bytecodes, atom maps, + * object graphs, etc.) for later restoration. These portions + * are implemented in various appropriate files, such as jsscript.c + * for the script portions and jsobj.c for object state. + * - the callback APIs through which the runtime requests an opaque + * representation of a native object, and through which the runtime + * constructs a live native object from an opaque representation. These + * portions are the responsibility of the native object implementor. + * - utility functions for en/decoding of primitive types, such as + * JSStrings. This portion is implemented in jsxdrapi.c. + * + * Spiritually guided by Sun's XDR, where appropriate. + */ + +#include "jspubtd.h" +#include "jsprvtd.h" + +JS_BEGIN_EXTERN_C + +/* We use little-endian byteorder for all encoded data */ + +#if defined IS_LITTLE_ENDIAN +#define JSXDR_SWAB32(x) x +#define JSXDR_SWAB16(x) x +#elif defined IS_BIG_ENDIAN +#define JSXDR_SWAB32(x) (((uint32)(x) >> 24) | \ + (((uint32)(x) >> 8) & 0xff00) | \ + (((uint32)(x) << 8) & 0xff0000) | \ + ((uint32)(x) << 24)) +#define JSXDR_SWAB16(x) (((uint16)(x) >> 8) | ((uint16)(x) << 8)) +#else +#error "unknown byte order" +#endif + +#define JSXDR_ALIGN 4 + +typedef enum JSXDRMode { + JSXDR_ENCODE, + JSXDR_DECODE, + JSXDR_FREE +} JSXDRMode; + +typedef enum JSXDRWhence { + JSXDR_SEEK_SET, + JSXDR_SEEK_CUR, + JSXDR_SEEK_END +} JSXDRWhence; + +typedef struct JSXDROps { + JSBool (*get32)(JSXDRState *, uint32 *); + JSBool (*set32)(JSXDRState *, uint32 *); + JSBool (*getbytes)(JSXDRState *, char *, uint32); + JSBool (*setbytes)(JSXDRState *, char *, uint32); + void * (*raw)(JSXDRState *, uint32); + JSBool (*seek)(JSXDRState *, int32, JSXDRWhence); + uint32 (*tell)(JSXDRState *); + void (*finalize)(JSXDRState *); +} JSXDROps; + +struct JSXDRState { + JSXDRMode mode; + JSXDROps *ops; + JSContext *cx; + JSClass **registry; + uintN numclasses; + uintN maxclasses; + void *reghash; + void *userdata; + JSScript *script; +}; + +extern JS_PUBLIC_API(void) +JS_XDRInitBase(JSXDRState *xdr, JSXDRMode mode, JSContext *cx); + +extern JS_PUBLIC_API(JSXDRState *) +JS_XDRNewMem(JSContext *cx, JSXDRMode mode); + +extern JS_PUBLIC_API(void *) +JS_XDRMemGetData(JSXDRState *xdr, uint32 *lp); + +extern JS_PUBLIC_API(void) +JS_XDRMemSetData(JSXDRState *xdr, void *data, uint32 len); + +extern JS_PUBLIC_API(uint32) +JS_XDRMemDataLeft(JSXDRState *xdr); + +extern JS_PUBLIC_API(void) +JS_XDRMemResetData(JSXDRState *xdr); + +extern JS_PUBLIC_API(void) +JS_XDRDestroy(JSXDRState *xdr); + +extern JS_PUBLIC_API(JSBool) +JS_XDRUint8(JSXDRState *xdr, uint8 *b); + +extern JS_PUBLIC_API(JSBool) +JS_XDRUint16(JSXDRState *xdr, uint16 *s); + +extern JS_PUBLIC_API(JSBool) +JS_XDRUint32(JSXDRState *xdr, uint32 *lp); + +extern JS_PUBLIC_API(JSBool) +JS_XDRBytes(JSXDRState *xdr, char *bytes, uint32 len); + +extern JS_PUBLIC_API(JSBool) +JS_XDRCString(JSXDRState *xdr, char **sp); + +extern JS_PUBLIC_API(JSBool) +JS_XDRCStringOrNull(JSXDRState *xdr, char **sp); + +extern JS_PUBLIC_API(JSBool) +JS_XDRString(JSXDRState *xdr, JSString **strp); + +extern JS_PUBLIC_API(JSBool) +JS_XDRStringOrNull(JSXDRState *xdr, JSString **strp); + +extern JS_PUBLIC_API(JSBool) +JS_XDRDouble(JSXDRState *xdr, jsdouble **dp); + +extern JS_PUBLIC_API(JSBool) +JS_XDRValue(JSXDRState *xdr, jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_XDRScript(JSXDRState *xdr, JSScript **scriptp); + +extern JS_PUBLIC_API(JSBool) +JS_XDRRegisterClass(JSXDRState *xdr, JSClass *clasp, uint32 *lp); + +extern JS_PUBLIC_API(uint32) +JS_XDRFindClassIdByName(JSXDRState *xdr, const char *name); + +extern JS_PUBLIC_API(JSClass *) +JS_XDRFindClassById(JSXDRState *xdr, uint32 id); + +/* + * Magic numbers. + */ +#define JSXDR_MAGIC_SCRIPT_1 0xdead0001 +#define JSXDR_MAGIC_SCRIPT_2 0xdead0002 +#define JSXDR_MAGIC_SCRIPT_3 0xdead0003 +#define JSXDR_MAGIC_SCRIPT_4 0xdead0004 +#define JSXDR_MAGIC_SCRIPT_5 0xdead0005 +#define JSXDR_MAGIC_SCRIPT_6 0xdead0006 +#define JSXDR_MAGIC_SCRIPT_7 0xdead0007 +#define JSXDR_MAGIC_SCRIPT_8 0xdead0008 +#define JSXDR_MAGIC_SCRIPT_9 0xdead0009 +#define JSXDR_MAGIC_SCRIPT_10 0xdead000a +#define JSXDR_MAGIC_SCRIPT_CURRENT JSXDR_MAGIC_SCRIPT_10 + +/* + * Bytecode version number. Increment the subtrahend whenever JS bytecode + * changes incompatibly. + * + * This version number should be XDR'ed once near the front of any file or + * larger storage unit containing XDR'ed bytecode and other data, and checked + * before deserialization of bytecode. If the saved version does not match + * the current version, abort deserialization and invalidate the file. + */ +#define JSXDR_BYTECODE_VERSION (0xb973c0de - 58) + +/* + * Library-private functions. + */ +extern JSBool +js_XDRAtom(JSXDRState *xdr, JSAtom **atomp); + +extern JSBool +js_XDRStringAtom(JSXDRState *xdr, JSAtom **atomp); + +JS_END_EXTERN_C + +#endif /* ! jsxdrapi_h___ */ diff --git a/ape-server/deps/js/src/jsxml.cpp b/ape-server/deps/js/src/jsxml.cpp new file mode 100755 index 0000000..31b3ecc --- /dev/null +++ b/ape-server/deps/js/src/jsxml.cpp @@ -0,0 +1,8155 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is SpiderMonkey E4X code, released August, 2004. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "jsversion.h" + +#if JS_HAS_XML_SUPPORT + +#include +#include +#include +#include "jstypes.h" +#include "jsstdint.h" +#include "jsbit.h" +#include "jsprf.h" +#include "jsutil.h" +#include "jsapi.h" +#include "jsarray.h" +#include "jsatom.h" +#include "jsbool.h" +#include "jscntxt.h" +#include "jsfun.h" +#include "jsgc.h" +#include "jsinterp.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsobj.h" +#include "jsopcode.h" +#include "jsparse.h" +#include "jsscan.h" +#include "jsscope.h" +#include "jsscript.h" +#include "jsstr.h" +#include "jsxml.h" +#include "jsstaticcheck.h" +#include "jsvector.h" + +#ifdef DEBUG +#include /* for #ifdef DEBUG memset calls */ +#endif + +/* + * NOTES + * - in the js shell, you must use the -x command line option, or call + * options('xml') before compiling anything that uses XML literals + * + * TODO + * - XXXbe patrol + * - Fuse objects and their JSXML* private data into single GC-things + * - fix function::foo vs. x.(foo == 42) collision using proper namespacing + * - JSCLASS_DOCUMENT_OBSERVER support -- live two-way binding to Gecko's DOM! + * - JS_TypeOfValue sure could use a cleaner interface to "types" + */ + +#ifdef XML_METERING +static struct { + jsrefcount qname; + jsrefcount xmlnamespace; + jsrefcount xml; + jsrefcount xmlobj; +} xml_stats; + +#define METER(x) JS_ATOMIC_INCREMENT(&(x)) +#define UNMETER(x) JS_ATOMIC_DECREMENT(&(x)) +#else +#define METER(x) /* nothing */ +#define UNMETER(x) /* nothing */ +#endif + +/* + * Random utilities and global functions. + */ +const char js_isXMLName_str[] = "isXMLName"; +const char js_XMLList_str[] = "XMLList"; +const char js_localName_str[] = "localName"; +const char js_xml_parent_str[] = "parent"; +const char js_prefix_str[] = "prefix"; +const char js_toXMLString_str[] = "toXMLString"; +const char js_uri_str[] = "uri"; + +const char js_amp_entity_str[] = "&"; +const char js_gt_entity_str[] = ">"; +const char js_lt_entity_str[] = "<"; +const char js_quot_entity_str[] = """; + +#define IS_STAR(str) ((str)->length() == 1 && *(str)->chars() == '*') + +/* Slot indexes shared between Namespace and QName objects. */ +const uint32 JSSLOT_PREFIX = JSSLOT_PRIVATE; +const uint32 JSSLOT_URI = JSSLOT_PRIVATE + 1; + +/* Namespace-specific slot. */ +const uint32 JSSLOT_DECLARED = JSSLOT_PRIVATE + 2; + +/* QName-specific slot. */ +const uint32 JSSLOT_LOCAL_NAME = JSSLOT_PRIVATE + 2; + +const uint32 NAMESPACE_RESERVED_SLOTS = 3; +const uint32 QNAME_RESERVED_SLOTS = 3; + +static JSBool +GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp); + +static JSBool +IsQNameClass(JSClass *clasp) +{ + return clasp == &js_QNameClass.base || + clasp == &js_AttributeNameClass || + clasp == &js_AnyNameClass; +} + +static JSString * +GetSlotString(const JSObject *obj, uint32 slot) +{ + jsval v; + + JS_ASSERT(slot == JSSLOT_PREFIX || + slot == JSSLOT_URI || + slot == JSSLOT_LOCAL_NAME); + JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_NamespaceClass.base || + IsQNameClass(STOBJ_GET_CLASS(obj))); + JS_ASSERT_IF(STOBJ_GET_CLASS(obj) == &js_NamespaceClass.base, + slot != JSSLOT_LOCAL_NAME); + + v = obj->fslots[slot]; + if (JSVAL_IS_VOID(v)) + return NULL; + JS_ASSERT(JSVAL_IS_STRING(v)); + return JSVAL_TO_STRING(v); +} + +static JS_INLINE JSString * +GetPrefix(const JSObject *obj) +{ + return GetSlotString(obj, JSSLOT_PREFIX); +} + +static JSString * +GetURI(const JSObject *obj) +{ + return GetSlotString(obj, JSSLOT_URI); +} + +static JSString * +GetLocalName(const JSObject *obj) +{ + return GetSlotString(obj, JSSLOT_LOCAL_NAME); +} + +static JSBool +IsDeclared(const JSObject *obj) +{ + jsval v; + + JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_NamespaceClass.base); + v = obj->fslots[JSSLOT_DECLARED]; + JS_ASSERT(JSVAL_IS_VOID(v) || v == JSVAL_TRUE); + return v == JSVAL_TRUE; +} + +static JSBool +xml_isXMLName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + *rval = BOOLEAN_TO_JSVAL(js_IsXMLName(cx, argv[0])); + return JS_TRUE; +} + +static inline bool +AppendString(JSCharBuffer &cb, JSString *str) +{ + const jschar *chars, *end; + str->getCharsAndEnd(chars, end); + return cb.append(chars, end); +} + +/* + * Namespace class and library functions. + */ +enum namespace_tinyid { + NAMESPACE_PREFIX = -1, + NAMESPACE_URI = -2 +}; + +static JSBool +namespace_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + if (!JSVAL_IS_INT(id)) + return JS_TRUE; + + if (STOBJ_GET_CLASS(obj) != &js_NamespaceClass.base) + return JS_TRUE; + + switch (JSVAL_TO_INT(id)) { + case NAMESPACE_PREFIX: + *vp = obj->fslots[JSSLOT_PREFIX]; + break; + case NAMESPACE_URI: + *vp = obj->fslots[JSSLOT_URI]; + break; + } + return JS_TRUE; +} + +static void +namespace_finalize(JSContext *cx, JSObject *obj) +{ + if (cx->runtime->functionNamespaceObject == obj) + cx->runtime->functionNamespaceObject = NULL; +} + +static JSBool +namespace_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) +{ + JSObject *obj2; + + JS_ASSERT(JSVAL_IS_OBJECT(v)); + obj2 = JSVAL_TO_OBJECT(v); + *bp = (!obj2 || OBJ_GET_CLASS(cx, obj2) != &js_NamespaceClass.base) + ? JS_FALSE + : js_EqualStrings(GetURI(obj), GetURI(obj2)); + return JS_TRUE; +} + +JS_FRIEND_DATA(JSExtendedClass) js_NamespaceClass = { + { "Namespace", + JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED | + JSCLASS_HAS_RESERVED_SLOTS(NAMESPACE_RESERVED_SLOTS) | + JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Namespace), + JS_PropertyStub, JS_PropertyStub, namespace_getProperty, NULL, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, namespace_finalize, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL }, + namespace_equality,NULL, NULL, NULL, + NULL, NULL, NULL, NULL +}; + +#define NAMESPACE_ATTRS \ + (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED) + +static JSPropertySpec namespace_props[] = { + {js_prefix_str, NAMESPACE_PREFIX, NAMESPACE_ATTRS, 0, 0}, + {js_uri_str, NAMESPACE_URI, NAMESPACE_ATTRS, 0, 0}, + {0,0,0,0,0} +}; + +static JSBool +namespace_toString(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *obj; + + obj = JS_THIS_OBJECT(cx, vp); + if (!JS_InstanceOf(cx, obj, &js_NamespaceClass.base, vp)) + return JS_FALSE; + *vp = obj->fslots[JSSLOT_URI]; + return JS_TRUE; +} + +static JSFunctionSpec namespace_methods[] = { + JS_FN(js_toString_str, namespace_toString, 0,0), + JS_FS_END +}; + +static JSObject * +NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri, JSBool declared) +{ + JSObject *obj; + + obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL); + if (!obj) + return JS_FALSE; + JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_PREFIX])); + JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_URI])); + JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_DECLARED])); + if (prefix) + obj->fslots[JSSLOT_PREFIX] = STRING_TO_JSVAL(prefix); + if (uri) + obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(uri); + if (declared) + obj->fslots[JSSLOT_DECLARED] = JSVAL_TRUE; + METER(xml_stats.xmlnamespace); + return obj; +} + +/* + * QName class and library functions. + */ +enum qname_tinyid { + QNAME_URI = -1, + QNAME_LOCALNAME = -2 +}; + +static JSBool +qname_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + if (!JSVAL_IS_INT(id)) + return JS_TRUE; + + if (STOBJ_GET_CLASS(obj) != &js_QNameClass.base) + return JS_TRUE; + + switch (JSVAL_TO_INT(id)) { + case QNAME_URI: + *vp = obj->fslots[JSSLOT_URI]; + if (*vp == JSVAL_VOID) + *vp = JSVAL_NULL; + break; + case QNAME_LOCALNAME: + *vp = obj->fslots[JSSLOT_LOCAL_NAME]; + break; + } + return JS_TRUE; +} + +static void +anyname_finalize(JSContext* cx, JSObject* obj) +{ + /* Make sure the next call to js_GetAnyName doesn't try to use obj. */ + if (cx->runtime->anynameObject == obj) + cx->runtime->anynameObject = NULL; +} + +static JSBool +qname_identity(JSObject *qna, JSObject *qnb) +{ + JSString *uri1 = GetURI(qna); + JSString *uri2 = GetURI(qnb); + + if (!uri1 ^ !uri2) + return JS_FALSE; + if (uri1 && !js_EqualStrings(uri1, uri2)) + return JS_FALSE; + return js_EqualStrings(GetLocalName(qna), GetLocalName(qnb)); +} + +static JSBool +qname_equality(JSContext *cx, JSObject *qn, jsval v, JSBool *bp) +{ + JSObject *obj2; + + JS_ASSERT(JSVAL_IS_OBJECT(v)); + obj2 = JSVAL_TO_OBJECT(v); + *bp = (!obj2 || OBJ_GET_CLASS(cx, obj2) != &js_QNameClass.base) + ? JS_FALSE + : qname_identity(qn, obj2); + return JS_TRUE; +} + +JS_FRIEND_DATA(JSExtendedClass) js_QNameClass = { + { "QName", + JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED | + JSCLASS_HAS_RESERVED_SLOTS(QNAME_RESERVED_SLOTS) | + JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_QName), + JS_PropertyStub, JS_PropertyStub, qname_getProperty, NULL, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL }, + qname_equality, NULL, NULL, NULL, + NULL, NULL, NULL, NULL +}; + +/* + * Classes for the ECMA-357-internal types AttributeName and AnyName, which + * are like QName, except that they have no property getters. They share the + * qname_toString method, and therefore are exposed as constructable objects + * in this implementation. + */ +JS_FRIEND_DATA(JSClass) js_AttributeNameClass = { + js_AttributeName_str, + JSCLASS_CONSTRUCT_PROTOTYPE | + JSCLASS_HAS_RESERVED_SLOTS(QNAME_RESERVED_SLOTS) | + JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_AttributeName), + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL +}; + +JS_FRIEND_DATA(JSClass) js_AnyNameClass = { + js_AnyName_str, + JSCLASS_CONSTRUCT_PROTOTYPE | + JSCLASS_HAS_RESERVED_SLOTS(QNAME_RESERVED_SLOTS) | + JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_AnyName), + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, anyname_finalize, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL +}; + +#define QNAME_ATTRS \ + (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED) + +static JSPropertySpec qname_props[] = { + {js_uri_str, QNAME_URI, QNAME_ATTRS, 0, 0}, + {js_localName_str, QNAME_LOCALNAME, QNAME_ATTRS, 0, 0}, + {0,0,0,0,0} +}; + +static JSBool +qname_toString(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *obj; + JSClass *clasp; + JSString *uri, *str, *qualstr; + size_t length; + jschar *chars; + + obj = JS_THIS_OBJECT(cx, vp); + if (!obj) + return JS_FALSE; + clasp = OBJ_GET_CLASS(cx, obj); + if (clasp != &js_AttributeNameClass && + clasp != &js_AnyNameClass && + !JS_InstanceOf(cx, obj, &js_QNameClass.base, vp + 2)) { + return JS_FALSE; + } + + uri = GetURI(obj); + if (!uri) { + /* No uri means wildcard qualifier. */ + str = ATOM_TO_STRING(cx->runtime->atomState.starQualifierAtom); + } else if (uri->empty()) { + /* Empty string for uri means localName is in no namespace. */ + str = cx->runtime->emptyString; + } else { + qualstr = ATOM_TO_STRING(cx->runtime->atomState.qualifierAtom); + str = js_ConcatStrings(cx, uri, qualstr); + if (!str) + return JS_FALSE; + } + str = js_ConcatStrings(cx, str, GetLocalName(obj)); + if (!str) + return JS_FALSE; + + if (str && clasp == &js_AttributeNameClass) { + length = str->length(); + chars = (jschar *) cx->malloc((length + 2) * sizeof(jschar)); + if (!chars) + return JS_FALSE; + *chars = '@'; + js_strncpy(chars + 1, str->chars(), length); + chars[++length] = 0; + str = js_NewString(cx, chars, length); + if (!str) { + cx->free(chars); + return JS_FALSE; + } + } + + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSFunctionSpec qname_methods[] = { + JS_FN(js_toString_str, qname_toString, 0,0), + JS_FS_END +}; + + +static void +InitXMLQName(JSObject *obj, JSString *uri, JSString *prefix, + JSString *localName) +{ + JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_PREFIX])); + JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_URI])); + JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_LOCAL_NAME])); + if (uri) + obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(uri); + if (prefix) + obj->fslots[JSSLOT_PREFIX] = STRING_TO_JSVAL(prefix); + if (localName) + obj->fslots[JSSLOT_LOCAL_NAME] = STRING_TO_JSVAL(localName); +} + +static JSObject * +NewXMLQName(JSContext *cx, JSString *uri, JSString *prefix, JSString *localName, + JSClass *clasp = &js_QNameClass.base) +{ + JSObject *obj; + + JS_ASSERT(IsQNameClass(clasp)); + obj = js_NewObject(cx, clasp, NULL, NULL); + if (!obj) + return NULL; + InitXMLQName(obj, uri, prefix, localName); + METER(xml_stats.qname); + return obj; +} + +JSObject * +js_ConstructXMLQNameObject(JSContext *cx, jsval nsval, jsval lnval) +{ + jsval argv[2]; + + /* + * ECMA-357 11.1.2, + * The _QualifiedIdentifier : PropertySelector :: PropertySelector_ + * production, step 2. + */ + if (!JSVAL_IS_PRIMITIVE(nsval) && + OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nsval)) == &js_AnyNameClass) { + nsval = JSVAL_NULL; + } + + argv[0] = nsval; + argv[1] = lnval; + return js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 2, argv); +} + +static JSBool +IsXMLName(const jschar *cp, size_t n) +{ + JSBool rv; + jschar c; + + rv = JS_FALSE; + if (n != 0 && JS_ISXMLNSSTART(*cp)) { + while (--n != 0) { + c = *++cp; + if (!JS_ISXMLNS(c)) + return rv; + } + rv = JS_TRUE; + } + return rv; +} + +JSBool +js_IsXMLName(JSContext *cx, jsval v) +{ + JSString *name; + JSErrorReporter older; + + /* + * Inline specialization of the QName constructor called with v passed as + * the only argument, to compute the localName for the constructed qname, + * without actually allocating the object or computing its uri and prefix. + * See ECMA-357 13.1.2.1 step 1 and 13.3.2. + */ + if (!JSVAL_IS_PRIMITIVE(v) && + IsQNameClass(OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)))) { + name = GetLocalName(JSVAL_TO_OBJECT(v)); + } else { + older = JS_SetErrorReporter(cx, NULL); + name = js_ValueToString(cx, v); + JS_SetErrorReporter(cx, older); + if (!name) { + JS_ClearPendingException(cx); + return JS_FALSE; + } + } + + return IsXMLName(name->chars(), name->length()); +} + +/* + * When argc is -1, it indicates argv is empty but the code should behave as + * if argc is 1 and argv[0] is JSVAL_VOID. + */ +static JSBool +NamespaceHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv, + jsval *rval) +{ + jsval urival, prefixval; + JSObject *uriobj; + JSBool isNamespace, isQName; + JSClass *clasp; + JSString *empty, *uri, *prefix; + + isNamespace = isQName = JS_FALSE; +#ifdef __GNUC__ /* suppress bogus gcc warnings */ + uriobj = NULL; +#endif + if (argc <= 0) { + urival = JSVAL_VOID; + } else { + urival = argv[argc > 1]; + if (!JSVAL_IS_PRIMITIVE(urival)) { + uriobj = JSVAL_TO_OBJECT(urival); + clasp = OBJ_GET_CLASS(cx, uriobj); + isNamespace = (clasp == &js_NamespaceClass.base); + isQName = (clasp == &js_QNameClass.base); + } + } + + if (!obj) { + /* Namespace called as function. */ + if (argc == 1 && isNamespace) { + /* Namespace called with one Namespace argument is identity. */ + *rval = urival; + return JS_TRUE; + } + + obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL); + if (!obj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + } + METER(xml_stats.xmlnamespace); + + empty = cx->runtime->emptyString; + obj->fslots[JSSLOT_PREFIX] = STRING_TO_JSVAL(empty); + obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(empty); + + if (argc == 1 || argc == -1) { + if (isNamespace) { + obj->fslots[JSSLOT_URI] = uriobj->fslots[JSSLOT_URI]; + obj->fslots[JSSLOT_PREFIX] = uriobj->fslots[JSSLOT_PREFIX]; + } else if (isQName && (uri = GetURI(uriobj))) { + obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(uri); + obj->fslots[JSSLOT_PREFIX] = uriobj->fslots[JSSLOT_PREFIX]; + } else { + uri = js_ValueToString(cx, urival); + if (!uri) + return JS_FALSE; + obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(uri); + if (!uri->empty()) + obj->fslots[JSSLOT_PREFIX] = JSVAL_VOID; + } + } else if (argc == 2) { + if (!isQName || !(uri = GetURI(uriobj))) { + uri = js_ValueToString(cx, urival); + if (!uri) + return JS_FALSE; + } + obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(uri); + + prefixval = argv[0]; + if (uri->empty()) { + if (!JSVAL_IS_VOID(prefixval)) { + prefix = js_ValueToString(cx, prefixval); + if (!prefix) + return JS_FALSE; + if (!prefix->empty()) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XML_NAMESPACE, + js_ValueToPrintableString(cx, + STRING_TO_JSVAL(prefix))); + return JS_FALSE; + } + } + } else if (JSVAL_IS_VOID(prefixval) || !js_IsXMLName(cx, prefixval)) { + obj->fslots[JSSLOT_PREFIX] = JSVAL_VOID; + } else { + prefix = js_ValueToString(cx, prefixval); + if (!prefix) + return JS_FALSE; + obj->fslots[JSSLOT_PREFIX] = STRING_TO_JSVAL(prefix); + } + } + + return JS_TRUE; +} + +static JSBool +Namespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return NamespaceHelper(cx, + JS_IsConstructing(cx) ? obj : NULL, + argc, argv, rval); +} + +/* + * When argc is -1, it indicates argv is empty but the code should behave as + * if argc is 1 and argv[0] is JSVAL_VOID. + */ +static JSBool +QNameHelper(JSContext *cx, JSObject *obj, JSClass *clasp, intN argc, + jsval *argv, jsval *rval) +{ + jsval nameval, nsval; + JSBool isQName, isNamespace; + JSObject *qn; + JSString *uri, *prefix, *name; + JSObject *obj2; + + JS_ASSERT(clasp == &js_QNameClass.base || + clasp == &js_AttributeNameClass); + if (argc <= 0) { + nameval = JSVAL_VOID; + isQName = JS_FALSE; + } else { + nameval = argv[argc > 1]; + isQName = + !JSVAL_IS_PRIMITIVE(nameval) && + OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nameval)) == &js_QNameClass.base; + } + + if (!obj) { + /* QName called as function. */ + if (argc == 1 && isQName) { + /* QName called with one QName argument is identity. */ + *rval = nameval; + return JS_TRUE; + } + + /* + * Create and return a new QName or AttributeName object exactly as if + * constructed. + */ + obj = js_NewObject(cx, clasp, NULL, NULL); + if (!obj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + } + METER(xml_stats.qname); + + if (isQName) { + /* If namespace is not specified and name is a QName, clone it. */ + qn = JSVAL_TO_OBJECT(nameval); + if (argc == 1) { + uri = GetURI(qn); + prefix = GetPrefix(qn); + name = GetLocalName(qn); + goto out; + } + + /* Namespace and qname were passed -- use the qname's localName. */ + nameval = qn->fslots[JSSLOT_LOCAL_NAME]; + } + + if (argc == 0) { + name = cx->runtime->emptyString; + } else if (argc < 0) { + name = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]); + } else { + name = js_ValueToString(cx, nameval); + if (!name) + return JS_FALSE; + argv[argc > 1] = STRING_TO_JSVAL(name); + } + + if (argc > 1 && !JSVAL_IS_VOID(argv[0])) { + nsval = argv[0]; + } else if (IS_STAR(name)) { + nsval = JSVAL_NULL; + } else { + if (!js_GetDefaultXMLNamespace(cx, &nsval)) + return JS_FALSE; + JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval)); + JS_ASSERT(OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nsval)) == + &js_NamespaceClass.base); + } + + if (JSVAL_IS_NULL(nsval)) { + /* NULL prefix represents *undefined* in ECMA-357 13.3.2 5(a). */ + uri = prefix = NULL; + } else { + /* + * Inline specialization of the Namespace constructor called with + * nsval passed as the only argument, to compute the uri and prefix + * for the constructed namespace, without actually allocating the + * object or computing other members. See ECMA-357 13.3.2 6(a) and + * 13.2.2. + */ + isNamespace = isQName = JS_FALSE; + if (!JSVAL_IS_PRIMITIVE(nsval)) { + obj2 = JSVAL_TO_OBJECT(nsval); + clasp = OBJ_GET_CLASS(cx, obj2); + isNamespace = (clasp == &js_NamespaceClass.base); + isQName = (clasp == &js_QNameClass.base); + } +#ifdef __GNUC__ /* suppress bogus gcc warnings */ + else obj2 = NULL; +#endif + + if (isNamespace) { + uri = GetURI(obj2); + prefix = GetPrefix(obj2); + } else if (isQName && (uri = GetURI(obj2))) { + JS_ASSERT(argc > 1); + prefix = GetPrefix(obj2); + } else { + JS_ASSERT(argc > 1); + uri = js_ValueToString(cx, nsval); + if (!uri) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(uri); /* local root */ + + /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */ + prefix = uri->empty() ? cx->runtime->emptyString : NULL; + } + } + +out: + InitXMLQName(obj, uri, prefix, name); + return JS_TRUE; +} + +static JSBool +QName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return QNameHelper(cx, JS_IsConstructing(cx) ? obj : NULL, + &js_QNameClass.base, argc, argv, rval); +} + +static JSBool +AttributeName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + return QNameHelper(cx, JS_IsConstructing(cx) ? obj : NULL, + &js_AttributeNameClass, argc, argv, rval); +} + +/* + * XMLArray library functions. + */ +static JSBool +namespace_identity(const void *a, const void *b) +{ + const JSObject *nsa = (const JSObject *) a; + const JSObject *nsb = (const JSObject *) b; + JSString *prefixa = GetPrefix(nsa); + JSString *prefixb = GetPrefix(nsb); + + if (prefixa && prefixb) { + if (!js_EqualStrings(prefixa, prefixb)) + return JS_FALSE; + } else { + if (prefixa || prefixb) + return JS_FALSE; + } + return js_EqualStrings(GetURI(nsa), GetURI(nsb)); +} + +static JSBool +attr_identity(const void *a, const void *b) +{ + const JSXML *xmla = (const JSXML *) a; + const JSXML *xmlb = (const JSXML *) b; + + return qname_identity(xmla->name, xmlb->name); +} + +struct JSXMLArrayCursor +{ + JSXMLArray *array; + uint32 index; + JSXMLArrayCursor *next; + JSXMLArrayCursor **prevp; + void *root; + + JSXMLArrayCursor(JSXMLArray *array) + : array(array), index(0), next(array->cursors), prevp(&array->cursors), + root(NULL) + { + if (next) + next->prevp = &next; + array->cursors = this; + } + + ~JSXMLArrayCursor() { disconnect(); } + + void disconnect() { + if (!array) + return; + if (next) + next->prevp = prevp; + *prevp = next; + array = NULL; + } + + void *getNext() { + if (!array || index >= array->length) + return NULL; + return root = array->vector[index++]; + } + + void *getCurrent() { + if (!array || index >= array->length) + return NULL; + return root = array->vector[index]; + } +}; + +static void +XMLArrayCursorTrace(JSTracer *trc, JSXMLArrayCursor *cursor) +{ + void *root; +#ifdef DEBUG + size_t index = 0; +#endif + + for (; cursor; cursor = cursor->next) { + root = cursor->root; + JS_SET_TRACING_INDEX(trc, "cursor_root", index++); + js_CallValueTracerIfGCThing(trc, (jsval)root); + } +} + +/* NB: called with null cx from the GC, via xml_trace => XMLArrayTrim. */ +static JSBool +XMLArraySetCapacity(JSContext *cx, JSXMLArray *array, uint32 capacity) +{ + void **vector; + + if (capacity == 0) { + /* We could let realloc(p, 0) free this, but purify gets confused. */ + if (array->vector) { + if (cx) + cx->free(array->vector); + else + js_free(array->vector); + } + vector = NULL; + } else { + if ( +#if JS_BITS_PER_WORD == 32 + (size_t)capacity > ~(size_t)0 / sizeof(void *) || +#endif + !(vector = (void **) + js_realloc(array->vector, capacity * sizeof(void *)))) { + if (cx) + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + } + array->capacity = JSXML_PRESET_CAPACITY | capacity; + array->vector = vector; + return JS_TRUE; +} + +static void +XMLArrayTrim(JSXMLArray *array) +{ + if (array->capacity & JSXML_PRESET_CAPACITY) + return; + if (array->length < array->capacity) + XMLArraySetCapacity(NULL, array, array->length); +} + +static JSBool +XMLArrayInit(JSContext *cx, JSXMLArray *array, uint32 capacity) +{ + array->length = array->capacity = 0; + array->vector = NULL; + array->cursors = NULL; + return capacity == 0 || XMLArraySetCapacity(cx, array, capacity); +} + +static void +XMLArrayFinish(JSContext *cx, JSXMLArray *array) +{ + cx->free(array->vector); + + while (JSXMLArrayCursor *cursor = array->cursors) + cursor->disconnect(); + +#ifdef DEBUG + memset(array, 0xd5, sizeof *array); +#endif +} + +#define XML_NOT_FOUND ((uint32) -1) + +static uint32 +XMLArrayFindMember(const JSXMLArray *array, void *elt, JSIdentityOp identity) +{ + void **vector; + uint32 i, n; + + /* The identity op must not reallocate array->vector. */ + vector = array->vector; + if (identity) { + for (i = 0, n = array->length; i < n; i++) { + if (identity(vector[i], elt)) + return i; + } + } else { + for (i = 0, n = array->length; i < n; i++) { + if (vector[i] == elt) + return i; + } + } + return XML_NOT_FOUND; +} + +/* + * Grow array vector capacity by powers of two to LINEAR_THRESHOLD, and after + * that, grow by LINEAR_INCREMENT. Both must be powers of two, and threshold + * should be greater than increment. + */ +#define LINEAR_THRESHOLD 256 +#define LINEAR_INCREMENT 32 + +static JSBool +XMLArrayAddMember(JSContext *cx, JSXMLArray *array, uint32 index, void *elt) +{ + uint32 capacity, i; + int log2; + void **vector; + + if (index >= array->length) { + if (index >= JSXML_CAPACITY(array)) { + /* Arrange to clear JSXML_PRESET_CAPACITY from array->capacity. */ + capacity = index + 1; + if (index >= LINEAR_THRESHOLD) { + capacity = JS_ROUNDUP(capacity, LINEAR_INCREMENT); + } else { + JS_CEILING_LOG2(log2, capacity); + capacity = JS_BIT(log2); + } + if ( +#if JS_BITS_PER_WORD == 32 + (size_t)capacity > ~(size_t)0 / sizeof(void *) || +#endif + !(vector = (void **) + js_realloc(array->vector, capacity * sizeof(void *)))) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + array->capacity = capacity; + array->vector = vector; + for (i = array->length; i < index; i++) + vector[i] = NULL; + } + array->length = index + 1; + } + + array->vector[index] = elt; + return JS_TRUE; +} + +static JSBool +XMLArrayInsert(JSContext *cx, JSXMLArray *array, uint32 i, uint32 n) +{ + uint32 j; + JSXMLArrayCursor *cursor; + + j = array->length; + JS_ASSERT(i <= j); + if (!XMLArraySetCapacity(cx, array, j + n)) + return JS_FALSE; + + array->length = j + n; + JS_ASSERT(n != (uint32)-1); + while (j != i) { + --j; + array->vector[j + n] = array->vector[j]; + } + + for (cursor = array->cursors; cursor; cursor = cursor->next) { + if (cursor->index > i) + cursor->index += n; + } + return JS_TRUE; +} + +static void * +XMLArrayDelete(JSContext *cx, JSXMLArray *array, uint32 index, JSBool compress) +{ + uint32 length; + void **vector, *elt; + JSXMLArrayCursor *cursor; + + length = array->length; + if (index >= length) + return NULL; + + vector = array->vector; + elt = vector[index]; + if (compress) { + while (++index < length) + vector[index-1] = vector[index]; + array->length = length - 1; + array->capacity = JSXML_CAPACITY(array); + } else { + vector[index] = NULL; + } + + for (cursor = array->cursors; cursor; cursor = cursor->next) { + if (cursor->index > index) + --cursor->index; + } + return elt; +} + +static void +XMLArrayTruncate(JSContext *cx, JSXMLArray *array, uint32 length) +{ + void **vector; + + JS_ASSERT(!array->cursors); + if (length >= array->length) + return; + + if (length == 0) { + if (array->vector) + cx->free(array->vector); + vector = NULL; + } else { + vector = (void **) js_realloc(array->vector, length * sizeof(void *)); + if (!vector) + return; + } + + if (array->length > length) + array->length = length; + array->capacity = length; + array->vector = vector; +} + +#define XMLARRAY_FIND_MEMBER(a,e,f) XMLArrayFindMember(a, (void *)(e), f) +#define XMLARRAY_HAS_MEMBER(a,e,f) (XMLArrayFindMember(a, (void *)(e), f) != \ + XML_NOT_FOUND) +#define XMLARRAY_MEMBER(a,i,t) (((i) < (a)->length) \ + ? (t *) (a)->vector[i] \ + : NULL) +#define XMLARRAY_SET_MEMBER(a,i,e) JS_BEGIN_MACRO \ + if ((a)->length <= (i)) \ + (a)->length = (i) + 1; \ + ((a)->vector[i] = (void *)(e)); \ + JS_END_MACRO +#define XMLARRAY_ADD_MEMBER(x,a,i,e)XMLArrayAddMember(x, a, i, (void *)(e)) +#define XMLARRAY_INSERT(x,a,i,n) XMLArrayInsert(x, a, i, n) +#define XMLARRAY_APPEND(x,a,e) XMLARRAY_ADD_MEMBER(x, a, (a)->length, (e)) +#define XMLARRAY_DELETE(x,a,i,c,t) ((t *) XMLArrayDelete(x, a, i, c)) +#define XMLARRAY_TRUNCATE(x,a,n) XMLArrayTruncate(x, a, n) + +/* + * Define XML setting property strings and constants early, so everyone can + * use the same names and their magic numbers (tinyids, flags). + */ +static const char js_ignoreComments_str[] = "ignoreComments"; +static const char js_ignoreProcessingInstructions_str[] + = "ignoreProcessingInstructions"; +static const char js_ignoreWhitespace_str[] = "ignoreWhitespace"; +static const char js_prettyPrinting_str[] = "prettyPrinting"; +static const char js_prettyIndent_str[] = "prettyIndent"; + +/* + * NB: These XML static property tinyids must + * (a) not collide with the generic negative tinyids at the top of jsfun.c; + * (b) index their corresponding xml_static_props array elements. + * Don't change 'em! + */ +enum xml_static_tinyid { + XML_IGNORE_COMMENTS, + XML_IGNORE_PROCESSING_INSTRUCTIONS, + XML_IGNORE_WHITESPACE, + XML_PRETTY_PRINTING, + XML_PRETTY_INDENT +}; + +static JSBool +xml_setting_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + return JS_TRUE; +} + +static JSBool +xml_setting_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + uint8 flag; + + JS_ASSERT(JSVAL_IS_INT(id)); + + flag = JS_BIT(JSVAL_TO_INT(id)); + if (js_ValueToBoolean(*vp)) + cx->xmlSettingFlags |= flag; + else + cx->xmlSettingFlags &= ~flag; + return JS_TRUE; +} + +static JSPropertySpec xml_static_props[] = { + {js_ignoreComments_str, XML_IGNORE_COMMENTS, JSPROP_PERMANENT, + xml_setting_getter, xml_setting_setter}, + {js_ignoreProcessingInstructions_str, + XML_IGNORE_PROCESSING_INSTRUCTIONS, JSPROP_PERMANENT, + xml_setting_getter, xml_setting_setter}, + {js_ignoreWhitespace_str, XML_IGNORE_WHITESPACE, JSPROP_PERMANENT, + xml_setting_getter, xml_setting_setter}, + {js_prettyPrinting_str, XML_PRETTY_PRINTING, JSPROP_PERMANENT, + xml_setting_getter, xml_setting_setter}, + {js_prettyIndent_str, XML_PRETTY_INDENT, JSPROP_PERMANENT, + xml_setting_getter, NULL}, + {0,0,0,0,0} +}; + +/* Derive cx->xmlSettingFlags bits from xml_static_props tinyids. */ +#define XSF_IGNORE_COMMENTS JS_BIT(XML_IGNORE_COMMENTS) +#define XSF_IGNORE_PROCESSING_INSTRUCTIONS \ + JS_BIT(XML_IGNORE_PROCESSING_INSTRUCTIONS) +#define XSF_IGNORE_WHITESPACE JS_BIT(XML_IGNORE_WHITESPACE) +#define XSF_PRETTY_PRINTING JS_BIT(XML_PRETTY_PRINTING) +#define XSF_CACHE_VALID JS_BIT(XML_PRETTY_INDENT) + +/* + * Extra, unrelated but necessarily disjoint flag used by ParseNodeToXML. + * This flag means a couple of things: + * + * - The top JSXML created for a parse tree must have an object owning it. + * + * - That the default namespace normally inherited from the temporary + * tag that wraps a runtime-concatenated XML source + * string must, in the case of a precompiled XML object tree, inherit via + * ad-hoc code in ParseNodeToXML. + * + * Because of the second purpose, we name this flag XSF_PRECOMPILED_ROOT. + */ +#define XSF_PRECOMPILED_ROOT (XSF_CACHE_VALID << 1) + +/* Macros for special-casing xml:, xmlns= and xmlns:foo= in ParseNodeToQName. */ +#define IS_XML(str) \ + (str->length() == 3 && IS_XML_CHARS(str->chars())) + +#define IS_XMLNS(str) \ + (str->length() == 5 && IS_XMLNS_CHARS(str->chars())) + +#define IS_XML_CHARS(chars) \ + (JS_TOLOWER((chars)[0]) == 'x' && \ + JS_TOLOWER((chars)[1]) == 'm' && \ + JS_TOLOWER((chars)[2]) == 'l') + +#define HAS_NS_AFTER_XML(chars) \ + (JS_TOLOWER((chars)[3]) == 'n' && \ + JS_TOLOWER((chars)[4]) == 's') + +#define IS_XMLNS_CHARS(chars) \ + (IS_XML_CHARS(chars) && HAS_NS_AFTER_XML(chars)) + +#define STARTS_WITH_XML(chars,length) \ + (length >= 3 && IS_XML_CHARS(chars)) + +static const char xml_namespace_str[] = "http://www.w3.org/XML/1998/namespace"; +static const char xmlns_namespace_str[] = "http://www.w3.org/2000/xmlns/"; + +static JSObject * +ParseNodeToQName(JSCompiler *jsc, JSParseNode *pn, + JSXMLArray *inScopeNSes, JSBool isAttributeName) +{ + JSContext *cx = jsc->context; + JSString *str, *uri, *prefix, *localName; + size_t length, offset; + const jschar *start, *limit, *colon; + uint32 n; + JSObject *ns; + JSString *nsprefix; + + JS_ASSERT(pn->pn_arity == PN_NULLARY); + str = ATOM_TO_STRING(pn->pn_atom); + str->getCharsAndLength(start, length); + JS_ASSERT(length != 0 && *start != '@'); + JS_ASSERT(length != 1 || *start != '*'); + + uri = cx->runtime->emptyString; + limit = start + length; + colon = js_strchr_limit(start, ':', limit); + if (colon) { + offset = colon - start; + prefix = js_NewDependentString(cx, str, 0, offset); + if (!prefix) + return NULL; + + if (STARTS_WITH_XML(start, offset)) { + if (offset == 3) { + uri = JS_InternString(cx, xml_namespace_str); + if (!uri) + return NULL; + } else if (offset == 5 && HAS_NS_AFTER_XML(start)) { + uri = JS_InternString(cx, xmlns_namespace_str); + if (!uri) + return NULL; + } else { + uri = NULL; + } + } else { + uri = NULL; + n = inScopeNSes->length; + while (n != 0) { + --n; + ns = XMLARRAY_MEMBER(inScopeNSes, n, JSObject); + nsprefix = GetPrefix(ns); + if (nsprefix && js_EqualStrings(nsprefix, prefix)) { + uri = GetURI(ns); + break; + } + } + } + + if (!uri) { + js_ReportCompileErrorNumber(jsc->context, &jsc->tokenStream, pn, + JSREPORT_ERROR, + JSMSG_BAD_XML_NAMESPACE, + js_ValueToPrintableString(jsc->context, + STRING_TO_JSVAL(prefix))); + return NULL; + } + + localName = js_NewStringCopyN(jsc->context, colon + 1, length - (offset + 1)); + if (!localName) + return NULL; + } else { + if (isAttributeName) { + /* + * An unprefixed attribute is not in any namespace, so set prefix + * as well as uri to the empty string. + */ + prefix = uri; + } else { + /* + * Loop from back to front looking for the closest declared default + * namespace. + */ + n = inScopeNSes->length; + while (n != 0) { + --n; + ns = XMLARRAY_MEMBER(inScopeNSes, n, JSObject); + nsprefix = GetPrefix(ns); + if (!nsprefix || nsprefix->empty()) { + uri = GetURI(ns); + break; + } + } + prefix = uri->empty() ? jsc->context->runtime->emptyString : NULL; + } + localName = str; + } + + return NewXMLQName(jsc->context, uri, prefix, localName); +} + +static JSString * +ChompXMLWhitespace(JSContext *cx, JSString *str) +{ + size_t length, newlength, offset; + const jschar *cp, *start, *end; + jschar c; + + str->getCharsAndLength(start, length); + for (cp = start, end = cp + length; cp < end; cp++) { + c = *cp; + if (!JS_ISXMLSPACE(c)) + break; + } + while (end > cp) { + c = end[-1]; + if (!JS_ISXMLSPACE(c)) + break; + --end; + } + newlength = end - cp; + if (newlength == length) + return str; + offset = cp - start; + return js_NewDependentString(cx, str, offset, newlength); +} + +static JSXML * +ParseNodeToXML(JSCompiler *jsc, JSParseNode *pn, + JSXMLArray *inScopeNSes, uintN flags) +{ + JSContext *cx = jsc->context; + JSXML *xml, *kid, *attr, *attrj; + JSString *str; + uint32 length, n, i, j; + JSParseNode *pn2, *pn3, *head, **pnp; + JSObject *ns; + JSObject *qn, *attrjqn; + JSXMLClass xml_class; + int stackDummy; + + if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { + js_ReportCompileErrorNumber(cx, &jsc->tokenStream, pn, JSREPORT_ERROR, + JSMSG_OVER_RECURSED); + return NULL; + } + +#define PN2X_SKIP_CHILD ((JSXML *) 1) + + /* + * Cases return early to avoid common code that gets an outermost xml's + * object, which protects GC-things owned by xml and its descendants from + * garbage collection. + */ + xml = NULL; + if (!js_EnterLocalRootScope(cx)) + return NULL; + switch (pn->pn_type) { + case TOK_XMLELEM: + length = inScopeNSes->length; + pn2 = pn->pn_head; + xml = ParseNodeToXML(jsc, pn2, inScopeNSes, flags); + if (!xml) + goto fail; + + flags &= ~XSF_PRECOMPILED_ROOT; + n = pn->pn_count; + JS_ASSERT(n >= 2); + n -= 2; + if (!XMLArraySetCapacity(cx, &xml->xml_kids, n)) + goto fail; + + i = 0; + while ((pn2 = pn2->pn_next) != NULL) { + if (!pn2->pn_next) { + /* Don't append the end tag! */ + JS_ASSERT(pn2->pn_type == TOK_XMLETAGO); + break; + } + + if ((flags & XSF_IGNORE_WHITESPACE) && + n > 1 && pn2->pn_type == TOK_XMLSPACE) { + --n; + continue; + } + + kid = ParseNodeToXML(jsc, pn2, inScopeNSes, flags); + if (kid == PN2X_SKIP_CHILD) { + --n; + continue; + } + + if (!kid) + goto fail; + + /* Store kid in xml right away, to protect it from GC. */ + XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid); + kid->parent = xml; + ++i; + + /* XXX where is this documented in an XML spec, or in E4X? */ + if ((flags & XSF_IGNORE_WHITESPACE) && + n > 1 && kid->xml_class == JSXML_CLASS_TEXT) { + str = ChompXMLWhitespace(cx, kid->xml_value); + if (!str) + goto fail; + kid->xml_value = str; + } + } + + JS_ASSERT(i == n); + if (n < pn->pn_count - 2) + XMLArrayTrim(&xml->xml_kids); + XMLARRAY_TRUNCATE(cx, inScopeNSes, length); + break; + + case TOK_XMLLIST: + xml = js_NewXML(cx, JSXML_CLASS_LIST); + if (!xml) + goto fail; + + n = pn->pn_count; + if (!XMLArraySetCapacity(cx, &xml->xml_kids, n)) + goto fail; + + i = 0; + for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { + /* + * Always ignore insignificant whitespace in lists -- we shouldn't + * condition this on an XML.ignoreWhitespace setting when the list + * constructor is XMLList (note XML/XMLList unification hazard). + */ + if (pn2->pn_type == TOK_XMLSPACE) { + --n; + continue; + } + + kid = ParseNodeToXML(jsc, pn2, inScopeNSes, flags); + if (kid == PN2X_SKIP_CHILD) { + --n; + continue; + } + + if (!kid) + goto fail; + + XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid); + ++i; + } + + if (n < pn->pn_count) + XMLArrayTrim(&xml->xml_kids); + break; + + case TOK_XMLSTAGO: + case TOK_XMLPTAGC: + length = inScopeNSes->length; + pn2 = pn->pn_head; + JS_ASSERT(pn2->pn_type == TOK_XMLNAME); + if (pn2->pn_arity == PN_LIST) + goto syntax; + + xml = js_NewXML(cx, JSXML_CLASS_ELEMENT); + if (!xml) + goto fail; + + /* First pass: check syntax and process namespace declarations. */ + JS_ASSERT(pn->pn_count >= 1); + n = pn->pn_count - 1; + pnp = &pn2->pn_next; + head = *pnp; + while ((pn2 = *pnp) != NULL) { + size_t length; + const jschar *chars; + + if (pn2->pn_type != TOK_XMLNAME || pn2->pn_arity != PN_NULLARY) + goto syntax; + + /* Enforce "Well-formedness constraint: Unique Att Spec". */ + for (pn3 = head; pn3 != pn2; pn3 = pn3->pn_next->pn_next) { + if (pn3->pn_atom == pn2->pn_atom) { + js_ReportCompileErrorNumber(cx, &jsc->tokenStream, pn2, + JSREPORT_ERROR, + JSMSG_DUPLICATE_XML_ATTR, + js_ValueToPrintableString(cx, + ATOM_KEY(pn2->pn_atom))); + goto fail; + } + } + + str = ATOM_TO_STRING(pn2->pn_atom); + pn2 = pn2->pn_next; + JS_ASSERT(pn2); + if (pn2->pn_type != TOK_XMLATTR) + goto syntax; + + str->getCharsAndLength(chars, length); + if (length >= 5 && + IS_XMLNS_CHARS(chars) && + (length == 5 || chars[5] == ':')) { + JSString *uri, *prefix; + + uri = ATOM_TO_STRING(pn2->pn_atom); + if (length == 5) { + /* 10.3.2.1. Step 6(h)(i)(1)(a). */ + prefix = cx->runtime->emptyString; + } else { + prefix = js_NewStringCopyN(cx, chars + 6, length - 6); + if (!prefix) + goto fail; + } + + /* + * Once the new ns is appended to xml->xml_namespaces, it is + * protected from GC by the object that owns xml -- which is + * either xml->object if outermost, or the object owning xml's + * oldest ancestor if !outermost. + */ + ns = NewXMLNamespace(cx, prefix, uri, JS_TRUE); + if (!ns) + goto fail; + + /* + * Don't add a namespace that's already in scope. If someone + * extracts a child property from its parent via [[Get]], then + * we enforce the invariant, noted many times in ECMA-357, that + * the child's namespaces form a possibly-improper superset of + * its ancestors' namespaces. + */ + if (!XMLARRAY_HAS_MEMBER(inScopeNSes, ns, namespace_identity)) { + if (!XMLARRAY_APPEND(cx, inScopeNSes, ns) || + !XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) { + goto fail; + } + } + + JS_ASSERT(n >= 2); + n -= 2; + *pnp = pn2->pn_next; + /* XXXbe recycle pn2 */ + continue; + } + + pnp = &pn2->pn_next; + } + + /* + * If called from js_ParseNodeToXMLObject, emulate the effect of the + * ... wrapping done by "ToXML Applied to + * the String Type" (ECMA-357 10.3.1). + */ + if (flags & XSF_PRECOMPILED_ROOT) { + JS_ASSERT(length >= 1); + ns = XMLARRAY_MEMBER(inScopeNSes, 0, JSObject); + JS_ASSERT(!XMLARRAY_HAS_MEMBER(&xml->xml_namespaces, ns, + namespace_identity)); + ns = NewXMLNamespace(cx, GetPrefix(ns), GetURI(ns), JS_FALSE); + if (!ns) + goto fail; + if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) + goto fail; + } + XMLArrayTrim(&xml->xml_namespaces); + + /* Second pass: process tag name and attributes, using namespaces. */ + pn2 = pn->pn_head; + qn = ParseNodeToQName(jsc, pn2, inScopeNSes, JS_FALSE); + if (!qn) + goto fail; + xml->name = qn; + + JS_ASSERT((n & 1) == 0); + n >>= 1; + if (!XMLArraySetCapacity(cx, &xml->xml_attrs, n)) + goto fail; + + for (i = 0; (pn2 = pn2->pn_next) != NULL; i++) { + qn = ParseNodeToQName(jsc, pn2, inScopeNSes, JS_TRUE); + if (!qn) { + xml->xml_attrs.length = i; + goto fail; + } + + /* + * Enforce "Well-formedness constraint: Unique Att Spec", part 2: + * this time checking local name and namespace URI. + */ + for (j = 0; j < i; j++) { + attrj = XMLARRAY_MEMBER(&xml->xml_attrs, j, JSXML); + attrjqn = attrj->name; + if (js_EqualStrings(GetURI(attrjqn), GetURI(qn)) && + js_EqualStrings(GetLocalName(attrjqn), GetLocalName(qn))) { + js_ReportCompileErrorNumber(cx, &jsc->tokenStream, pn2, + JSREPORT_ERROR, + JSMSG_DUPLICATE_XML_ATTR, + js_ValueToPrintableString(cx, + ATOM_KEY(pn2->pn_atom))); + goto fail; + } + } + + pn2 = pn2->pn_next; + JS_ASSERT(pn2); + JS_ASSERT(pn2->pn_type == TOK_XMLATTR); + + attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE); + if (!attr) + goto fail; + + XMLARRAY_SET_MEMBER(&xml->xml_attrs, i, attr); + attr->parent = xml; + attr->name = qn; + attr->xml_value = ATOM_TO_STRING(pn2->pn_atom); + } + + /* Point tag closes its own namespace scope. */ + if (pn->pn_type == TOK_XMLPTAGC) + XMLARRAY_TRUNCATE(cx, inScopeNSes, length); + break; + + case TOK_XMLSPACE: + case TOK_XMLTEXT: + case TOK_XMLCDATA: + case TOK_XMLCOMMENT: + case TOK_XMLPI: + str = ATOM_TO_STRING(pn->pn_atom); + qn = NULL; + if (pn->pn_type == TOK_XMLCOMMENT) { + if (flags & XSF_IGNORE_COMMENTS) + goto skip_child; + xml_class = JSXML_CLASS_COMMENT; + } else if (pn->pn_type == TOK_XMLPI) { + if (IS_XML(str)) { + js_ReportCompileErrorNumber(cx, &jsc->tokenStream, pn, + JSREPORT_ERROR, + JSMSG_RESERVED_ID, + js_ValueToPrintableString(cx, + STRING_TO_JSVAL(str))); + goto fail; + } + + if (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS) + goto skip_child; + + qn = ParseNodeToQName(jsc, pn, inScopeNSes, JS_FALSE); + if (!qn) + goto fail; + + str = pn->pn_atom2 + ? ATOM_TO_STRING(pn->pn_atom2) + : cx->runtime->emptyString; + xml_class = JSXML_CLASS_PROCESSING_INSTRUCTION; + } else { + /* CDATA section content, or element text. */ + xml_class = JSXML_CLASS_TEXT; + } + + xml = js_NewXML(cx, xml_class); + if (!xml) + goto fail; + xml->name = qn; + if (pn->pn_type == TOK_XMLSPACE) + xml->xml_flags |= XMLF_WHITESPACE_TEXT; + xml->xml_value = str; + break; + + default: + goto syntax; + } + + js_LeaveLocalRootScopeWithResult(cx, (jsval) xml); + if ((flags & XSF_PRECOMPILED_ROOT) && !js_GetXMLObject(cx, xml)) + return NULL; + return xml; + +skip_child: + js_LeaveLocalRootScope(cx); + return PN2X_SKIP_CHILD; + +#undef PN2X_SKIP_CHILD + +syntax: + js_ReportCompileErrorNumber(cx, &jsc->tokenStream, pn, JSREPORT_ERROR, + JSMSG_BAD_XML_MARKUP); +fail: + js_LeaveLocalRootScope(cx); + return NULL; +} + +/* + * XML helper, object-ops, and library functions. We start with the helpers, + * in ECMA-357 order, but merging XML (9.1) and XMLList (9.2) helpers. + */ +static JSBool +GetXMLSetting(JSContext *cx, const char *name, jsval *vp) +{ + jsval v; + + if (!js_FindClassObject(cx, NULL, INT_TO_JSID(JSProto_XML), &v)) + return JS_FALSE; + if (!VALUE_IS_FUNCTION(cx, v)) { + *vp = JSVAL_VOID; + return JS_TRUE; + } + return JS_GetProperty(cx, JSVAL_TO_OBJECT(v), name, vp); +} + +static JSBool +FillSettingsCache(JSContext *cx) +{ + int i; + const char *name; + jsval v; + + /* Note: XML_PRETTY_INDENT is not a boolean setting. */ + for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) { + name = xml_static_props[i].name; + if (!GetXMLSetting(cx, name, &v)) + return JS_FALSE; + if (js_ValueToBoolean(v)) + cx->xmlSettingFlags |= JS_BIT(i); + else + cx->xmlSettingFlags &= ~JS_BIT(i); + } + + cx->xmlSettingFlags |= XSF_CACHE_VALID; + return JS_TRUE; +} + +static JSBool +GetBooleanXMLSetting(JSContext *cx, const char *name, JSBool *bp) +{ + int i; + + if (!(cx->xmlSettingFlags & XSF_CACHE_VALID) && !FillSettingsCache(cx)) + return JS_FALSE; + + for (i = 0; xml_static_props[i].name; i++) { + if (!strcmp(xml_static_props[i].name, name)) { + *bp = (cx->xmlSettingFlags & JS_BIT(i)) != 0; + return JS_TRUE; + } + } + *bp = JS_FALSE; + return JS_TRUE; +} + +static JSBool +GetUint32XMLSetting(JSContext *cx, const char *name, uint32 *uip) +{ + jsval v; + + return GetXMLSetting(cx, name, &v) && JS_ValueToECMAUint32(cx, v, uip); +} + +static JSBool +GetXMLSettingFlags(JSContext *cx, uintN *flagsp) +{ + JSBool flag; + + /* Just get the first flag to validate the setting flags cache. */ + if (!GetBooleanXMLSetting(cx, js_ignoreComments_str, &flag)) + return JS_FALSE; + *flagsp = cx->xmlSettingFlags; + return JS_TRUE; +} + +static JSXML * +ParseXMLSource(JSContext *cx, JSString *src) +{ + jsval nsval; + JSString *uri; + size_t urilen, srclen, length, offset, dstlen; + jschar *chars; + const jschar *srcp, *endp; + JSXML *xml; + const char *filename; + uintN lineno; + JSStackFrame *fp; + JSOp op; + JSParseNode *pn; + JSXMLArray nsarray; + uintN flags; + + static const char prefix[] = ""; + static const char suffix[] = ""; + +#define constrlen(constr) (sizeof(constr) - 1) + + if (!js_GetDefaultXMLNamespace(cx, &nsval)) + return NULL; + uri = GetURI(JSVAL_TO_OBJECT(nsval)); + uri = js_EscapeAttributeValue(cx, uri, JS_FALSE); + + urilen = uri->length(); + srclen = src->length(); + length = constrlen(prefix) + urilen + constrlen(middle) + srclen + + constrlen(suffix); + + chars = (jschar *) cx->malloc((length + 1) * sizeof(jschar)); + if (!chars) + return NULL; + + dstlen = length; + js_InflateStringToBuffer(cx, prefix, constrlen(prefix), chars, &dstlen); + offset = dstlen; + js_strncpy(chars + offset, uri->chars(), urilen); + offset += urilen; + dstlen = length - offset + 1; + js_InflateStringToBuffer(cx, middle, constrlen(middle), chars + offset, + &dstlen); + offset += dstlen; + srcp = src->chars(); + js_strncpy(chars + offset, srcp, srclen); + offset += srclen; + dstlen = length - offset + 1; + js_InflateStringToBuffer(cx, suffix, constrlen(suffix), chars + offset, + &dstlen); + chars [offset + dstlen] = 0; + + xml = NULL; + for (fp = js_GetTopStackFrame(cx); fp && !fp->regs; fp = fp->down) + JS_ASSERT(!fp->script); + filename = NULL; + lineno = 1; + if (fp) { + op = (JSOp) *fp->regs->pc; + if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) { + filename = fp->script->filename; + lineno = js_FramePCToLineNumber(cx, fp); + for (endp = srcp + srclen; srcp < endp; srcp++) { + if (*srcp == '\n') + --lineno; + } + } + } + + { + JSCompiler jsc(cx); + if (jsc.init(chars, length, NULL, filename, lineno)) { + pn = jsc.parseXMLText(js_GetTopStackFrame(cx)->scopeChain, false); + if (pn && XMLArrayInit(cx, &nsarray, 1)) { + if (GetXMLSettingFlags(cx, &flags)) + xml = ParseNodeToXML(&jsc, pn, &nsarray, flags); + + XMLArrayFinish(cx, &nsarray); + } + } + } + + cx->free(chars); + return xml; + +#undef constrlen +} + +/* + * Errata in 10.3.1, 10.4.1, and 13.4.4.24 (at least). + * + * 10.3.1 Step 6(a) fails to NOTE that implementations that do not enforce + * the constraint: + * + * for all x belonging to XML: + * x.[[InScopeNamespaces]] >= x.[[Parent]].[[InScopeNamespaces]] + * + * must union x.[[InScopeNamespaces]] into x[0].[[InScopeNamespaces]] here + * (in new sub-step 6(a), renumbering the others to (b) and (c)). + * + * Same goes for 10.4.1 Step 7(a). + * + * In order for XML.prototype.namespaceDeclarations() to work correctly, the + * default namespace thereby unioned into x[0].[[InScopeNamespaces]] must be + * flagged as not declared, so that 13.4.4.24 Step 8(a) can exclude all such + * undeclared namespaces associated with x not belonging to ancestorNS. + */ +static JSXML * +OrphanXMLChild(JSContext *cx, JSXML *xml, uint32 i) +{ + JSObject *ns; + + ns = XMLARRAY_MEMBER(&xml->xml_namespaces, 0, JSObject); + xml = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (!ns || !xml) + return xml; + if (xml->xml_class == JSXML_CLASS_ELEMENT) { + if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) + return NULL; + ns->fslots[JSSLOT_DECLARED] = JSVAL_VOID; + } + xml->parent = NULL; + return xml; +} + +static JSObject * +ToXML(JSContext *cx, jsval v) +{ + JSObject *obj; + JSXML *xml; + JSClass *clasp; + JSString *str; + uint32 length; + + if (JSVAL_IS_PRIMITIVE(v)) { + if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) + goto bad; + } else { + obj = JSVAL_TO_OBJECT(v); + if (OBJECT_IS_XML(cx, obj)) { + xml = (JSXML *) obj->getPrivate(); + if (xml->xml_class == JSXML_CLASS_LIST) { + if (xml->xml_kids.length != 1) + goto bad; + xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + if (xml) { + JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST); + return js_GetXMLObject(cx, xml); + } + } + return obj; + } + + clasp = OBJ_GET_CLASS(cx, obj); + if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) { + JS_ASSERT(0); + } + + if (clasp != &js_StringClass && + clasp != &js_NumberClass && + clasp != &js_BooleanClass) { + goto bad; + } + } + + str = js_ValueToString(cx, v); + if (!str) + return NULL; + if (str->empty()) { + length = 0; +#ifdef __GNUC__ /* suppress bogus gcc warnings */ + xml = NULL; +#endif + } else { + xml = ParseXMLSource(cx, str); + if (!xml) + return NULL; + length = JSXML_LENGTH(xml); + } + + if (length == 0) { + obj = js_NewXMLObject(cx, JSXML_CLASS_TEXT); + if (!obj) + return NULL; + } else if (length == 1) { + xml = OrphanXMLChild(cx, xml, 0); + if (!xml) + return NULL; + obj = js_GetXMLObject(cx, xml); + if (!obj) + return NULL; + } else { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SYNTAX_ERROR); + return NULL; + } + return obj; + +bad: + js_ReportValueError(cx, JSMSG_BAD_XML_CONVERSION, + JSDVG_IGNORE_STACK, v, NULL); + return NULL; +} + +static JSBool +Append(JSContext *cx, JSXML *list, JSXML *kid); + +static JSObject * +ToXMLList(JSContext *cx, jsval v) +{ + JSObject *obj, *listobj; + JSXML *xml, *list, *kid; + JSClass *clasp; + JSString *str; + uint32 i, length; + + if (JSVAL_IS_PRIMITIVE(v)) { + if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) + goto bad; + } else { + obj = JSVAL_TO_OBJECT(v); + if (OBJECT_IS_XML(cx, obj)) { + xml = (JSXML *) obj->getPrivate(); + if (xml->xml_class != JSXML_CLASS_LIST) { + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) + return NULL; + list = (JSXML *) listobj->getPrivate(); + if (!Append(cx, list, xml)) + return NULL; + return listobj; + } + return obj; + } + + clasp = OBJ_GET_CLASS(cx, obj); + if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) { + JS_ASSERT(0); + } + + if (clasp != &js_StringClass && + clasp != &js_NumberClass && + clasp != &js_BooleanClass) { + goto bad; + } + } + + str = js_ValueToString(cx, v); + if (!str) + return NULL; + if (str->empty()) { + xml = NULL; + length = 0; + } else { + if (!js_EnterLocalRootScope(cx)) + return NULL; + xml = ParseXMLSource(cx, str); + if (!xml) { + js_LeaveLocalRootScope(cx); + return NULL; + } + length = JSXML_LENGTH(xml); + } + + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (listobj) { + list = (JSXML *) listobj->getPrivate(); + for (i = 0; i < length; i++) { + kid = OrphanXMLChild(cx, xml, i); + if (!kid || !Append(cx, list, kid)) { + listobj = NULL; + break; + } + } + } + + if (xml) + js_LeaveLocalRootScopeWithResult(cx, (jsval) listobj); + return listobj; + +bad: + js_ReportValueError(cx, JSMSG_BAD_XMLLIST_CONVERSION, + JSDVG_IGNORE_STACK, v, NULL); + return NULL; +} + +/* + * ECMA-357 10.2.1 Steps 5-7 pulled out as common subroutines of XMLToXMLString + * and their library-public js_* counterparts. The guts of MakeXMLCDataString, + * MakeXMLCommentString, and MakeXMLPIString are further factored into a common + * MakeXMLSpecialString subroutine. + * + * These functions mutate cb, leaving it empty. + */ +static JSString * +MakeXMLSpecialString(JSContext *cx, JSCharBuffer &cb, + JSString *str, JSString *str2, + const jschar *prefix, size_t prefixlength, + const jschar *suffix, size_t suffixlength) +{ + if (!cb.append(prefix, prefixlength) || !AppendString(cb, str)) + return NULL; + if (str2 && !str2->empty()) { + if (!cb.append(' ') || !AppendString(cb, str2)) + return NULL; + } + if (!cb.append(suffix, suffixlength)) + return NULL; + + return js_NewStringFromCharBuffer(cx, cb); +} + +static JSString * +MakeXMLCDATAString(JSContext *cx, JSCharBuffer &cb, JSString *str) +{ + static const jschar cdata_prefix_ucNstr[] = {'<', '!', '[', + 'C', 'D', 'A', 'T', 'A', + '['}; + static const jschar cdata_suffix_ucNstr[] = {']', ']', '>'}; + + return MakeXMLSpecialString(cx, cb, str, NULL, + cdata_prefix_ucNstr, 9, + cdata_suffix_ucNstr, 3); +} + +static JSString * +MakeXMLCommentString(JSContext *cx, JSCharBuffer &cb, JSString *str) +{ + static const jschar comment_prefix_ucNstr[] = {'<', '!', '-', '-'}; + static const jschar comment_suffix_ucNstr[] = {'-', '-', '>'}; + + return MakeXMLSpecialString(cx, cb, str, NULL, + comment_prefix_ucNstr, 4, + comment_suffix_ucNstr, 3); +} + +static JSString * +MakeXMLPIString(JSContext *cx, JSCharBuffer &cb, JSString *name, + JSString *value) +{ + static const jschar pi_prefix_ucNstr[] = {'<', '?'}; + static const jschar pi_suffix_ucNstr[] = {'?', '>'}; + + return MakeXMLSpecialString(cx, cb, name, value, + pi_prefix_ucNstr, 2, + pi_suffix_ucNstr, 2); +} + +/* + * ECMA-357 10.2.1 17(d-g) pulled out into a common subroutine that appends + * equals, a double quote, an attribute value, and a closing double quote. + */ +static bool +AppendAttributeValue(JSContext *cx, JSCharBuffer &cb, JSString *valstr) +{ + if (!cb.append('=')) + return false; + valstr = js_EscapeAttributeValue(cx, valstr, JS_TRUE); + return valstr && AppendString(cb, valstr); +} + +/* + * ECMA-357 10.2.1.1 EscapeElementValue helper method. + + * These functions mutate cb, leaving it empty. + */ +static JSString * +EscapeElementValue(JSContext *cx, JSCharBuffer &cb, JSString *str) +{ + size_t length; + const jschar *start; + str->getCharsAndLength(start, length); + + for (const jschar *cp = start, *end = start + length; cp != end; ++cp) { + jschar c = *cp; + switch (*cp) { + case '<': + if (!js_AppendLiteral(cb, js_lt_entity_str)) + return NULL; + break; + case '>': + if (!js_AppendLiteral(cb, js_gt_entity_str)) + return NULL; + break; + case '&': + if (!js_AppendLiteral(cb, js_amp_entity_str)) + return NULL; + break; + default: + if (!cb.append(c)) + return NULL; + } + } + return js_NewStringFromCharBuffer(cx, cb); +} + +/* + * ECMA-357 10.2.1.2 EscapeAttributeValue helper method. + * + * These functions mutate cb, leaving it empty. + */ +static JSString * +EscapeAttributeValue(JSContext *cx, JSCharBuffer &cb, JSString *str, + JSBool quote) +{ + size_t length; + const jschar *start; + str->getCharsAndLength(start, length); + + if (quote && !cb.append('"')) + return NULL; + + for (const jschar *cp = start, *end = start + length; cp != end; ++cp) { + jschar c = *cp; + switch (c) { + case '"': + if (!js_AppendLiteral(cb, js_quot_entity_str)) + return NULL; + break; + case '<': + if (!js_AppendLiteral(cb, js_lt_entity_str)) + return NULL; + break; + case '&': + if (!js_AppendLiteral(cb, js_amp_entity_str)) + return NULL; + break; + case '\n': + if (!js_AppendLiteral(cb, " ")) + return NULL; + break; + case '\r': + if (!js_AppendLiteral(cb, " ")) + return NULL; + break; + case '\t': + if (!js_AppendLiteral(cb, " ")) + return NULL; + break; + default: + if (!cb.append(c)) + return NULL; + } + } + + if (quote && !cb.append('"')) + return NULL; + + return js_NewStringFromCharBuffer(cx, cb); +} + +/* 13.3.5.4 [[GetNamespace]]([InScopeNamespaces]) */ +static JSObject * +GetNamespace(JSContext *cx, JSObject *qn, const JSXMLArray *inScopeNSes) +{ + JSString *uri, *prefix, *nsprefix; + JSObject *match, *ns; + uint32 i, n; + jsval argv[2]; + + uri = GetURI(qn); + prefix = GetPrefix(qn); + JS_ASSERT(uri); + if (!uri) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XML_NAMESPACE, + prefix + ? js_ValueToPrintableString(cx, + STRING_TO_JSVAL(prefix)) + : js_undefined_str); + return NULL; + } + + /* Look for a matching namespace in inScopeNSes, if provided. */ + match = NULL; + if (inScopeNSes) { + for (i = 0, n = inScopeNSes->length; i < n; i++) { + ns = XMLARRAY_MEMBER(inScopeNSes, i, JSObject); + if (!ns) + continue; + + /* + * Erratum, very tricky, and not specified in ECMA-357 13.3.5.4: + * If we preserve prefixes, we must match null prefix against + * an empty prefix of ns, in order to avoid generating redundant + * prefixed and default namespaces for cases such as: + * + * x = + * print(x.toXMLString()); + * + * Per 10.3.2.1, the namespace attribute in t has an empty string + * prefix (*not* a null prefix), per 10.3.2.1 Step 6(h)(i)(1): + * + * 1. If the [local name] property of a is "xmlns" + * a. Map ns.prefix to the empty string + * + * But t's name has a null prefix in this implementation, meaning + * *undefined*, per 10.3.2.1 Step 6(c)'s NOTE (which refers to + * the http://www.w3.org/TR/xml-infoset/ spec, item 2.2.3, without + * saying how "no value" maps to an ECMA-357 value -- but it must + * map to the *undefined* prefix value). + * + * Since "" != undefined (or null, in the current implementation) + * the ECMA-357 spec will fail to match in [[GetNamespace]] called + * on t with argument {} U {(prefix="", uri="http://foo.com")}. + * This spec bug leads to ToXMLString results that duplicate the + * declared namespace. + */ + if (js_EqualStrings(GetURI(ns), uri)) { + nsprefix = GetPrefix(ns); + if (nsprefix == prefix || + ((nsprefix && prefix) + ? js_EqualStrings(nsprefix, prefix) + : (nsprefix ? nsprefix : prefix)->empty())) { + match = ns; + break; + } + } + } + } + + /* If we didn't match, make a new namespace from qn. */ + if (!match) { + argv[0] = prefix ? STRING_TO_JSVAL(prefix) : JSVAL_VOID; + argv[1] = STRING_TO_JSVAL(uri); + ns = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, NULL, + 2, argv); + if (!ns) + return NULL; + match = ns; + } + return match; +} + +static JSString * +GeneratePrefix(JSContext *cx, JSString *uri, JSXMLArray *decls) +{ + const jschar *cp, *start, *end; + size_t length, newlength, offset; + uint32 i, n, m, serial; + jschar *bp, *dp; + JSBool done; + JSObject *ns; + JSString *nsprefix, *prefix; + + JS_ASSERT(!uri->empty()); + + /* + * If there are no *declared* namespaces, skip all collision detection and + * return a short prefix quickly; an example of such a situation: + * + * var x = ; + * var n = new Namespace("http://example.com/"); + * x.@n::att = "val"; + * x.toXMLString(); + * + * This is necessary for various log10 uses below to be valid. + */ + if (decls->length == 0) + return JS_NewStringCopyZ(cx, "a"); + + /* + * Try peeling off the last filename suffix or pathname component till + * we have a valid XML name. This heuristic will prefer "xul" given + * ".../there.is.only.xul", "xbl" given ".../xbl", and "xbl2" given any + * likely URI of the form ".../xbl2/2005". + */ + uri->getCharsAndEnd(start, end); + cp = end; + while (--cp > start) { + if (*cp == '.' || *cp == '/' || *cp == ':') { + ++cp; + length = end - cp; + if (IsXMLName(cp, length) && !STARTS_WITH_XML(cp, length)) + break; + end = --cp; + } + } + length = end - cp; + + /* + * If the namespace consisted only of non-XML names or names that begin + * case-insensitively with "xml", arbitrarily create a prefix consisting + * of 'a's of size length (allowing dp-calculating code to work with or + * without this branch executing) plus the space for storing a hyphen and + * the serial number (avoiding reallocation if a collision happens). + */ + bp = (jschar *) cp; + newlength = length; + if (STARTS_WITH_XML(cp, length) || !IsXMLName(cp, length)) { + newlength = length + 2 + (size_t) log10((double) decls->length); + bp = (jschar *) + cx->malloc((newlength + 1) * sizeof(jschar)); + if (!bp) + return NULL; + + bp[newlength] = 0; + for (i = 0; i < newlength; i++) + bp[i] = 'a'; + } + + /* + * Now search through decls looking for a collision. If we collide with + * an existing prefix, start tacking on a hyphen and a serial number. + */ + serial = 0; + do { + done = JS_TRUE; + for (i = 0, n = decls->length; i < n; i++) { + ns = XMLARRAY_MEMBER(decls, i, JSObject); + if (ns && (nsprefix = GetPrefix(ns)) && + nsprefix->length() == newlength && + !memcmp(nsprefix->chars(), bp, + newlength * sizeof(jschar))) { + if (bp == cp) { + newlength = length + 2 + (size_t) log10((double) n); + bp = (jschar *) + cx->malloc((newlength + 1) * sizeof(jschar)); + if (!bp) + return NULL; + js_strncpy(bp, cp, length); + } + + ++serial; + JS_ASSERT(serial <= n); + dp = bp + length + 2 + (size_t) log10((double) serial); + *dp = 0; + for (m = serial; m != 0; m /= 10) + *--dp = (jschar)('0' + m % 10); + *--dp = '-'; + JS_ASSERT(dp == bp + length); + + done = JS_FALSE; + break; + } + } + } while (!done); + + if (bp == cp) { + offset = cp - start; + prefix = js_NewDependentString(cx, uri, offset, length); + } else { + prefix = js_NewString(cx, bp, newlength); + if (!prefix) + cx->free(bp); + } + return prefix; +} + +static JSBool +namespace_match(const void *a, const void *b) +{ + const JSObject *nsa = (const JSObject *) a; + const JSObject *nsb = (const JSObject *) b; + JSString *prefixa, *prefixb = GetPrefix(nsb); + + if (prefixb) { + prefixa = GetPrefix(nsa); + return prefixa && js_EqualStrings(prefixa, prefixb); + } + return js_EqualStrings(GetURI(nsa), GetURI(nsb)); +} + +/* ECMA-357 10.2.1 and 10.2.2 */ +#define TO_SOURCE_FLAG 0x80000000 + +static JSString * +XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, + uint32 indentLevel) +{ + JSBool pretty, indentKids; + JSCharBuffer cb(cx); + JSString *str, *prefix, *nsuri; + uint32 i, n, nextIndentLevel; + JSXMLArray empty, decls, ancdecls; + JSObject *ns, *ns2; + + if (!GetBooleanXMLSetting(cx, js_prettyPrinting_str, &pretty)) + return NULL; + + if (pretty) { + if (!cb.appendN(' ', indentLevel & ~TO_SOURCE_FLAG)) + return NULL; + } + + str = NULL; + + switch (xml->xml_class) { + case JSXML_CLASS_TEXT: + /* Step 4. */ + if (pretty) { + str = ChompXMLWhitespace(cx, xml->xml_value); + if (!str) + return NULL; + } else { + str = xml->xml_value; + } + return EscapeElementValue(cx, cb, str); + + case JSXML_CLASS_ATTRIBUTE: + /* Step 5. */ + return EscapeAttributeValue(cx, cb, xml->xml_value, + (indentLevel & TO_SOURCE_FLAG) != 0); + + case JSXML_CLASS_COMMENT: + /* Step 6. */ + return MakeXMLCommentString(cx, cb, xml->xml_value); + + case JSXML_CLASS_PROCESSING_INSTRUCTION: + /* Step 7. */ + return MakeXMLPIString(cx, cb, GetLocalName(xml->name), + xml->xml_value); + + case JSXML_CLASS_LIST: + /* ECMA-357 10.2.2. */ + { + JSXMLArrayCursor cursor(&xml->xml_kids); + i = 0; + while (JSXML *kid = (JSXML *) cursor.getNext()) { + if (pretty && i != 0) { + if (!cb.append('\n')) + return NULL; + } + + JSString *kidstr = XMLToXMLString(cx, kid, ancestorNSes, indentLevel); + if (!kidstr || !AppendString(cb, kidstr)) + return NULL; + ++i; + } + } + + if (cb.empty()) + return cx->runtime->emptyString; + return js_NewStringFromCharBuffer(cx, cb); + + default:; + } + + /* After this point, control must flow through label out: to exit. */ + if (!js_EnterLocalRootScope(cx)) + return NULL; + + /* ECMA-357 10.2.1 step 8 onward: handle ToXMLString on an XML element. */ + if (!ancestorNSes) { + XMLArrayInit(cx, &empty, 0); + ancestorNSes = ∅ + } + XMLArrayInit(cx, &decls, 0); + ancdecls.capacity = 0; + + /* Clone in-scope namespaces not in ancestorNSes into decls. */ + { + JSXMLArrayCursor cursor(&xml->xml_namespaces); + while ((ns = (JSObject *) cursor.getNext()) != NULL) { + if (!IsDeclared(ns)) + continue; + if (!XMLARRAY_HAS_MEMBER(ancestorNSes, ns, namespace_identity)) { + /* NOTE: may want to exclude unused namespaces here. */ + ns2 = NewXMLNamespace(cx, GetPrefix(ns), GetURI(ns), JS_TRUE); + if (!ns2 || !XMLARRAY_APPEND(cx, &decls, ns2)) + goto out; + } + } + } + + /* + * Union ancestorNSes and decls into ancdecls. Note that ancdecls does + * not own its member references. In the spec, ancdecls has no name, but + * is always written out as (AncestorNamespaces U namespaceDeclarations). + */ + if (!XMLArrayInit(cx, &ancdecls, ancestorNSes->length + decls.length)) + goto out; + for (i = 0, n = ancestorNSes->length; i < n; i++) { + ns2 = XMLARRAY_MEMBER(ancestorNSes, i, JSObject); + if (!ns2) + continue; + JS_ASSERT(!XMLARRAY_HAS_MEMBER(&decls, ns2, namespace_identity)); + if (!XMLARRAY_APPEND(cx, &ancdecls, ns2)) + goto out; + } + for (i = 0, n = decls.length; i < n; i++) { + ns2 = XMLARRAY_MEMBER(&decls, i, JSObject); + if (!ns2) + continue; + JS_ASSERT(!XMLARRAY_HAS_MEMBER(&ancdecls, ns2, namespace_identity)); + if (!XMLARRAY_APPEND(cx, &ancdecls, ns2)) + goto out; + } + + /* Step 11, except we don't clone ns unless its prefix is undefined. */ + ns = GetNamespace(cx, xml->name, &ancdecls); + if (!ns) + goto out; + + /* Step 12 (NULL means *undefined* here), plus the deferred ns cloning. */ + prefix = GetPrefix(ns); + if (!prefix) { + /* + * Create a namespace prefix that isn't used by any member of decls. + * Assign the new prefix to a copy of ns. Flag this namespace as if + * it were declared, for assertion-testing's sake later below. + * + * Erratum: if prefix and xml->name are both null (*undefined* in + * ECMA-357), we know that xml was named using the default namespace + * (proof: see GetNamespace and the Namespace constructor called with + * two arguments). So we ought not generate a new prefix here, when + * we can declare ns as the default namespace for xml. + * + * This helps descendants inherit the namespace instead of redundantly + * redeclaring it with generated prefixes in each descendant. + */ + nsuri = GetURI(ns); + if (!GetPrefix(xml->name)) { + prefix = cx->runtime->emptyString; + } else { + prefix = GeneratePrefix(cx, nsuri, &ancdecls); + if (!prefix) + goto out; + } + ns = NewXMLNamespace(cx, prefix, nsuri, JS_TRUE); + if (!ns) + goto out; + + /* + * If the xml->name was unprefixed, we must remove any declared default + * namespace from decls before appending ns. How can you get a default + * namespace in decls that doesn't match the one from name? Apparently + * by calling x.setNamespace(ns) where ns has no prefix. The other way + * to fix this is to update x's in-scope namespaces when setNamespace + * is called, but that's not specified by ECMA-357. + * + * Likely Erratum here, depending on whether the lack of update to x's + * in-scope namespace in XML.prototype.setNamespace (13.4.4.36) is an + * erratum or not. Note that changing setNamespace to update the list + * of in-scope namespaces will change x.namespaceDeclarations(). + */ + if (prefix->empty()) { + i = XMLArrayFindMember(&decls, ns, namespace_match); + if (i != XML_NOT_FOUND) + XMLArrayDelete(cx, &decls, i, JS_TRUE); + } + + /* + * In the spec, ancdecls has no name, but is always written out as + * (AncestorNamespaces U namespaceDeclarations). Since we compute + * that union in ancdecls, any time we append a namespace strong + * ref to decls, we must also append a weak ref to ancdecls. Order + * matters here: code at label out: releases strong refs in decls. + */ + if (!XMLARRAY_APPEND(cx, &ancdecls, ns) || + !XMLARRAY_APPEND(cx, &decls, ns)) { + goto out; + } + } + + /* Format the element or point-tag into cb. */ + if (!cb.append('<')) + goto out; + + if (prefix && !prefix->empty()) { + if (!AppendString(cb, prefix) || !cb.append(':')) + goto out; + } + if (!AppendString(cb, GetLocalName(xml->name))) + goto out; + + /* + * Step 16 makes a union to avoid writing two loops in step 17, to share + * common attribute value appending spec-code. We prefer two loops for + * faster code and less data overhead. + */ + + /* Step 17(b): append attributes. */ + { + JSXMLArrayCursor cursor(&xml->xml_attrs); + while (JSXML *attr = (JSXML *) cursor.getNext()) { + if (!cb.append(' ')) + goto out; + ns2 = GetNamespace(cx, attr->name, &ancdecls); + if (!ns2) + goto out; + + /* 17(b)(ii): NULL means *undefined* here. */ + prefix = GetPrefix(ns2); + if (!prefix) { + prefix = GeneratePrefix(cx, GetURI(ns2), &ancdecls); + if (!prefix) + goto out; + + /* Again, we avoid copying ns2 until we know it's prefix-less. */ + ns2 = NewXMLNamespace(cx, prefix, GetURI(ns2), JS_TRUE); + if (!ns2) + goto out; + + /* + * In the spec, ancdecls has no name, but is always written out as + * (AncestorNamespaces U namespaceDeclarations). Since we compute + * that union in ancdecls, any time we append a namespace strong + * ref to decls, we must also append a weak ref to ancdecls. Order + * matters here: code at label out: releases strong refs in decls. + */ + if (!XMLARRAY_APPEND(cx, &ancdecls, ns2) || + !XMLARRAY_APPEND(cx, &decls, ns2)) { + goto out; + } + } + + /* 17(b)(iii). */ + if (!prefix->empty()) { + if (!AppendString(cb, prefix) || !cb.append(':')) + goto out; + } + + /* 17(b)(iv). */ + if (!AppendString(cb, GetLocalName(attr->name))) + goto out; + + /* 17(d-g). */ + if (!AppendAttributeValue(cx, cb, attr->xml_value)) + goto out; + } + } + + /* Step 17(c): append XML namespace declarations. */ + { + JSXMLArrayCursor cursor(&decls); + while (JSObject *ns3 = (JSObject *) cursor.getNext()) { + JS_ASSERT(IsDeclared(ns3)); + + if (!js_AppendLiteral(cb, " xmlns")) + goto out; + + /* 17(c)(ii): NULL means *undefined* here. */ + prefix = GetPrefix(ns3); + if (!prefix) { + prefix = GeneratePrefix(cx, GetURI(ns3), &ancdecls); + if (!prefix) + goto out; + ns3->fslots[JSSLOT_PREFIX] = STRING_TO_JSVAL(prefix); + } + + /* 17(c)(iii). */ + if (!prefix->empty()) { + if (!cb.append(':') || !AppendString(cb, prefix)) + goto out; + } + + /* 17(d-g). */ + if (!AppendAttributeValue(cx, cb, GetURI(ns3))) + goto out; + } + } + + /* Step 18: handle point tags. */ + n = xml->xml_kids.length; + if (n == 0) { + if (!js_AppendLiteral(cb, "/>")) + goto out; + } else { + /* Steps 19 through 25: handle element content, and open the end-tag. */ + if (!cb.append('>')) + goto out; + { + JSXML *kid; + indentKids = n > 1 || + (n == 1 && + (kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML)) && + kid->xml_class != JSXML_CLASS_TEXT); + } + + if (pretty && indentKids) { + if (!GetUint32XMLSetting(cx, js_prettyIndent_str, &i)) + goto out; + nextIndentLevel = indentLevel + i; + } else { + nextIndentLevel = indentLevel & TO_SOURCE_FLAG; + } + + { + JSXMLArrayCursor cursor(&xml->xml_kids); + while (JSXML *kid = (JSXML *) cursor.getNext()) { + if (pretty && indentKids) { + if (!cb.append('\n')) + goto out; + } + + JSString *kidstr = XMLToXMLString(cx, kid, &ancdecls, nextIndentLevel); + if (!kidstr) + goto out; + + if (!AppendString(cb, kidstr)) + goto out; + } + } + + if (pretty && indentKids) { + if (!cb.append('\n') || + !cb.appendN(' ', indentLevel & ~TO_SOURCE_FLAG)) + goto out; + } + if (!js_AppendLiteral(cb, "empty()) { + if (!AppendString(cb, prefix) || !cb.append(':')) + goto out; + } + + /* Step 27. */ + if (!AppendString(cb, GetLocalName(xml->name)) || !cb.append('>')) + goto out; + } + + str = js_NewStringFromCharBuffer(cx, cb); +out: + js_LeaveLocalRootScopeWithResult(cx, STRING_TO_JSVAL(str)); + XMLArrayFinish(cx, &decls); + if (ancdecls.capacity != 0) + XMLArrayFinish(cx, &ancdecls); + return str; +} + +/* ECMA-357 10.2 */ +static JSString * +ToXMLString(JSContext *cx, jsval v, uint32 toSourceFlag) +{ + JSObject *obj; + JSString *str; + JSXML *xml; + + if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XML_CONVERSION, + JSVAL_IS_NULL(v) ? js_null_str : js_undefined_str); + return NULL; + } + + if (JSVAL_IS_BOOLEAN(v) || JSVAL_IS_NUMBER(v)) + return js_ValueToString(cx, v); + + if (JSVAL_IS_STRING(v)) { + JSCharBuffer cb(cx); + return EscapeElementValue(cx, cb, JSVAL_TO_STRING(v)); + } + + obj = JSVAL_TO_OBJECT(v); + if (!OBJECT_IS_XML(cx, obj)) { + if (!obj->defaultValue(cx, JSTYPE_STRING, &v)) + return NULL; + str = js_ValueToString(cx, v); + if (!str) + return NULL; + JSCharBuffer cb(cx); + return EscapeElementValue(cx, cb, str); + } + + /* Handle non-element cases in this switch, returning from each case. */ + xml = (JSXML *) obj->getPrivate(); + return XMLToXMLString(cx, xml, NULL, toSourceFlag | 0); +} + +static JSObject * +ToAttributeName(JSContext *cx, jsval v) +{ + JSString *name, *uri, *prefix; + JSObject *obj; + JSClass *clasp; + JSObject *qn; + + if (JSVAL_IS_STRING(v)) { + name = JSVAL_TO_STRING(v); + uri = prefix = cx->runtime->emptyString; + } else { + if (JSVAL_IS_PRIMITIVE(v)) { + js_ReportValueError(cx, JSMSG_BAD_XML_ATTR_NAME, + JSDVG_IGNORE_STACK, v, NULL); + return NULL; + } + + obj = JSVAL_TO_OBJECT(v); + clasp = OBJ_GET_CLASS(cx, obj); + if (clasp == &js_AttributeNameClass) + return obj; + + if (clasp == &js_QNameClass.base) { + qn = obj; + uri = GetURI(qn); + prefix = GetPrefix(qn); + name = GetLocalName(qn); + } else { + if (clasp == &js_AnyNameClass) { + name = ATOM_TO_STRING(cx->runtime->atomState.starAtom); + } else { + name = js_ValueToString(cx, v); + if (!name) + return NULL; + } + uri = prefix = cx->runtime->emptyString; + } + } + + qn = NewXMLQName(cx, uri, prefix, name, &js_AttributeNameClass); + if (!qn) + return NULL; + return qn; +} + +static void +ReportBadXMLName(JSContext *cx, jsval id) +{ + js_ReportValueError(cx, JSMSG_BAD_XML_NAME, JSDVG_IGNORE_STACK, id, NULL); +} + +static JSBool +IsFunctionQName(JSContext *cx, JSObject *qn, jsid *funidp) +{ + JSAtom *atom; + JSString *uri; + + atom = cx->runtime->atomState.lazy.functionNamespaceURIAtom; + uri = GetURI(qn); + if (uri && atom && + (uri == ATOM_TO_STRING(atom) || + js_EqualStrings(uri, ATOM_TO_STRING(atom)))) { + return JS_ValueToId(cx, STRING_TO_JSVAL(GetLocalName(qn)), funidp); + } + *funidp = 0; + return JS_TRUE; +} + +JSBool +js_IsFunctionQName(JSContext *cx, JSObject *obj, jsid *funidp) +{ + if (OBJ_GET_CLASS(cx, obj) == &js_QNameClass.base) + return IsFunctionQName(cx, obj, funidp); + *funidp = 0; + return JS_TRUE; +} + +static JSObject * +ToXMLName(JSContext *cx, jsval v, jsid *funidp) +{ + JSString *name; + JSObject *obj; + JSClass *clasp; + uint32 index; + + if (JSVAL_IS_STRING(v)) { + name = JSVAL_TO_STRING(v); + } else { + if (JSVAL_IS_PRIMITIVE(v)) { + ReportBadXMLName(cx, v); + return NULL; + } + + obj = JSVAL_TO_OBJECT(v); + clasp = OBJ_GET_CLASS(cx, obj); + if (clasp == &js_AttributeNameClass || clasp == &js_QNameClass.base) + goto out; + if (clasp == &js_AnyNameClass) { + name = ATOM_TO_STRING(cx->runtime->atomState.starAtom); + goto construct; + } + name = js_ValueToString(cx, v); + if (!name) + return NULL; + } + + /* + * ECMA-357 10.6.1 step 1 seems to be incorrect. The spec says: + * + * 1. If ToString(ToNumber(P)) == ToString(P), throw a TypeError exception + * + * First, _P_ should be _s_, to refer to the given string. + * + * Second, why does ToXMLName applied to the string type throw TypeError + * only for numeric literals without any leading or trailing whitespace? + * + * If the idea is to reject uint32 property names, then the check needs to + * be stricter, to exclude hexadecimal and floating point literals. + */ + if (js_IdIsIndex(STRING_TO_JSVAL(name), &index)) + goto bad; + + if (*name->chars() == '@') { + name = js_NewDependentString(cx, name, 1, name->length() - 1); + if (!name) + return NULL; + *funidp = 0; + return ToAttributeName(cx, STRING_TO_JSVAL(name)); + } + +construct: + v = STRING_TO_JSVAL(name); + obj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 1, &v); + if (!obj) + return NULL; + +out: + if (!IsFunctionQName(cx, obj, funidp)) + return NULL; + return obj; + +bad: + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XML_NAME, + js_ValueToPrintableString(cx, STRING_TO_JSVAL(name))); + return NULL; +} + +/* ECMA-357 9.1.1.13 XML [[AddInScopeNamespace]]. */ +static JSBool +AddInScopeNamespace(JSContext *cx, JSXML *xml, JSObject *ns) +{ + JSString *prefix, *prefix2; + JSObject *match, *ns2; + uint32 i, n, m; + + if (xml->xml_class != JSXML_CLASS_ELEMENT) + return JS_TRUE; + + /* NULL means *undefined* here -- see ECMA-357 9.1.1.13 step 2. */ + prefix = GetPrefix(ns); + if (!prefix) { + match = NULL; + for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { + ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject); + if (ns2 && js_EqualStrings(GetURI(ns2), GetURI(ns))) { + match = ns2; + break; + } + } + if (!match && !XMLARRAY_ADD_MEMBER(cx, &xml->xml_namespaces, n, ns)) + return JS_FALSE; + } else { + if (prefix->empty() && GetURI(xml->name)->empty()) + return JS_TRUE; + match = NULL; +#ifdef __GNUC__ /* suppress bogus gcc warnings */ + m = XML_NOT_FOUND; +#endif + for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { + ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject); + if (ns2 && (prefix2 = GetPrefix(ns2)) && + js_EqualStrings(prefix2, prefix)) { + match = ns2; + m = i; + break; + } + } + if (match && !js_EqualStrings(GetURI(match), GetURI(ns))) { + ns2 = XMLARRAY_DELETE(cx, &xml->xml_namespaces, m, JS_TRUE, + JSObject); + JS_ASSERT(ns2 == match); + match->fslots[JSSLOT_PREFIX] = JSVAL_VOID; + if (!AddInScopeNamespace(cx, xml, match)) + return JS_FALSE; + } + if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) + return JS_FALSE; + } + + /* OPTION: enforce that descendants have superset namespaces. */ + return JS_TRUE; +} + +/* ECMA-357 9.2.1.6 XMLList [[Append]]. */ +static JSBool +Append(JSContext *cx, JSXML *list, JSXML *xml) +{ + uint32 i, j, k, n; + JSXML *kid; + + JS_ASSERT(list->xml_class == JSXML_CLASS_LIST); + i = list->xml_kids.length; + n = 1; + if (xml->xml_class == JSXML_CLASS_LIST) { + list->xml_target = xml->xml_target; + list->xml_targetprop = xml->xml_targetprop; + n = JSXML_LENGTH(xml); + k = i + n; + if (!XMLArraySetCapacity(cx, &list->xml_kids, k)) + return JS_FALSE; + for (j = 0; j < n; j++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, j, JSXML); + if (kid) + XMLARRAY_SET_MEMBER(&list->xml_kids, i + j, kid); + } + return JS_TRUE; + } + + list->xml_target = xml->parent; + if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) + list->xml_targetprop = NULL; + else + list->xml_targetprop = xml->name; + if (!XMLARRAY_ADD_MEMBER(cx, &list->xml_kids, i, xml)) + return JS_FALSE; + return JS_TRUE; +} + +/* ECMA-357 9.1.1.7 XML [[DeepCopy]] and 9.2.1.7 XMLList [[DeepCopy]]. */ +static JSXML * +DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags); + +static JSXML * +DeepCopy(JSContext *cx, JSXML *xml, JSObject *obj, uintN flags) +{ + JSXML *copy; + + /* Our caller may not be protecting newborns with a local root scope. */ + if (!js_EnterLocalRootScope(cx)) + return NULL; + copy = DeepCopyInLRS(cx, xml, flags); + if (copy) { + if (obj) { + /* Caller provided the object for this copy, hook 'em up. */ + obj->setPrivate(copy); + copy->object = obj; + } else if (!js_GetXMLObject(cx, copy)) { + copy = NULL; + } + } + js_LeaveLocalRootScopeWithResult(cx, (jsval) copy); + return copy; +} + +/* + * (i) We must be in a local root scope (InLRS). + * (ii) parent must have a rooted object. + * (iii) from's owning object must be locked if not thread-local. + */ +static JSBool +DeepCopySetInLRS(JSContext *cx, JSXMLArray *from, JSXMLArray *to, JSXML *parent, + uintN flags) +{ + uint32 j, n; + JSXML *kid2; + JSString *str; + + JS_ASSERT(JS_THREAD_DATA(cx)->localRootStack); + + n = from->length; + if (!XMLArraySetCapacity(cx, to, n)) + return JS_FALSE; + + JSXMLArrayCursor cursor(from); + j = 0; + while (JSXML *kid = (JSXML *) cursor.getNext()) { + if ((flags & XSF_IGNORE_COMMENTS) && + kid->xml_class == JSXML_CLASS_COMMENT) { + continue; + } + if ((flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS) && + kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) { + continue; + } + if ((flags & XSF_IGNORE_WHITESPACE) && + (kid->xml_flags & XMLF_WHITESPACE_TEXT)) { + continue; + } + kid2 = DeepCopyInLRS(cx, kid, flags); + if (!kid2) { + to->length = j; + return JS_FALSE; + } + + if ((flags & XSF_IGNORE_WHITESPACE) && + n > 1 && kid2->xml_class == JSXML_CLASS_TEXT) { + str = ChompXMLWhitespace(cx, kid2->xml_value); + if (!str) { + to->length = j; + return JS_FALSE; + } + kid2->xml_value = str; + } + + XMLARRAY_SET_MEMBER(to, j, kid2); + ++j; + if (parent->xml_class != JSXML_CLASS_LIST) + kid2->parent = parent; + } + + if (j < n) + XMLArrayTrim(to); + return JS_TRUE; +} + +static JSXML * +DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags) +{ + JSXML *copy; + JSObject *qn; + JSBool ok; + uint32 i, n; + JSObject *ns, *ns2; + + /* Our caller must be protecting newborn objects. */ + JS_ASSERT(JS_THREAD_DATA(cx)->localRootStack); + + JS_CHECK_RECURSION(cx, return NULL); + + copy = js_NewXML(cx, JSXMLClass(xml->xml_class)); + if (!copy) + return NULL; + qn = xml->name; + if (qn) { + qn = NewXMLQName(cx, GetURI(qn), GetPrefix(qn), GetLocalName(qn)); + if (!qn) { + ok = JS_FALSE; + goto out; + } + } + copy->name = qn; + copy->xml_flags = xml->xml_flags; + + if (JSXML_HAS_VALUE(xml)) { + copy->xml_value = xml->xml_value; + ok = JS_TRUE; + } else { + ok = DeepCopySetInLRS(cx, &xml->xml_kids, ©->xml_kids, copy, flags); + if (!ok) + goto out; + + if (xml->xml_class == JSXML_CLASS_LIST) { + copy->xml_target = xml->xml_target; + copy->xml_targetprop = xml->xml_targetprop; + } else { + n = xml->xml_namespaces.length; + ok = XMLArraySetCapacity(cx, ©->xml_namespaces, n); + if (!ok) + goto out; + for (i = 0; i < n; i++) { + ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject); + if (!ns) + continue; + ns2 = NewXMLNamespace(cx, GetPrefix(ns), GetURI(ns), + IsDeclared(ns)); + if (!ns2) { + copy->xml_namespaces.length = i; + ok = JS_FALSE; + goto out; + } + XMLARRAY_SET_MEMBER(©->xml_namespaces, i, ns2); + } + + ok = DeepCopySetInLRS(cx, &xml->xml_attrs, ©->xml_attrs, copy, + 0); + if (!ok) + goto out; + } + } + +out: + if (!ok) + return NULL; + return copy; +} + +/* ECMA-357 9.1.1.4 XML [[DeleteByIndex]]. */ +static void +DeleteByIndex(JSContext *cx, JSXML *xml, uint32 index) +{ + JSXML *kid; + + if (JSXML_HAS_KIDS(xml) && index < xml->xml_kids.length) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); + if (kid) + kid->parent = NULL; + XMLArrayDelete(cx, &xml->xml_kids, index, JS_TRUE); + } +} + +typedef JSBool (*JSXMLNameMatcher)(JSObject *nameqn, JSXML *xml); + +static JSBool +MatchAttrName(JSObject *nameqn, JSXML *attr) +{ + JSObject *attrqn = attr->name; + JSString *localName = GetLocalName(nameqn); + JSString *uri; + + return (IS_STAR(localName) || + js_EqualStrings(GetLocalName(attrqn), localName)) && + (!(uri = GetURI(nameqn)) || + js_EqualStrings(GetURI(attrqn), uri)); +} + +static JSBool +MatchElemName(JSObject *nameqn, JSXML *elem) +{ + JSString *localName = GetLocalName(nameqn); + JSString *uri; + + return (IS_STAR(localName) || + (elem->xml_class == JSXML_CLASS_ELEMENT && + js_EqualStrings(GetLocalName(elem->name), localName))) && + (!(uri = GetURI(nameqn)) || + (elem->xml_class == JSXML_CLASS_ELEMENT && + js_EqualStrings(GetURI(elem->name), uri))); +} + +/* ECMA-357 9.1.1.8 XML [[Descendants]] and 9.2.1.8 XMLList [[Descendants]]. */ +static JSBool +DescendantsHelper(JSContext *cx, JSXML *xml, JSObject *nameqn, JSXML *list) +{ + uint32 i, n; + JSXML *attr, *kid; + + JS_CHECK_RECURSION(cx, return JS_FALSE); + + if (xml->xml_class == JSXML_CLASS_ELEMENT && + OBJ_GET_CLASS(cx, nameqn) == &js_AttributeNameClass) { + for (i = 0, n = xml->xml_attrs.length; i < n; i++) { + attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); + if (attr && MatchAttrName(nameqn, attr)) { + if (!Append(cx, list, attr)) + return JS_FALSE; + } + } + } + + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (!kid) + continue; + if (OBJ_GET_CLASS(cx, nameqn) != &js_AttributeNameClass && + MatchElemName(nameqn, kid)) { + if (!Append(cx, list, kid)) + return JS_FALSE; + } + if (!DescendantsHelper(cx, kid, nameqn, list)) + return JS_FALSE; + } + return JS_TRUE; +} + +static JSXML * +Descendants(JSContext *cx, JSXML *xml, jsval id) +{ + jsid funid; + JSObject *nameqn; + JSObject *listobj; + JSXML *list, *kid; + uint32 i, n; + JSBool ok; + + nameqn = ToXMLName(cx, id, &funid); + if (!nameqn) + return NULL; + + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) + return NULL; + list = (JSXML *) listobj->getPrivate(); + if (funid) + return list; + + /* + * Protect nameqn's object and strings from GC by linking list to it + * temporarily. The newborn GC root for the last allocated object + * protects listobj, which protects list. Any other object allocations + * occurring beneath DescendantsHelper use local roots. + */ + list->name = nameqn; + if (!js_EnterLocalRootScope(cx)) + return NULL; + if (xml->xml_class == JSXML_CLASS_LIST) { + ok = JS_TRUE; + for (i = 0, n = xml->xml_kids.length; i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { + ok = DescendantsHelper(cx, kid, nameqn, list); + if (!ok) + break; + } + } + } else { + ok = DescendantsHelper(cx, xml, nameqn, list); + } + js_LeaveLocalRootScopeWithResult(cx, (jsval) list); + if (!ok) + return NULL; + list->name = NULL; + return list; +} + +/* Recursive (JSXML *) parameterized version of Equals. */ +static JSBool +XMLEquals(JSContext *cx, JSXML *xml, JSXML *vxml, JSBool *bp) +{ + JSObject *qn, *vqn; + uint32 i, j, n; + JSXML *kid, *vkid, *attr, *vattr; + JSObject *xobj, *vobj; + +retry: + if (xml->xml_class != vxml->xml_class) { + if (xml->xml_class == JSXML_CLASS_LIST && xml->xml_kids.length == 1) { + xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + if (xml) + goto retry; + } + if (vxml->xml_class == JSXML_CLASS_LIST && vxml->xml_kids.length == 1) { + vxml = XMLARRAY_MEMBER(&vxml->xml_kids, 0, JSXML); + if (vxml) + goto retry; + } + *bp = JS_FALSE; + return JS_TRUE; + } + + qn = xml->name; + vqn = vxml->name; + if (qn) { + *bp = vqn && + js_EqualStrings(GetLocalName(qn), GetLocalName(vqn)) && + js_EqualStrings(GetURI(qn), GetURI(vqn)); + } else { + *bp = vqn == NULL; + } + if (!*bp) + return JS_TRUE; + + if (JSXML_HAS_VALUE(xml)) { + *bp = js_EqualStrings(xml->xml_value, vxml->xml_value); + } else if (xml->xml_kids.length != vxml->xml_kids.length) { + *bp = JS_FALSE; + } else { + { + JSXMLArrayCursor cursor(&xml->xml_kids); + JSXMLArrayCursor vcursor(&vxml->xml_kids); + for (;;) { + kid = (JSXML *) cursor.getNext(); + vkid = (JSXML *) vcursor.getNext(); + if (!kid || !vkid) { + *bp = !kid && !vkid; + break; + } + xobj = js_GetXMLObject(cx, kid); + vobj = js_GetXMLObject(cx, vkid); + if (!xobj || !vobj || + !js_TestXMLEquality(cx, xobj, OBJECT_TO_JSVAL(vobj), bp)) + return JS_FALSE; + if (!*bp) + break; + } + } + + if (*bp && xml->xml_class == JSXML_CLASS_ELEMENT) { + n = xml->xml_attrs.length; + if (n != vxml->xml_attrs.length) + *bp = JS_FALSE; + for (i = 0; *bp && i < n; i++) { + attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); + if (!attr) + continue; + j = XMLARRAY_FIND_MEMBER(&vxml->xml_attrs, attr, attr_identity); + if (j == XML_NOT_FOUND) { + *bp = JS_FALSE; + break; + } + vattr = XMLARRAY_MEMBER(&vxml->xml_attrs, j, JSXML); + if (!vattr) + continue; + *bp = js_EqualStrings(attr->xml_value, vattr->xml_value); + } + } + } + + return JS_TRUE; +} + +/* ECMA-357 9.1.1.9 XML [[Equals]] and 9.2.1.9 XMLList [[Equals]]. */ +static JSBool +Equals(JSContext *cx, JSXML *xml, jsval v, JSBool *bp) +{ + JSObject *vobj; + JSXML *vxml; + + if (JSVAL_IS_PRIMITIVE(v)) { + *bp = JS_FALSE; + if (xml->xml_class == JSXML_CLASS_LIST) { + if (xml->xml_kids.length == 1) { + vxml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + if (!vxml) + return JS_TRUE; + vobj = js_GetXMLObject(cx, vxml); + if (!vobj) + return JS_FALSE; + return js_TestXMLEquality(cx, vobj, v, bp); + } + if (JSVAL_IS_VOID(v) && xml->xml_kids.length == 0) + *bp = JS_TRUE; + } + } else { + vobj = JSVAL_TO_OBJECT(v); + if (!OBJECT_IS_XML(cx, vobj)) { + *bp = JS_FALSE; + } else { + vxml = (JSXML *) vobj->getPrivate(); + if (!XMLEquals(cx, xml, vxml, bp)) + return JS_FALSE; + } + } + return JS_TRUE; +} + +static JSBool +CheckCycle(JSContext *cx, JSXML *xml, JSXML *kid) +{ + JS_ASSERT(kid->xml_class != JSXML_CLASS_LIST); + + do { + if (xml == kid) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_CYCLIC_VALUE, js_XML_str); + return JS_FALSE; + } + } while ((xml = xml->parent) != NULL); + + return JS_TRUE; +} + +/* ECMA-357 9.1.1.11 XML [[Insert]]. */ +static JSBool +Insert(JSContext *cx, JSXML *xml, uint32 i, jsval v) +{ + uint32 j, n; + JSXML *vxml, *kid; + JSObject *vobj; + JSString *str; + + if (!JSXML_HAS_KIDS(xml)) + return JS_TRUE; + + n = 1; + vxml = NULL; + if (!JSVAL_IS_PRIMITIVE(v)) { + vobj = JSVAL_TO_OBJECT(v); + if (OBJECT_IS_XML(cx, vobj)) { + vxml = (JSXML *) vobj->getPrivate(); + if (vxml->xml_class == JSXML_CLASS_LIST) { + n = vxml->xml_kids.length; + if (n == 0) + return JS_TRUE; + for (j = 0; j < n; j++) { + kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML); + if (!kid) + continue; + if (!CheckCycle(cx, xml, kid)) + return JS_FALSE; + } + } else if (vxml->xml_class == JSXML_CLASS_ELEMENT) { + /* OPTION: enforce that descendants have superset namespaces. */ + if (!CheckCycle(cx, xml, vxml)) + return JS_FALSE; + } + } + } + if (!vxml) { + str = js_ValueToString(cx, v); + if (!str) + return JS_FALSE; + + vxml = js_NewXML(cx, JSXML_CLASS_TEXT); + if (!vxml) + return JS_FALSE; + vxml->xml_value = str; + } + + if (i > xml->xml_kids.length) + i = xml->xml_kids.length; + + if (!XMLArrayInsert(cx, &xml->xml_kids, i, n)) + return JS_FALSE; + + if (vxml->xml_class == JSXML_CLASS_LIST) { + for (j = 0; j < n; j++) { + kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML); + if (!kid) + continue; + kid->parent = xml; + XMLARRAY_SET_MEMBER(&xml->xml_kids, i + j, kid); + + /* OPTION: enforce that descendants have superset namespaces. */ + } + } else { + vxml->parent = xml; + XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml); + } + return JS_TRUE; +} + +static JSBool +IndexToIdVal(JSContext *cx, uint32 index, jsval *idvp) +{ + JSString *str; + + if (index <= JSVAL_INT_MAX) { + *idvp = INT_TO_JSVAL(index); + } else { + str = js_NumberToString(cx, (jsdouble) index); + if (!str) + return JS_FALSE; + *idvp = STRING_TO_JSVAL(str); + } + return JS_TRUE; +} + +/* ECMA-357 9.1.1.12 XML [[Replace]]. */ +static JSBool +Replace(JSContext *cx, JSXML *xml, uint32 i, jsval v) +{ + uint32 n; + JSXML *vxml, *kid; + JSObject *vobj; + JSString *str; + + if (!JSXML_HAS_KIDS(xml)) + return JS_TRUE; + + /* + * 9.1.1.12 + * [[Replace]] handles _i >= x.[[Length]]_ by incrementing _x.[[Length]_. + * It should therefore constrain callers to pass in _i <= x.[[Length]]_. + */ + n = xml->xml_kids.length; + if (i > n) + i = n; + + vxml = NULL; + if (!JSVAL_IS_PRIMITIVE(v)) { + vobj = JSVAL_TO_OBJECT(v); + if (OBJECT_IS_XML(cx, vobj)) + vxml = (JSXML *) vobj->getPrivate(); + } + + switch (vxml ? JSXMLClass(vxml->xml_class) : JSXML_CLASS_LIMIT) { + case JSXML_CLASS_ELEMENT: + /* OPTION: enforce that descendants have superset namespaces. */ + if (!CheckCycle(cx, xml, vxml)) + return JS_FALSE; + case JSXML_CLASS_COMMENT: + case JSXML_CLASS_PROCESSING_INSTRUCTION: + case JSXML_CLASS_TEXT: + goto do_replace; + + case JSXML_CLASS_LIST: + if (i < n) + DeleteByIndex(cx, xml, i); + if (!Insert(cx, xml, i, v)) + return JS_FALSE; + break; + + default: + str = js_ValueToString(cx, v); + if (!str) + return JS_FALSE; + + vxml = js_NewXML(cx, JSXML_CLASS_TEXT); + if (!vxml) + return JS_FALSE; + vxml->xml_value = str; + + do_replace: + vxml->parent = xml; + if (i < n) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid) + kid->parent = NULL; + } + if (!XMLARRAY_ADD_MEMBER(cx, &xml->xml_kids, i, vxml)) + return JS_FALSE; + break; + } + + return JS_TRUE; +} + +/* ECMA-357 9.1.1.3 XML [[Delete]], 9.2.1.3 XML [[Delete]] qname cases. */ +static void +DeleteNamedProperty(JSContext *cx, JSXML *xml, JSObject *nameqn, + JSBool attributes) +{ + JSXMLArray *array; + uint32 index, deleteCount; + JSXML *kid; + JSXMLNameMatcher matcher; + + if (xml->xml_class == JSXML_CLASS_LIST) { + array = &xml->xml_kids; + for (index = 0; index < array->length; index++) { + kid = XMLARRAY_MEMBER(array, index, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) + DeleteNamedProperty(cx, kid, nameqn, attributes); + } + } else if (xml->xml_class == JSXML_CLASS_ELEMENT) { + if (attributes) { + array = &xml->xml_attrs; + matcher = MatchAttrName; + } else { + array = &xml->xml_kids; + matcher = MatchElemName; + } + deleteCount = 0; + for (index = 0; index < array->length; index++) { + kid = XMLARRAY_MEMBER(array, index, JSXML); + if (kid && matcher(nameqn, kid)) { + kid->parent = NULL; + XMLArrayDelete(cx, array, index, JS_FALSE); + ++deleteCount; + } else if (deleteCount != 0) { + XMLARRAY_SET_MEMBER(array, + index - deleteCount, + array->vector[index]); + } + } + array->length -= deleteCount; + } +} + +/* ECMA-357 9.2.1.3 index case. */ +static void +DeleteListElement(JSContext *cx, JSXML *xml, uint32 index) +{ + JSXML *kid, *parent; + uint32 kidIndex; + + JS_ASSERT(xml->xml_class == JSXML_CLASS_LIST); + + if (index < xml->xml_kids.length) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); + if (kid) { + parent = kid->parent; + if (parent) { + JS_ASSERT(parent != xml); + JS_ASSERT(JSXML_HAS_KIDS(parent)); + + if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) { + DeleteNamedProperty(cx, parent, kid->name, JS_TRUE); + } else { + kidIndex = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, + NULL); + JS_ASSERT(kidIndex != XML_NOT_FOUND); + DeleteByIndex(cx, parent, kidIndex); + } + } + XMLArrayDelete(cx, &xml->xml_kids, index, JS_TRUE); + } + } +} + +static JSBool +SyncInScopeNamespaces(JSContext *cx, JSXML *xml) +{ + JSXMLArray *nsarray; + uint32 i, n; + JSObject *ns; + + nsarray = &xml->xml_namespaces; + while ((xml = xml->parent) != NULL) { + for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { + ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject); + if (ns && !XMLARRAY_HAS_MEMBER(nsarray, ns, namespace_identity)) { + if (!XMLARRAY_APPEND(cx, nsarray, ns)) + return JS_FALSE; + } + } + } + return JS_TRUE; +} + +static JSBool +GetNamedProperty(JSContext *cx, JSXML *xml, JSObject* nameqn, JSXML *list) +{ + JSXMLArray *array; + JSXMLNameMatcher matcher; + JSBool attrs; + + if (xml->xml_class == JSXML_CLASS_LIST) { + JSXMLArrayCursor cursor(&xml->xml_kids); + while (JSXML *kid = (JSXML *) cursor.getNext()) { + if (kid->xml_class == JSXML_CLASS_ELEMENT && + !GetNamedProperty(cx, kid, nameqn, list)) { + return JS_FALSE; + } + } + } else if (xml->xml_class == JSXML_CLASS_ELEMENT) { + attrs = (OBJ_GET_CLASS(cx, nameqn) == &js_AttributeNameClass); + if (attrs) { + array = &xml->xml_attrs; + matcher = MatchAttrName; + } else { + array = &xml->xml_kids; + matcher = MatchElemName; + } + + JSXMLArrayCursor cursor(array); + while (JSXML *kid = (JSXML *) cursor.getNext()) { + if (matcher(nameqn, kid)) { + if (!attrs && + kid->xml_class == JSXML_CLASS_ELEMENT && + !SyncInScopeNamespaces(cx, kid)) { + return JS_FALSE; + } + if (!Append(cx, list, kid)) + return JS_FALSE; + } + } + } + + return JS_TRUE; +} + +/* ECMA-357 9.1.1.1 XML [[Get]] and 9.2.1.1 XMLList [[Get]]. */ +static JSBool +GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSXML *xml, *list, *kid; + uint32 index; + JSObject *kidobj, *listobj; + JSObject *nameqn; + jsid funid; + jsval roots[2]; + JSTempValueRooter tvr; + + xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL); + if (!xml) + return JS_TRUE; + + if (js_IdIsIndex(id, &index)) { + if (xml->xml_class != JSXML_CLASS_LIST) { + *vp = (index == 0) ? OBJECT_TO_JSVAL(obj) : JSVAL_VOID; + } else { + /* + * ECMA-357 9.2.1.1 starts here. + * + * Erratum: 9.2 is not completely clear that indexed properties + * correspond to kids, but that's what it seems to say, and it's + * what any sane user would want. + */ + if (index < xml->xml_kids.length) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); + if (!kid) { + *vp = JSVAL_VOID; + return JS_TRUE; + } + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + return JS_FALSE; + + *vp = OBJECT_TO_JSVAL(kidobj); + } else { + *vp = JSVAL_VOID; + } + } + return JS_TRUE; + } + + /* + * ECMA-357 9.2.1.1/9.1.1.1 qname case. + */ + nameqn = ToXMLName(cx, id, &funid); + if (!nameqn) + return JS_FALSE; + if (funid) + return GetXMLFunction(cx, obj, funid, vp); + + roots[0] = OBJECT_TO_JSVAL(nameqn); + JS_PUSH_TEMP_ROOT(cx, 1, roots, &tvr); + + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (listobj) { + roots[1] = OBJECT_TO_JSVAL(listobj); + tvr.count++; + + list = (JSXML *) listobj->getPrivate(); + if (!GetNamedProperty(cx, xml, nameqn, list)) { + listobj = NULL; + } else { + /* + * Erratum: ECMA-357 9.1.1.1 misses that [[Append]] sets the + * given list's [[TargetProperty]] to the property that is being + * appended. This means that any use of the internal [[Get]] + * property returns a list which, when used by e.g. [[Insert]] + * duplicates the last element matched by id. See bug 336921. + */ + list->xml_target = xml; + list->xml_targetprop = nameqn; + *vp = OBJECT_TO_JSVAL(listobj); + } + } + + JS_POP_TEMP_ROOT(cx, &tvr); + return listobj != NULL; +} + +static JSXML * +CopyOnWrite(JSContext *cx, JSXML *xml, JSObject *obj) +{ + JS_ASSERT(xml->object != obj); + + xml = DeepCopy(cx, xml, obj, 0); + if (!xml) + return NULL; + + JS_ASSERT(xml->object == obj); + return xml; +} + +#define CHECK_COPY_ON_WRITE(cx,xml,obj) \ + (xml->object == obj ? xml : CopyOnWrite(cx, xml, obj)) + +static JSString * +KidToString(JSContext *cx, JSXML *xml, uint32 index) +{ + JSXML *kid; + JSObject *kidobj; + + kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); + if (!kid) + return cx->runtime->emptyString; + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + return NULL; + return js_ValueToString(cx, OBJECT_TO_JSVAL(kidobj)); +} + +/* Forward declared -- its implementation uses other statics that call it. */ +static JSBool +ResolveValue(JSContext *cx, JSXML *list, JSXML **result); + +/* ECMA-357 9.1.1.2 XML [[Put]] and 9.2.1.2 XMLList [[Put]]. */ +static JSBool +PutProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSBool ok, primitiveAssign; + enum { OBJ_ROOT, ID_ROOT, VAL_ROOT }; + jsval roots[3]; + JSTempValueRooter tvr; + JSXML *xml, *vxml, *rxml, *kid, *attr, *parent, *copy, *kid2, *match; + JSObject *vobj, *nameobj, *attrobj, *parentobj, *kidobj, *copyobj; + JSObject *targetprop, *nameqn, *attrqn; + uint32 index, i, j, k, n, q, matchIndex; + jsval attrval, nsval; + jsid funid; + JSString *left, *right, *space, *uri; + JSObject *ns; + + xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL); + if (!xml) + return JS_TRUE; + + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + + /* Precompute vxml for 9.2.1.2 2(c)(vii)(2-3) and 2(d) and 9.1.1.2 1. */ + vxml = NULL; + if (!JSVAL_IS_PRIMITIVE(*vp)) { + vobj = JSVAL_TO_OBJECT(*vp); + if (OBJECT_IS_XML(cx, vobj)) + vxml = (JSXML *) vobj->getPrivate(); + } + + ok = js_EnterLocalRootScope(cx); + if (!ok) + return JS_FALSE; + MUST_FLOW_THROUGH("out"); + roots[OBJ_ROOT] = OBJECT_TO_JSVAL(obj); + roots[ID_ROOT] = id; + roots[VAL_ROOT] = *vp; + JS_PUSH_TEMP_ROOT(cx, 3, roots, &tvr); + + if (js_IdIsIndex(id, &index)) { + if (xml->xml_class != JSXML_CLASS_LIST) { + /* See NOTE in spec: this variation is reserved for future use. */ + ReportBadXMLName(cx, id); + goto bad; + } + + /* + * Step 1 of ECMA-357 9.2.1.2 index case sets i to the property index. + */ + i = index; + + /* 2(a-b). */ + if (xml->xml_target) { + ok = ResolveValue(cx, xml->xml_target, &rxml); + if (!ok) + goto out; + if (!rxml) + goto out; + JS_ASSERT(rxml->object); + } else { + rxml = NULL; + } + + /* 2(c). */ + if (index >= xml->xml_kids.length) { + /* 2(c)(i). */ + if (rxml) { + if (rxml->xml_class == JSXML_CLASS_LIST) { + if (rxml->xml_kids.length != 1) + goto out; + rxml = XMLARRAY_MEMBER(&rxml->xml_kids, 0, JSXML); + if (!rxml) + goto out; + ok = js_GetXMLObject(cx, rxml) != NULL; + if (!ok) + goto out; + } + + /* + * Erratum: ECMA-357 9.2.1.2 step 2(c)(ii) sets + * _y.[[Parent]] = r_ where _r_ is the result of + * [[ResolveValue]] called on _x.[[TargetObject]] in + * 2(a)(i). This can result in text parenting text: + * + * var MYXML = new XML(); + * MYXML.appendChild(new XML("Giants")); + * + * (testcase from Werner Sharp ). + * + * To match insertChildAfter, insertChildBefore, + * prependChild, and setChildren, we should silently + * do nothing in this case. + */ + if (!JSXML_HAS_KIDS(rxml)) + goto out; + } + + /* 2(c)(ii) is distributed below as several js_NewXML calls. */ + targetprop = xml->xml_targetprop; + if (!targetprop || IS_STAR(GetLocalName(targetprop))) { + /* 2(c)(iv)(1-2), out of order w.r.t. 2(c)(iii). */ + kid = js_NewXML(cx, JSXML_CLASS_TEXT); + if (!kid) + goto bad; + } else { + nameobj = targetprop; + if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) { + /* + * 2(c)(iii)(1-3). + * Note that rxml can't be null here, because target + * and targetprop are non-null. + */ + ok = GetProperty(cx, rxml->object, id, &attrval); + if (!ok) + goto out; + if (JSVAL_IS_PRIMITIVE(attrval)) /* no such attribute */ + goto out; + attrobj = JSVAL_TO_OBJECT(attrval); + attr = (JSXML *) attrobj->getPrivate(); + if (JSXML_LENGTH(attr) != 0) + goto out; + + kid = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE); + } else { + /* 2(c)(v). */ + kid = js_NewXML(cx, JSXML_CLASS_ELEMENT); + } + if (!kid) + goto bad; + + /* An important bit of 2(c)(ii). */ + kid->name = targetprop; + } + + /* Final important bit of 2(c)(ii). */ + kid->parent = rxml; + + /* 2(c)(vi-vii). */ + i = xml->xml_kids.length; + if (kid->xml_class != JSXML_CLASS_ATTRIBUTE) { + /* + * 2(c)(vii)(1) tests whether _y.[[Parent]]_ is not null. + * y.[[Parent]] is here called kid->parent, which we know + * from 2(c)(ii) is _r_, here called rxml. So let's just + * test that! Erratum, the spec should be simpler here. + */ + if (rxml) { + JS_ASSERT(JSXML_HAS_KIDS(rxml)); + n = rxml->xml_kids.length; + j = n - 1; + if (n != 0 && i != 0) { + for (n = j, j = 0; j < n; j++) { + if (rxml->xml_kids.vector[j] == + xml->xml_kids.vector[i-1]) { + break; + } + } + } + + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + goto bad; + ok = Insert(cx, rxml, j + 1, OBJECT_TO_JSVAL(kidobj)); + if (!ok) + goto out; + } + + /* + * 2(c)(vii)(2-3). + * Erratum: [[PropertyName]] in 2(c)(vii)(3) must be a + * typo for [[TargetProperty]]. + */ + if (vxml) { + kid->name = (vxml->xml_class == JSXML_CLASS_LIST) + ? vxml->xml_targetprop + : vxml->name; + } + } + + /* 2(c)(viii). */ + ok = Append(cx, xml, kid); + if (!ok) + goto out; + } + + /* 2(d). */ + if (!vxml || + vxml->xml_class == JSXML_CLASS_TEXT || + vxml->xml_class == JSXML_CLASS_ATTRIBUTE) { + ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); + if (!ok) + goto out; + roots[VAL_ROOT] = *vp; + } + + /* 2(e). */ + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (!kid) + goto out; + parent = kid->parent; + if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) { + nameobj = kid->name; + if (OBJ_GET_CLASS(cx, nameobj) != &js_AttributeNameClass) { + nameobj = NewXMLQName(cx, GetURI(nameobj), GetPrefix(nameobj), + GetLocalName(nameobj), + &js_AttributeNameClass); + if (!nameobj) + goto bad; + } + id = OBJECT_TO_JSVAL(nameobj); + + if (parent) { + /* 2(e)(i). */ + parentobj = js_GetXMLObject(cx, parent); + if (!parentobj) + goto bad; + ok = PutProperty(cx, parentobj, id, vp); + if (!ok) + goto out; + + /* 2(e)(ii). */ + ok = GetProperty(cx, parentobj, id, vp); + if (!ok) + goto out; + attr = (JSXML *) JSVAL_TO_OBJECT(*vp)->getPrivate(); + + /* 2(e)(iii) - the length check comes from the bug 375406. */ + if (attr->xml_kids.length != 0) + xml->xml_kids.vector[i] = attr->xml_kids.vector[0]; + } + } + + /* 2(f). */ + else if (vxml && vxml->xml_class == JSXML_CLASS_LIST) { + /* + * 2(f)(i) + * + * Erratum: the spec says to create a shallow copy _c_ of _V_, but + * if we do that we never change the parent of each child in the + * list. Since [[Put]] when called on an XML object deeply copies + * the provided list _V_, we also do so here. Perhaps the shallow + * copy was a misguided optimization? + */ + copy = DeepCopyInLRS(cx, vxml, 0); + if (!copy) + goto bad; + copyobj = js_GetXMLObject(cx, copy); + if (!copyobj) + goto bad; + + JS_ASSERT(parent != xml); + if (parent) { + q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL); + JS_ASSERT(q != XML_NOT_FOUND); + ok = Replace(cx, parent, q, OBJECT_TO_JSVAL(copyobj)); + if (!ok) + goto out; + +#ifdef DEBUG + /* Erratum: this loop in the spec is useless. */ + for (j = 0, n = copy->xml_kids.length; j < n; j++) { + kid2 = XMLARRAY_MEMBER(&parent->xml_kids, q + j, JSXML); + JS_ASSERT(XMLARRAY_MEMBER(©->xml_kids, j, JSXML) + == kid2); + } +#endif + } + + /* + * 2(f)(iv-vi). + * Erratum: notice the unhandled zero-length V basis case and + * the off-by-one errors for the n != 0 cases in the spec. + */ + n = copy->xml_kids.length; + if (n == 0) { + XMLArrayDelete(cx, &xml->xml_kids, i, JS_TRUE); + } else { + ok = XMLArrayInsert(cx, &xml->xml_kids, i + 1, n - 1); + if (!ok) + goto out; + + for (j = 0; j < n; j++) + xml->xml_kids.vector[i + j] = copy->xml_kids.vector[j]; + } + } + + /* 2(g). */ + else if (vxml || JSXML_HAS_VALUE(kid)) { + if (parent) { + q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL); + JS_ASSERT(q != XML_NOT_FOUND); + ok = Replace(cx, parent, q, *vp); + if (!ok) + goto out; + + vxml = XMLARRAY_MEMBER(&parent->xml_kids, q, JSXML); + if (!vxml) + goto out; + roots[VAL_ROOT] = *vp = OBJECT_TO_JSVAL(vxml->object); + } + + /* + * 2(g)(iii). + * Erratum: _V_ may not be of type XML, but all index-named + * properties _x[i]_ in an XMLList _x_ must be of type XML, + * according to 9.2.1.1 Overview and other places in the spec. + * + * Thanks to 2(d), we know _V_ (*vp here) is either a string + * or an XML/XMLList object. If *vp is a string, call ToXML + * on it to satisfy the constraint. + */ + if (!vxml) { + JS_ASSERT(JSVAL_IS_STRING(*vp)); + vobj = ToXML(cx, *vp); + if (!vobj) + goto bad; + roots[VAL_ROOT] = *vp = OBJECT_TO_JSVAL(vobj); + vxml = (JSXML *) vobj->getPrivate(); + } + XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml); + } + + /* 2(h). */ + else { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + goto bad; + id = ATOM_KEY(cx->runtime->atomState.starAtom); + ok = PutProperty(cx, kidobj, id, vp); + if (!ok) + goto out; + } + } else { + /* + * ECMA-357 9.2.1.2/9.1.1.2 qname case. + */ + nameqn = ToXMLName(cx, id, &funid); + if (!nameqn) + goto bad; + if (funid) { + ok = js_SetProperty(cx, obj, funid, vp); + goto out; + } + nameobj = nameqn; + roots[ID_ROOT] = OBJECT_TO_JSVAL(nameobj); + + if (xml->xml_class == JSXML_CLASS_LIST) { + /* + * Step 3 of 9.2.1.2. + * Erratum: if x.[[Length]] > 1 or [[ResolveValue]] returns null + * or an r with r.[[Length]] != 1, throw TypeError. + */ + n = JSXML_LENGTH(xml); + if (n > 1) + goto type_error; + if (n == 0) { + ok = ResolveValue(cx, xml, &rxml); + if (!ok) + goto out; + if (!rxml || JSXML_LENGTH(rxml) != 1) + goto type_error; + ok = Append(cx, xml, rxml); + if (!ok) + goto out; + } + JS_ASSERT(JSXML_LENGTH(xml) == 1); + xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + if (!xml) + goto out; + JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST); + obj = js_GetXMLObject(cx, xml); + if (!obj) + goto bad; + roots[OBJ_ROOT] = OBJECT_TO_JSVAL(obj); + + /* FALL THROUGH to non-list case */ + } + + /* + * ECMA-357 9.1.1.2. + * Erratum: move steps 3 and 4 to before 1 and 2, to avoid wasted + * effort in ToString or [[DeepCopy]]. + */ + + if (JSXML_HAS_VALUE(xml)) + goto out; + + if (!vxml || + vxml->xml_class == JSXML_CLASS_TEXT || + vxml->xml_class == JSXML_CLASS_ATTRIBUTE) { + ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); + if (!ok) + goto out; + } else { + rxml = DeepCopyInLRS(cx, vxml, 0); + if (!rxml || !js_GetXMLObject(cx, rxml)) + goto bad; + vxml = rxml; + *vp = OBJECT_TO_JSVAL(vxml->object); + } + roots[VAL_ROOT] = *vp; + + /* + * 6. + * Erratum: why is this done here, so early? use is way later.... + */ + ok = js_GetDefaultXMLNamespace(cx, &nsval); + if (!ok) + goto out; + + if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) { + /* 7(a). */ + if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj))) + goto out; + + /* 7(b-c). */ + if (vxml && vxml->xml_class == JSXML_CLASS_LIST) { + n = vxml->xml_kids.length; + if (n == 0) { + *vp = STRING_TO_JSVAL(cx->runtime->emptyString); + } else { + left = KidToString(cx, vxml, 0); + if (!left) + goto bad; + + space = ATOM_TO_STRING(cx->runtime->atomState.spaceAtom); + for (i = 1; i < n; i++) { + left = js_ConcatStrings(cx, left, space); + if (!left) + goto bad; + right = KidToString(cx, vxml, i); + if (!right) + goto bad; + left = js_ConcatStrings(cx, left, right); + if (!left) + goto bad; + } + + roots[VAL_ROOT] = *vp = STRING_TO_JSVAL(left); + } + } else { + ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); + if (!ok) + goto out; + roots[VAL_ROOT] = *vp; + } + + /* 7(d-e). */ + match = NULL; + for (i = 0, n = xml->xml_attrs.length; i < n; i++) { + attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); + if (!attr) + continue; + attrqn = attr->name; + if (js_EqualStrings(GetLocalName(attrqn), + GetLocalName(nameqn))) { + uri = GetURI(nameqn); + if (!uri || js_EqualStrings(GetURI(attrqn), uri)) { + if (!match) { + match = attr; + } else { + DeleteNamedProperty(cx, xml, attrqn, JS_TRUE); + --i; + } + } + } + } + + /* 7(f). */ + attr = match; + if (!attr) { + /* 7(f)(i-ii). */ + uri = GetURI(nameqn); + if (!uri) { + left = right = cx->runtime->emptyString; + } else { + left = uri; + right = GetPrefix(nameqn); + } + nameqn = NewXMLQName(cx, left, right, GetLocalName(nameqn)); + if (!nameqn) + goto bad; + + /* 7(f)(iii). */ + attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE); + if (!attr) + goto bad; + attr->parent = xml; + attr->name = nameqn; + + /* 7(f)(iv). */ + ok = XMLARRAY_ADD_MEMBER(cx, &xml->xml_attrs, n, attr); + if (!ok) + goto out; + + /* 7(f)(v-vi). */ + ns = GetNamespace(cx, nameqn, NULL); + if (!ns) + goto bad; + ok = AddInScopeNamespace(cx, xml, ns); + if (!ok) + goto out; + } + + /* 7(g). */ + attr->xml_value = JSVAL_TO_STRING(*vp); + goto out; + } + + /* 8-9. */ + if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj)) && + !IS_STAR(GetLocalName(nameqn))) { + goto out; + } + + /* 10-11. */ + id = JSVAL_VOID; + primitiveAssign = !vxml && !IS_STAR(GetLocalName(nameqn)); + + /* 12. */ + k = n = xml->xml_kids.length; + matchIndex = XML_NOT_FOUND; + kid2 = NULL; + while (k != 0) { + --k; + kid = XMLARRAY_MEMBER(&xml->xml_kids, k, JSXML); + if (kid && MatchElemName(nameqn, kid)) { + if (matchIndex != XML_NOT_FOUND) + DeleteByIndex(cx, xml, matchIndex); + matchIndex = k; + kid2 = kid; + } + } + + /* + * Erratum: ECMA-357 specified child insertion inconsistently: + * insertChildBefore and insertChildAfter insert an arbitrary XML + * instance, and therefore can create cycles, but appendChild as + * specified by the "Overview" of 13.4.4.3 calls [[DeepCopy]] on + * its argument. But the "Semantics" in 13.4.4.3 do not include + * any [[DeepCopy]] call. + * + * Fixing this (https://bugzilla.mozilla.org/show_bug.cgi?id=312692) + * required adding cycle detection, and allowing duplicate kids to + * be created (see comment 6 in the bug). Allowing duplicate kid + * references means the loop above will delete all but the lowest + * indexed reference, and each [[DeleteByIndex]] nulls the kid's + * parent. Thus the need to restore parent here. This is covered + * by https://bugzilla.mozilla.org/show_bug.cgi?id=327564. + */ + if (kid2) { + JS_ASSERT(kid2->parent == xml || !kid2->parent); + if (!kid2->parent) + kid2->parent = xml; + } + + /* 13. */ + if (matchIndex == XML_NOT_FOUND) { + /* 13(a). */ + matchIndex = n; + + /* 13(b). */ + if (primitiveAssign) { + uri = GetURI(nameqn); + if (!uri) { + ns = JSVAL_TO_OBJECT(nsval); + left = GetURI(ns); + right = GetPrefix(ns); + } else { + left = uri; + right = GetPrefix(nameqn); + } + nameqn = NewXMLQName(cx, left, right, GetLocalName(nameqn)); + if (!nameqn) + goto bad; + + /* 13(b)(iii). */ + vobj = js_NewXMLObject(cx, JSXML_CLASS_ELEMENT); + if (!vobj) + goto bad; + vxml = (JSXML *) vobj->getPrivate(); + vxml->parent = xml; + vxml->name = nameqn; + + /* 13(b)(iv-vi). */ + ns = GetNamespace(cx, nameqn, NULL); + if (!ns) + goto bad; + ok = Replace(cx, xml, matchIndex, OBJECT_TO_JSVAL(vobj)); + if (!ok) + goto out; + ok = AddInScopeNamespace(cx, vxml, ns); + if (!ok) + goto out; + } + } + + /* 14. */ + if (primitiveAssign) { + JSXMLArrayCursor cursor(&xml->xml_kids); + cursor.index = matchIndex; + kid = (JSXML *) cursor.getCurrent(); + if (JSXML_HAS_KIDS(kid)) { + XMLArrayFinish(cx, &kid->xml_kids); + ok = XMLArrayInit(cx, &kid->xml_kids, 1); + } + + /* 14(b-c). */ + /* XXXbe Erratum? redundant w.r.t. 7(b-c) else clause above */ + if (ok) { + ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); + if (ok && !JSVAL_TO_STRING(*vp)->empty()) { + roots[VAL_ROOT] = *vp; + if ((JSXML *) cursor.getCurrent() == kid) + ok = Replace(cx, kid, 0, *vp); + } + } + } else { + /* 15(a). */ + ok = Replace(cx, xml, matchIndex, *vp); + } + } + +out: + JS_POP_TEMP_ROOT(cx, &tvr); + js_LeaveLocalRootScope(cx); + return ok; + +type_error: + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XMLLIST_PUT, + js_ValueToPrintableString(cx, id)); +bad: + ok = JS_FALSE; + goto out; +} + +/* ECMA-357 9.1.1.10 XML [[ResolveValue]], 9.2.1.10 XMLList [[ResolveValue]]. */ +static JSBool +ResolveValue(JSContext *cx, JSXML *list, JSXML **result) +{ + JSXML *target, *base; + JSObject *targetprop; + jsval id, tv; + + /* Our caller must be protecting newborn objects. */ + JS_ASSERT(JS_THREAD_DATA(cx)->localRootStack); + + if (list->xml_class != JSXML_CLASS_LIST || list->xml_kids.length != 0) { + if (!js_GetXMLObject(cx, list)) + return JS_FALSE; + *result = list; + return JS_TRUE; + } + + target = list->xml_target; + targetprop = list->xml_targetprop; + if (!target || !targetprop || IS_STAR(GetLocalName(targetprop))) { + *result = NULL; + return JS_TRUE; + } + + if (OBJ_GET_CLASS(cx, targetprop) == &js_AttributeNameClass) { + *result = NULL; + return JS_TRUE; + } + + if (!ResolveValue(cx, target, &base)) + return JS_FALSE; + if (!base) { + *result = NULL; + return JS_TRUE; + } + if (!js_GetXMLObject(cx, base)) + return JS_FALSE; + + id = OBJECT_TO_JSVAL(targetprop); + if (!GetProperty(cx, base->object, id, &tv)) + return JS_FALSE; + target = (JSXML *) JSVAL_TO_OBJECT(tv)->getPrivate(); + + if (JSXML_LENGTH(target) == 0) { + if (base->xml_class == JSXML_CLASS_LIST && JSXML_LENGTH(base) > 1) { + *result = NULL; + return JS_TRUE; + } + tv = STRING_TO_JSVAL(cx->runtime->emptyString); + if (!PutProperty(cx, base->object, id, &tv)) + return JS_FALSE; + if (!GetProperty(cx, base->object, id, &tv)) + return JS_FALSE; + target = (JSXML *) JSVAL_TO_OBJECT(tv)->getPrivate(); + } + + *result = target; + return JS_TRUE; +} + +static JSBool +HasNamedProperty(JSXML *xml, JSObject *nameqn) +{ + JSBool found; + JSXMLArray *array; + JSXMLNameMatcher matcher; + uint32 i, n; + + if (xml->xml_class == JSXML_CLASS_LIST) { + found = JS_FALSE; + JSXMLArrayCursor cursor(&xml->xml_kids); + while (JSXML *kid = (JSXML *) cursor.getNext()) { + found = HasNamedProperty(kid, nameqn); + if (found) + break; + } + return found; + } + + if (xml->xml_class == JSXML_CLASS_ELEMENT) { + if (OBJ_GET_CLASS(cx, nameqn) == &js_AttributeNameClass) { + array = &xml->xml_attrs; + matcher = MatchAttrName; + } else { + array = &xml->xml_kids; + matcher = MatchElemName; + } + for (i = 0, n = array->length; i < n; i++) { + JSXML *kid = XMLARRAY_MEMBER(array, i, JSXML); + if (kid && matcher(nameqn, kid)) + return JS_TRUE; + } + } + + return JS_FALSE; +} + +static JSBool +HasIndexedProperty(JSXML *xml, uint32 i) +{ + if (xml->xml_class == JSXML_CLASS_LIST) + return i < JSXML_LENGTH(xml); + + if (xml->xml_class == JSXML_CLASS_ELEMENT) + return i == 0; + + return JS_FALSE; +} + +static JSBool +HasSimpleContent(JSXML *xml); + +static JSBool +HasFunctionProperty(JSContext *cx, JSObject *obj, jsid funid, JSBool *found) +{ + JSObject *pobj; + JSProperty *prop; + JSXML *xml; + JSTempValueRooter tvr; + JSBool ok; + + JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_XMLClass); + + if (!js_LookupProperty(cx, obj, funid, &pobj, &prop)) + return JS_FALSE; + if (prop) { + pobj->dropProperty(cx, prop); + } else { + xml = (JSXML *) obj->getPrivate(); + if (HasSimpleContent(xml)) { + /* + * Search in String.prototype to set found whenever + * GetXMLFunction returns existing function. + */ + JS_PUSH_TEMP_ROOT_OBJECT(cx, NULL, &tvr); + ok = js_GetClassPrototype(cx, NULL, INT_TO_JSID(JSProto_String), + &tvr.u.object); + JS_ASSERT(tvr.u.object); + if (ok) { + ok = js_LookupProperty(cx, tvr.u.object, funid, &pobj, &prop); + if (ok && prop) + pobj->dropProperty(cx, prop); + } + JS_POP_TEMP_ROOT(cx, &tvr); + if (!ok) + return JS_FALSE; + } + } + *found = (prop != NULL); + return JS_TRUE; +} + +/* ECMA-357 9.1.1.6 XML [[HasProperty]] and 9.2.1.5 XMLList [[HasProperty]]. */ +static JSBool +HasProperty(JSContext *cx, JSObject *obj, jsval id, JSBool *found) +{ + JSXML *xml; + uint32 i; + JSObject *qn; + jsid funid; + + xml = (JSXML *) obj->getPrivate(); + if (js_IdIsIndex(id, &i)) { + *found = HasIndexedProperty(xml, i); + } else { + qn = ToXMLName(cx, id, &funid); + if (!qn) + return JS_FALSE; + if (funid) { + if (!HasFunctionProperty(cx, obj, funid, found)) + return JS_FALSE; + } else { + *found = HasNamedProperty(xml, qn); + } + } + return JS_TRUE; +} + +static void +xml_finalize(JSContext *cx, JSObject *obj) +{ + JSXML *xml = (JSXML *) obj->getPrivate(); + if (!xml) + return; + if (xml->object == obj) + xml->object = NULL; +} + +static void +xml_trace_vector(JSTracer *trc, JSXML **vec, uint32 len) +{ + uint32 i; + JSXML *xml; + + for (i = 0; i < len; i++) { + xml = vec[i]; + if (xml) { + JS_SET_TRACING_INDEX(trc, "xml_vector", i); + JS_CallTracer(trc, xml, JSTRACE_XML); + } + } +} + +/* + * js_XMLObjectOps.newObjectMap is null, so XML objects appear to be native. + * Thus xml_lookupProperty must return a valid JSScopeProperty pointer + * parameter via *propp to signify "property found". Since the only call to + * xml_lookupProperty is via JSObject::lookupProperty, and then only from + * js_FindProperty (in jsobj.c, called from jsinterp.c) or from JSOP_IN case + * in the interpreter, the only time we add a JSScopeProperty here is when an + * unqualified name is being accessed or when "name in xml" is called. + * + * This scope property keeps the JSOP_NAME code in js_Interpret happy by + * giving it an sprop with (getter, setter) == (GetProperty, PutProperty). + * + * NB: xml_deleteProperty must take care to remove any property added here. + * + * FIXME This clashes with the function namespace implementation which also + * uses native properties. Effectively after xml_lookupProperty any property + * stored previously using assignments to xml.function::name will be removed. + * We partially workaround the problem in GetXMLFunction. There we take + * advantage of the fact that typically function:: is used to access the + * functions from XML.prototype. So when js_GetProperty returns a non-function + * property, we assume that it represents the result of GetProperty setter + * hiding the function and use an extra prototype chain lookup to recover it. + * For a proper solution see bug 355257. +*/ +static JSBool +xml_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, + JSProperty **propp) +{ + jsval v; + JSBool found; + JSXML *xml; + uint32 i; + JSObject *qn; + jsid funid; + JSScopeProperty *sprop; + + v = ID_TO_VALUE(id); + xml = (JSXML *) obj->getPrivate(); + if (js_IdIsIndex(v, &i)) { + found = HasIndexedProperty(xml, i); + } else { + qn = ToXMLName(cx, v, &funid); + if (!qn) + return JS_FALSE; + if (funid) + return js_LookupProperty(cx, obj, funid, objp, propp); + found = HasNamedProperty(xml, qn); + } + if (!found) { + *objp = NULL; + *propp = NULL; + } else { + sprop = js_AddNativeProperty(cx, obj, id, GetProperty, PutProperty, + SPROP_INVALID_SLOT, JSPROP_ENUMERATE, + 0, 0); + if (!sprop) + return JS_FALSE; + + JS_LOCK_OBJ(cx, obj); + *objp = obj; + *propp = (JSProperty *) sprop; + } + return JS_TRUE; +} + +static JSBool +xml_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs) +{ + if (VALUE_IS_FUNCTION(cx, value) || getter || setter || + (attrs & JSPROP_ENUMERATE) == 0 || + (attrs & (JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED))) { + return js_DefineProperty(cx, obj, id, value, getter, setter, attrs); + } + + return PutProperty(cx, obj, ID_TO_VALUE(id), &value); +} + +static JSBool +xml_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + if (id == JS_DEFAULT_XML_NAMESPACE_ID) { + *vp = JSVAL_VOID; + return JS_TRUE; + } + + return GetProperty(cx, obj, ID_TO_VALUE(id), vp); +} + +static JSBool +xml_setProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + return PutProperty(cx, obj, ID_TO_VALUE(id), vp); +} + +static JSBool +FoundProperty(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, + JSBool *foundp) +{ + if (!prop) + return HasProperty(cx, obj, ID_TO_VALUE(id), foundp); + + *foundp = JS_TRUE; + return JS_TRUE; +} + +static JSBool +xml_getAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, + uintN *attrsp) +{ + JSBool found; + + if (!FoundProperty(cx, obj, id, prop, &found)) + return JS_FALSE; + *attrsp = found ? JSPROP_ENUMERATE : 0; + return JS_TRUE; +} + +static JSBool +xml_setAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, + uintN *attrsp) +{ + JSBool found; + + if (!FoundProperty(cx, obj, id, prop, &found)) + return JS_FALSE; + if (found) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_CANT_SET_XML_ATTRS); + } + return !found; +} + +static JSBool +xml_deleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval) +{ + JSXML *xml; + jsval idval; + uint32 index; + JSObject *nameqn; + jsid funid; + + idval = ID_TO_VALUE(id); + xml = (JSXML *) obj->getPrivate(); + if (js_IdIsIndex(idval, &index)) { + if (xml->xml_class != JSXML_CLASS_LIST) { + /* See NOTE in spec: this variation is reserved for future use. */ + ReportBadXMLName(cx, id); + return JS_FALSE; + } + + /* ECMA-357 9.2.1.3. */ + DeleteListElement(cx, xml, index); + } else { + nameqn = ToXMLName(cx, idval, &funid); + if (!nameqn) + return JS_FALSE; + if (funid) + return js_DeleteProperty(cx, obj, funid, rval); + + DeleteNamedProperty(cx, xml, nameqn, + OBJ_GET_CLASS(cx, nameqn) == + &js_AttributeNameClass); + } + + /* + * If this object has its own (mutable) scope, then we may have added a + * property to the scope in xml_lookupProperty for it to return to mean + * "found" and to provide a handle for access operations to call the + * property's getter or setter. But now it's time to remove any such + * property, to purge the property cache and remove the scope entry. + */ + if (OBJ_SCOPE(obj)->object == obj && !js_DeleteProperty(cx, obj, id, rval)) + return JS_FALSE; + + *rval = JSVAL_TRUE; + return JS_TRUE; +} + +static JSBool +xml_defaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp) +{ + JSXML *xml; + + if (hint == JSTYPE_OBJECT) { + /* Called from for..in code in js_Interpret: return an XMLList. */ + xml = (JSXML *) obj->getPrivate(); + if (xml->xml_class != JSXML_CLASS_LIST) { + obj = ToXMLList(cx, OBJECT_TO_JSVAL(obj)); + if (!obj) + return JS_FALSE; + } + *vp = OBJECT_TO_JSVAL(obj); + return JS_TRUE; + } + + return JS_CallFunctionName(cx, obj, js_toString_str, 0, NULL, vp); +} + +static JSBool +xml_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, + jsval *statep, jsid *idp) +{ + JSXML *xml; + uint32 length, index; + JSXMLArrayCursor *cursor; + + xml = (JSXML *)obj->getPrivate(); + length = JSXML_LENGTH(xml); + + switch (enum_op) { + case JSENUMERATE_INIT: + if (length == 0) { + cursor = NULL; + } else { + cursor = cx->create(&xml->xml_kids); + if (!cursor) + return JS_FALSE; + } + *statep = PRIVATE_TO_JSVAL(cursor); + if (idp) + *idp = INT_TO_JSID(length); + break; + + case JSENUMERATE_NEXT: + cursor = (JSXMLArrayCursor *) JSVAL_TO_PRIVATE(*statep); + if (cursor && cursor->array && (index = cursor->index) < length) { + *idp = INT_TO_JSID(index); + cursor->index = index + 1; + break; + } + /* FALL THROUGH */ + + case JSENUMERATE_DESTROY: + cursor = (JSXMLArrayCursor *) JSVAL_TO_PRIVATE(*statep); + if (cursor) + cx->destroy(cursor); + *statep = JSVAL_NULL; + break; + } + return JS_TRUE; +} + +static JSBool +xml_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) +{ + return JS_TRUE; +} + +static void +xml_trace(JSTracer *trc, JSObject *obj) +{ + JSXML *xml = (JSXML *) obj->getPrivate(); + if (xml) + JS_CALL_TRACER(trc, xml, JSTRACE_XML, "private"); +} + +static void +xml_clear(JSContext *cx, JSObject *obj) +{ +} + +static JSBool +HasSimpleContent(JSXML *xml) +{ + JSXML *kid; + JSBool simple; + uint32 i, n; + +again: + switch (xml->xml_class) { + case JSXML_CLASS_COMMENT: + case JSXML_CLASS_PROCESSING_INSTRUCTION: + return JS_FALSE; + case JSXML_CLASS_LIST: + if (xml->xml_kids.length == 0) + return JS_TRUE; + if (xml->xml_kids.length == 1) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + if (kid) { + xml = kid; + goto again; + } + } + /* FALL THROUGH */ + default: + simple = JS_TRUE; + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { + simple = JS_FALSE; + break; + } + } + return simple; + } +} + +/* + * 11.2.2.1 Step 3(d) onward. + */ +JSBool +js_GetXMLMethod(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + JS_ASSERT(JS_InstanceOf(cx, obj, &js_XMLClass, NULL)); + + if (JSID_IS_OBJECT(id)) { + jsid funid; + + if (!js_IsFunctionQName(cx, JSID_TO_OBJECT(id), &funid)) + return JS_FALSE; + if (funid != 0) + id = funid; + } + + /* + * As our callers have a bad habit of passing a pointer to an unrooted + * local value as vp, we use a proper root here. + */ + JSAutoTempValueRooter tvr(cx); + JSBool ok = GetXMLFunction(cx, obj, id, tvr.addr()); + *vp = tvr.value(); + return ok; +} + +JSBool +js_EnumerateXMLValues(JSContext *cx, JSObject *obj, JSIterateOp enum_op, + jsval *statep, jsid *idp, jsval *vp) +{ + JSXML *xml, *kid; + uint32 length, index; + JSXMLArrayCursor *cursor; + JSObject *kidobj; + + JS_ASSERT(JS_InstanceOf(cx, obj, &js_XMLClass, NULL)); + xml = (JSXML *) obj->getPrivate(); + length = JSXML_LENGTH(xml); + JS_ASSERT(INT_FITS_IN_JSVAL(length)); + + switch (enum_op) { + case JSENUMERATE_INIT: + if (length == 0) { + cursor = NULL; + } else { + cursor = cx->create(&xml->xml_kids); + if (!cursor) + return JS_FALSE; + } + *statep = PRIVATE_TO_JSVAL(cursor); + if (idp) + *idp = INT_TO_JSID(length); + if (vp) + *vp = JSVAL_VOID; + break; + + case JSENUMERATE_NEXT: + cursor = (JSXMLArrayCursor *) JSVAL_TO_PRIVATE(*statep); + if (cursor && cursor->array && (index = cursor->index) < length) { + while (!(kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML))) { + if (++index == length) + goto destroy; + } + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + return JS_FALSE; + JS_ASSERT(INT_FITS_IN_JSVAL(index)); + *idp = INT_TO_JSID(index); + *vp = OBJECT_TO_JSVAL(kidobj); + cursor->index = index + 1; + break; + } + /* FALL THROUGH */ + + case JSENUMERATE_DESTROY: + cursor = (JSXMLArrayCursor *) JSVAL_TO_PRIVATE(*statep); + if (cursor) { + destroy: + cx->destroy(cursor); + } + *statep = JSVAL_NULL; + break; + } + return JS_TRUE; +} + +JSBool +js_TestXMLEquality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) +{ + JSXML *xml, *vxml; + JSObject *vobj; + JSBool ok; + JSString *str, *vstr; + jsdouble d, d2; + + JS_ASSERT(JS_InstanceOf(cx, obj, &js_XMLClass, NULL)); + xml = (JSXML *) obj->getPrivate(); + vxml = NULL; + if (!JSVAL_IS_PRIMITIVE(v)) { + vobj = JSVAL_TO_OBJECT(v); + if (OBJECT_IS_XML(cx, vobj)) + vxml = (JSXML *) vobj->getPrivate(); + } + + if (xml->xml_class == JSXML_CLASS_LIST) { + ok = Equals(cx, xml, v, bp); + } else if (vxml) { + if (vxml->xml_class == JSXML_CLASS_LIST) { + ok = Equals(cx, vxml, OBJECT_TO_JSVAL(obj), bp); + } else { + if (((xml->xml_class == JSXML_CLASS_TEXT || + xml->xml_class == JSXML_CLASS_ATTRIBUTE) && + HasSimpleContent(vxml)) || + ((vxml->xml_class == JSXML_CLASS_TEXT || + vxml->xml_class == JSXML_CLASS_ATTRIBUTE) && + HasSimpleContent(xml))) { + ok = js_EnterLocalRootScope(cx); + if (ok) { + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + vstr = js_ValueToString(cx, v); + ok = str && vstr; + if (ok) + *bp = js_EqualStrings(str, vstr); + js_LeaveLocalRootScope(cx); + } + } else { + ok = XMLEquals(cx, xml, vxml, bp); + } + } + } else { + ok = js_EnterLocalRootScope(cx); + if (ok) { + if (HasSimpleContent(xml)) { + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + vstr = js_ValueToString(cx, v); + ok = str && vstr; + if (ok) + *bp = js_EqualStrings(str, vstr); + } else if (JSVAL_IS_STRING(v) || JSVAL_IS_NUMBER(v)) { + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) { + ok = JS_FALSE; + } else if (JSVAL_IS_STRING(v)) { + *bp = js_EqualStrings(str, JSVAL_TO_STRING(v)); + } else { + ok = JS_ValueToNumber(cx, STRING_TO_JSVAL(str), &d); + if (ok) { + d2 = JSVAL_IS_INT(v) ? JSVAL_TO_INT(v) + : *JSVAL_TO_DOUBLE(v); + *bp = JSDOUBLE_COMPARE(d, ==, d2, JS_FALSE); + } + } + } else { + *bp = JS_FALSE; + } + js_LeaveLocalRootScope(cx); + } + } + return ok; +} + +JSBool +js_ConcatenateXML(JSContext *cx, JSObject *obj, jsval v, jsval *vp) +{ + JSBool ok; + JSObject *listobj, *robj; + JSXML *list, *lxml, *rxml; + + JS_ASSERT(JS_InstanceOf(cx, obj, &js_XMLClass, NULL)); + ok = js_EnterLocalRootScope(cx); + if (!ok) + return JS_FALSE; + + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) { + ok = JS_FALSE; + goto out; + } + + list = (JSXML *) listobj->getPrivate(); + lxml = (JSXML *) obj->getPrivate(); + ok = Append(cx, list, lxml); + if (!ok) + goto out; + + if (VALUE_IS_XML(cx, v)) { + rxml = (JSXML *) JSVAL_TO_OBJECT(v)->getPrivate(); + } else { + robj = ToXML(cx, v); + if (!robj) { + ok = JS_FALSE; + goto out; + } + rxml = (JSXML *) robj->getPrivate(); + } + ok = Append(cx, list, rxml); + if (!ok) + goto out; + + *vp = OBJECT_TO_JSVAL(listobj); +out: + js_LeaveLocalRootScopeWithResult(cx, *vp); + return ok; +} + +/* Use NULL for objectMap so XML objects satisfy OBJ_IS_NATIVE tests. */ +JS_FRIEND_DATA(JSObjectOps) js_XMLObjectOps = { + NULL, + xml_lookupProperty, xml_defineProperty, + xml_getProperty, xml_setProperty, + xml_getAttributes, xml_setAttributes, + xml_deleteProperty, xml_defaultValue, + xml_enumerate, js_CheckAccess, + NULL, NULL, + NULL, NULL, + xml_hasInstance, js_TraceObject, + xml_clear +}; + +static JSObjectOps * +xml_getObjectOps(JSContext *cx, JSClass *clasp) +{ + return &js_XMLObjectOps; +} + +JS_FRIEND_DATA(JSClass) js_XMLClass = { + js_XML_str, + JSCLASS_HAS_PRIVATE | JSCLASS_MARK_IS_TRACE | + JSCLASS_HAS_CACHED_PROTO(JSProto_XML), + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, xml_finalize, + xml_getObjectOps, NULL, NULL, NULL, + NULL, NULL, JS_CLASS_TRACE(xml_trace), NULL +}; + +static JSXML * +StartNonListXMLMethod(JSContext *cx, jsval *vp, JSObject **objp) +{ + JSXML *xml; + JSFunction *fun; + char numBuf[12]; + + JS_ASSERT(VALUE_IS_FUNCTION(cx, *vp)); + + *objp = JS_THIS_OBJECT(cx, vp); + xml = (JSXML *) JS_GetInstancePrivate(cx, *objp, &js_XMLClass, vp + 2); + if (!xml || xml->xml_class != JSXML_CLASS_LIST) + return xml; + + if (xml->xml_kids.length == 1) { + xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + if (xml) { + *objp = js_GetXMLObject(cx, xml); + if (!*objp) + return NULL; + vp[1] = OBJECT_TO_JSVAL(*objp); + return xml; + } + } + + fun = GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(*vp)); + JS_snprintf(numBuf, sizeof numBuf, "%u", xml->xml_kids.length); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_NON_LIST_XML_METHOD, + JS_GetFunctionName(fun), numBuf); + return NULL; +} + +/* Beware: these two are not bracketed by JS_BEGIN/END_MACRO. */ +#define XML_METHOD_PROLOG \ + JSObject *obj = JS_THIS_OBJECT(cx, vp); \ + JSXML *xml = (JSXML *)JS_GetInstancePrivate(cx, obj, &js_XMLClass, vp+2); \ + if (!xml) \ + return JS_FALSE + +#define NON_LIST_XML_METHOD_PROLOG \ + JSObject *obj; \ + JSXML *xml = StartNonListXMLMethod(cx, vp, &obj); \ + if (!xml) \ + return JS_FALSE; \ + JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST) + +static JSBool +xml_addNamespace(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *ns; + + NON_LIST_XML_METHOD_PROLOG; + if (xml->xml_class != JSXML_CLASS_ELEMENT) + goto done; + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + + if (!NamespaceHelper(cx, NULL, argc == 0 ? -1 : 1, vp + 2, vp)) + return JS_FALSE; + JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp)); + + ns = JSVAL_TO_OBJECT(*vp); + if (!AddInScopeNamespace(cx, xml, ns)) + return JS_FALSE; + ns->fslots[JSSLOT_DECLARED] = JSVAL_TRUE; + + done: + *vp = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static JSBool +xml_appendChild(JSContext *cx, uintN argc, jsval *vp) +{ + jsval name, v; + JSObject *vobj; + JSXML *vxml; + + NON_LIST_XML_METHOD_PROLOG; + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + + if (!js_GetAnyName(cx, &name)) + return JS_FALSE; + + if (!GetProperty(cx, obj, name, &v)) + return JS_FALSE; + + JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); + vobj = JSVAL_TO_OBJECT(v); + JS_ASSERT(OBJECT_IS_XML(cx, vobj)); + vxml = (JSXML *) vobj->getPrivate(); + JS_ASSERT(vxml->xml_class == JSXML_CLASS_LIST); + + if (!IndexToIdVal(cx, vxml->xml_kids.length, &name)) + return JS_FALSE; + *vp = (argc != 0) ? vp[2] : JSVAL_VOID; + if (!PutProperty(cx, JSVAL_TO_OBJECT(v), name, vp)) + return JS_FALSE; + + *vp = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_attribute(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *qn; + + if (argc == 0) { + js_ReportMissingArg(cx, vp, 0); + return JS_FALSE; + } + + qn = ToAttributeName(cx, vp[2]); + if (!qn) + return JS_FALSE; + vp[2] = OBJECT_TO_JSVAL(qn); /* local root */ + + return GetProperty(cx, JS_THIS_OBJECT(cx, vp), vp[2], vp); +} + +/* XML and XMLList */ +static JSBool +xml_attributes(JSContext *cx, uintN argc, jsval *vp) +{ + jsval name = ATOM_KEY(cx->runtime->atomState.starAtom); + JSObject *qn = ToAttributeName(cx, name); + if (!qn) + return JS_FALSE; + name = OBJECT_TO_JSVAL(qn); + + JSAutoTempValueRooter tvr(cx, name); + return GetProperty(cx, JS_THIS_OBJECT(cx, vp), name, vp); +} + +static JSXML * +xml_list_helper(JSContext *cx, JSXML *xml, jsval *rval) +{ + JSObject *listobj; + JSXML *list; + + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) + return NULL; + + *rval = OBJECT_TO_JSVAL(listobj); + list = (JSXML *) listobj->getPrivate(); + list->xml_target = xml; + return list; +} + +static JSBool +xml_child_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval name, + jsval *rval) +{ + uint32 index; + JSXML *kid; + JSObject *kidobj; + + /* ECMA-357 13.4.4.6 */ + JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST); + + if (js_IdIsIndex(name, &index)) { + if (index >= JSXML_LENGTH(xml)) { + *rval = JSVAL_VOID; + } else { + kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); + if (!kid) { + *rval = JSVAL_VOID; + } else { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(kidobj); + } + } + return JS_TRUE; + } + + return GetProperty(cx, obj, name, rval); +} + +/* XML and XMLList */ +static JSBool +xml_child(JSContext *cx, uintN argc, jsval *vp) +{ + jsval name, v; + JSXML *list, *vxml; + JSObject *kidobj; + + XML_METHOD_PROLOG; + name = argc != 0 ? vp[2] : JSVAL_VOID; + if (xml->xml_class == JSXML_CLASS_LIST) { + /* ECMA-357 13.5.4.4 */ + list = xml_list_helper(cx, xml, vp); + if (!list) + return JS_FALSE; + + JSXMLArrayCursor cursor(&xml->xml_kids); + while (JSXML *kid = (JSXML *) cursor.getNext()) { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + return JS_FALSE; + if (!xml_child_helper(cx, kidobj, kid, name, &v)) + return JS_FALSE; + if (JSVAL_IS_VOID(v)) { + /* The property didn't exist in this kid. */ + continue; + } + + JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); + vxml = (JSXML *) JSVAL_TO_OBJECT(v)->getPrivate(); + if ((!JSXML_HAS_KIDS(vxml) || vxml->xml_kids.length != 0) && + !Append(cx, list, vxml)) { + return JS_FALSE; + } + } + return JS_TRUE; + } + + /* ECMA-357 Edition 2 13.3.4.6 (note 13.3, not 13.4 as in Edition 1). */ + if (!xml_child_helper(cx, obj, xml, name, vp)) + return JS_FALSE; + if (JSVAL_IS_VOID(*vp) && !xml_list_helper(cx, xml, vp)) + return JS_FALSE; + return JS_TRUE; +} + +static JSBool +xml_childIndex(JSContext *cx, uintN argc, jsval *vp) +{ + JSXML *parent; + uint32 i, n; + + NON_LIST_XML_METHOD_PROLOG; + parent = xml->parent; + if (!parent || xml->xml_class == JSXML_CLASS_ATTRIBUTE) { + *vp = cx->runtime->NaNValue; + return JS_TRUE; + } + for (i = 0, n = JSXML_LENGTH(parent); i < n; i++) { + if (XMLARRAY_MEMBER(&parent->xml_kids, i, JSXML) == xml) + break; + } + JS_ASSERT(i < n); + return js_NewNumberInRootedValue(cx, i, vp); +} + +/* XML and XMLList */ +static JSBool +xml_children(JSContext *cx, uintN argc, jsval *vp) +{ + jsval name; + + name = ATOM_KEY(cx->runtime->atomState.starAtom); + return GetProperty(cx, JS_THIS_OBJECT(cx, vp), name, vp); +} + +/* XML and XMLList */ +static JSBool +xml_comments_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval *vp) +{ + JSXML *list, *kid, *vxml; + JSBool ok; + uint32 i, n; + JSObject *kidobj; + jsval v; + + list = xml_list_helper(cx, xml, vp); + if (!list) + return JS_FALSE; + + ok = JS_TRUE; + + if (xml->xml_class == JSXML_CLASS_LIST) { + /* 13.5.4.6 Step 2. */ + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { + ok = js_EnterLocalRootScope(cx); + if (!ok) + break; + kidobj = js_GetXMLObject(cx, kid); + if (kidobj) { + ok = xml_comments_helper(cx, kidobj, kid, &v); + } else { + ok = JS_FALSE; + v = JSVAL_NULL; + } + js_LeaveLocalRootScopeWithResult(cx, v); + if (!ok) + break; + vxml = (JSXML *) JSVAL_TO_OBJECT(v)->getPrivate(); + if (JSXML_LENGTH(vxml) != 0) { + ok = Append(cx, list, vxml); + if (!ok) + break; + } + } + } + } else { + /* 13.4.4.9 Step 2. */ + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_COMMENT) { + ok = Append(cx, list, kid); + if (!ok) + break; + } + } + } + + return ok; +} + +static JSBool +xml_comments(JSContext *cx, uintN argc, jsval *vp) +{ + XML_METHOD_PROLOG; + return xml_comments_helper(cx, obj, xml, vp); +} + +/* XML and XMLList */ +static JSBool +xml_contains(JSContext *cx, uintN argc, jsval *vp) +{ + jsval value; + JSBool eq; + JSObject *kidobj; + + XML_METHOD_PROLOG; + value = argc != 0 ? vp[2] : JSVAL_VOID; + if (xml->xml_class == JSXML_CLASS_LIST) { + eq = JS_FALSE; + JSXMLArrayCursor cursor(&xml->xml_kids); + while (JSXML *kid = (JSXML *) cursor.getNext()) { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj || !js_TestXMLEquality(cx, kidobj, value, &eq)) + return JS_FALSE; + if (eq) + break; + } + } else { + if (!js_TestXMLEquality(cx, obj, value, &eq)) + return JS_FALSE; + } + *vp = BOOLEAN_TO_JSVAL(eq); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_copy(JSContext *cx, uintN argc, jsval *vp) +{ + JSXML *copy; + + XML_METHOD_PROLOG; + copy = DeepCopy(cx, xml, NULL, 0); + if (!copy) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(copy->object); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_descendants(JSContext *cx, uintN argc, jsval *vp) +{ + jsval name; + JSXML *list; + + XML_METHOD_PROLOG; + name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : vp[2]; + list = Descendants(cx, xml, name); + if (!list) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(list->object); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_elements_helper(JSContext *cx, JSObject *obj, JSXML *xml, + JSObject *nameqn, jsval *vp) +{ + JSXML *list, *vxml; + jsval v; + JSBool ok; + JSObject *kidobj; + uint32 i, n; + + list = xml_list_helper(cx, xml, vp); + if (!list) + return JS_FALSE; + + list->xml_targetprop = nameqn; + ok = JS_TRUE; + + if (xml->xml_class == JSXML_CLASS_LIST) { + /* 13.5.4.6 */ + JSXMLArrayCursor cursor(&xml->xml_kids); + while (JSXML *kid = (JSXML *) cursor.getNext()) { + if (kid->xml_class == JSXML_CLASS_ELEMENT) { + ok = js_EnterLocalRootScope(cx); + if (!ok) + break; + kidobj = js_GetXMLObject(cx, kid); + if (kidobj) { + ok = xml_elements_helper(cx, kidobj, kid, nameqn, &v); + } else { + ok = JS_FALSE; + v = JSVAL_NULL; + } + js_LeaveLocalRootScopeWithResult(cx, v); + if (!ok) + break; + vxml = (JSXML *) JSVAL_TO_OBJECT(v)->getPrivate(); + if (JSXML_LENGTH(vxml) != 0) { + ok = Append(cx, list, vxml); + if (!ok) + break; + } + } + } + } else { + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + JSXML *kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_ELEMENT && + MatchElemName(nameqn, kid)) { + ok = Append(cx, list, kid); + if (!ok) + break; + } + } + } + + return ok; +} + +static JSBool +xml_elements(JSContext *cx, uintN argc, jsval *vp) +{ + jsval name; + JSObject *nameqn; + jsid funid; + + XML_METHOD_PROLOG; + + name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : vp[2]; + nameqn = ToXMLName(cx, name, &funid); + if (!nameqn) + return JS_FALSE; + vp[2] = OBJECT_TO_JSVAL(nameqn); + + if (funid) + return xml_list_helper(cx, xml, vp) != NULL; + + return xml_elements_helper(cx, obj, xml, nameqn, vp); +} + +/* XML and XMLList */ +static JSBool +xml_hasOwnProperty(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *obj; + jsval name; + JSBool found; + + obj = JS_THIS_OBJECT(cx, vp); + if (!JS_InstanceOf(cx, obj, &js_XMLClass, vp + 2)) + return JS_FALSE; + + name = argc != 0 ? vp[2] : JSVAL_VOID; + if (!HasProperty(cx, obj, name, &found)) + return JS_FALSE; + if (found) { + *vp = JSVAL_TRUE; + return JS_TRUE; + } + return js_HasOwnPropertyHelper(cx, js_LookupProperty, argc, vp); +} + +/* XML and XMLList */ +static JSBool +xml_hasComplexContent(JSContext *cx, uintN argc, jsval *vp) +{ + JSXML *kid; + JSObject *kidobj; + uint32 i, n; + + XML_METHOD_PROLOG; +again: + switch (xml->xml_class) { + case JSXML_CLASS_ATTRIBUTE: + case JSXML_CLASS_COMMENT: + case JSXML_CLASS_PROCESSING_INSTRUCTION: + case JSXML_CLASS_TEXT: + *vp = JSVAL_FALSE; + break; + case JSXML_CLASS_LIST: + if (xml->xml_kids.length == 0) { + *vp = JSVAL_TRUE; + } else if (xml->xml_kids.length == 1) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + if (kid) { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + return JS_FALSE; + obj = kidobj; + xml = (JSXML *) obj->getPrivate(); + goto again; + } + } + /* FALL THROUGH */ + default: + *vp = JSVAL_FALSE; + for (i = 0, n = xml->xml_kids.length; i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { + *vp = JSVAL_TRUE; + break; + } + } + break; + } + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_hasSimpleContent(JSContext *cx, uintN argc, jsval *vp) +{ + XML_METHOD_PROLOG; + *vp = BOOLEAN_TO_JSVAL(HasSimpleContent(xml)); + return JS_TRUE; +} + +typedef struct JSTempRootedNSArray { + JSTempValueRooter tvr; + JSXMLArray array; + jsval value; /* extra root for temporaries */ +} JSTempRootedNSArray; + +static void +TraceObjectVector(JSTracer *trc, JSObject **vec, uint32 len) +{ + uint32 i; + JSObject *obj; + + for (i = 0; i < len; i++) { + obj = vec[i]; + if (obj) { + JS_SET_TRACING_INDEX(trc, "vector", i); + JS_CallTracer(trc, obj, JSTRACE_OBJECT); + } + } +} + +static void +trace_temp_ns_array(JSTracer *trc, JSTempValueRooter *tvr) +{ + JSTempRootedNSArray *tmp = (JSTempRootedNSArray *)tvr; + + TraceObjectVector(trc, + (JSObject **) tmp->array.vector, + tmp->array.length); + XMLArrayCursorTrace(trc, tmp->array.cursors); + JS_CALL_VALUE_TRACER(trc, tmp->value, "temp_ns_array_value"); +} + +static void +InitTempNSArray(JSContext *cx, JSTempRootedNSArray *tmp) +{ + XMLArrayInit(cx, &tmp->array, 0); + tmp->value = JSVAL_NULL; + JS_PUSH_TEMP_ROOT_TRACE(cx, trace_temp_ns_array, &tmp->tvr); +} + +static void +FinishTempNSArray(JSContext *cx, JSTempRootedNSArray *tmp) +{ + JS_ASSERT(tmp->tvr.u.trace == trace_temp_ns_array); + JS_POP_TEMP_ROOT(cx, &tmp->tvr); + XMLArrayFinish(cx, &tmp->array); +} + +/* + * Populate a new JS array with elements of JSTempRootedNSArray.array and + * place the result into rval. rval must point to a rooted location. + */ +static JSBool +TempNSArrayToJSArray(JSContext *cx, JSTempRootedNSArray *tmp, jsval *rval) +{ + JSObject *arrayobj; + uint32 i, n; + JSObject *ns; + + arrayobj = js_NewArrayObject(cx, 0, NULL); + if (!arrayobj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(arrayobj); + for (i = 0, n = tmp->array.length; i < n; i++) { + ns = XMLARRAY_MEMBER(&tmp->array, i, JSObject); + if (!ns) + continue; + tmp->value = OBJECT_TO_JSVAL(ns); + if (!arrayobj->setProperty(cx, INT_TO_JSID(i), &tmp->value)) + return JS_FALSE; + } + return JS_TRUE; +} + +static JSBool +FindInScopeNamespaces(JSContext *cx, JSXML *xml, JSXMLArray *nsarray) +{ + uint32 length, i, j, n; + JSObject *ns, *ns2; + JSString *prefix, *prefix2; + + length = nsarray->length; + do { + if (xml->xml_class != JSXML_CLASS_ELEMENT) + continue; + for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { + ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject); + if (!ns) + continue; + + prefix = GetPrefix(ns); + for (j = 0; j < length; j++) { + ns2 = XMLARRAY_MEMBER(nsarray, j, JSObject); + if (ns2) { + prefix2 = GetPrefix(ns2); + if ((prefix2 && prefix) + ? js_EqualStrings(prefix2, prefix) + : js_EqualStrings(GetURI(ns2), GetURI(ns))) { + break; + } + } + } + + if (j == length) { + if (!XMLARRAY_APPEND(cx, nsarray, ns)) + return JS_FALSE; + ++length; + } + } + } while ((xml = xml->parent) != NULL); + JS_ASSERT(length == nsarray->length); + + return JS_TRUE; +} + +static JSBool +xml_inScopeNamespaces(JSContext *cx, uintN argc, jsval *vp) +{ + JSTempRootedNSArray namespaces; + JSBool ok; + + NON_LIST_XML_METHOD_PROLOG; + + InitTempNSArray(cx, &namespaces); + ok = FindInScopeNamespaces(cx, xml, &namespaces.array) && + TempNSArrayToJSArray(cx, &namespaces, vp); + FinishTempNSArray(cx, &namespaces); + return ok; +} + +static JSBool +xml_insertChildAfter(JSContext *cx, uintN argc, jsval *vp) +{ + jsval arg; + JSXML *kid; + uint32 i; + + NON_LIST_XML_METHOD_PROLOG; + *vp = OBJECT_TO_JSVAL(obj); + if (!JSXML_HAS_KIDS(xml) || argc == 0) + return JS_TRUE; + + arg = vp[2]; + if (JSVAL_IS_NULL(arg)) { + kid = NULL; + i = 0; + } else { + if (!VALUE_IS_XML(cx, arg)) + return JS_TRUE; + kid = (JSXML *) JSVAL_TO_OBJECT(arg)->getPrivate(); + i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, NULL); + if (i == XML_NOT_FOUND) + return JS_TRUE; + ++i; + } + + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + return Insert(cx, xml, i, argc >= 2 ? vp[3] : JSVAL_VOID); +} + +static JSBool +xml_insertChildBefore(JSContext *cx, uintN argc, jsval *vp) +{ + jsval arg; + JSXML *kid; + uint32 i; + + NON_LIST_XML_METHOD_PROLOG; + *vp = OBJECT_TO_JSVAL(obj); + if (!JSXML_HAS_KIDS(xml) || argc == 0) + return JS_TRUE; + + arg = vp[2]; + if (JSVAL_IS_NULL(arg)) { + kid = NULL; + i = xml->xml_kids.length; + } else { + if (!VALUE_IS_XML(cx, arg)) + return JS_TRUE; + kid = (JSXML *) JSVAL_TO_OBJECT(arg)->getPrivate(); + i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, NULL); + if (i == XML_NOT_FOUND) + return JS_TRUE; + } + + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + return Insert(cx, xml, i, argc >= 2 ? vp[3] : JSVAL_VOID); +} + +/* XML and XMLList */ +static JSBool +xml_length(JSContext *cx, uintN argc, jsval *vp) +{ + XML_METHOD_PROLOG; + if (xml->xml_class != JSXML_CLASS_LIST) { + *vp = JSVAL_ONE; + } else { + if (!js_NewNumberInRootedValue(cx, xml->xml_kids.length, vp)) + return JS_FALSE; + } + return JS_TRUE; +} + +static JSBool +xml_localName(JSContext *cx, uintN argc, jsval *vp) +{ + NON_LIST_XML_METHOD_PROLOG; + *vp = xml->name ? xml->name->fslots[JSSLOT_LOCAL_NAME] : JSVAL_NULL; + return JS_TRUE; +} + +static JSBool +xml_name(JSContext *cx, uintN argc, jsval *vp) +{ + NON_LIST_XML_METHOD_PROLOG; + *vp = OBJECT_TO_JSVAL(xml->name); + return JS_TRUE; +} + +static JSBool +xml_namespace(JSContext *cx, uintN argc, jsval *vp) +{ + JSString *prefix, *nsprefix; + JSTempRootedNSArray inScopeNSes; + JSBool ok; + jsuint i, length; + JSObject *ns; + + NON_LIST_XML_METHOD_PROLOG; + if (argc == 0 && !JSXML_HAS_NAME(xml)) { + *vp = JSVAL_NULL; + return JS_TRUE; + } + + if (argc == 0) { + prefix = NULL; + } else { + prefix = js_ValueToString(cx, vp[2]); + if (!prefix) + return JS_FALSE; + vp[2] = STRING_TO_JSVAL(prefix); /* local root */ + } + + InitTempNSArray(cx, &inScopeNSes); + MUST_FLOW_THROUGH("out"); + ok = FindInScopeNamespaces(cx, xml, &inScopeNSes.array); + if (!ok) + goto out; + + if (!prefix) { + ns = GetNamespace(cx, xml->name, &inScopeNSes.array); + if (!ns) { + ok = JS_FALSE; + goto out; + } + } else { + ns = NULL; + for (i = 0, length = inScopeNSes.array.length; i < length; i++) { + ns = XMLARRAY_MEMBER(&inScopeNSes.array, i, JSObject); + if (ns) { + nsprefix = GetPrefix(ns); + if (nsprefix && js_EqualStrings(nsprefix, prefix)) + break; + ns = NULL; + } + } + } + + *vp = (!ns) ? JSVAL_VOID : OBJECT_TO_JSVAL(ns); + + out: + FinishTempNSArray(cx, &inScopeNSes); + return JS_TRUE; +} + +static JSBool +xml_namespaceDeclarations(JSContext *cx, uintN argc, jsval *vp) +{ + JSBool ok; + JSTempRootedNSArray ancestors, declared; + JSXML *yml; + uint32 i, n; + JSObject *ns; + + NON_LIST_XML_METHOD_PROLOG; + if (JSXML_HAS_VALUE(xml)) + return JS_TRUE; + + /* From here, control flow must goto out to finish these arrays. */ + ok = JS_TRUE; + InitTempNSArray(cx, &ancestors); + InitTempNSArray(cx, &declared); + yml = xml; + + while ((yml = yml->parent) != NULL) { + JS_ASSERT(yml->xml_class == JSXML_CLASS_ELEMENT); + for (i = 0, n = yml->xml_namespaces.length; i < n; i++) { + ns = XMLARRAY_MEMBER(&yml->xml_namespaces, i, JSObject); + if (ns && + !XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) { + ok = XMLARRAY_APPEND(cx, &ancestors.array, ns); + if (!ok) + goto out; + } + } + } + + for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { + ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject); + if (!ns) + continue; + if (!IsDeclared(ns)) + continue; + if (!XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) { + ok = XMLARRAY_APPEND(cx, &declared.array, ns); + if (!ok) + goto out; + } + } + + ok = TempNSArrayToJSArray(cx, &declared, vp); + +out: + /* Finishing must be in reverse order of initialization to follow LIFO. */ + FinishTempNSArray(cx, &declared); + FinishTempNSArray(cx, &ancestors); + return ok; +} + +static const char js_attribute_str[] = "attribute"; +static const char js_text_str[] = "text"; + +/* Exported to jsgc.c #ifdef DEBUG. */ +const char *js_xml_class_str[] = { + "list", + "element", + js_attribute_str, + "processing-instruction", + js_text_str, + "comment" +}; + +static JSBool +xml_nodeKind(JSContext *cx, uintN argc, jsval *vp) +{ + JSString *str; + + NON_LIST_XML_METHOD_PROLOG; + str = JS_InternString(cx, js_xml_class_str[xml->xml_class]); + if (!str) + return JS_FALSE; + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static void +NormalizingDelete(JSContext *cx, JSXML *xml, uint32 index) +{ + if (xml->xml_class == JSXML_CLASS_LIST) + DeleteListElement(cx, xml, index); + else + DeleteByIndex(cx, xml, index); +} + +/* XML and XMLList */ +static JSBool +xml_normalize_helper(JSContext *cx, JSObject *obj, JSXML *xml) +{ + JSXML *kid, *kid2; + uint32 i, n; + JSObject *kidobj; + JSString *str; + + if (!JSXML_HAS_KIDS(xml)) + return JS_TRUE; + + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + + for (i = 0, n = xml->xml_kids.length; i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (!kid) + continue; + if (kid->xml_class == JSXML_CLASS_ELEMENT) { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj || !xml_normalize_helper(cx, kidobj, kid)) + return JS_FALSE; + } else if (kid->xml_class == JSXML_CLASS_TEXT) { + while (i + 1 < n && + (kid2 = XMLARRAY_MEMBER(&xml->xml_kids, i + 1, JSXML)) && + kid2->xml_class == JSXML_CLASS_TEXT) { + str = js_ConcatStrings(cx, kid->xml_value, kid2->xml_value); + if (!str) + return JS_FALSE; + NormalizingDelete(cx, xml, i + 1); + n = xml->xml_kids.length; + kid->xml_value = str; + } + if (kid->xml_value->empty()) { + NormalizingDelete(cx, xml, i); + n = xml->xml_kids.length; + --i; + } + } + } + + return JS_TRUE; +} + +static JSBool +xml_normalize(JSContext *cx, uintN argc, jsval *vp) +{ + XML_METHOD_PROLOG; + *vp = OBJECT_TO_JSVAL(obj); + return xml_normalize_helper(cx, obj, xml); +} + +/* XML and XMLList */ +static JSBool +xml_parent(JSContext *cx, uintN argc, jsval *vp) +{ + JSXML *parent, *kid; + uint32 i, n; + JSObject *parentobj; + + XML_METHOD_PROLOG; + parent = xml->parent; + if (xml->xml_class == JSXML_CLASS_LIST) { + *vp = JSVAL_VOID; + n = xml->xml_kids.length; + if (n == 0) + return JS_TRUE; + + kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + if (!kid) + return JS_TRUE; + parent = kid->parent; + for (i = 1; i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->parent != parent) + return JS_TRUE; + } + } + + if (!parent) { + *vp = JSVAL_NULL; + return JS_TRUE; + } + + parentobj = js_GetXMLObject(cx, parent); + if (!parentobj) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(parentobj); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_processingInstructions_helper(JSContext *cx, JSObject *obj, JSXML *xml, + JSObject *nameqn, jsval *vp) +{ + JSXML *list, *vxml; + JSBool ok; + JSObject *kidobj; + jsval v; + uint32 i, n; + JSString *localName; + + list = xml_list_helper(cx, xml, vp); + if (!list) + return JS_FALSE; + + list->xml_targetprop = nameqn; + ok = JS_TRUE; + + if (xml->xml_class == JSXML_CLASS_LIST) { + /* 13.5.4.17 Step 4 (misnumbered 9 -- Erratum?). */ + JSXMLArrayCursor cursor(&xml->xml_kids); + while (JSXML *kid = (JSXML *) cursor.getNext()) { + if (kid->xml_class == JSXML_CLASS_ELEMENT) { + ok = js_EnterLocalRootScope(cx); + if (!ok) + break; + kidobj = js_GetXMLObject(cx, kid); + if (kidobj) { + ok = xml_processingInstructions_helper(cx, kidobj, kid, + nameqn, &v); + } else { + ok = JS_FALSE; + v = JSVAL_NULL; + } + js_LeaveLocalRootScopeWithResult(cx, v); + if (!ok) + break; + vxml = (JSXML *) JSVAL_TO_OBJECT(v)->getPrivate(); + if (JSXML_LENGTH(vxml) != 0) { + ok = Append(cx, list, vxml); + if (!ok) + break; + } + } + } + } else { + /* 13.4.4.28 Step 4. */ + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + JSXML *kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) { + localName = GetLocalName(nameqn); + if (IS_STAR(localName) || + js_EqualStrings(localName, GetLocalName(kid->name))) { + ok = Append(cx, list, kid); + if (!ok) + break; + } + } + } + } + + return ok; +} + +static JSBool +xml_processingInstructions(JSContext *cx, uintN argc, jsval *vp) +{ + jsval name; + JSObject *nameqn; + jsid funid; + + XML_METHOD_PROLOG; + + name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : vp[2]; + nameqn = ToXMLName(cx, name, &funid); + if (!nameqn) + return JS_FALSE; + vp[2] = OBJECT_TO_JSVAL(nameqn); + + if (funid) + return xml_list_helper(cx, xml, vp) != NULL; + + return xml_processingInstructions_helper(cx, obj, xml, nameqn, vp); +} + +static JSBool +xml_prependChild(JSContext *cx, uintN argc, jsval *vp) +{ + NON_LIST_XML_METHOD_PROLOG; + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(obj); + return Insert(cx, xml, 0, argc != 0 ? vp[2] : JSVAL_VOID); +} + +/* XML and XMLList */ +static JSBool +xml_propertyIsEnumerable(JSContext *cx, uintN argc, jsval *vp) +{ + uint32 index; + + XML_METHOD_PROLOG; + *vp = JSVAL_FALSE; + if (argc != 0 && js_IdIsIndex(vp[2], &index)) { + if (xml->xml_class == JSXML_CLASS_LIST) { + /* 13.5.4.18. */ + *vp = BOOLEAN_TO_JSVAL(index < xml->xml_kids.length); + } else { + /* 13.4.4.30. */ + *vp = BOOLEAN_TO_JSVAL(index == 0); + } + } + return JS_TRUE; +} + +static JSBool +namespace_full_match(const void *a, const void *b) +{ + const JSObject *nsa = (const JSObject *) a; + const JSObject *nsb = (const JSObject *) b; + JSString *prefixa = GetPrefix(nsa); + JSString *prefixb; + + if (prefixa) { + prefixb = GetPrefix(nsb); + if (prefixb && !js_EqualStrings(prefixa, prefixb)) + return JS_FALSE; + } + return js_EqualStrings(GetURI(nsa), GetURI(nsb)); +} + +static JSBool +xml_removeNamespace_helper(JSContext *cx, JSXML *xml, JSObject *ns) +{ + JSObject *thisns, *attrns; + uint32 i, n; + JSXML *attr, *kid; + + thisns = GetNamespace(cx, xml->name, &xml->xml_namespaces); + JS_ASSERT(thisns); + if (thisns == ns) + return JS_TRUE; + + for (i = 0, n = xml->xml_attrs.length; i < n; i++) { + attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); + if (!attr) + continue; + attrns = GetNamespace(cx, attr->name, &xml->xml_namespaces); + JS_ASSERT(attrns); + if (attrns == ns) + return JS_TRUE; + } + + i = XMLARRAY_FIND_MEMBER(&xml->xml_namespaces, ns, namespace_full_match); + if (i != XML_NOT_FOUND) + XMLArrayDelete(cx, &xml->xml_namespaces, i, JS_TRUE); + + for (i = 0, n = xml->xml_kids.length; i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { + if (!xml_removeNamespace_helper(cx, kid, ns)) + return JS_FALSE; + } + } + return JS_TRUE; +} + +static JSBool +xml_removeNamespace(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *ns; + + NON_LIST_XML_METHOD_PROLOG; + if (xml->xml_class != JSXML_CLASS_ELEMENT) + goto done; + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + + if (!NamespaceHelper(cx, NULL, argc == 0 ? -1 : 1, vp + 2, vp)) + return JS_FALSE; + JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp)); + ns = JSVAL_TO_OBJECT(*vp); + + /* NOTE: remove ns from each ancestor if not used by that ancestor. */ + if (!xml_removeNamespace_helper(cx, xml, ns)) + return JS_FALSE; + done: + *vp = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static JSBool +xml_replace(JSContext *cx, uintN argc, jsval *vp) +{ + jsval value; + JSXML *vxml, *kid; + uint32 index, i; + JSObject *nameqn; + + NON_LIST_XML_METHOD_PROLOG; + if (xml->xml_class != JSXML_CLASS_ELEMENT) + goto done; + + if (argc <= 1) { + value = STRING_TO_JSVAL(ATOM_TO_STRING(cx->runtime->atomState. + typeAtoms[JSTYPE_VOID])); + } else { + value = vp[3]; + vxml = VALUE_IS_XML(cx, value) + ? (JSXML *) JSVAL_TO_OBJECT(value)->getPrivate() + : NULL; + if (!vxml) { + if (!JS_ConvertValue(cx, value, JSTYPE_STRING, &vp[3])) + return JS_FALSE; + value = vp[3]; + } else { + vxml = DeepCopy(cx, vxml, NULL, 0); + if (!vxml) + return JS_FALSE; + value = vp[3] = OBJECT_TO_JSVAL(vxml->object); + } + } + + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + + if (argc == 0 || !js_IdIsIndex(vp[2], &index)) { + /* + * Call function QName per spec, not ToXMLName, to avoid attribute + * names. + */ + if (!QNameHelper(cx, NULL, &js_QNameClass.base, argc == 0 ? -1 : 1, + vp + 2, vp)) { + return JS_FALSE; + } + JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp)); + nameqn = JSVAL_TO_OBJECT(*vp); + + i = xml->xml_kids.length; + index = XML_NOT_FOUND; + while (i != 0) { + --i; + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && MatchElemName(nameqn, kid)) { + if (i != XML_NOT_FOUND) + DeleteByIndex(cx, xml, i); + index = i; + } + } + + if (index == XML_NOT_FOUND) + goto done; + } + + if (!Replace(cx, xml, index, value)) + return JS_FALSE; + + done: + *vp = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static JSBool +xml_setChildren(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *obj; + + if (!StartNonListXMLMethod(cx, vp, &obj)) + return JS_FALSE; + + *vp = argc != 0 ? vp[2] : JSVAL_VOID; /* local root */ + if (!PutProperty(cx, obj, ATOM_KEY(cx->runtime->atomState.starAtom), vp)) + return JS_FALSE; + + *vp = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static JSBool +xml_setLocalName(JSContext *cx, uintN argc, jsval *vp) +{ + jsval name; + JSObject *nameqn; + JSString *namestr; + + NON_LIST_XML_METHOD_PROLOG; + if (!JSXML_HAS_NAME(xml)) + return JS_TRUE; + + if (argc == 0) { + namestr = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]); + } else { + name = vp[2]; + if (!JSVAL_IS_PRIMITIVE(name) && + OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(name)) == &js_QNameClass.base) { + nameqn = JSVAL_TO_OBJECT(name); + namestr = GetLocalName(nameqn); + } else { + if (!JS_ConvertValue(cx, name, JSTYPE_STRING, &vp[2])) + return JS_FALSE; + name = vp[2]; + namestr = JSVAL_TO_STRING(name); + } + } + + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + xml->name->fslots[JSSLOT_LOCAL_NAME] = namestr + ? STRING_TO_JSVAL(namestr) + : JSVAL_VOID; + return JS_TRUE; +} + +static JSBool +xml_setName(JSContext *cx, uintN argc, jsval *vp) +{ + jsval name; + JSObject *nameqn; + JSXML *nsowner; + JSXMLArray *nsarray; + uint32 i, n; + JSObject *ns; + + NON_LIST_XML_METHOD_PROLOG; + if (!JSXML_HAS_NAME(xml)) + return JS_TRUE; + + if (argc == 0) { + name = STRING_TO_JSVAL(ATOM_TO_STRING(cx->runtime->atomState. + typeAtoms[JSTYPE_VOID])); + } else { + name = vp[2]; + if (!JSVAL_IS_PRIMITIVE(name) && + OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(name)) == &js_QNameClass.base && + !GetURI(nameqn = JSVAL_TO_OBJECT(name))) { + name = vp[2] = nameqn->fslots[JSSLOT_LOCAL_NAME]; + } + } + + nameqn = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 1, &name); + if (!nameqn) + return JS_FALSE; + + /* ECMA-357 13.4.4.35 Step 4. */ + if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) + nameqn->fslots[JSSLOT_URI] = STRING_TO_JSVAL(cx->runtime->emptyString); + + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + xml->name = nameqn; + + /* + * Erratum: nothing in 13.4.4.35 talks about making the name match the + * in-scope namespaces, either by finding an in-scope namespace with a + * matching uri and setting the new name's prefix to that namespace's + * prefix, or by extending the in-scope namespaces for xml (which are in + * xml->parent if xml is an attribute or a PI). + */ + if (xml->xml_class == JSXML_CLASS_ELEMENT) { + nsowner = xml; + } else { + if (!xml->parent || xml->parent->xml_class != JSXML_CLASS_ELEMENT) + return JS_TRUE; + nsowner = xml->parent; + } + + if (GetPrefix(nameqn)) { + /* + * The name being set has a prefix, which originally came from some + * namespace object (which may be the null namespace, where both the + * prefix and uri are the empty string). We must go through a full + * GetNamespace in case that namespace is in-scope in nsowner. + * + * If we find such an in-scope namespace, we return true right away, + * in this block. Otherwise, we fall through to the final return of + * AddInScopeNamespace(cx, nsowner, ns). + */ + ns = GetNamespace(cx, nameqn, &nsowner->xml_namespaces); + if (!ns) + return JS_FALSE; + + /* XXXbe have to test membership to see whether GetNamespace added */ + if (XMLARRAY_HAS_MEMBER(&nsowner->xml_namespaces, ns, NULL)) + return JS_TRUE; + } else { + /* + * At this point, we know prefix of nameqn is null, so its uri can't + * be the empty string (the null namespace always uses the empty string + * for both prefix and uri). + * + * This means we must inline GetNamespace and specialize it to match + * uri only, never prefix. If we find a namespace with nameqn's uri + * already in nsowner->xml_namespaces, then all that we need do is set + * prefix of nameqn to that namespace's prefix. + * + * If no such namespace exists, we can create one without going through + * the constructor, because we know uri of nameqn is non-empty (so + * prefix does not need to be converted from null to empty by QName). + */ + JS_ASSERT(!GetURI(nameqn)->empty()); + + nsarray = &nsowner->xml_namespaces; + for (i = 0, n = nsarray->length; i < n; i++) { + ns = XMLARRAY_MEMBER(nsarray, i, JSObject); + if (ns && js_EqualStrings(GetURI(ns), GetURI(nameqn))) { + nameqn->fslots[JSSLOT_PREFIX] = ns->fslots[JSSLOT_PREFIX]; + return JS_TRUE; + } + } + + ns = NewXMLNamespace(cx, NULL, GetURI(nameqn), JS_TRUE); + if (!ns) + return JS_FALSE; + } + + if (!AddInScopeNamespace(cx, nsowner, ns)) + return JS_FALSE; + vp[0] = JSVAL_VOID; + return JS_TRUE; +} + +static JSBool +xml_setNamespace(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *qn; + JSObject *ns; + jsval qnargv[2]; + JSXML *nsowner; + + NON_LIST_XML_METHOD_PROLOG; + if (!JSXML_HAS_NAME(xml)) + return JS_TRUE; + + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + + ns = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, obj, + argc == 0 ? 0 : 1, vp + 2); + if (!ns) + return JS_FALSE; + vp[0] = OBJECT_TO_JSVAL(ns); + ns->fslots[JSSLOT_DECLARED] = JSVAL_TRUE; + + qnargv[0] = vp[2] = OBJECT_TO_JSVAL(ns); + qnargv[1] = OBJECT_TO_JSVAL(xml->name); + qn = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 2, qnargv); + if (!qn) + return JS_FALSE; + + xml->name = qn; + + /* + * Erratum: the spec fails to update the governing in-scope namespaces. + * See the erratum noted in xml_setName, above. + */ + if (xml->xml_class == JSXML_CLASS_ELEMENT) { + nsowner = xml; + } else { + if (!xml->parent || xml->parent->xml_class != JSXML_CLASS_ELEMENT) + return JS_TRUE; + nsowner = xml->parent; + } + if (!AddInScopeNamespace(cx, nsowner, ns)) + return JS_FALSE; + vp[0] = JSVAL_VOID; + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_text_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval *vp) +{ + JSXML *list, *kid, *vxml; + uint32 i, n; + JSBool ok; + JSObject *kidobj; + jsval v; + + list = xml_list_helper(cx, xml, vp); + if (!list) + return JS_FALSE; + + if (xml->xml_class == JSXML_CLASS_LIST) { + ok = JS_TRUE; + for (i = 0, n = xml->xml_kids.length; i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { + ok = js_EnterLocalRootScope(cx); + if (!ok) + break; + kidobj = js_GetXMLObject(cx, kid); + if (kidobj) { + ok = xml_text_helper(cx, kidobj, kid, &v); + } else { + ok = JS_FALSE; + v = JSVAL_NULL; + } + js_LeaveLocalRootScopeWithResult(cx, v); + if (!ok) + return JS_FALSE; + vxml = (JSXML *) JSVAL_TO_OBJECT(v)->getPrivate(); + if (JSXML_LENGTH(vxml) != 0 && !Append(cx, list, vxml)) + return JS_FALSE; + } + } + } else { + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_TEXT) { + if (!Append(cx, list, kid)) + return JS_FALSE; + } + } + } + return JS_TRUE; +} + +static JSBool +xml_text(JSContext *cx, uintN argc, jsval *vp) +{ + XML_METHOD_PROLOG; + return xml_text_helper(cx, obj, xml, vp); +} + +/* XML and XMLList */ +static JSString * +xml_toString_helper(JSContext *cx, JSXML *xml) +{ + JSString *str, *kidstr; + + if (xml->xml_class == JSXML_CLASS_ATTRIBUTE || + xml->xml_class == JSXML_CLASS_TEXT) { + return xml->xml_value; + } + + if (!HasSimpleContent(xml)) + return ToXMLString(cx, OBJECT_TO_JSVAL(xml->object), 0); + + str = cx->runtime->emptyString; + if (!js_EnterLocalRootScope(cx)) + return NULL; + JSXMLArrayCursor cursor(&xml->xml_kids); + while (JSXML *kid = (JSXML *) cursor.getNext()) { + if (kid->xml_class != JSXML_CLASS_COMMENT && + kid->xml_class != JSXML_CLASS_PROCESSING_INSTRUCTION) { + kidstr = xml_toString_helper(cx, kid); + if (!kidstr) { + str = NULL; + break; + } + str = js_ConcatStrings(cx, str, kidstr); + if (!str) + break; + } + } + js_LeaveLocalRootScopeWithResult(cx, STRING_TO_JSVAL(str)); + return str; +} + +static JSBool +xml_toSource(JSContext *cx, uintN argc, jsval *vp) +{ + jsval thisv; + JSString *str; + + thisv = JS_THIS(cx, vp); + if (JSVAL_IS_NULL(thisv)) + return JS_FALSE; + str = ToXMLString(cx, thisv, TO_SOURCE_FLAG); + if (!str) + return JS_FALSE; + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +xml_toString(JSContext *cx, uintN argc, jsval *vp) +{ + JSString *str; + + XML_METHOD_PROLOG; + str = xml_toString_helper(cx, xml); + if (!str) + return JS_FALSE; + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_toXMLString(JSContext *cx, uintN argc, jsval *vp) +{ + jsval thisv; + JSString *str; + + thisv = JS_THIS(cx, vp); + if (JSVAL_IS_NULL(thisv)) + return JS_FALSE; + str = ToXMLString(cx, thisv, 0); + if (!str) + return JS_FALSE; + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_valueOf(JSContext *cx, uintN argc, jsval *vp) +{ + *vp = JS_THIS(cx, vp); + return !JSVAL_IS_NULL(*vp); +} + +static JSFunctionSpec xml_methods[] = { + JS_FN("addNamespace", xml_addNamespace, 1,0), + JS_FN("appendChild", xml_appendChild, 1,0), + JS_FN(js_attribute_str, xml_attribute, 1,0), + JS_FN("attributes", xml_attributes, 0,0), + JS_FN("child", xml_child, 1,0), + JS_FN("childIndex", xml_childIndex, 0,0), + JS_FN("children", xml_children, 0,0), + JS_FN("comments", xml_comments, 0,0), + JS_FN("contains", xml_contains, 1,0), + JS_FN("copy", xml_copy, 0,0), + JS_FN("descendants", xml_descendants, 1,0), + JS_FN("elements", xml_elements, 1,0), + JS_FN("hasOwnProperty", xml_hasOwnProperty, 1,0), + JS_FN("hasComplexContent", xml_hasComplexContent, 1,0), + JS_FN("hasSimpleContent", xml_hasSimpleContent, 1,0), + JS_FN("inScopeNamespaces", xml_inScopeNamespaces, 0,0), + JS_FN("insertChildAfter", xml_insertChildAfter, 2,0), + JS_FN("insertChildBefore", xml_insertChildBefore, 2,0), + JS_FN(js_length_str, xml_length, 0,0), + JS_FN(js_localName_str, xml_localName, 0,0), + JS_FN(js_name_str, xml_name, 0,0), + JS_FN(js_namespace_str, xml_namespace, 1,0), + JS_FN("namespaceDeclarations", xml_namespaceDeclarations, 0,0), + JS_FN("nodeKind", xml_nodeKind, 0,0), + JS_FN("normalize", xml_normalize, 0,0), + JS_FN(js_xml_parent_str, xml_parent, 0,0), + JS_FN("processingInstructions",xml_processingInstructions,1,0), + JS_FN("prependChild", xml_prependChild, 1,0), + JS_FN("propertyIsEnumerable", xml_propertyIsEnumerable, 1,0), + JS_FN("removeNamespace", xml_removeNamespace, 1,0), + JS_FN("replace", xml_replace, 2,0), + JS_FN("setChildren", xml_setChildren, 1,0), + JS_FN("setLocalName", xml_setLocalName, 1,0), + JS_FN("setName", xml_setName, 1,0), + JS_FN("setNamespace", xml_setNamespace, 1,0), + JS_FN(js_text_str, xml_text, 0,0), + JS_FN(js_toSource_str, xml_toSource, 0,0), + JS_FN(js_toString_str, xml_toString, 0,0), + JS_FN(js_toXMLString_str, xml_toXMLString, 0,0), + JS_FN(js_valueOf_str, xml_valueOf, 0,0), + JS_FS_END +}; + +static JSBool +CopyXMLSettings(JSContext *cx, JSObject *from, JSObject *to) +{ + int i; + const char *name; + jsval v; + + for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) { + name = xml_static_props[i].name; + if (!JS_GetProperty(cx, from, name, &v)) + return JS_FALSE; + if (JSVAL_IS_BOOLEAN(v) && !JS_SetProperty(cx, to, name, &v)) + return JS_FALSE; + } + + name = xml_static_props[i].name; + if (!JS_GetProperty(cx, from, name, &v)) + return JS_FALSE; + if (JSVAL_IS_NUMBER(v) && !JS_SetProperty(cx, to, name, &v)) + return JS_FALSE; + return JS_TRUE; +} + +static JSBool +SetDefaultXMLSettings(JSContext *cx, JSObject *obj) +{ + int i; + jsval v; + + for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) { + v = JSVAL_TRUE; + if (!JS_SetProperty(cx, obj, xml_static_props[i].name, &v)) + return JS_FALSE; + } + v = INT_TO_JSVAL(2); + return JS_SetProperty(cx, obj, xml_static_props[i].name, &v); +} + +static JSBool +xml_settings(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *settings; + JSObject *obj; + + settings = JS_NewObject(cx, NULL, NULL, NULL); + if (!settings) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(settings); + obj = JS_THIS_OBJECT(cx, vp); + return obj && CopyXMLSettings(cx, obj, settings); +} + +static JSBool +xml_setSettings(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *obj, *settings; + jsval v; + JSBool ok; + + obj = JS_THIS_OBJECT(cx, vp); + if (!obj) + return JS_FALSE; + v = (argc == 0) ? JSVAL_VOID : vp[2]; + if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) { + cx->xmlSettingFlags = 0; + ok = SetDefaultXMLSettings(cx, obj); + } else { + if (JSVAL_IS_PRIMITIVE(v)) + return JS_TRUE; + settings = JSVAL_TO_OBJECT(v); + cx->xmlSettingFlags = 0; + ok = CopyXMLSettings(cx, settings, obj); + } + if (ok) + cx->xmlSettingFlags |= XSF_CACHE_VALID; + return ok; +} + +static JSBool +xml_defaultSettings(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *settings; + + settings = JS_NewObject(cx, NULL, NULL, NULL); + if (!settings) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(settings); + return SetDefaultXMLSettings(cx, settings); +} + +static JSFunctionSpec xml_static_methods[] = { + JS_FN("settings", xml_settings, 0,0), + JS_FN("setSettings", xml_setSettings, 1,0), + JS_FN("defaultSettings", xml_defaultSettings, 0,0), + JS_FS_END +}; + +static JSBool +XML(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval v; + JSXML *xml, *copy; + JSObject *xobj, *vobj; + JSClass *clasp; + + v = argv[0]; + if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) + v = STRING_TO_JSVAL(cx->runtime->emptyString); + + xobj = ToXML(cx, v); + if (!xobj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(xobj); + xml = (JSXML *) xobj->getPrivate(); + + if (JS_IsConstructing(cx) && !JSVAL_IS_PRIMITIVE(v)) { + vobj = JSVAL_TO_OBJECT(v); + clasp = OBJ_GET_CLASS(cx, vobj); + if (clasp == &js_XMLClass || + (clasp->flags & JSCLASS_DOCUMENT_OBSERVER)) { + /* No need to lock obj, it's newly constructed and thread local. */ + copy = DeepCopy(cx, xml, obj, 0); + if (!copy) + return JS_FALSE; + JS_ASSERT(copy->object == obj); + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; + } + } + return JS_TRUE; +} + +static JSBool +XMLList(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval v; + JSObject *vobj, *listobj; + JSXML *xml, *list; + + v = argv[0]; + if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) + v = STRING_TO_JSVAL(cx->runtime->emptyString); + + if (JS_IsConstructing(cx) && !JSVAL_IS_PRIMITIVE(v)) { + vobj = JSVAL_TO_OBJECT(v); + if (OBJECT_IS_XML(cx, vobj)) { + xml = (JSXML *) vobj->getPrivate(); + if (xml->xml_class == JSXML_CLASS_LIST) { + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(listobj); + + list = (JSXML *) listobj->getPrivate(); + if (!Append(cx, list, xml)) + return JS_FALSE; + return JS_TRUE; + } + } + } + + /* Toggle on XML support since the script has explicitly requested it. */ + listobj = ToXMLList(cx, v); + if (!listobj) + return JS_FALSE; + + *rval = OBJECT_TO_JSVAL(listobj); + return JS_TRUE; +} + +#ifdef DEBUG_notme +JSCList xml_leaks = JS_INIT_STATIC_CLIST(&xml_leaks); +uint32 xml_serial; +#endif + +JSXML * +js_NewXML(JSContext *cx, JSXMLClass xml_class) +{ + JSXML *xml = js_NewGCXML(cx); + if (!xml) + return NULL; + + xml->object = NULL; + xml->domnode = NULL; + xml->parent = NULL; + xml->name = NULL; + xml->xml_class = xml_class; + xml->xml_flags = 0; + if (JSXML_CLASS_HAS_VALUE(xml_class)) { + xml->xml_value = cx->runtime->emptyString; + } else { + XMLArrayInit(cx, &xml->xml_kids, 0); + if (xml_class == JSXML_CLASS_LIST) { + xml->xml_target = NULL; + xml->xml_targetprop = NULL; + } else { + XMLArrayInit(cx, &xml->xml_namespaces, 0); + XMLArrayInit(cx, &xml->xml_attrs, 0); + } + } + +#ifdef DEBUG_notme + JS_APPEND_LINK(&xml->links, &xml_leaks); + xml->serial = xml_serial++; +#endif + METER(xml_stats.xml); + return xml; +} + +void +js_TraceXML(JSTracer *trc, JSXML *xml) +{ + if (xml->object) + JS_CALL_OBJECT_TRACER(trc, xml->object, "object"); + if (xml->name) + JS_CALL_OBJECT_TRACER(trc, xml->name, "name"); + if (xml->parent) + JS_CALL_TRACER(trc, xml->parent, JSTRACE_XML, "xml_parent"); + + if (JSXML_HAS_VALUE(xml)) { + if (xml->xml_value) + JS_CALL_STRING_TRACER(trc, xml->xml_value, "value"); + return; + } + + xml_trace_vector(trc, + (JSXML **) xml->xml_kids.vector, + xml->xml_kids.length); + XMLArrayCursorTrace(trc, xml->xml_kids.cursors); + if (IS_GC_MARKING_TRACER(trc)) + XMLArrayTrim(&xml->xml_kids); + + if (xml->xml_class == JSXML_CLASS_LIST) { + if (xml->xml_target) + JS_CALL_TRACER(trc, xml->xml_target, JSTRACE_XML, "target"); + if (xml->xml_targetprop) + JS_CALL_OBJECT_TRACER(trc, xml->xml_targetprop, "targetprop"); + } else { + TraceObjectVector(trc, + (JSObject **) xml->xml_namespaces.vector, + xml->xml_namespaces.length); + XMLArrayCursorTrace(trc, xml->xml_namespaces.cursors); + if (IS_GC_MARKING_TRACER(trc)) + XMLArrayTrim(&xml->xml_namespaces); + + xml_trace_vector(trc, + (JSXML **) xml->xml_attrs.vector, + xml->xml_attrs.length); + XMLArrayCursorTrace(trc, xml->xml_attrs.cursors); + if (IS_GC_MARKING_TRACER(trc)) + XMLArrayTrim(&xml->xml_attrs); + } +} + +void +js_FinalizeXML(JSContext *cx, JSXML *xml) +{ + if (JSXML_HAS_KIDS(xml)) { + XMLArrayFinish(cx, &xml->xml_kids); + if (xml->xml_class == JSXML_CLASS_ELEMENT) { + XMLArrayFinish(cx, &xml->xml_namespaces); + XMLArrayFinish(cx, &xml->xml_attrs); + } + } + +#ifdef DEBUG_notme + JS_REMOVE_LINK(&xml->links); +#endif +} + +JSObject * +js_ParseNodeToXMLObject(JSCompiler *jsc, JSParseNode *pn) +{ + jsval nsval; + JSObject *ns; + JSXMLArray nsarray; + JSXML *xml; + + if (!js_GetDefaultXMLNamespace(jsc->context, &nsval)) + return NULL; + JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval)); + ns = JSVAL_TO_OBJECT(nsval); + + if (!XMLArrayInit(jsc->context, &nsarray, 1)) + return NULL; + + XMLARRAY_APPEND(jsc->context, &nsarray, ns); + xml = ParseNodeToXML(jsc, pn, &nsarray, XSF_PRECOMPILED_ROOT); + XMLArrayFinish(jsc->context, &nsarray); + if (!xml) + return NULL; + + return xml->object; +} + +JSObject * +js_NewXMLObject(JSContext *cx, JSXMLClass xml_class) +{ + JSXML *xml; + JSObject *obj; + JSTempValueRooter tvr; + + xml = js_NewXML(cx, xml_class); + if (!xml) + return NULL; + JS_PUSH_TEMP_ROOT_XML(cx, xml, &tvr); + obj = js_GetXMLObject(cx, xml); + JS_POP_TEMP_ROOT(cx, &tvr); + return obj; +} + +static JSObject * +NewXMLObject(JSContext *cx, JSXML *xml) +{ + JSObject *obj; + + obj = js_NewObject(cx, &js_XMLClass, NULL, NULL); + if (!obj) + return NULL; + obj->setPrivate(xml); + METER(xml_stats.xmlobj); + return obj; +} + +JSObject * +js_GetXMLObject(JSContext *cx, JSXML *xml) +{ + JSObject *obj; + + obj = xml->object; + if (obj) { + JS_ASSERT(obj->getPrivate() == xml); + return obj; + } + + /* + * A JSXML cannot be shared among threads unless it has an object. + * A JSXML cannot be given an object unless: + * (a) it has no parent; or + * (b) its parent has no object (therefore is thread-private); or + * (c) its parent's object is locked. + * + * Once given an object, a JSXML is immutable. + */ + JS_ASSERT(!xml->parent || + !xml->parent->object || + JS_IS_OBJ_LOCKED(cx, xml->parent->object)); + + obj = NewXMLObject(cx, xml); + if (!obj) + return NULL; + xml->object = obj; + return obj; +} + +JSObject * +js_InitNamespaceClass(JSContext *cx, JSObject *obj) +{ + return JS_InitClass(cx, obj, NULL, &js_NamespaceClass.base, Namespace, 2, + namespace_props, namespace_methods, NULL, NULL); +} + +JSObject * +js_InitQNameClass(JSContext *cx, JSObject *obj) +{ + return JS_InitClass(cx, obj, NULL, &js_QNameClass.base, QName, 2, + qname_props, qname_methods, NULL, NULL); +} + +JSObject * +js_InitAttributeNameClass(JSContext *cx, JSObject *obj) +{ + return JS_InitClass(cx, obj, NULL, &js_AttributeNameClass, AttributeName, 2, + qname_props, qname_methods, NULL, NULL); +} + +JSObject * +js_InitAnyNameClass(JSContext *cx, JSObject *obj) +{ + jsval v; + + if (!js_GetAnyName(cx, &v)) + return NULL; + return JSVAL_TO_OBJECT(v); +} + +JSObject * +js_InitXMLClass(JSContext *cx, JSObject *obj) +{ + JSObject *proto, *pobj; + JSFunction *fun; + JSXML *xml; + JSProperty *prop; + JSScopeProperty *sprop; + jsval cval, vp[3]; + + /* Define the isXMLName function. */ + if (!JS_DefineFunction(cx, obj, js_isXMLName_str, xml_isXMLName, 1, 0)) + return NULL; + + /* Define the XML class constructor and prototype. */ + proto = JS_InitClass(cx, obj, NULL, &js_XMLClass, XML, 1, + NULL, xml_methods, + xml_static_props, xml_static_methods); + if (!proto) + return NULL; + + xml = js_NewXML(cx, JSXML_CLASS_TEXT); + if (!xml) + return NULL; + proto->setPrivate(xml); + xml->object = proto; + METER(xml_stats.xmlobj); + + /* + * Prepare to set default settings on the XML constructor we just made. + * NB: We can't use JS_GetConstructor, because it calls + * JSObject::getProperty, which is xml_getProperty, which creates a new + * XMLList every time! We must instead call js_LookupProperty directly. + */ + if (!js_LookupProperty(cx, proto, + ATOM_TO_JSID(cx->runtime->atomState.constructorAtom), + &pobj, &prop)) { + return NULL; + } + JS_ASSERT(prop); + sprop = (JSScopeProperty *) prop; + JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))); + cval = OBJ_GET_SLOT(cx, pobj, sprop->slot); + pobj->dropProperty(cx, prop); + JS_ASSERT(VALUE_IS_FUNCTION(cx, cval)); + + /* Set default settings. */ + vp[0] = JSVAL_NULL; + vp[1] = cval; + vp[2] = JSVAL_VOID; + if (!xml_setSettings(cx, 1, vp)) + return NULL; + + /* Define the XMLList function and give it the same prototype as XML. */ + fun = JS_DefineFunction(cx, obj, js_XMLList_str, XMLList, 1, 0); + if (!fun) + return NULL; + if (!js_SetClassPrototype(cx, FUN_OBJECT(fun), proto, + JSPROP_READONLY | JSPROP_PERMANENT)) { + return NULL; + } + return proto; +} + +JSObject * +js_InitXMLClasses(JSContext *cx, JSObject *obj) +{ + if (!js_InitNamespaceClass(cx, obj)) + return NULL; + if (!js_InitQNameClass(cx, obj)) + return NULL; + if (!js_InitAttributeNameClass(cx, obj)) + return NULL; + if (!js_InitAnyNameClass(cx, obj)) + return NULL; + return js_InitXMLClass(cx, obj); +} + +JSBool +js_GetFunctionNamespace(JSContext *cx, jsval *vp) +{ + JSRuntime *rt; + JSObject *obj; + JSAtom *atom; + JSString *prefix, *uri; + + /* An invalid URI, for internal use only, guaranteed not to collide. */ + static const char anti_uri[] = "@mozilla.org/js/function"; + + /* Optimize by avoiding JS_LOCK_GC(rt) for the common case. */ + rt = cx->runtime; + obj = rt->functionNamespaceObject; + if (!obj) { + JS_LOCK_GC(rt); + obj = rt->functionNamespaceObject; + if (!obj) { + JS_UNLOCK_GC(rt); + + /* + * Note that any race to atomize anti_uri here is resolved by + * the atom table code, such that at most one atom for anti_uri + * is created. We store in rt->atomState.lazy unconditionally, + * since we are guaranteed to overwrite either null or the same + * atom pointer. + */ + atom = js_Atomize(cx, anti_uri, sizeof anti_uri - 1, ATOM_PINNED); + if (!atom) + return JS_FALSE; + rt->atomState.lazy.functionNamespaceURIAtom = atom; + + prefix = ATOM_TO_STRING(rt->atomState.typeAtoms[JSTYPE_FUNCTION]); + uri = ATOM_TO_STRING(atom); + obj = NewXMLNamespace(cx, prefix, uri, JS_FALSE); + if (!obj) + return JS_FALSE; + + /* + * Avoid entraining any in-scope Object.prototype. The loss of + * Namespace.prototype is not detectable, as there is no way to + * refer to this instance in scripts. When used to qualify method + * names, its prefix and uri references are copied to the QName. + */ + OBJ_CLEAR_PROTO(cx, obj); + OBJ_CLEAR_PARENT(cx, obj); + + JS_LOCK_GC(rt); + if (!rt->functionNamespaceObject) + rt->functionNamespaceObject = obj; + else + obj = rt->functionNamespaceObject; + } + JS_UNLOCK_GC(rt); + } + *vp = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +/* + * Note the asymmetry between js_GetDefaultXMLNamespace and js_SetDefaultXML- + * Namespace. Get searches fp->scopeChain for JS_DEFAULT_XML_NAMESPACE_ID, + * while Set sets JS_DEFAULT_XML_NAMESPACE_ID in fp->varobj. There's no + * requirement that fp->varobj lie directly on fp->scopeChain, although it + * should be reachable using the prototype chain from a scope object (cf. + * JSOPTION_VAROBJFIX in jsapi.h). + * + * If Get can't find JS_DEFAULT_XML_NAMESPACE_ID along the scope chain, it + * creates a default namespace via 'new Namespace()'. In contrast, Set uses + * its v argument as the uri of a new Namespace, with "" as the prefix. See + * ECMA-357 12.1 and 12.1.1. Note that if Set is called with a Namespace n, + * the default XML namespace will be set to ("", n.uri). So the uri string + * is really the only usefully stored value of the default namespace. + */ +JSBool +js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp) +{ + JSStackFrame *fp; + JSObject *ns, *obj, *tmp; + jsval v; + + fp = js_GetTopStackFrame(cx); + + obj = NULL; + for (tmp = fp->scopeChain; tmp; tmp = OBJ_GET_PARENT(cx, tmp)) { + JSClass *clasp = OBJ_GET_CLASS(cx, tmp); + if (clasp == &js_BlockClass || clasp == &js_WithClass) + continue; + if (!tmp->getProperty(cx, JS_DEFAULT_XML_NAMESPACE_ID, &v)) + return JS_FALSE; + if (!JSVAL_IS_PRIMITIVE(v)) { + *vp = v; + return JS_TRUE; + } + obj = tmp; + } + + ns = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, obj, 0, NULL); + if (!ns) + return JS_FALSE; + v = OBJECT_TO_JSVAL(ns); + if (!obj->defineProperty(cx, JS_DEFAULT_XML_NAMESPACE_ID, v, JS_PropertyStub, JS_PropertyStub, + JSPROP_PERMANENT)) { + return JS_FALSE; + } + *vp = v; + return JS_TRUE; +} + +JSBool +js_SetDefaultXMLNamespace(JSContext *cx, jsval v) +{ + jsval argv[2]; + JSObject *ns, *varobj; + JSStackFrame *fp; + + argv[0] = STRING_TO_JSVAL(cx->runtime->emptyString); + argv[1] = v; + ns = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, NULL, 2, argv); + if (!ns) + return JS_FALSE; + v = OBJECT_TO_JSVAL(ns); + + fp = js_GetTopStackFrame(cx); + varobj = fp->varobj; + if (!varobj->defineProperty(cx, JS_DEFAULT_XML_NAMESPACE_ID, v, + JS_PropertyStub, JS_PropertyStub, JSPROP_PERMANENT)) { + return JS_FALSE; + } + return JS_TRUE; +} + +JSBool +js_ToAttributeName(JSContext *cx, jsval *vp) +{ + JSObject *qn; + + qn = ToAttributeName(cx, *vp); + if (!qn) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(qn); + return JS_TRUE; +} + +JSString * +js_EscapeAttributeValue(JSContext *cx, JSString *str, JSBool quote) +{ + JSCharBuffer cb(cx); + return EscapeAttributeValue(cx, cb, str, quote); +} + +JSString * +js_AddAttributePart(JSContext *cx, JSBool isName, JSString *str, JSString *str2) +{ + size_t len, len2, newlen; + jschar *chars; + const jschar *chars2; + + str->getCharsAndLength(const_cast(chars), len); + if (!str->isMutable()) { + str = js_NewStringCopyN(cx, chars, len); + if (!str) + return NULL; + chars = str->flatChars(); + } else { + /* + * Reallocating str (because we know it has no other references) + * requires purging any deflated string cached for it. + */ + js_PurgeDeflatedStringCache(cx->runtime, str); + } + + str2->getCharsAndLength(chars2, len2); + newlen = (isName) ? len + 1 + len2 : len + 2 + len2 + 1; + chars = (jschar *) cx->realloc(chars, (newlen+1) * sizeof(jschar)); + if (!chars) + return NULL; + + str->initFlat(chars, newlen); + chars += len; + if (isName) { + *chars++ = ' '; + js_strncpy(chars, chars2, len2); + chars += len2; + } else { + *chars++ = '='; + *chars++ = '"'; + js_strncpy(chars, chars2, len2); + chars += len2; + *chars++ = '"'; + } + *chars = 0; + return str; +} + +JSString * +js_EscapeElementValue(JSContext *cx, JSString *str) +{ + JSCharBuffer cb(cx); + return EscapeElementValue(cx, cb, str); +} + +JSString * +js_ValueToXMLString(JSContext *cx, jsval v) +{ + return ToXMLString(cx, v, 0); +} + +static JSBool +anyname_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + *rval = ATOM_KEY(cx->runtime->atomState.starAtom); + return JS_TRUE; +} + +JSBool +js_GetAnyName(JSContext *cx, jsval *vp) +{ + JSRuntime *rt; + JSObject *obj; + JSBool ok; + + /* Optimize by avoiding JS_LOCK_GC(rt) for the common case. */ + rt = cx->runtime; + obj = rt->anynameObject; + if (!obj) { + JS_LOCK_GC(rt); + obj = rt->anynameObject; + if (!obj) { + JS_UNLOCK_GC(rt); + + /* + * Protect multiple newborns created below, in the do-while(0) + * loop used to ensure that we leave this local root scope. + */ + ok = js_EnterLocalRootScope(cx); + if (!ok) + return JS_FALSE; + + do { + obj = js_NewObjectWithGivenProto(cx, &js_AnyNameClass, NULL, + NULL); + if (!obj) { + ok = JS_FALSE; + break; + } + InitXMLQName(obj, rt->emptyString, rt->emptyString, + ATOM_TO_STRING(rt->atomState.starAtom)); + METER(xml_stats.qname); + + /* + * Avoid entraining any Object.prototype found via cx's scope + * chain or global object. This loses the default toString, + * but no big deal: we want to customize toString anyway for + * clearer diagnostics. + */ + if (!JS_DefineFunction(cx, obj, js_toString_str, + anyname_toString, 0, 0)) { + ok = JS_FALSE; + break; + } + JS_ASSERT(!OBJ_GET_PROTO(cx, obj)); + JS_ASSERT(!OBJ_GET_PARENT(cx, obj)); + } while (0); + + js_LeaveLocalRootScopeWithResult(cx, OBJECT_TO_JSVAL(obj)); + if (!ok) + return JS_FALSE; + + JS_LOCK_GC(rt); + if (!rt->anynameObject) + rt->anynameObject = obj; + else + obj = rt->anynameObject; + } + JS_UNLOCK_GC(rt); + } + *vp = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +JSBool +js_FindXMLProperty(JSContext *cx, jsval nameval, JSObject **objp, jsid *idp) +{ + JSObject *nameobj; + jsval v; + JSObject *qn; + jsid funid; + JSObject *obj, *target, *proto, *pobj; + JSXML *xml; + JSBool found; + JSProperty *prop; + const char *printable; + + JS_ASSERT(!JSVAL_IS_PRIMITIVE(nameval)); + nameobj = JSVAL_TO_OBJECT(nameval); + if (OBJ_GET_CLASS(cx, nameobj) == &js_AnyNameClass) { + v = STRING_TO_JSVAL(ATOM_TO_STRING(cx->runtime->atomState.starAtom)); + nameobj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 1, + &v); + if (!nameobj) + return JS_FALSE; + } else { + JS_ASSERT(OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass || + OBJ_GET_CLASS(cx, nameobj) == &js_QNameClass.base); + } + + qn = nameobj; + if (!IsFunctionQName(cx, qn, &funid)) + return JS_FALSE; + + obj = js_GetTopStackFrame(cx)->scopeChain; + do { + /* Skip any With object that can wrap XML. */ + target = obj; + while (OBJ_GET_CLASS(cx, target) == &js_WithClass) { + proto = OBJ_GET_PROTO(cx, target); + if (!proto) + break; + target = proto; + } + + if (OBJECT_IS_XML(cx, target)) { + if (funid == 0) { + xml = (JSXML *) target->getPrivate(); + found = HasNamedProperty(xml, qn); + } else { + if (!HasFunctionProperty(cx, target, funid, &found)) + return JS_FALSE; + } + if (found) { + *idp = OBJECT_TO_JSID(nameobj); + *objp = target; + return JS_TRUE; + } + } else if (funid != 0) { + if (!target->lookupProperty(cx, funid, &pobj, &prop)) + return JS_FALSE; + if (prop) { + pobj->dropProperty(cx, prop); + *idp = funid; + *objp = target; + return JS_TRUE; + } + } + } while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL); + + printable = js_ValueToPrintableString(cx, OBJECT_TO_JSVAL(nameobj)); + if (printable) { + JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, + js_GetErrorMessage, NULL, + JSMSG_UNDEFINED_XML_NAME, printable); + } + return JS_FALSE; +} + +static JSBool +GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + JSObject *target; + JSXML *xml; + JSTempValueRooter tvr; + JSBool ok; + + JS_ASSERT(OBJECT_IS_XML(cx, obj)); + + MUST_FLOW_THROUGH("out"); + JS_PUSH_TEMP_ROOT_OBJECT(cx, NULL, &tvr); + + /* + * See comments before xml_lookupProperty about the need for the proto + * chain lookup. + */ + target = obj; + for (;;) { + ok = js_GetProperty(cx, target, id, vp); + if (!ok) + goto out; + if (VALUE_IS_FUNCTION(cx, *vp)) { + ok = JS_TRUE; + goto out; + } + target = OBJ_GET_PROTO(cx, target); + if (target == NULL) + break; + tvr.u.object = target; + } + + xml = (JSXML *) obj->getPrivate(); + if (HasSimpleContent(xml)) { + /* Search in String.prototype to implement 11.2.2.1 Step 3(f). */ + ok = js_GetClassPrototype(cx, NULL, INT_TO_JSID(JSProto_String), + &tvr.u.object); + if (!ok) + goto out; + JS_ASSERT(tvr.u.object); + ok = tvr.u.object->getProperty(cx, id, vp); + } + + out: + JS_POP_TEMP_ROOT(cx, &tvr); + return ok; +} + +static JSXML * +GetPrivate(JSContext *cx, JSObject *obj, const char *method) +{ + JSXML *xml; + + xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL); + if (!xml) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_INCOMPATIBLE_METHOD, + js_XML_str, method, OBJ_GET_CLASS(cx, obj)->name); + } + return xml; +} + +JSBool +js_GetXMLDescendants(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSXML *xml, *list; + + xml = GetPrivate(cx, obj, "descendants internal method"); + if (!xml) + return JS_FALSE; + + list = Descendants(cx, xml, id); + if (!list) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(list->object); + return JS_TRUE; +} + +JSBool +js_DeleteXMLListElements(JSContext *cx, JSObject *listobj) +{ + JSXML *list; + uint32 n; + + list = (JSXML *) listobj->getPrivate(); + for (n = list->xml_kids.length; n != 0; --n) + DeleteListElement(cx, list, 0); + + return JS_TRUE; +} + +struct JSXMLFilter +{ + JSXML *list; + JSXML *result; + JSXML *kid; + JSXMLArrayCursor cursor; + + JSXMLFilter(JSXML *list, JSXMLArray *array) + : list(list), result(NULL), kid(NULL), cursor(array) {} + + ~JSXMLFilter() {} +}; + +static void +xmlfilter_trace(JSTracer *trc, JSObject *obj) +{ + JSXMLFilter *filter = (JSXMLFilter *) obj->getPrivate(); + if (!filter) + return; + + JS_ASSERT(filter->list); + JS_CALL_TRACER(trc, filter->list, JSTRACE_XML, "list"); + if (filter->result) + JS_CALL_TRACER(trc, filter->result, JSTRACE_XML, "result"); + if (filter->kid) + JS_CALL_TRACER(trc, filter->kid, JSTRACE_XML, "kid"); + + /* + * We do not need to trace the cursor as that would be done when + * tracing the filter->list. + */ +} + +static void +xmlfilter_finalize(JSContext *cx, JSObject *obj) +{ + JSXMLFilter *filter = (JSXMLFilter *) obj->getPrivate(); + if (!filter) + return; + + cx->destroy(filter); +} + +JSClass js_XMLFilterClass = { + "XMLFilter", + JSCLASS_HAS_PRIVATE | JSCLASS_IS_ANONYMOUS | JSCLASS_MARK_IS_TRACE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, xmlfilter_finalize, + NULL, NULL, NULL, NULL, + NULL, NULL, JS_CLASS_TRACE(xmlfilter_trace), NULL +}; + +JSBool +js_StepXMLListFilter(JSContext *cx, JSBool initialized) +{ + jsval *sp; + JSObject *obj, *filterobj, *resobj, *kidobj; + JSXML *xml, *list; + JSXMLFilter *filter; + + sp = js_GetTopStackFrame(cx)->regs->sp; + if (!initialized) { + /* + * We haven't iterated yet, so initialize the filter based on the + * value stored in sp[-2]. + */ + if (!VALUE_IS_XML(cx, sp[-2])) { + js_ReportValueError(cx, JSMSG_NON_XML_FILTER, -2, sp[-2], NULL); + return JS_FALSE; + } + obj = JSVAL_TO_OBJECT(sp[-2]); + xml = (JSXML *) obj->getPrivate(); + + if (xml->xml_class == JSXML_CLASS_LIST) { + list = xml; + } else { + obj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!obj) + return JS_FALSE; + + /* + * Root just-created obj. sp[-2] cannot be used yet for rooting + * as it may be the only root holding xml. + */ + sp[-1] = OBJECT_TO_JSVAL(obj); + list = (JSXML *) obj->getPrivate(); + if (!Append(cx, list, xml)) + return JS_FALSE; + } + + filterobj = js_NewObjectWithGivenProto(cx, &js_XMLFilterClass, + NULL, NULL); + if (!filterobj) + return JS_FALSE; + + /* + * Init all filter fields before setPrivate exposes it to + * xmlfilter_trace or xmlfilter_finalize. + */ + filter = cx->create(list, &list->xml_kids); + if (!filter) + return JS_FALSE; + filterobj->setPrivate(filter); + + /* Store filterobj to use in the later iterations. */ + sp[-2] = OBJECT_TO_JSVAL(filterobj); + + resobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!resobj) + return JS_FALSE; + + /* This also roots resobj. */ + filter->result = (JSXML *) resobj->getPrivate(); + } else { + /* We have iterated at least once. */ + JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp[-2])); + JS_ASSERT(OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(sp[-2])) == + &js_XMLFilterClass); + filter = (JSXMLFilter *) JSVAL_TO_OBJECT(sp[-2])->getPrivate(); + JS_ASSERT(filter->kid); + + /* Check if the filter expression wants to append the element. */ + if (js_ValueToBoolean(sp[-1]) && + !Append(cx, filter->result, filter->kid)) { + return JS_FALSE; + } + } + + /* Do the iteration. */ + filter->kid = (JSXML *) filter->cursor.getNext(); + if (!filter->kid) { + /* + * Do not defer finishing the cursor until the next GC cycle to avoid + * accumulation of dead cursors associated with filter->list. + */ + filter->cursor.disconnect(); + JS_ASSERT(filter->result->object); + sp[-2] = OBJECT_TO_JSVAL(filter->result->object); + kidobj = NULL; + } else { + kidobj = js_GetXMLObject(cx, filter->kid); + if (!kidobj) + return JS_FALSE; + } + + /* Null as kidobj at sp[-1] signals filter termination. */ + sp[-1] = OBJECT_TO_JSVAL(kidobj); + return JS_TRUE; +} + +JSObject * +js_ValueToXMLObject(JSContext *cx, jsval v) +{ + return ToXML(cx, v); +} + +JSObject * +js_ValueToXMLListObject(JSContext *cx, jsval v) +{ + return ToXMLList(cx, v); +} + +JSObject * +js_CloneXMLObject(JSContext *cx, JSObject *obj) +{ + uintN flags; + JSXML *xml; + + if (!GetXMLSettingFlags(cx, &flags)) + return NULL; + xml = (JSXML *) obj->getPrivate(); + if (flags & (XSF_IGNORE_COMMENTS | + XSF_IGNORE_PROCESSING_INSTRUCTIONS | + XSF_IGNORE_WHITESPACE)) { + xml = DeepCopy(cx, xml, NULL, flags); + if (!xml) + return NULL; + return xml->object; + } + return NewXMLObject(cx, xml); +} + +JSObject * +js_NewXMLSpecialObject(JSContext *cx, JSXMLClass xml_class, JSString *name, + JSString *value) +{ + uintN flags; + JSObject *obj; + JSXML *xml; + JSObject *qn; + + if (!GetXMLSettingFlags(cx, &flags)) + return NULL; + + if ((xml_class == JSXML_CLASS_COMMENT && + (flags & XSF_IGNORE_COMMENTS)) || + (xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION && + (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS))) { + return js_NewXMLObject(cx, JSXML_CLASS_TEXT); + } + + obj = js_NewXMLObject(cx, xml_class); + if (!obj) + return NULL; + xml = (JSXML *) obj->getPrivate(); + if (name) { + qn = NewXMLQName(cx, cx->runtime->emptyString, NULL, name); + if (!qn) + return NULL; + xml->name = qn; + } + xml->xml_value = value; + return obj; +} + +JSString * +js_MakeXMLCDATAString(JSContext *cx, JSString *str) +{ + JSCharBuffer cb(cx); + return MakeXMLCDATAString(cx, cb, str); +} + +JSString * +js_MakeXMLCommentString(JSContext *cx, JSString *str) +{ + JSCharBuffer cb(cx); + return MakeXMLCommentString(cx, cb, str); +} + +JSString * +js_MakeXMLPIString(JSContext *cx, JSString *name, JSString *str) +{ + JSCharBuffer cb(cx); + return MakeXMLPIString(cx, cb, name, str); +} + +#endif /* JS_HAS_XML_SUPPORT */ diff --git a/ape-server/deps/js/src/jsxml.h b/ape-server/deps/js/src/jsxml.h new file mode 100755 index 0000000..77392cd --- /dev/null +++ b/ape-server/deps/js/src/jsxml.h @@ -0,0 +1,296 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is SpiderMonkey E4X code, released August, 2004. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsxml_h___ +#define jsxml_h___ + +#include "jspubtd.h" + +JS_BEGIN_EXTERN_C + +extern const char js_AnyName_str[]; +extern const char js_AttributeName_str[]; +extern const char js_isXMLName_str[]; +extern const char js_XMLList_str[]; + +extern const char js_amp_entity_str[]; +extern const char js_gt_entity_str[]; +extern const char js_lt_entity_str[]; +extern const char js_quot_entity_str[]; + +typedef JSBool +(* JSIdentityOp)(const void *a, const void *b); + +struct JSXMLArray { + uint32 length; + uint32 capacity; + void **vector; + JSXMLArrayCursor *cursors; +}; + +#define JSXML_PRESET_CAPACITY JS_BIT(31) +#define JSXML_CAPACITY_MASK JS_BITMASK(31) +#define JSXML_CAPACITY(array) ((array)->capacity & JSXML_CAPACITY_MASK) + +/* + * NB: don't reorder this enum without changing all array initializers that + * depend on it in jsxml.c. + */ +typedef enum JSXMLClass { + JSXML_CLASS_LIST, + JSXML_CLASS_ELEMENT, + JSXML_CLASS_ATTRIBUTE, + JSXML_CLASS_PROCESSING_INSTRUCTION, + JSXML_CLASS_TEXT, + JSXML_CLASS_COMMENT, + JSXML_CLASS_LIMIT +} JSXMLClass; + +#define JSXML_CLASS_HAS_KIDS(class_) ((class_) < JSXML_CLASS_ATTRIBUTE) +#define JSXML_CLASS_HAS_VALUE(class_) ((class_) >= JSXML_CLASS_ATTRIBUTE) +#define JSXML_CLASS_HAS_NAME(class_) \ + ((uintN)((class_) - JSXML_CLASS_ELEMENT) <= \ + (uintN)(JSXML_CLASS_PROCESSING_INSTRUCTION - JSXML_CLASS_ELEMENT)) + +#ifdef DEBUG_notme +#include "jsclist.h" +#endif + +typedef struct JSXMLListVar { + JSXMLArray kids; /* NB: must come first */ + JSXML *target; + JSObject *targetprop; +} JSXMLListVar; + +typedef struct JSXMLElemVar { + JSXMLArray kids; /* NB: must come first */ + JSXMLArray namespaces; + JSXMLArray attrs; +} JSXMLElemVar; + +struct JSXML { +#ifdef DEBUG_notme + JSCList links; + uint32 serial; +#endif + JSObject *object; + void *domnode; /* DOM node if mapped info item */ + JSXML *parent; + JSObject *name; + uint32 xml_class; /* discriminates u, below */ + uint32 xml_flags; /* flags, see below */ + union { + JSXMLListVar list; + JSXMLElemVar elem; + JSString *value; + } u; +}; + +JS_STATIC_ASSERT(sizeof(JSXML) % JSVAL_ALIGN == 0); + +/* union member shorthands */ +#define xml_kids u.list.kids +#define xml_target u.list.target +#define xml_targetprop u.list.targetprop +#define xml_namespaces u.elem.namespaces +#define xml_attrs u.elem.attrs +#define xml_value u.value + +/* xml_flags values */ +#define XMLF_WHITESPACE_TEXT 0x1 + +/* xml_class-testing macros */ +#define JSXML_HAS_KIDS(xml) JSXML_CLASS_HAS_KIDS((xml)->xml_class) +#define JSXML_HAS_VALUE(xml) JSXML_CLASS_HAS_VALUE((xml)->xml_class) +#define JSXML_HAS_NAME(xml) JSXML_CLASS_HAS_NAME((xml)->xml_class) +#define JSXML_LENGTH(xml) (JSXML_CLASS_HAS_KIDS((xml)->xml_class) \ + ? (xml)->xml_kids.length \ + : 0) + +extern JSXML * +js_NewXML(JSContext *cx, JSXMLClass xml_class); + +extern void +js_TraceXML(JSTracer *trc, JSXML *xml); + +extern void +js_FinalizeXML(JSContext *cx, JSXML *xml); + +extern JSObject * +js_ParseNodeToXMLObject(JSCompiler *jsc, JSParseNode *pn); + +extern JSObject * +js_NewXMLObject(JSContext *cx, JSXMLClass xml_class); + +extern JSObject * +js_GetXMLObject(JSContext *cx, JSXML *xml); + +extern JS_FRIEND_DATA(JSObjectOps) js_XMLObjectOps; +extern JS_FRIEND_DATA(JSClass) js_XMLClass; +extern JS_FRIEND_DATA(JSExtendedClass) js_NamespaceClass; +extern JS_FRIEND_DATA(JSExtendedClass) js_QNameClass; +extern JS_FRIEND_DATA(JSClass) js_AttributeNameClass; +extern JS_FRIEND_DATA(JSClass) js_AnyNameClass; +extern JSClass js_XMLFilterClass; + +/* + * Macros to test whether an object or a value is of type "xml" (per typeof). + * NB: jsobj.h must be included before any call to OBJECT_IS_XML, and jsapi.h + * and jsobj.h must be included before any call to VALUE_IS_XML. + */ +#define OBJECT_IS_XML(cx,obj) ((obj)->map->ops == &js_XMLObjectOps) +#define VALUE_IS_XML(cx,v) (!JSVAL_IS_PRIMITIVE(v) && \ + OBJECT_IS_XML(cx, JSVAL_TO_OBJECT(v))) + +extern JSObject * +js_InitNamespaceClass(JSContext *cx, JSObject *obj); + +extern JSObject * +js_InitQNameClass(JSContext *cx, JSObject *obj); + +extern JSObject * +js_InitAttributeNameClass(JSContext *cx, JSObject *obj); + +extern JSObject * +js_InitAnyNameClass(JSContext *cx, JSObject *obj); + +extern JSObject * +js_InitXMLClass(JSContext *cx, JSObject *obj); + +extern JSObject * +js_InitXMLClasses(JSContext *cx, JSObject *obj); + +extern JSBool +js_GetFunctionNamespace(JSContext *cx, jsval *vp); + +/* + * If obj is QName corresponding to function::name, set *funidp to name's id, + * otherwise set *funidp to 0. + */ +JSBool +js_IsFunctionQName(JSContext *cx, JSObject *obj, jsid *funidp); + +extern JSBool +js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp); + +extern JSBool +js_SetDefaultXMLNamespace(JSContext *cx, jsval v); + +/* + * Return true if v is a XML QName object, or if it converts to a string that + * contains a valid XML qualified name (one containing no :), false otherwise. + * NB: This function is an infallible predicate, it hides exceptions. + */ +extern JSBool +js_IsXMLName(JSContext *cx, jsval v); + +extern JSBool +js_ToAttributeName(JSContext *cx, jsval *vp); + +extern JSString * +js_EscapeAttributeValue(JSContext *cx, JSString *str, JSBool quote); + +extern JSString * +js_AddAttributePart(JSContext *cx, JSBool isName, JSString *str, + JSString *str2); + +extern JSString * +js_EscapeElementValue(JSContext *cx, JSString *str); + +extern JSString * +js_ValueToXMLString(JSContext *cx, jsval v); + +extern JSObject * +js_ConstructXMLQNameObject(JSContext *cx, jsval nsval, jsval lnval); + +extern JSBool +js_GetAnyName(JSContext *cx, jsval *vp); + +/* + * Note: nameval must be either QName, AttributeName, or AnyName. + */ +extern JSBool +js_FindXMLProperty(JSContext *cx, jsval nameval, JSObject **objp, jsid *idp); + +extern JSBool +js_GetXMLMethod(JSContext *cx, JSObject *obj, jsid id, jsval *vp); + +extern JSBool +js_GetXMLDescendants(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + +extern JSBool +js_DeleteXMLListElements(JSContext *cx, JSObject *listobj); + +extern JSBool +js_StepXMLListFilter(JSContext *cx, JSBool initialized); + +extern JSObject * +js_ValueToXMLObject(JSContext *cx, jsval v); + +extern JSObject * +js_ValueToXMLListObject(JSContext *cx, jsval v); + +extern JSObject * +js_CloneXMLObject(JSContext *cx, JSObject *obj); + +extern JSObject * +js_NewXMLSpecialObject(JSContext *cx, JSXMLClass xml_class, JSString *name, + JSString *value); + +extern JSString * +js_MakeXMLCDATAString(JSContext *cx, JSString *str); + +extern JSString * +js_MakeXMLCommentString(JSContext *cx, JSString *str); + +extern JSString * +js_MakeXMLPIString(JSContext *cx, JSString *name, JSString *str); + +extern JSBool +js_EnumerateXMLValues(JSContext *cx, JSObject *obj, JSIterateOp enum_op, + jsval *statep, jsid *idp, jsval *vp); + +extern JSBool +js_TestXMLEquality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp); + +extern JSBool +js_ConcatenateXML(JSContext *cx, JSObject *obj, jsval v, jsval *vp); + +JS_END_EXTERN_C + +#endif /* jsxml_h___ */ diff --git a/ape-server/deps/js/src/lock_sparcv8plus.il b/ape-server/deps/js/src/lock_sparcv8plus.il new file mode 100755 index 0000000..e7b55d5 --- /dev/null +++ b/ape-server/deps/js/src/lock_sparcv8plus.il @@ -0,0 +1,84 @@ +! +! ***** BEGIN LICENSE BLOCK ***** +! Version: MPL 1.1/GPL 2.0/LGPL 2.1 +! +! The contents of this file are subject to the Mozilla Public License Version +! 1.1 (the "License"); you may not use this file except in compliance with +! the License. You may obtain a copy of the License at +! http://www.mozilla.org/MPL/ +! +! Software distributed under the License is distributed on an "AS IS" basis, +! WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +! for the specific language governing rights and limitations under the +! License. +! +! The Original Code is Mozilla Communicator client code, released +! March 31, 1998. +! +! The Initial Developer of the Original Code is +! Netscape Communications Corporation. +! Portions created by the Initial Developer are Copyright (C) 1998-1999 +! the Initial Developer. All Rights Reserved. +! +! Contributor(s): +! +! Alternatively, the contents of this file may be used under the terms of +! either the GNU General Public License Version 2 or later (the "GPL"), or +! the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +! in which case the provisions of the GPL or the LGPL are applicable instead +! of those above. If you wish to allow use of your version of this file only +! under the terms of either the GPL or the LGPL, and not to allow others to +! use your version of this file under the terms of the MPL, indicate your +! decision by deleting the provisions above and replace them with the notice +! and other provisions required by the GPL or the LGPL. If you do not delete +! the provisions above, a recipient may use your version of this file under +! the terms of any one of the MPL, the GPL or the LGPL. +! +! ***** END LICENSE BLOCK ***** + +! +! atomic compare-and-swap routines for V8+ (ultrasparc) +! +! ====================================================================== +! +! Perform the sequence *a = b atomically with respect to previous value +! of a (a0). If *a==a0 then assign *a to b, all in one atomic operation. +! Returns 1 if assignment happened, and 0 otherwise. +! +! usage : old_val = compare_and_swap(address, oldval, newval) +! +! ----------------------- +! Note on REGISTER USAGE: +! as this is a LEAF procedure, a new stack frame is not created; +! we use the caller stack frame so what would normally be %i (input) +! registers are actually %o (output registers). Also, we must not +! overwrite the contents of %l (local) registers as they are not +! assumed to be volatile during calls. +! +! So, the registers used are: +! %o0 [input] - the address of the value to increment +! %o1 [input] - the old value to compare with +! %o2 [input] - the new value to set for [%o0] +! %o3 [local] - work register +! ----------------------- +! ====================================================================== +! +! v8plus + + .inline NativeCompareAndSwap,3 + + stbar + cas [%o0],%o1,%o2 ! compare *w with old value and set to new if equal + cmp %o1,%o2 ! did we succeed? + be,a 1f ! yes + mov 1,%o0 ! return true (annulled when no jump) + mov 0,%o0 ! return false +1: + + .end + +! +! end +! +! ====================================================================== +! diff --git a/ape-server/deps/js/src/lock_sparcv9.il b/ape-server/deps/js/src/lock_sparcv9.il new file mode 100755 index 0000000..e87e065 --- /dev/null +++ b/ape-server/deps/js/src/lock_sparcv9.il @@ -0,0 +1,84 @@ +! +! ***** BEGIN LICENSE BLOCK ***** +! Version: MPL 1.1/GPL 2.0/LGPL 2.1 +! +! The contents of this file are subject to the Mozilla Public License Version +! 1.1 (the "License"); you may not use this file except in compliance with +! the License. You may obtain a copy of the License at +! http://www.mozilla.org/MPL/ +! +! Software distributed under the License is distributed on an "AS IS" basis, +! WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +! for the specific language governing rights and limitations under the +! License. +! +! The Original Code is Mozilla Communicator client code, released +! March 31, 1998. +! +! The Initial Developer of the Original Code is +! Netscape Communications Corporation. +! Portions created by the Initial Developer are Copyright (C) 1998-1999 +! the Initial Developer. All Rights Reserved. +! +! Contributor(s): +! +! Alternatively, the contents of this file may be used under the terms of +! either the GNU General Public License Version 2 or later (the "GPL"), or +! the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +! in which case the provisions of the GPL or the LGPL are applicable instead +! of those above. If you wish to allow use of your version of this file only +! under the terms of either the GPL or the LGPL, and not to allow others to +! use your version of this file under the terms of the MPL, indicate your +! decision by deleting the provisions above and replace them with the notice +! and other provisions required by the GPL or the LGPL. If you do not delete +! the provisions above, a recipient may use your version of this file under +! the terms of any one of the MPL, the GPL or the LGPL. +! +! ***** END LICENSE BLOCK ***** + +! +! atomic compare-and-swap routines for V9 (ultrasparc) +! +! ====================================================================== +! +! Perform the sequence *a = b atomically with respect to previous value +! of a (a0). If *a==a0 then assign *a to b, all in one atomic operation. +! Returns 1 if assignment happened, and 0 otherwise. +! +! usage : old_val = compare_and_swap(address, oldval, newval) +! +! ----------------------- +! Note on REGISTER USAGE: +! as this is a LEAF procedure, a new stack frame is not created; +! we use the caller stack frame so what would normally be %i (input) +! registers are actually %o (output registers). Also, we must not +! overwrite the contents of %l (local) registers as they are not +! assumed to be volatile during calls. +! +! So, the registers used are: +! %o0 [input] - the address of the value to increment +! %o1 [input] - the old value to compare with +! %o2 [input] - the new value to set for [%o0] +! %o3 [local] - work register +! ----------------------- +! ====================================================================== +! +! v9 + + .inline NativeCompareAndSwap,3 + + stbar + casx [%o0],%o1,%o2 ! compare *w with old value and set to new if equal + cmp %o1,%o2 ! did we succeed? + be,a 1f ! yes + mov 1,%o0 ! return true (annulled when no jump) + mov 0,%o0 ! return false +1: + + .end + +! +! end +! +! ====================================================================== +! diff --git a/ape-server/deps/js/src/nanojit-import-filemap b/ape-server/deps/js/src/nanojit-import-filemap new file mode 100755 index 0000000..d26ac7a --- /dev/null +++ b/ape-server/deps/js/src/nanojit-import-filemap @@ -0,0 +1,13 @@ +include nanojit +include vprof +include lirasm + +exclude Makefile.in +exclude configure.in +exclude config.h.in +exclude autoconf +exclude .hgtags + +rename vprof js/src/vprof +rename nanojit js/src/nanojit +rename lirasm js/src/lirasm diff --git a/ape-server/deps/js/src/nanojit-import-rev b/ape-server/deps/js/src/nanojit-import-rev new file mode 100755 index 0000000..bb6cc34 --- /dev/null +++ b/ape-server/deps/js/src/nanojit-import-rev @@ -0,0 +1 @@ +23ed78f42df2b7b1a590fc7e986e6d446ef4d3d4 diff --git a/ape-server/deps/js/src/nanojit/Allocator.cpp b/ape-server/deps/js/src/nanojit/Allocator.cpp new file mode 100755 index 0000000..1c582fa --- /dev/null +++ b/ape-server/deps/js/src/nanojit/Allocator.cpp @@ -0,0 +1,96 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is [Open Source Virtual Machine]. + * + * The Initial Developer of the Original Code is + * Adobe System Incorporated. + * Portions created by the Initial Developer are Copyright (C) 2004-2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adobe AS3 Team + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nanojit.h" + +#ifdef FEATURE_NANOJIT + +namespace nanojit +{ + Allocator::Allocator() + : current_chunk(NULL) + , current_top(NULL) + , current_limit(NULL) + { } + + Allocator::~Allocator() + { + reset(); + } + + void Allocator::reset() + { + Chunk *c = current_chunk; + while (c) { + Chunk *prev = c->prev; + freeChunk(c); + c = prev; + } + current_chunk = NULL; + current_top = NULL; + current_limit = NULL; + postReset(); + } + + void* Allocator::allocSlow(size_t nbytes) + { + NanoAssert((nbytes & 7) == 0); + fill(nbytes); + NanoAssert(current_top + nbytes <= current_limit); + void* p = current_top; + current_top += nbytes; + return p; + } + + void Allocator::fill(size_t nbytes) + { + const size_t minChunk = 2000; + if (nbytes < minChunk) + nbytes = minChunk; + size_t chunkbytes = sizeof(Chunk) + nbytes - sizeof(int64_t); + void* mem = allocChunk(chunkbytes); + Chunk* chunk = (Chunk*) mem; + chunk->prev = current_chunk; + current_chunk = chunk; + current_top = (char*)chunk->data; + current_limit = (char*)mem + chunkbytes; + } +} + +#endif // FEATURE_NANOJIT diff --git a/ape-server/deps/js/src/nanojit/Allocator.h b/ape-server/deps/js/src/nanojit/Allocator.h new file mode 100755 index 0000000..309a87b --- /dev/null +++ b/ape-server/deps/js/src/nanojit/Allocator.h @@ -0,0 +1,117 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is [Open Source Virtual Machine]. + * + * The Initial Developer of the Original Code is + * Adobe System Incorporated. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adobe AS3 Team + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef __nanojit_Allocator__ +#define __nanojit_Allocator__ + +namespace nanojit +{ + /** + * Allocator is a bump-pointer allocator with an SPI for getting more + * memory from embedder-implemented allocator, such as malloc()/free(). + * + * allocations never return NULL. The implementation of allocChunk() + * is expected to perform a longjmp or exception when an allocation can't + * proceed. + */ + class Allocator { + public: + Allocator(); + ~Allocator(); + void reset(); + + /** alloc memory, never return null. */ + void* alloc(size_t nbytes) { + nbytes = (nbytes + 7) & ~7; // round up + if (current_top + nbytes <= current_limit) { + void *p = current_top; + current_top += nbytes; + return p; + } + return allocSlow(nbytes); + } + + protected: + void* allocSlow(size_t nbytes); + void fill(size_t minbytes); + + class Chunk { + public: + Chunk* prev; + int64_t data[1]; // int64_t forces 8-byte alignment. + }; + + Chunk* current_chunk; + char* current_top; + char* current_limit; + + // allocator SPI + + /** allocate another block from a host provided allocator */ + void* allocChunk(size_t nbytes); + + /** free back to the same allocator */ + void freeChunk(void*); + + /** hook for post-reset action. */ + void postReset(); + }; +} + +/** global new overload enabling this pattern: new (allocator) T(...) */ +inline void* operator new(size_t size, nanojit::Allocator &a) { + return a.alloc(size); +} + +/** global new overload enabling this pattern: new (allocator) T(...) */ +inline void* operator new(size_t size, nanojit::Allocator *a) { + return a->alloc(size); +} + +/** global new[] overload enabling this pattern: new (allocator) T[] */ +inline void* operator new[](size_t size, nanojit::Allocator& a) { + return a.alloc(size); +} + +/** global new[] overload enabling this pattern: new (allocator) T[] */ +inline void* operator new[](size_t size, nanojit::Allocator* a) { + return a->alloc(size); +} + +#endif // __nanojit_Allocator__ diff --git a/ape-server/deps/js/src/nanojit/Assembler.cpp b/ape-server/deps/js/src/nanojit/Assembler.cpp new file mode 100755 index 0000000..0bd4ccd --- /dev/null +++ b/ape-server/deps/js/src/nanojit/Assembler.cpp @@ -0,0 +1,2023 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is [Open Source Virtual Machine]. + * + * The Initial Developer of the Original Code is + * Adobe System Incorporated. + * Portions created by the Initial Developer are Copyright (C) 2004-2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adobe AS3 Team + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nanojit.h" + +#ifdef FEATURE_NANOJIT + +#ifdef VTUNE +#include "../core/CodegenLIR.h" +#endif + +#ifdef _MSC_VER + // disable some specific warnings which are normally useful, but pervasive in the code-gen macros + #pragma warning(disable:4310) // cast truncates constant value +#endif + +namespace nanojit +{ + /** + * Need the following: + * + * - merging paths ( build a graph? ), possibly use external rep to drive codegen + */ + Assembler::Assembler(CodeAlloc& codeAlloc, Allocator& dataAlloc, Allocator& alloc, AvmCore* core, LogControl* logc) + : codeList(NULL) + , alloc(alloc) + , _codeAlloc(codeAlloc) + , _dataAlloc(dataAlloc) + , _thisfrag(NULL) + , _branchStateMap(alloc) + , _patches(alloc) + , _labels(alloc) + , _epilogue(NULL) + , _err(None) + #if PEDANTIC + , pedanticTop(NULL) + #endif + #ifdef VTUNE + , cgen(NULL) + #endif + , config(core->config) + { + VMPI_memset(&_stats, 0, sizeof(_stats)); + nInit(core); + (void)logc; + verbose_only( _logc = logc; ) + verbose_only( _outputCache = 0; ) + verbose_only( outline[0] = '\0'; ) + verbose_only( outlineEOL[0] = '\0'; ) + verbose_only( outputAddr = false; ) + + reset(); + } + + void Assembler::arReset() + { + _activation.lowwatermark = 0; + _activation.tos = 0; + + for(uint32_t i=0; iisUsed() must be true). + Register Assembler::registerAlloc(LIns* ins, RegisterMask allow) + { + RegisterMask allowedAndFree = allow & _allocator.free; + Register r; + NanoAssert(ins->isUsed()); + + if (allowedAndFree) { + // At least one usable register is free -- no need to steal. + // Pick a preferred one if possible. + RegisterMask preferredAndFree = allowedAndFree & SavedRegs; + RegisterMask set = ( preferredAndFree ? preferredAndFree : allowedAndFree ); + r = nRegisterAllocFromSet(set); + _allocator.addActive(r, ins); + ins->setReg(r); + } else { + counter_increment(steals); + + // Nothing free, steal one. + // LSRA says pick the one with the furthest use. + LIns* vicIns = findVictim(allow); + NanoAssert(vicIns->isUsed()); + r = vicIns->getReg(); + + _allocator.removeActive(r); + vicIns->setReg(UnknownReg); + + // Restore vicIns. + verbose_only( if (_logc->lcbits & LC_Assembly) { + setOutputForEOL(" <= restore %s", + _thisfrag->lirbuf->names->formatRef(vicIns)); } ) + asm_restore(vicIns, r); + + // r ends up staying active, but the LIns defining it changes. + _allocator.addActive(r, ins); + ins->setReg(r); + } + return r; + } + + // Finds a register in 'allow' to store a temporary value (one not + // associated with a particular LIns), evicting one if necessary. The + // returned register is marked as being free and so can only be safely + // used for code generation purposes until the register state is next + // inspected or updated. + Register Assembler::registerAllocTmp(RegisterMask allow) + { + LIns dummyIns; + dummyIns.markAsUsed(); + Register r = registerAlloc(&dummyIns, allow); + + // Mark r as free, ready for use as a temporary value. + _allocator.removeActive(r); + _allocator.addFree(r); + return r; + } + + /** + * these instructions don't have to be saved & reloaded to spill, + * they can just be recalculated w/out any inputs. + */ + bool Assembler::canRemat(LIns *i) { + return i->isconst() || i->isconstq() || i->isop(LIR_alloc); + } + + void Assembler::codeAlloc(NIns *&start, NIns *&end, NIns *&eip + verbose_only(, size_t &nBytes)) + { + // save the block we just filled + if (start) + CodeAlloc::add(codeList, start, end); + + // CodeAlloc contract: allocations never fail + _codeAlloc.alloc(start, end); + verbose_only( nBytes += (end - start) * sizeof(NIns); ) + NanoAssert(uintptr_t(end) - uintptr_t(start) >= (size_t)LARGEST_UNDERRUN_PROT); + eip = end; + + #ifdef VTUNE + if (_nIns && _nExitIns) { + //cgen->jitAddRecord((uintptr_t)list->code, 0, 0, true); // add placeholder record for top of page + cgen->jitCodePosUpdate((uintptr_t)list->code); + cgen->jitPushInfo(); // new page requires new entry + } + #endif + } + + void Assembler::reset() + { + _nIns = 0; + _nExitIns = 0; + codeStart = codeEnd = 0; + exitStart = exitEnd = 0; + _stats.pages = 0; + codeList = 0; + + nativePageReset(); + registerResetAll(); + arReset(); + } + + #ifdef _DEBUG + void Assembler::pageValidate() + { + if (error()) return; + // This may be a normal code chunk or an exit code chunk. + NanoAssertMsg(containsPtr(codeStart, codeEnd, _nIns), + "Native instruction pointer overstep paging bounds; check overrideProtect for last instruction"); + } + #endif + + #ifdef _DEBUG + + void Assembler::resourceConsistencyCheck() + { + NanoAssert(!error()); + +#ifdef NANOJIT_IA32 + NanoAssert((_allocator.active[FST0] && _fpuStkDepth == -1) || + (!_allocator.active[FST0] && _fpuStkDepth == 0)); +#endif + + AR &ar = _activation; + // check AR entries + NanoAssert(ar.tos < NJ_MAX_STACK_ENTRY); + LIns* ins = 0; + RegAlloc* regs = &_allocator; + for(uint32_t i = ar.lowwatermark; i < ar.tos; i++) + { + ins = ar.entry[i]; + if ( !ins ) + continue; + Register r = ins->getReg(); + uint32_t arIndex = ins->getArIndex(); + if (arIndex != 0) { + if (ins->isop(LIR_alloc)) { + int j=i+1; + for (int n = i + (ins->size()>>2); j < n; j++) { + NanoAssert(ar.entry[j]==ins); + } + NanoAssert(arIndex == (uint32_t)j-1); + i = j-1; + } + else if (ins->isQuad()) { + NanoAssert(ar.entry[i - stack_direction(1)]==ins); + i += 1; // skip high word + } + else { + NanoAssertMsg(arIndex == i, "Stack record index mismatch"); + } + } + NanoAssertMsg( !isKnownReg(r) || regs->isConsistent(r,ins), "Register record mismatch"); + } + + registerConsistencyCheck(); + } + + void Assembler::registerConsistencyCheck() + { + RegisterMask managed = _allocator.managed; + for (Register r = FirstReg; r <= LastReg; r = nextreg(r)) { + if (rmask(r) & managed) { + // A register managed by register allocation must be either + // free or active, but not both. + if (_allocator.isFree(r)) { + NanoAssertMsgf(_allocator.getActive(r)==0, + "register %s is free but assigned to ins", gpn(r)); + } else { + // An LIns defining a register must have that register in + // its reservation. + LIns* ins = _allocator.getActive(r); + NanoAssert(ins); + NanoAssertMsg(r == ins->getReg(), "Register record mismatch"); + } + } else { + // A register not managed by register allocation must be + // neither free nor active. + NanoAssert(!_allocator.isFree(r)); + NanoAssert(!_allocator.getActive(r)); + } + } + } + #endif /* _DEBUG */ + + void Assembler::findRegFor2(RegisterMask allow, LIns* ia, Register& ra, LIns* ib, Register& rb) + { + if (ia == ib) { + ra = rb = findRegFor(ia, allow); + } else { + // You might think we could just do this: + // + // ra = findRegFor(ia, allow); + // rb = findRegFor(ib, allow & ~rmask(ra)); + // + // But if 'ib' was already in an allowed register, the first + // findRegFor() call could evict it, whereupon the second + // findRegFor() call would immediately restore it, which is + // sub-optimal. What we effectively do instead is this: + // + // ra = findRegFor(ia, allow & ~rmask(rb)); + // rb = findRegFor(ib, allow & ~rmask(ra)); + // + // but we have to determine what 'rb' initially is to avoid the + // mutual dependency between the assignments. + bool rbDone = !ib->isUnusedOrHasUnknownReg() && (rb = ib->getReg(), allow & rmask(rb)); + if (rbDone) { + allow &= ~rmask(rb); // ib already in an allowable reg, keep that one + } + ra = findRegFor(ia, allow); + if (!rbDone) { + allow &= ~rmask(ra); + rb = findRegFor(ib, allow); + } + } + } + + Register Assembler::findSpecificRegFor(LIns* i, Register w) + { + return findRegFor(i, rmask(w)); + } + + // The 'op' argument is the opcode of the instruction containing the + // displaced i[d] operand we're finding a register for. It is only used + // for differentiating classes of valid displacement in the native + // backends; a bit of a hack. + Register Assembler::getBaseReg(LOpcode op, LIns *i, int &d, RegisterMask allow) + { + #if !PEDANTIC + if (i->isop(LIR_alloc)) { + int d2 = d; + d2 += findMemFor(i); + if (isValidDisplacement(op, d2)) { + d = d2; + return FP; + } + } + #else + (void) d; + #endif + return findRegFor(i, allow); + } + + // Finds a register in 'allow' to hold the result of 'ins'. Used when we + // encounter a use of 'ins'. The actions depend on the prior state of + // 'ins': + // - If the result of 'ins' is not in any register, we find an allowed + // one, evicting one if necessary. + // - If the result of 'ins' is already in an allowed register, we use that. + // - If the result of 'ins' is already in a not-allowed register, we find an + // allowed one and move it. + // + Register Assembler::findRegFor(LIns* ins, RegisterMask allow) + { + if (ins->isop(LIR_alloc)) { + // never allocate a reg for this w/out stack space too + findMemFor(ins); + } + + Register r; + + if (!ins->isUsed()) { + // No reservation. Create one, and do a fresh allocation. + ins->markAsUsed(); + RegisterMask prefer = hint(ins, allow); + r = registerAlloc(ins, prefer); + + } else if (!ins->hasKnownReg()) { + // Existing reservation with an unknown register. Do a fresh + // allocation. + RegisterMask prefer = hint(ins, allow); + r = registerAlloc(ins, prefer); + + } else if (rmask(r = ins->getReg()) & allow) { + // Existing reservation with a known register allocated, and + // that register is allowed. Use it. + _allocator.useActive(r); + + } else { + // Existing reservation with a known register allocated, but + // the register is not allowed. + RegisterMask prefer = hint(ins, allow); +#ifdef NANOJIT_IA32 + if (((rmask(r)&XmmRegs) && !(allow&XmmRegs)) || + ((rmask(r)&x87Regs) && !(allow&x87Regs))) + { + // x87 <-> xmm copy required + //_nvprof("fpu-evict",1); + evict(r, ins); + r = registerAlloc(ins, prefer); + } else +#elif defined(NANOJIT_PPC) + if (((rmask(r)&GpRegs) && !(allow&GpRegs)) || + ((rmask(r)&FpRegs) && !(allow&FpRegs))) + { + evict(r, ins); + r = registerAlloc(ins, prefer); + } else +#endif + { + // The post-state register holding 'ins' is 's', the pre-state + // register holding 'ins' is 'r'. For example, if s=eax and + // r=ecx: + // + // pre-state: ecx(ins) + // instruction: mov eax, ecx + // post-state: eax(ins) + // + _allocator.retire(r); + Register s = r; + r = registerAlloc(ins, prefer); + if ((rmask(s) & GpRegs) && (rmask(r) & GpRegs)) { +#ifdef NANOJIT_ARM + MOV(s, r); // ie. move 'ins' from its pre-state reg to its post-state reg +#else + MR(s, r); +#endif + } + else { + asm_nongp_copy(s, r); + } + } + } + return r; + } + + // Like findSpecificRegFor(), but only for when 'r' is known to be free + // and 'ins' is known to not already have a register allocated. Updates + // the register state (maintaining the invariants) but does not generate + // any code. The return value is redundant, always being 'r', but it's + // sometimes useful to have it there for assignments. + Register Assembler::findSpecificRegForUnallocated(LIns* ins, Register r) + { + if (ins->isop(LIR_alloc)) { + // never allocate a reg for this w/out stack space too + findMemFor(ins); + } + + NanoAssert(ins->isUnusedOrHasUnknownReg()); + NanoAssert(_allocator.free & rmask(r)); + + if (!ins->isUsed()) + ins->markAsUsed(); + ins->setReg(r); + _allocator.removeFree(r); + _allocator.addActive(r, ins); + + return r; + } + + int Assembler::findMemFor(LIns *ins) + { + if (!ins->isUsed()) + ins->markAsUsed(); + if (!ins->getArIndex()) { + ins->setArIndex(arReserve(ins)); + NanoAssert(ins->getArIndex() <= _activation.tos); + } + return disp(ins); + } + + Register Assembler::prepResultReg(LIns *ins, RegisterMask allow) + { + // 'pop' is only relevant on i386 and if 'allow' includes FST0, in + // which case we have to pop if 'ins' isn't in FST0 in the post-state. + // This could be because 'ins' is unused, is in a spill slot, or is in + // an XMM register. +#ifdef NANOJIT_IA32 + const bool pop = (allow & rmask(FST0)) && + (ins->isUnusedOrHasUnknownReg() || ins->getReg() != FST0); +#else + const bool pop = false; +#endif + Register r = findRegFor(ins, allow); + freeRsrcOf(ins, pop); + return r; + } + + void Assembler::asm_spilli(LInsp ins, bool pop) + { + int d = disp(ins); + Register r = ins->getReg(); + verbose_only( if (d && (_logc->lcbits & LC_Assembly)) { + setOutputForEOL(" <= spill %s", + _thisfrag->lirbuf->names->formatRef(ins)); } ) + asm_spill(r, d, pop, ins->isQuad()); + } + + // NOTE: Because this function frees slots on the stack, it is not safe to + // follow a call to this with a call to anything which might spill a + // register, as the stack can be corrupted. Refer to bug 495239 for a more + // detailed description. + void Assembler::freeRsrcOf(LIns *ins, bool pop) + { + Register r = ins->getReg(); + if (isKnownReg(r)) { + asm_spilli(ins, pop); + _allocator.retire(r); // free any register associated with entry + } + int arIndex = ins->getArIndex(); + if (arIndex) { + NanoAssert(_activation.entry[arIndex] == ins); + arFree(arIndex); // free any stack stack space associated with entry + } + ins->markAsClear(); + } + + // Frees 'r' in the RegAlloc state, if it's not already free. + void Assembler::evictIfActive(Register r) + { + if (LIns* vic = _allocator.getActive(r)) { + evict(r, vic); + } + } + + // Frees 'r' (which currently holds the result of 'vic') in the RegAlloc + // state. An example: + // + // pre-state: eax(ld1) + // instruction: mov ebx,-4(ebp) <= restore add1 # %ebx is dest + // post-state: eax(ld1) ebx(add1) + // + // At run-time we are *restoring* 'add1' into %ebx, hence the call to + // asm_restore(). But at regalloc-time we are moving backwards through + // the code, so in that sense we are *evicting* 'add1' from %ebx. + // + void Assembler::evict(Register r, LIns* vic) + { + // Not free, need to steal. + counter_increment(steals); + + // Get vic's resv, check r matches. + NanoAssert(!_allocator.isFree(r)); + NanoAssert(vic == _allocator.getActive(r)); + NanoAssert(r == vic->getReg()); + + // Free r. + _allocator.retire(r); + vic->setReg(UnknownReg); + + // Restore vic. + verbose_only( if (_logc->lcbits & LC_Assembly) { + setOutputForEOL(" <= restore %s", + _thisfrag->lirbuf->names->formatRef(vic)); } ) + asm_restore(vic, r); + } + + void Assembler::patch(GuardRecord *lr) + { + if (!lr->jmp) // the guard might have been eliminated as redundant + return; + Fragment *frag = lr->exit->target; + NanoAssert(frag->fragEntry != 0); + nPatchBranch((NIns*)lr->jmp, frag->fragEntry); + CodeAlloc::flushICache(lr->jmp, LARGEST_BRANCH_PATCH); + verbose_only(verbose_outputf("patching jump at %p to target %p\n", + lr->jmp, frag->fragEntry);) + } + + void Assembler::patch(SideExit *exit) + { + GuardRecord *rec = exit->guards; + NanoAssert(rec); + while (rec) { + patch(rec); + rec = rec->next; + } + } + +#ifdef NANOJIT_IA32 + void Assembler::patch(SideExit* exit, SwitchInfo* si) + { + for (GuardRecord* lr = exit->guards; lr; lr = lr->next) { + Fragment *frag = lr->exit->target; + NanoAssert(frag->fragEntry != 0); + si->table[si->index] = frag->fragEntry; + } + } +#endif + + NIns* Assembler::asm_exit(LInsp guard) + { + SideExit *exit = guard->record()->exit; + NIns* at = 0; + if (!_branchStateMap.get(exit)) + { + at = asm_leave_trace(guard); + } + else + { + RegAlloc* captured = _branchStateMap.get(exit); + intersectRegisterState(*captured); + at = exit->target->fragEntry; + NanoAssert(at != 0); + _branchStateMap.remove(exit); + } + return at; + } + + NIns* Assembler::asm_leave_trace(LInsp guard) + { + verbose_only( int32_t nativeSave = _stats.native ); + verbose_only( verbose_outputf("----------------------------------- ## END exit block %p", guard);) + + RegAlloc capture = _allocator; + + // this point is unreachable. so free all the registers. + // if an instruction has a stack entry we will leave it alone, + // otherwise we free it entirely. intersectRegisterState will restore. + releaseRegisters(); + + swapCodeChunks(); + _inExit = true; + +#ifdef NANOJIT_IA32 + debug_only( _sv_fpuStkDepth = _fpuStkDepth; _fpuStkDepth = 0; ) +#endif + + nFragExit(guard); + + // restore the callee-saved register and parameters + assignSavedRegs(); + assignParamRegs(); + + intersectRegisterState(capture); + + // this can be useful for breaking whenever an exit is taken + //INT3(); + //NOP(); + + // we are done producing the exit logic for the guard so demark where our exit block code begins + NIns* jmpTarget = _nIns; // target in exit path for our mainline conditional jump + + // swap back pointers, effectively storing the last location used in the exit path + swapCodeChunks(); + _inExit = false; + + //verbose_only( verbose_outputf(" LIR_xt/xf swapCodeChunks, _nIns is now %08X(%08X), _nExitIns is now %08X(%08X)",_nIns, *_nIns,_nExitIns,*_nExitIns) ); + verbose_only( verbose_outputf("%010lx:", (unsigned long)jmpTarget);) + verbose_only( verbose_outputf("----------------------------------- ## BEGIN exit block (LIR_xt|LIR_xf)") ); + +#ifdef NANOJIT_IA32 + NanoAssertMsgf(_fpuStkDepth == _sv_fpuStkDepth, "LIR_xtf, _fpuStkDepth=%d, expect %d",_fpuStkDepth, _sv_fpuStkDepth); + debug_only( _fpuStkDepth = _sv_fpuStkDepth; _sv_fpuStkDepth = 9999; ) +#endif + + verbose_only(_stats.exitnative += (_stats.native-nativeSave)); + + return jmpTarget; + } + + void Assembler::beginAssembly(Fragment *frag) + { + verbose_only( codeBytes = 0; ) + verbose_only( exitBytes = 0; ) + + reset(); + + NanoAssert(codeList == 0); + NanoAssert(codeStart == 0); + NanoAssert(codeEnd == 0); + NanoAssert(exitStart == 0); + NanoAssert(exitEnd == 0); + NanoAssert(_nIns == 0); + NanoAssert(_nExitIns == 0); + + _thisfrag = frag; + _activation.lowwatermark = 1; + _activation.tos = _activation.lowwatermark; + _inExit = false; + + counter_reset(native); + counter_reset(exitnative); + counter_reset(steals); + counter_reset(spills); + counter_reset(remats); + + setError(None); + + // native code gen buffer setup + nativePageSetup(); + + // make sure we got memory at least one page + if (error()) return; + +#ifdef PERFM + _stats.pages = 0; + _stats.codeStart = _nIns-1; + _stats.codeExitStart = _nExitIns-1; +#endif /* PERFM */ + + _epilogue = NULL; + + nBeginAssembly(); + } + + void Assembler::assemble(Fragment* frag, LirFilter* reader) + { + if (error()) return; + _thisfrag = frag; + + // check the fragment is starting out with a sane profiling state + verbose_only( NanoAssert(frag->nStaticExits == 0); ) + verbose_only( NanoAssert(frag->nCodeBytes == 0); ) + verbose_only( NanoAssert(frag->nExitBytes == 0); ) + verbose_only( NanoAssert(frag->profCount == 0); ) + verbose_only( if (_logc->lcbits & LC_FragProfile) + NanoAssert(frag->profFragID > 0); + else + NanoAssert(frag->profFragID == 0); ) + + _inExit = false; + + gen(reader); + + if (!error()) { + // patch all branches + NInsMap::Iter iter(_patches); + while (iter.next()) { + NIns* where = iter.key(); + LIns* target = iter.value(); + if (target->isop(LIR_jtbl)) { + // Need to patch up a whole jump table, 'where' is the table. + LIns *jtbl = target; + NIns** native_table = (NIns**) where; + for (uint32_t i = 0, n = jtbl->getTableSize(); i < n; i++) { + LabelState* lstate = _labels.get(jtbl->getTarget(i)); + NIns* ntarget = lstate->addr; + if (ntarget) { + native_table[i] = ntarget; + } else { + setError(UnknownBranch); + break; + } + } + } else { + // target is a label for a single-target branch + LabelState *lstate = _labels.get(target); + NIns* ntarget = lstate->addr; + if (ntarget) { + nPatchBranch(where, ntarget); + } else { + setError(UnknownBranch); + break; + } + } + } + } + } + + void Assembler::endAssembly(Fragment* frag) + { + // don't try to patch code if we are in an error state since we might have partially + // overwritten the code cache already + if (error()) { + // something went wrong, release all allocated code memory + _codeAlloc.freeAll(codeList); + _codeAlloc.free(exitStart, exitEnd); + _codeAlloc.free(codeStart, codeEnd); + return; + } + + NIns* fragEntry = genPrologue(); + verbose_only( outputAddr=true; ) + verbose_only( asm_output("[prologue]"); ) + + // check for resource leaks + debug_only( + for (uint32_t i = _activation.lowwatermark; i < _activation.tos; i++) { + NanoAssertMsgf(_activation.entry[i] == 0, "frame entry %d wasn't freed\n",-4*i); + } + ) + + NanoAssert(!_inExit); + // save used parts of current block on fragment's code list, free the rest +#ifdef NANOJIT_ARM + // [codeStart, _nSlot) ... gap ... [_nIns, codeEnd) + _codeAlloc.addRemainder(codeList, exitStart, exitEnd, _nExitSlot, _nExitIns); + _codeAlloc.addRemainder(codeList, codeStart, codeEnd, _nSlot, _nIns); + verbose_only( exitBytes -= (_nExitIns - _nExitSlot) * sizeof(NIns); ) + verbose_only( codeBytes -= (_nIns - _nSlot) * sizeof(NIns); ) +#else + // [codeStart ... gap ... [_nIns, codeEnd)) + _codeAlloc.addRemainder(codeList, exitStart, exitEnd, exitStart, _nExitIns); + _codeAlloc.addRemainder(codeList, codeStart, codeEnd, codeStart, _nIns); + verbose_only( exitBytes -= (_nExitIns - exitStart) * sizeof(NIns); ) + verbose_only( codeBytes -= (_nIns - codeStart) * sizeof(NIns); ) +#endif + + // at this point all our new code is in the d-cache and not the i-cache, + // so flush the i-cache on cpu's that need it. + CodeAlloc::flushICache(codeList); + + // save entry point pointers + frag->fragEntry = fragEntry; + frag->setCode(_nIns); + PERFM_NVPROF("code", CodeAlloc::size(codeList)); + +#ifdef NANOJIT_IA32 + NanoAssertMsgf(_fpuStkDepth == 0,"_fpuStkDepth %d\n",_fpuStkDepth); +#endif + + debug_only( pageValidate(); ) + NanoAssert(_branchStateMap.isEmpty()); + } + + void Assembler::releaseRegisters() + { + for (Register r = FirstReg; r <= LastReg; r = nextreg(r)) + { + LIns *ins = _allocator.getActive(r); + if (ins) { + // Clear reg allocation, preserve stack allocation. + _allocator.retire(r); + NanoAssert(r == ins->getReg()); + ins->setReg(UnknownReg); + + if (!ins->getArIndex()) { + ins->markAsClear(); + } + } + } + } + +#ifdef PERFM +#define countlir_live() _nvprof("lir-live",1) +#define countlir_ret() _nvprof("lir-ret",1) +#define countlir_alloc() _nvprof("lir-alloc",1) +#define countlir_var() _nvprof("lir-var",1) +#define countlir_use() _nvprof("lir-use",1) +#define countlir_def() _nvprof("lir-def",1) +#define countlir_imm() _nvprof("lir-imm",1) +#define countlir_param() _nvprof("lir-param",1) +#define countlir_cmov() _nvprof("lir-cmov",1) +#define countlir_ld() _nvprof("lir-ld",1) +#define countlir_ldq() _nvprof("lir-ldq",1) +#define countlir_alu() _nvprof("lir-alu",1) +#define countlir_qjoin() _nvprof("lir-qjoin",1) +#define countlir_qlo() _nvprof("lir-qlo",1) +#define countlir_qhi() _nvprof("lir-qhi",1) +#define countlir_fpu() _nvprof("lir-fpu",1) +#define countlir_st() _nvprof("lir-st",1) +#define countlir_stq() _nvprof("lir-stq",1) +#define countlir_jmp() _nvprof("lir-jmp",1) +#define countlir_jcc() _nvprof("lir-jcc",1) +#define countlir_label() _nvprof("lir-label",1) +#define countlir_xcc() _nvprof("lir-xcc",1) +#define countlir_x() _nvprof("lir-x",1) +#define countlir_call() _nvprof("lir-call",1) +#define countlir_jtbl() _nvprof("lir-jtbl",1) +#else +#define countlir_live() +#define countlir_ret() +#define countlir_alloc() +#define countlir_var() +#define countlir_use() +#define countlir_def() +#define countlir_imm() +#define countlir_param() +#define countlir_cmov() +#define countlir_ld() +#define countlir_ldq() +#define countlir_alu() +#define countlir_qjoin() +#define countlir_qlo() +#define countlir_qhi() +#define countlir_fpu() +#define countlir_st() +#define countlir_stq() +#define countlir_jmp() +#define countlir_jcc() +#define countlir_label() +#define countlir_xcc() +#define countlir_x() +#define countlir_call() +#define countlir_jtbl() +#endif + + void Assembler::gen(LirFilter* reader) + { + NanoAssert(_thisfrag->nStaticExits == 0); + + // trace must end with LIR_x, LIR_[f]ret, LIR_xtbl, or LIR_[f]live + NanoAssert(reader->pos()->isop(LIR_x) || + reader->pos()->isop(LIR_ret) || + reader->pos()->isop(LIR_fret) || + reader->pos()->isop(LIR_xtbl) || + reader->pos()->isop(LIR_flive) || + reader->pos()->isop(LIR_live)); + + InsList pending_lives(alloc); + + for (LInsp ins = reader->read(); !ins->isop(LIR_start) && !error(); + ins = reader->read()) + { + /* What's going on here: we're visiting all the LIR instructions + in the buffer, working strictly backwards in buffer-order, and + generating machine instructions for them as we go. + + For each LIns, we first determine whether it's actually + necessary, and if not skip it. Otherwise we generate code for + it. There are two kinds of "necessary" instructions: + + - "Statement" instructions, which have side effects. Anything + that could change control flow or the state of memory. + + - "Value" or "expression" instructions, which compute a value + based only on the operands to the instruction (and, in the + case of loads, the state of memory). Because we visit + instructions in reverse order, if some previously visited + instruction uses the value computed by this instruction, then + this instruction will already have a register assigned to + hold that value. Hence we can consult the instruction to + detect whether its value is in fact used (i.e. not dead). + + Note that the backwards code traversal can make register + allocation confusing. (For example, we restore a value before + we spill it!) In particular, words like "before" and "after" + must be used very carefully -- their meaning at regalloc-time is + opposite to their meaning at run-time. We use the term + "pre-state" to refer to the register allocation state that + occurs prior to an instruction's execution, and "post-state" to + refer to the state that occurs after an instruction's execution, + e.g.: + + pre-state: ebx(ins) + instruction: mov eax, ebx // mov dst, src + post-state: eax(ins) + + At run-time, the instruction updates the pre-state into the + post-state (and these states are the real machine's states). + But when allocating registers, because we go backwards, the + pre-state is constructed from the post-state (and these states + are those stored in RegAlloc). + */ + bool required = ins->isStmt() || ins->isUsed(); + if (!required) + continue; + +#ifdef NJ_VERBOSE + // Output the register post-state and/or activation post-state. + // Because asm output comes in reverse order, doing it now means + // it is printed after the LIR and asm, exactly when the + // post-state should be shown. + if ((_logc->lcbits & LC_Assembly) && (_logc->lcbits & LC_Activation)) + printActivationState(); + if ((_logc->lcbits & LC_Assembly) && (_logc->lcbits & LC_RegAlloc)) + printRegState(); +#endif + + LOpcode op = ins->opcode(); + switch(op) + { + default: + NanoAssertMsgf(false, "unsupported LIR instruction: %d (~0x40: %d)\n", op, op&~LIR64); + break; + + case LIR_regfence: + evictAllActiveRegs(); + break; + + case LIR_flive: + case LIR_live: { + countlir_live(); + LInsp op1 = ins->oprnd1(); + // alloca's are meant to live until the point of the LIR_live instruction, marking + // other expressions as live ensures that they remain so at loop bottoms. + // alloca areas require special treatment because they are accessed indirectly and + // the indirect accesses are invisible to the assembler, other than via LIR_live. + // other expression results are only accessed directly in ways that are visible to + // the assembler, so extending those expression's lifetimes past the last loop edge + // isn't necessary. + if (op1->isop(LIR_alloc)) { + findMemFor(op1); + } else { + pending_lives.add(ins); + } + break; + } + + case LIR_fret: + case LIR_ret: { + countlir_ret(); + asm_ret(ins); + break; + } + + // allocate some stack space. the value of this instruction + // is the address of the stack space. + case LIR_alloc: { + countlir_alloc(); + NanoAssert(ins->getArIndex() != 0); + Register r = ins->getReg(); + if (isKnownReg(r)) { + _allocator.retire(r); + ins->setReg(UnknownReg); + asm_restore(ins, r); + } + freeRsrcOf(ins, 0); + break; + } + case LIR_int: + { + countlir_imm(); + asm_int(ins); + break; + } + case LIR_float: + case LIR_quad: + { + countlir_imm(); + asm_quad(ins); + break; + } +#if !defined NANOJIT_64BIT + case LIR_callh: + { + // return result of quad-call in register + prepResultReg(ins, rmask(retRegs[1])); + // if hi half was used, we must use the call to ensure it happens + findSpecificRegFor(ins->oprnd1(), retRegs[0]); + break; + } +#endif + case LIR_param: + { + countlir_param(); + asm_param(ins); + break; + } + case LIR_qlo: + { + countlir_qlo(); + asm_qlo(ins); + break; + } + case LIR_qhi: + { + countlir_qhi(); + asm_qhi(ins); + break; + } + case LIR_qcmov: + case LIR_cmov: + { + countlir_cmov(); + asm_cmov(ins); + break; + } + case LIR_ld: + case LIR_ldc: + case LIR_ldcb: + case LIR_ldcs: + { + countlir_ld(); + asm_ld(ins); + break; + } + case LIR_ldq: + case LIR_ldqc: + { + countlir_ldq(); + asm_load64(ins); + break; + } + case LIR_neg: + case LIR_not: + { + countlir_alu(); + asm_neg_not(ins); + break; + } + case LIR_qjoin: + { + countlir_qjoin(); + asm_qjoin(ins); + break; + } + +#if defined NANOJIT_64BIT + case LIR_qiadd: + case LIR_qiand: + case LIR_qilsh: + case LIR_qursh: + case LIR_qirsh: + case LIR_qior: + case LIR_qaddp: + case LIR_qxor: + { + asm_qbinop(ins); + break; + } +#endif + + case LIR_add: + case LIR_iaddp: + case LIR_sub: + case LIR_mul: + case LIR_and: + case LIR_or: + case LIR_xor: + case LIR_lsh: + case LIR_rsh: + case LIR_ush: + case LIR_div: + case LIR_mod: + { + countlir_alu(); + asm_arith(ins); + break; + } + case LIR_fneg: + { + countlir_fpu(); + asm_fneg(ins); + break; + } + case LIR_fadd: + case LIR_fsub: + case LIR_fmul: + case LIR_fdiv: + { + countlir_fpu(); + asm_fop(ins); + break; + } + case LIR_i2f: + { + countlir_fpu(); + asm_i2f(ins); + break; + } + case LIR_u2f: + { + countlir_fpu(); + asm_u2f(ins); + break; + } + case LIR_i2q: + case LIR_u2q: + { + countlir_alu(); + asm_promote(ins); + break; + } + case LIR_sti: + { + countlir_st(); + asm_store32(ins->oprnd1(), ins->disp(), ins->oprnd2()); + break; + } + case LIR_stqi: + { + countlir_stq(); + LIns* value = ins->oprnd1(); + LIns* base = ins->oprnd2(); + int dr = ins->disp(); + if (value->isop(LIR_qjoin)) + { + // this is correct for little-endian only + asm_store32(value->oprnd1(), dr, base); + asm_store32(value->oprnd2(), dr+4, base); + } + else + { + asm_store64(value, dr, base); + } + break; + } + + case LIR_j: + { + countlir_jmp(); + LInsp to = ins->getTarget(); + LabelState *label = _labels.get(to); + // the jump is always taken so whatever register state we + // have from downstream code, is irrelevant to code before + // this jump. so clear it out. we will pick up register + // state from the jump target, if we have seen that label. + releaseRegisters(); + if (label && label->addr) { + // forward jump - pick up register state from target. + unionRegisterState(label->regs); + JMP(label->addr); + } + else { + // backwards jump + handleLoopCarriedExprs(pending_lives); + if (!label) { + // save empty register state at loop header + _labels.add(to, 0, _allocator); + } + else { + intersectRegisterState(label->regs); + } + JMP(0); + _patches.put(_nIns, to); + } + break; + } + + case LIR_jt: + case LIR_jf: + { + countlir_jcc(); + LInsp to = ins->getTarget(); + LIns* cond = ins->oprnd1(); + LabelState *label = _labels.get(to); + if (label && label->addr) { + // forward jump to known label. need to merge with label's register state. + unionRegisterState(label->regs); + asm_branch(op == LIR_jf, cond, label->addr); + } + else { + // back edge. + handleLoopCarriedExprs(pending_lives); + if (!label) { + // evict all registers, most conservative approach. + evictAllActiveRegs(); + _labels.add(to, 0, _allocator); + } + else { + // evict all registers, most conservative approach. + intersectRegisterState(label->regs); + } + NIns *branch = asm_branch(op == LIR_jf, cond, 0); + _patches.put(branch,to); + } + break; + } + + #if NJ_JTBL_SUPPORTED + case LIR_jtbl: + { + countlir_jtbl(); + // Multiway jump can contain both forward and backward jumps. + // Out of range indices aren't allowed or checked. + // Code after this jtbl instruction is unreachable. + releaseRegisters(); + AvmAssert(_allocator.countActive() == 0); + + uint32_t count = ins->getTableSize(); + bool has_back_edges = false; + + // Merge the register states of labels we have already seen. + for (uint32_t i = count; i-- > 0;) { + LIns* to = ins->getTarget(i); + LabelState *lstate = _labels.get(to); + if (lstate) { + unionRegisterState(lstate->regs); + asm_output(" %u: [&%s]", i, _thisfrag->lirbuf->names->formatRef(to)); + } else { + has_back_edges = true; + } + } + asm_output("forward edges"); + + // In a multi-way jump, the register allocator has no ability to deal + // with two existing edges that have conflicting register assignments, unlike + // a conditional branch where code can be inserted on the fall-through path + // to reconcile registers. So, frontends *must* insert LIR_regfence at labels of + // forward jtbl jumps. Check here to make sure no registers were picked up from + // any forward edges. + AvmAssert(_allocator.countActive() == 0); + + if (has_back_edges) { + handleLoopCarriedExprs(pending_lives); + // save merged (empty) register state at target labels we haven't seen yet + for (uint32_t i = count; i-- > 0;) { + LIns* to = ins->getTarget(i); + LabelState *lstate = _labels.get(to); + if (!lstate) { + _labels.add(to, 0, _allocator); + asm_output(" %u: [&%s]", i, _thisfrag->lirbuf->names->formatRef(to)); + } + } + asm_output("backward edges"); + } + + // Emit the jump instruction, which allocates 1 register for the jump index. + NIns** native_table = new (_dataAlloc) NIns*[count]; + asm_output("[%p]:", (void*)native_table); + _patches.put((NIns*)native_table, ins); + asm_jtbl(ins, native_table); + break; + } + #endif + + case LIR_label: + { + countlir_label(); + LabelState *label = _labels.get(ins); + // add profiling inc, if necessary. + verbose_only( if (_logc->lcbits & LC_FragProfile) { + if (ins == _thisfrag->loopLabel) + asm_inc_m32(& _thisfrag->profCount); + }) + if (!label) { + // label seen first, normal target of forward jump, save addr & allocator + _labels.add(ins, _nIns, _allocator); + } + else { + // we're at the top of a loop + NanoAssert(label->addr == 0); + //evictAllActiveRegs(); + intersectRegisterState(label->regs); + label->addr = _nIns; + } + verbose_only( if (_logc->lcbits & LC_Assembly) { + outputAddr=true; asm_output("[%s]", + _thisfrag->lirbuf->names->formatRef(ins)); + }) + break; + } + case LIR_xbarrier: { + break; + } +#ifdef NANOJIT_IA32 + case LIR_xtbl: { + NIns* exit = asm_exit(ins); // does intersectRegisterState() + asm_switch(ins, exit); + break; + } +#else + case LIR_xtbl: + NanoAssertMsg(0, "Not supported for this architecture"); + break; +#endif + case LIR_xt: + case LIR_xf: + { + verbose_only( _thisfrag->nStaticExits++; ) + countlir_xcc(); + // we only support cmp with guard right now, also assume it is 'close' and only emit the branch + NIns* exit = asm_exit(ins); // does intersectRegisterState() + LIns* cond = ins->oprnd1(); + asm_branch(op == LIR_xf, cond, exit); + break; + } + case LIR_x: + { + verbose_only( _thisfrag->nStaticExits++; ) + countlir_x(); + // generate the side exit branch on the main trace. + NIns *exit = asm_exit(ins); + JMP( exit ); + break; + } + + case LIR_feq: + case LIR_fle: + case LIR_flt: + case LIR_fgt: + case LIR_fge: + { + countlir_fpu(); + asm_fcond(ins); + break; + } + case LIR_eq: + case LIR_ov: + case LIR_le: + case LIR_lt: + case LIR_gt: + case LIR_ge: + case LIR_ult: + case LIR_ule: + case LIR_ugt: + case LIR_uge: +#ifdef NANOJIT_64BIT + case LIR_qeq: + case LIR_qle: + case LIR_qlt: + case LIR_qgt: + case LIR_qge: + case LIR_qult: + case LIR_qule: + case LIR_qugt: + case LIR_quge: +#endif + { + countlir_alu(); + asm_cond(ins); + break; + } + + case LIR_fcall: + #ifdef NANOJIT_64BIT + case LIR_qcall: + #endif + case LIR_icall: + { + countlir_call(); + Register rr = UnknownReg; + if (ARM_VFP && op == LIR_fcall) + { + // fcall + rr = asm_prep_fcall(ins); + } + else + { + rr = retRegs[0]; + prepResultReg(ins, rmask(rr)); + } + + // do this after we've handled the call result, so we dont + // force the call result to be spilled unnecessarily. + + evictScratchRegs(); + + asm_call(ins); + break; + } + + #ifdef VTUNE + case LIR_file: + { + // we traverse backwards so we are now hitting the file + // that is associated with a bunch of LIR_lines we already have seen + uintptr_t currentFile = ins->oprnd1()->imm32(); + cgen->jitFilenameUpdate(currentFile); + break; + } + case LIR_line: + { + // add a new table entry, we don't yet knwo which file it belongs + // to so we need to add it to the update table too + // note the alloc, actual act is delayed; see above + uint32_t currentLine = (uint32_t) ins->oprnd1()->imm32(); + cgen->jitLineNumUpdate(currentLine); + cgen->jitAddRecord((uintptr_t)_nIns, 0, currentLine, true); + break; + } + #endif // VTUNE + } + +#ifdef NJ_VERBOSE + // We have to do final LIR printing inside this loop. If we do it + // before this loop, we we end up printing a lot of dead LIR + // instructions. + // + // We print the LIns after generating the code. This ensures that + // the LIns will appear in debug output *before* the generated + // code, because Assembler::outputf() prints everything in reverse. + // + // Note that some live LIR instructions won't be printed. Eg. an + // immediate won't be printed unless it is explicitly loaded into + // a register (as opposed to being incorporated into an immediate + // field in another machine instruction). + // + if (_logc->lcbits & LC_Assembly) { + LirNameMap* names = _thisfrag->lirbuf->names; + outputf(" %s", names->formatIns(ins)); + if (ins->isGuard() && ins->oprnd1()) { + // Special case: code is generated for guard conditions at + // the same time that code is generated for the guard + // itself. If the condition is only used by the guard, we + // must print it now otherwise it won't get printed. So + // we do print it now, with an explanatory comment. If + // the condition *is* used again we'll end up printing it + // twice, but that's ok. + outputf(" %s # codegen'd with the %s", + names->formatIns(ins->oprnd1()), lirNames[op]); + + } else if (ins->isop(LIR_cmov) || ins->isop(LIR_qcmov)) { + // Likewise for cmov conditions. + outputf(" %s # codegen'd with the %s", + names->formatIns(ins->oprnd1()), lirNames[op]); + + } else if (ins->isop(LIR_mod)) { + // There's a similar case when a div feeds into a mod. + outputf(" %s # codegen'd with the mod", + names->formatIns(ins->oprnd1())); + } + } +#endif + + if (error()) + return; + + #ifdef VTUNE + cgen->jitCodePosUpdate((uintptr_t)_nIns); + #endif + + // check that all is well (don't check in exit paths since its more complicated) + debug_only( pageValidate(); ) + debug_only( resourceConsistencyCheck(); ) + } + } + + /* + * Write a jump table for the given SwitchInfo and store the table + * address in the SwitchInfo. Every entry will initially point to + * target. + */ + void Assembler::emitJumpTable(SwitchInfo* si, NIns* target) + { + si->table = (NIns **) alloc.alloc(si->count * sizeof(NIns*)); + for (uint32_t i = 0; i < si->count; ++i) + si->table[i] = target; + } + + void Assembler::assignSavedRegs() + { + // restore saved regs + releaseRegisters(); + LirBuffer *b = _thisfrag->lirbuf; + for (int i=0, n = NumSavedRegs; i < n; i++) { + LIns *p = b->savedRegs[i]; + if (p) + findSpecificRegForUnallocated(p, savedRegs[p->paramArg()]); + } + } + + void Assembler::reserveSavedRegs() + { + LirBuffer *b = _thisfrag->lirbuf; + for (int i=0, n = NumSavedRegs; i < n; i++) { + LIns *p = b->savedRegs[i]; + if (p) + findMemFor(p); + } + } + + // restore parameter registers + void Assembler::assignParamRegs() + { + LInsp state = _thisfrag->lirbuf->state; + if (state) + findSpecificRegForUnallocated(state, argRegs[state->paramArg()]); + LInsp param1 = _thisfrag->lirbuf->param1; + if (param1) + findSpecificRegForUnallocated(param1, argRegs[param1->paramArg()]); + } + + void Assembler::handleLoopCarriedExprs(InsList& pending_lives) + { + // ensure that exprs spanning the loop are marked live at the end of the loop + reserveSavedRegs(); + for (Seq *p = pending_lives.get(); p != NULL; p = p->tail) { + LIns *i = p->head; + NanoAssert(i->isop(LIR_live) || i->isop(LIR_flive)); + LIns *op1 = i->oprnd1(); + // must findMemFor even if we're going to findRegFor; loop-carried + // operands may spill on another edge, and we need them to always + // spill to the same place. + findMemFor(op1); + if (! (op1->isconst() || op1->isconstf() || op1->isconstq())) + findRegFor(op1, i->isop(LIR_flive) ? FpRegs : GpRegs); + } + + // clear this list since we have now dealt with those lifetimes. extending + // their lifetimes again later (earlier in the code) serves no purpose. + pending_lives.clear(); + } + + void Assembler::arFree(uint32_t idx) + { + AR &ar = _activation; + LIns *i = ar.entry[idx]; + NanoAssert(i != 0); + do { + ar.entry[idx] = 0; + idx--; + } while (ar.entry[idx] == i); + } + +#ifdef NJ_VERBOSE + void Assembler::printRegState() + { + char* s = &outline[0]; + VMPI_memset(s, ' ', 26); s[26] = '\0'; + s += VMPI_strlen(s); + VMPI_sprintf(s, "RR"); + s += VMPI_strlen(s); + + for (Register r = FirstReg; r <= LastReg; r = nextreg(r)) { + LIns *ins = _allocator.getActive(r); + if (ins) { + NanoAssertMsg(!_allocator.isFree(r), + "Coding error; register is both free and active! " ); + const char* n = _thisfrag->lirbuf->names->formatRef(ins); + + if (ins->isop(LIR_param) && ins->paramKind()==1 && + r == Assembler::savedRegs[ins->paramArg()]) + { + // dont print callee-saved regs that arent used + continue; + } + + const char* rname = ins->isQuad() ? fpn(r) : gpn(r); + VMPI_sprintf(s, " %s(%s)", rname, n); + s += VMPI_strlen(s); + } + } + output(); + } + + void Assembler::printActivationState() + { + char* s = &outline[0]; + VMPI_memset(s, ' ', 26); s[26] = '\0'; + s += VMPI_strlen(s); + VMPI_sprintf(s, "AR"); + s += VMPI_strlen(s); + + int32_t max = _activation.tos < NJ_MAX_STACK_ENTRY ? _activation.tos : NJ_MAX_STACK_ENTRY; + for (int32_t i = _activation.lowwatermark; i < max; i++) { + LIns *ins = _activation.entry[i]; + if (ins) { + const char* n = _thisfrag->lirbuf->names->formatRef(ins); + if (ins->isop(LIR_alloc)) { + int32_t count = ins->size()>>2; + VMPI_sprintf(s," %d-%d(%s)", 4*i, 4*(i+count-1), n); + count += i-1; + while (i < count) { + NanoAssert(_activation.entry[i] == ins); + i++; + } + } + else if (ins->isQuad()) { + VMPI_sprintf(s," %d+(%s)", 4*i, n); + NanoAssert(_activation.entry[i+1] == ins); + i++; + } + else { + VMPI_sprintf(s," %d(%s)", 4*i, n); + } + } + s += VMPI_strlen(s); + } + output(); + } +#endif + + bool canfit(int32_t size, int32_t loc, AR &ar) { + for (int i=0; i < size; i++) { + if (ar.entry[loc+stack_direction(i)]) + return false; + } + return true; + } + + uint32_t Assembler::arReserve(LIns* l) + { + int32_t size = l->isop(LIR_alloc) ? (l->size()>>2) : l->isQuad() ? 2 : 1; + AR &ar = _activation; + const int32_t tos = ar.tos; + int32_t start = ar.lowwatermark; + int32_t i = 0; + NanoAssert(start>0); + + if (size == 1) { + // easy most common case -- find a hole, or make the frame bigger + for (i=start; i < NJ_MAX_STACK_ENTRY; i++) { + if (ar.entry[i] == 0) { + // found a hole + ar.entry[i] = l; + break; + } + } + } + else if (size == 2) { + if ( (start&1)==1 ) start++; // even 8 boundary + for (i=start; i < NJ_MAX_STACK_ENTRY; i+=2) { + if ( (ar.entry[i+stack_direction(1)] == 0) && (i==tos || (ar.entry[i] == 0)) ) { + // found 2 adjacent aligned slots + NanoAssert(ar.entry[i] == 0); + NanoAssert(ar.entry[i+stack_direction(1)] == 0); + ar.entry[i] = l; + ar.entry[i+stack_direction(1)] = l; + break; + } + } + } + else { + // alloc larger block on 8byte boundary. + if (start < size) start = size; + if ((start&1)==1) start++; + for (i=start; i < NJ_MAX_STACK_ENTRY; i+=2) { + if (canfit(size, i, ar)) { + // place the entry in the table and mark the instruction with it + for (int32_t j=0; j < size; j++) { + NanoAssert(ar.entry[i+stack_direction(j)] == 0); + ar.entry[i+stack_direction(j)] = l; + } + break; + } + } + } + if (i >= (int32_t)ar.tos) { + ar.tos = i+1; + } + if (tos+size >= NJ_MAX_STACK_ENTRY) { + setError(StackFull); + } + return i; + } + + /** + * move regs around so the SavedRegs contains the highest priority regs. + */ + void Assembler::evictScratchRegs() + { + // find the top GpRegs that are candidates to put in SavedRegs + + // tosave is a binary heap stored in an array. the root is tosave[0], + // left child is at i+1, right child is at i+2. + + Register tosave[LastReg-FirstReg+1]; + int len=0; + RegAlloc *regs = &_allocator; + for (Register r = FirstReg; r <= LastReg; r = nextreg(r)) { + if (rmask(r) & GpRegs) { + LIns *i = regs->getActive(r); + if (i) { + if (canRemat(i)) { + evict(r, i); + } + else { + int32_t pri = regs->getPriority(r); + // add to heap by adding to end and bubbling up + int j = len++; + while (j > 0 && pri > regs->getPriority(tosave[j/2])) { + tosave[j] = tosave[j/2]; + j /= 2; + } + NanoAssert(size_t(j) < sizeof(tosave)/sizeof(tosave[0])); + tosave[j] = r; + } + } + } + } + + // now primap has the live exprs in priority order. + // allocate each of the top priority exprs to a SavedReg + + RegisterMask allow = SavedRegs; + while (allow && len > 0) { + // get the highest priority var + Register hi = tosave[0]; + if (!(rmask(hi) & SavedRegs)) { + LIns *i = regs->getActive(hi); + Register r = findRegFor(i, allow); + allow &= ~rmask(r); + } + else { + // hi is already in a saved reg, leave it alone. + allow &= ~rmask(hi); + } + + // remove from heap by replacing root with end element and bubbling down. + if (allow && --len > 0) { + Register last = tosave[len]; + int j = 0; + while (j+1 < len) { + int child = j+1; + if (j+2 < len && regs->getPriority(tosave[j+2]) > regs->getPriority(tosave[j+1])) + child++; + if (regs->getPriority(last) > regs->getPriority(tosave[child])) + break; + tosave[j] = tosave[child]; + j = child; + } + tosave[j] = last; + } + } + + // now evict everything else. + evictSomeActiveRegs(~SavedRegs); + } + + void Assembler::evictAllActiveRegs() + { + // generate code to restore callee saved registers + // @todo speed this up + for (Register r = FirstReg; r <= LastReg; r = nextreg(r)) { + evictIfActive(r); + } + } + + void Assembler::evictSomeActiveRegs(RegisterMask regs) + { + // generate code to restore callee saved registers + // @todo speed this up + for (Register r = FirstReg; r <= LastReg; r = nextreg(r)) { + if ((rmask(r) & regs)) { + evictIfActive(r); + } + } + } + + /** + * Merge the current state of the registers with a previously stored version + * current == saved skip + * current & saved evict current, keep saved + * current & !saved evict current (unionRegisterState would keep) + * !current & saved keep saved + */ + void Assembler::intersectRegisterState(RegAlloc& saved) + { + // evictions and pops first + RegisterMask skip = 0; + verbose_only(bool shouldMention=false; ) + // The obvious thing to do here is to iterate from FirstReg to LastReg. + // viz: for (Register r=FirstReg; r <= LastReg; r = nextreg(r)) ... + // However, on ARM that causes lower-numbered integer registers + // to be be saved at higher addresses, which inhibits the formation + // of load/store multiple instructions. Hence iterate the loop the + // other way. The "r <= LastReg" guards against wraparound in + // the case where Register is treated as unsigned and FirstReg is zero. + for (Register r=LastReg; r >= FirstReg && r <= LastReg; + r = prevreg(r)) + { + LIns * curins = _allocator.getActive(r); + LIns * savedins = saved.getActive(r); + if (curins == savedins) + { + //verbose_only( if (curins) verbose_outputf(" skip %s", regNames[r]); ) + skip |= rmask(r); + } + else + { + if (curins) { + //_nvprof("intersect-evict",1); + verbose_only( shouldMention=true; ) + evict(r, curins); + } + + #ifdef NANOJIT_IA32 + if (savedins && (rmask(r) & x87Regs)) { + verbose_only( shouldMention=true; ) + FSTP(r); + } + #endif + } + } + assignSaved(saved, skip); + verbose_only( + if (shouldMention) + verbose_outputf("## merging registers (intersect) " + "with existing edge"); + ) + } + + /** + * Merge the current state of the registers with a previously stored version. + * + * current == saved skip + * current & saved evict current, keep saved + * current & !saved keep current (intersectRegisterState would evict) + * !current & saved keep saved + */ + void Assembler::unionRegisterState(RegAlloc& saved) + { + // evictions and pops first + verbose_only(bool shouldMention=false; ) + RegisterMask skip = 0; + for (Register r=FirstReg; r <= LastReg; r = nextreg(r)) + { + LIns * curins = _allocator.getActive(r); + LIns * savedins = saved.getActive(r); + if (curins == savedins) + { + //verbose_only( if (curins) verbose_outputf(" skip %s", regNames[r]); ) + skip |= rmask(r); + } + else + { + if (curins && savedins) { + //_nvprof("union-evict",1); + verbose_only( shouldMention=true; ) + evict(r, curins); + } + + #ifdef NANOJIT_IA32 + if (rmask(r) & x87Regs) { + if (savedins) { + FSTP(r); + } + else { + // saved state did not have fpu reg allocated, + // so we must evict here to keep x87 stack balanced. + evictIfActive(r); + } + verbose_only( shouldMention=true; ) + } + #endif + } + } + assignSaved(saved, skip); + verbose_only( if (shouldMention) verbose_outputf(" merging registers (union) with existing edge"); ) + } + + void Assembler::assignSaved(RegAlloc &saved, RegisterMask skip) + { + // now reassign mainline registers + for (Register r=FirstReg; r <= LastReg; r = nextreg(r)) + { + LIns *i = saved.getActive(r); + if (i && !(skip&rmask(r))) + findSpecificRegFor(i, r); + } + } + + // Scan table for instruction with the lowest priority, meaning it is used + // furthest in the future. + LIns* Assembler::findVictim(RegisterMask allow) + { + NanoAssert(allow != 0); + LIns *i, *a=0; + int allow_pri = 0x7fffffff; + for (Register r=FirstReg; r <= LastReg; r = nextreg(r)) + { + if ((allow & rmask(r)) && (i = _allocator.getActive(r)) != 0) + { + int pri = canRemat(i) ? 0 : _allocator.getPriority(r); + if (!a || pri < allow_pri) { + a = i; + allow_pri = pri; + } + } + } + NanoAssert(a != 0); + return a; + } + +#ifdef NJ_VERBOSE + char Assembler::outline[8192]; + char Assembler::outlineEOL[512]; + + void Assembler::output() + { + // The +1 is for the terminating NUL char. + VMPI_strncat(outline, outlineEOL, sizeof(outline)-(strlen(outline)+1)); + + if (_outputCache) { + char* str = new (alloc) char[VMPI_strlen(outline)+1]; + VMPI_strcpy(str, outline); + _outputCache->insert(str); + } else { + _logc->printf("%s\n", outline); + } + + outline[0] = '\0'; + outlineEOL[0] = '\0'; + } + + void Assembler::outputf(const char* format, ...) + { + va_list args; + va_start(args, format); + + outline[0] = '\0'; + vsprintf(outline, format, args); + output(); + } + + void Assembler::setOutputForEOL(const char* format, ...) + { + va_list args; + va_start(args, format); + + outlineEOL[0] = '\0'; + vsprintf(outlineEOL, format, args); + } +#endif // NJ_VERBOSE + + uint32_t CallInfo::_count_args(uint32_t mask) const + { + uint32_t argc = 0; + uint32_t argt = _argtypes; + for (uint32_t i = 0; i < MAXARGS; ++i) { + argt >>= ARGSIZE_SHIFT; + if (!argt) + break; + argc += (argt & mask) != 0; + } + return argc; + } + + uint32_t CallInfo::get_sizes(ArgSize* sizes) const + { + uint32_t argt = _argtypes; + uint32_t argc = 0; + for (uint32_t i = 0; i < MAXARGS; i++) { + argt >>= ARGSIZE_SHIFT; + ArgSize a = ArgSize(argt & ARGSIZE_MASK_ANY); + if (a != ARGSIZE_NONE) { + sizes[argc++] = a; + } else { + break; + } + } + return argc; + } + + void LabelStateMap::add(LIns *label, NIns *addr, RegAlloc ®s) { + LabelState *st = new (alloc) LabelState(addr, regs); + labels.put(label, st); + } + + LabelState* LabelStateMap::get(LIns *label) { + return labels.get(label); + } +} +#endif /* FEATURE_NANOJIT */ diff --git a/ape-server/deps/js/src/nanojit/Assembler.h b/ape-server/deps/js/src/nanojit/Assembler.h new file mode 100755 index 0000000..b738bb9 --- /dev/null +++ b/ape-server/deps/js/src/nanojit/Assembler.h @@ -0,0 +1,416 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is [Open Source Virtual Machine]. + * + * The Initial Developer of the Original Code is + * Adobe System Incorporated. + * Portions created by the Initial Developer are Copyright (C) 2004-2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adobe AS3 Team + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + + +#ifndef __nanojit_Assembler__ +#define __nanojit_Assembler__ + + +namespace nanojit +{ + /** + * Some notes on this Assembler (Emitter). + * + * The class RegAlloc is essentially the register allocator from MIR + * + * The Assembler class parses the LIR instructions starting at any point and converts + * them to machine code. It does the translation using expression trees which are simply + * LIR instructions in the stream that have side-effects. Any other instruction in the + * stream is simply ignored. + * This approach is interesting in that dead code elimination occurs for 'free', strength + * reduction occurs fairly naturally, along with some other optimizations. + * + * A negative is that we require state as we 'push' and 'pop' nodes along the tree. + * Also, this is most easily performed using recursion which may not be desirable in + * the mobile environment. + * + */ + + #define STACK_GRANULARITY sizeof(void *) + + // Basics: + // - 'entry' records the state of the native machine stack at particular + // points during assembly. Each entry represents four bytes. + // + // - Parts of the stack can be allocated by LIR_ialloc, in which case each + // slot covered by the allocation contains a pointer to the LIR_ialloc + // LIns. + // + // - The stack also holds spilled values, in which case each slot holding + // a spilled value (one slot for 32-bit values, two slots for 64-bit + // values) contains a pointer to the instruction defining the spilled + // value. + // + // - Each LIns has a "reservation" which includes a stack index, + // 'arIndex'. Combined with AR, it provides a two-way mapping between + // stack slots and LIR instructions. + // + // - Invariant: the two-way mapping between active stack slots and their + // defining/allocating instructions must hold in both directions and be + // unambiguous. More specifically: + // + // * An LIns can appear in at most one contiguous sequence of slots in + // AR, and the length of that sequence depends on the opcode (1 slot + // for instructions producing 32-bit values, 2 slots for instructions + // producing 64-bit values, N slots for LIR_ialloc). + // + // * An LIns named by 'entry[i]' must have an in-use reservation with + // arIndex==i (or an 'i' indexing the start of the same contiguous + // sequence that 'entry[i]' belongs to). + // + // * And vice versa: an LIns with an in-use reservation with arIndex==i + // must be named by 'entry[i]'. + // + // * If an LIns's reservation names has arIndex==0 then LIns should not + // be in 'entry[]'. + // + struct AR + { + LIns* entry[ NJ_MAX_STACK_ENTRY ]; /* maps to 4B contiguous locations relative to the frame pointer */ + uint32_t tos; /* current top of stack entry */ + uint32_t lowwatermark; /* we pre-allocate entries from 0 upto this index-1; so dynamic entries are added above this index */ + }; + + #ifndef AVMPLUS_ALIGN16 + #ifdef AVMPLUS_WIN32 + #define AVMPLUS_ALIGN16(type) __declspec(align(16)) type + #else + #define AVMPLUS_ALIGN16(type) type __attribute__ ((aligned (16))) + #endif + #endif + + struct Stats + { + counter_define(steals;) + counter_define(remats;) + counter_define(spills;) + counter_define(native;) + counter_define(exitnative;) + + int32_t pages; + NIns* codeStart; + NIns* codeExitStart; + + DECLARE_PLATFORM_STATS() +#ifdef __GNUC__ + // inexplicably, gnuc gives padding/alignment warnings without this. pacify it. + bool pad[4]; +#endif + }; + + // error codes + enum AssmError + { + None = 0 + ,StackFull + ,UnknownBranch + }; + + typedef SeqBuilder NInsList; + typedef HashMap NInsMap; + +#ifdef VTUNE + class avmplus::CodegenLIR; +#endif + + class LabelState + { + public: + RegAlloc regs; + NIns *addr; + LabelState(NIns *a, RegAlloc &r) : regs(r), addr(a) + {} + }; + + class LabelStateMap + { + Allocator& alloc; + HashMap labels; + public: + LabelStateMap(Allocator& alloc) : alloc(alloc), labels(alloc) + {} + + void clear() { labels.clear(); } + void add(LIns *label, NIns *addr, RegAlloc ®s); + LabelState *get(LIns *); + }; + + typedef SeqBuilder StringList; + + /** map tracking the register allocation state at each bailout point + * (represented by SideExit*) in a trace fragment. */ + typedef HashMap RegAllocMap; + + /** + * Information about the activation record for the method is built up + * as we generate machine code. As part of the prologue, we issue + * a stack adjustment instruction and then later patch the adjustment + * value. Temporary values can be placed into the AR as method calls + * are issued. Also LIR_alloc instructions will consume space. + */ + class Assembler + { + friend class VerboseBlockReader; + public: + #ifdef NJ_VERBOSE + // Log controller object. Contains what-stuff-should-we-print + // bits, and a sink function for debug printing. + LogControl* _logc; + // Buffer for holding text as we generate it in reverse order. + StringList* _outputCache; + + // Outputs the format string and 'outlineEOL', and resets + // 'outline' and 'outlineEOL'. + void outputf(const char* format, ...); + + private: + // Buffer used in most of the output function. It must big enough + // to hold both the output line and the 'outlineEOL' buffer, which + // is concatenated onto 'outline' just before it is printed. + static char outline[8192]; + // Buffer used to hold extra text to be printed at the end of some + // lines. + static char outlineEOL[512]; + // If outputAddr=true the next asm instruction output will + // be prepended with its address. + bool outputAddr, vpad[3]; + + // Outputs 'outline' and 'outlineEOL', and resets them both. + // Output goes to '_outputCache' if it's non-NULL, or is printed + // directly via '_logc'. + void output(); + + // Sets 'outlineEOL'. + void setOutputForEOL(const char* format, ...); + + void printRegState(); + void printActivationState(); + #endif // NJ_VERBOSE + + public: + #ifdef VTUNE + avmplus::CodegenLIR *cgen; + #endif + + Assembler(CodeAlloc& codeAlloc, Allocator& dataAlloc, Allocator& alloc, AvmCore* core, LogControl* logc); + + void endAssembly(Fragment* frag); + void assemble(Fragment* frag, LirFilter* reader); + void beginAssembly(Fragment *frag); + + void releaseRegisters(); + void patch(GuardRecord *lr); + void patch(SideExit *exit); +#ifdef NANOJIT_IA32 + void patch(SideExit *exit, SwitchInfo* si); +#endif + AssmError error() { return _err; } + void setError(AssmError e) { _err = e; } + + void reset(); + + debug_only ( void pageValidate(); ) + + // support calling out from a fragment ; used to debug the jit + debug_only( void resourceConsistencyCheck(); ) + debug_only( void registerConsistencyCheck(); ) + + Stats _stats; + CodeList* codeList; // finished blocks of code. + + private: + + void gen(LirFilter* toCompile); + NIns* genPrologue(); + NIns* genEpilogue(); + + uint32_t arReserve(LIns* l); + void arFree(uint32_t idx); + void arReset(); + + Register registerAlloc(LIns* ins, RegisterMask allow); + Register registerAllocTmp(RegisterMask allow); + void registerResetAll(); + void evictAllActiveRegs(); + void evictSomeActiveRegs(RegisterMask regs); + void evictScratchRegs(); + void intersectRegisterState(RegAlloc& saved); + void unionRegisterState(RegAlloc& saved); + void assignSaved(RegAlloc &saved, RegisterMask skip); + LInsp findVictim(RegisterMask allow); + + Register getBaseReg(LOpcode op, LIns *i, int &d, RegisterMask allow); + int findMemFor(LIns* i); + Register findRegFor(LIns* i, RegisterMask allow); + void findRegFor2(RegisterMask allow, LIns* ia, Register &ra, LIns *ib, Register &rb); + Register findSpecificRegFor(LIns* i, Register r); + Register findSpecificRegForUnallocated(LIns* i, Register r); + Register prepResultReg(LIns *i, RegisterMask allow); + void freeRsrcOf(LIns *i, bool pop); + void evictIfActive(Register r); + void evict(Register r, LIns* vic); + RegisterMask hint(LIns*i, RegisterMask allow); + + void codeAlloc(NIns *&start, NIns *&end, NIns *&eip + verbose_only(, size_t &nBytes)); + bool canRemat(LIns*); + + bool isKnownReg(Register r) { + return r != UnknownReg; + } + + Allocator& alloc; // for items with same lifetime as this Assembler + CodeAlloc& _codeAlloc; // for code we generate + Allocator& _dataAlloc; // for data used by generated code + Fragment* _thisfrag; + RegAllocMap _branchStateMap; + NInsMap _patches; + LabelStateMap _labels; + + // We generate code into two places: normal code chunks, and exit + // code chunks (for exit stubs). We use a hack to avoid having to + // parameterise the code that does the generating -- we let that + // code assume that it's always generating into a normal code + // chunk (most of the time it is), and when we instead need to + // generate into an exit code chunk, we set _inExit to true and + // temporarily swap all the code/exit variables below (using + // swapCodeChunks()). Afterwards we swap them all back and set + // _inExit to false again. + bool _inExit, vpad2[3]; + NIns *codeStart, *codeEnd; // current normal code chunk + NIns *exitStart, *exitEnd; // current exit code chunk + NIns* _nIns; // current instruction in current normal code chunk + NIns* _nExitIns; // current instruction in current exit code chunk + #ifdef NJ_VERBOSE + public: + size_t codeBytes; // bytes allocated in normal code chunks + size_t exitBytes; // bytes allocated in exit code chunks + #endif + + private: + #define SWAP(t, a, b) do { t tmp = a; a = b; b = tmp; } while (0) + void swapCodeChunks(); + + NIns* _epilogue; + AssmError _err; // 0 = means assemble() appears ok, otherwise it failed + #if PEDANTIC + NIns* pedanticTop; + #endif + + AR _activation; + RegAlloc _allocator; + + verbose_only( void asm_inc_m32(uint32_t*); ) + void asm_mmq(Register rd, int dd, Register rs, int ds); + NIns* asm_exit(LInsp guard); + NIns* asm_leave_trace(LInsp guard); + void asm_qjoin(LIns *ins); + void asm_store32(LIns *val, int d, LIns *base); + void asm_store64(LIns *val, int d, LIns *base); + void asm_restore(LInsp, Register); + void asm_load(int d, Register r); + void asm_spilli(LInsp i, bool pop); + void asm_spill(Register rr, int d, bool pop, bool quad); + void asm_load64(LInsp i); + void asm_ret(LInsp p); + void asm_quad(LInsp i); + void asm_fcond(LInsp i); + void asm_cond(LInsp i); + void asm_arith(LInsp i); + void asm_neg_not(LInsp i); + void asm_ld(LInsp i); + void asm_cmov(LInsp i); + void asm_param(LInsp i); + void asm_int(LInsp i); + void asm_qlo(LInsp i); + void asm_qhi(LInsp i); + void asm_fneg(LInsp ins); + void asm_fop(LInsp ins); + void asm_i2f(LInsp ins); + void asm_u2f(LInsp ins); + void asm_promote(LIns *ins); + Register asm_prep_fcall(LInsp ins); + void asm_nongp_copy(Register r, Register s); + void asm_call(LInsp); + Register asm_binop_rhs_reg(LInsp ins); + NIns* asm_branch(bool branchOnFalse, LInsp cond, NIns* targ); + void asm_switch(LIns* ins, NIns* target); + void asm_jtbl(LIns* ins, NIns** table); + void emitJumpTable(SwitchInfo* si, NIns* target); + void assignSavedRegs(); + void reserveSavedRegs(); + void assignParamRegs(); + void handleLoopCarriedExprs(InsList& pending_lives); + + // platform specific implementation (see NativeXXX.cpp file) + void nInit(AvmCore *); + void nBeginAssembly(); + Register nRegisterAllocFromSet(RegisterMask set); + void nRegisterResetAll(RegAlloc& a); + static void nPatchBranch(NIns* branch, NIns* location); + void nFragExit(LIns* guard); + + // platform specific methods + public: + const static Register savedRegs[NumSavedRegs]; + DECLARE_PLATFORM_ASSEMBLER() + + private: +#ifdef NANOJIT_IA32 + debug_only( int32_t _fpuStkDepth; ) + debug_only( int32_t _sv_fpuStkDepth; ) + + // since we generate backwards the depth is negative + inline void fpu_push() { + debug_only( ++_fpuStkDepth; NanoAssert(_fpuStkDepth<=0); ) + } + inline void fpu_pop() { + debug_only( --_fpuStkDepth; NanoAssert(_fpuStkDepth<=0); ) + } +#endif + avmplus::Config &config; + }; + + inline int32_t disp(LIns* ins) + { + // even on 64bit cpu's, we allocate stack area in 4byte chunks + return stack_direction(4 * int32_t(ins->getArIndex())); + } +} +#endif // __nanojit_Assembler__ diff --git a/ape-server/deps/js/src/nanojit/CodeAlloc.cpp b/ape-server/deps/js/src/nanojit/CodeAlloc.cpp new file mode 100755 index 0000000..53470be --- /dev/null +++ b/ape-server/deps/js/src/nanojit/CodeAlloc.cpp @@ -0,0 +1,522 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is [Open Source Virtual Machine]. + * + * The Initial Developer of the Original Code is + * Adobe System Incorporated. + * Portions created by the Initial Developer are Copyright (C) 2004-2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adobe AS3 Team + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nanojit.h" + +//#define DOPROF +#include "../vprof/vprof.h" + +#ifdef FEATURE_NANOJIT + +namespace nanojit +{ + static const bool verbose = false; +#if defined(NANOJIT_ARM) + // ARM requires single-page allocations, due to the constant pool that + // lives on each page that must be reachable by a 4kb pcrel load. + static const int pagesPerAlloc = 1; +#else + static const int pagesPerAlloc = 16; +#endif + static const int bytesPerPage = 4096; + static const int bytesPerAlloc = pagesPerAlloc * bytesPerPage; + + CodeAlloc::CodeAlloc() + : heapblocks(0) + , availblocks(0) + , totalAllocated(0) + {} + + CodeAlloc::~CodeAlloc() { + reset(); + } + + void CodeAlloc::reset() { + // give all memory back to gcheap. Assumption is that all + // code is done being used by now. + for (CodeList* b = heapblocks; b != 0; ) { + _nvprof("free page",1); + CodeList* next = b->next; + void *mem = firstBlock(b); + VMPI_setPageProtection(mem, bytesPerAlloc, false /* executable */, true /* writable */); + freeCodeChunk(mem, bytesPerAlloc); + totalAllocated -= bytesPerAlloc; + b = next; + } + NanoAssert(!totalAllocated); + heapblocks = availblocks = 0; + } + + CodeList* CodeAlloc::firstBlock(CodeList* term) { + // use uintptr_t, rather than char*, to avoid "increases required alignment" warning + uintptr_t end = (uintptr_t)alignUp(term, bytesPerPage); + return (CodeList*) (end - (uintptr_t)bytesPerAlloc); + } + + int round(size_t x) { + return (int)((x + 512) >> 10); + } + void CodeAlloc::logStats() { + size_t total = 0; + size_t frag_size = 0; + size_t free_size = 0; + int free_count = 0; + for (CodeList* hb = heapblocks; hb != 0; hb = hb->next) { + total += bytesPerAlloc; + for (CodeList* b = hb->lower; b != 0; b = b->lower) { + if (b->isFree) { + free_count++; + free_size += b->blockSize(); + if (b->size() < minAllocSize) + frag_size += b->blockSize(); + } + } + } + avmplus::AvmLog("code-heap: %dk free %dk fragmented %d\n", + round(total), round(free_size), frag_size); + } + + void CodeAlloc::alloc(NIns* &start, NIns* &end) { + // Reuse a block if possible. + if (availblocks) { + CodeList* b = removeBlock(availblocks); + b->isFree = false; + start = b->start(); + end = b->end; + if (verbose) + avmplus::AvmLog("alloc %p-%p %d\n", start, end, int(end-start)); + return; + } + // no suitable block found, get more memory + void *mem = allocCodeChunk(bytesPerAlloc); // allocations never fail + totalAllocated += bytesPerAlloc; + NanoAssert(mem != NULL); // see allocCodeChunk contract in CodeAlloc.h + _nvprof("alloc page", uintptr_t(mem)>>12); + VMPI_setPageProtection(mem, bytesPerAlloc, true/*executable*/, true/*writable*/); + CodeList* b = addMem(mem, bytesPerAlloc); + b->isFree = false; + start = b->start(); + end = b->end; + if (verbose) + avmplus::AvmLog("alloc %p-%p %d\n", start, end, int(end-start)); + } + + void CodeAlloc::free(NIns* start, NIns *end) { + NanoAssert(heapblocks); + CodeList *blk = getBlock(start, end); + if (verbose) + avmplus::AvmLog("free %p-%p %d\n", start, end, (int)blk->size()); + + AvmAssert(!blk->isFree); + + // coalesce adjacent blocks. + bool already_on_avail_list; + + if (blk->lower && blk->lower->isFree) { + // combine blk into blk->lower (destroy blk) + CodeList* lower = blk->lower; + CodeList* higher = blk->higher; + already_on_avail_list = lower->size() >= minAllocSize; + lower->higher = higher; + higher->lower = lower; + blk = lower; + } + else + already_on_avail_list = false; + + // the last block in each heapblock is a terminator block, + // which is never free, therefore blk->higher != null + if (blk->higher->isFree) { + CodeList *higher = blk->higher->higher; + CodeList *coalescedBlock = blk->higher; + + if ( coalescedBlock->size() >= minAllocSize ) { + // Unlink coalescedBlock from the available block chain. + if ( availblocks == coalescedBlock ) { + removeBlock(availblocks); + } + else { + CodeList* free_block = availblocks; + while ( free_block && free_block->next != coalescedBlock) { + NanoAssert(free_block->size() >= minAllocSize); + NanoAssert(free_block->isFree); + NanoAssert(free_block->next); + free_block = free_block->next; + } + NanoAssert(free_block && free_block->next == coalescedBlock); + free_block->next = coalescedBlock->next; + } + } + + // combine blk->higher into blk (destroy coalescedBlock) + blk->higher = higher; + higher->lower = blk; + } + blk->isFree = true; + NanoAssert(!blk->lower || !blk->lower->isFree); + NanoAssert(blk->higher && !blk->higher->isFree); + //memset(blk->start(), 0xCC, blk->size()); // INT 3 instruction + if ( !already_on_avail_list && blk->size() >= minAllocSize ) + addBlock(availblocks, blk); + + NanoAssert(heapblocks); + debug_only(sanity_check();) + } + + void CodeAlloc::sweep() { + debug_only(sanity_check();) + + // Pass #1: remove fully-coalesced blocks from availblocks. + CodeList** prev = &availblocks; + for (CodeList* ab = availblocks; ab != 0; ab = *prev) { + NanoAssert(ab->higher != 0); + NanoAssert(ab->isFree); + if (!ab->higher->higher && !ab->lower) { + *prev = ab->next; + debug_only(ab->next = 0;) + } else { + prev = &ab->next; + } + } + + // Pass #2: remove same blocks from heapblocks, and free them. + prev = &heapblocks; + for (CodeList* hb = heapblocks; hb != 0; hb = *prev) { + NanoAssert(hb->lower != 0); + if (!hb->lower->lower && hb->lower->isFree) { + NanoAssert(!hb->lower->next); + // whole page is unused + void* mem = hb->lower; + *prev = hb->next; + _nvprof("free page",1); + VMPI_setPageProtection(mem, bytesPerAlloc, false /* executable */, true /* writable */); + freeCodeChunk(mem, bytesPerAlloc); + totalAllocated -= bytesPerAlloc; + } else { + prev = &hb->next; + } + } + } + + void CodeAlloc::freeAll(CodeList* &code) { + while (code) { + CodeList *b = removeBlock(code); + free(b->start(), b->end); + } + } + +#if defined NANOJIT_ARM && defined UNDER_CE + // Use a single flush for the whole CodeList, when we have no + // finer-granularity flush support, as on WinCE. + void CodeAlloc::flushICache(CodeList* &/*blocks*/) { + FlushInstructionCache(GetCurrentProcess(), NULL, NULL); + } +#else + void CodeAlloc::flushICache(CodeList* &blocks) { + for (CodeList *b = blocks; b != 0; b = b->next) + flushICache(b->start(), b->size()); + } +#endif + +#if defined(AVMPLUS_UNIX) && defined(NANOJIT_ARM) +#include +extern "C" void __clear_cache(char *BEG, char *END); +#endif + +#ifdef AVMPLUS_SPARC +#ifdef __linux__ // bugzilla 502369 +void sync_instruction_memory(caddr_t v, u_int len) +{ + caddr_t end = v + len; + caddr_t p = v; + while (p < end) { + asm("flush %0" : : "r" (p)); + p += 32; + } +} +#else +extern "C" void sync_instruction_memory(caddr_t v, u_int len); +#endif +#endif + +#if defined NANOJIT_IA32 || defined NANOJIT_X64 + // intel chips have dcache/icache interlock + void CodeAlloc::flushICache(void *start, size_t len) { + // Tell Valgrind that new code has been generated, and it must flush + // any translations it has for the memory range generated into. + (void)start; + (void)len; + VALGRIND_DISCARD_TRANSLATIONS(start, len); + } + +#elif defined NANOJIT_ARM && defined UNDER_CE + // On arm/winmo, just flush the whole icache. The + // WinCE docs indicate that this function actually ignores its + // 2nd and 3rd arguments, and wants them to be NULL. + void CodeAlloc::flushICache(void *, size_t) { + FlushInstructionCache(GetCurrentProcess(), NULL, NULL); + } + +#elif defined AVMPLUS_MAC && defined NANOJIT_PPC + +# ifdef NANOJIT_64BIT + extern "C" void sys_icache_invalidate(const void*, size_t len); + extern "C" void sys_dcache_flush(const void*, size_t len); + + // mac 64bit requires 10.5 so use that api + void CodeAlloc::flushICache(void *start, size_t len) { + sys_dcache_flush(start, len); + sys_icache_invalidate(start, len); + } +# else + // mac ppc 32 could be 10.0 or later + // uses MakeDataExecutable() from Carbon api, OSUtils.h + // see http://developer.apple.com/documentation/Carbon/Reference/Memory_Manag_nt_Utilities/Reference/reference.html#//apple_ref/c/func/MakeDataExecutable + void CodeAlloc::flushICache(void *start, size_t len) { + MakeDataExecutable(start, len); + } +# endif + +#elif defined AVMPLUS_SPARC + // fixme: sync_instruction_memory is a solaris api, test for solaris not sparc + void CodeAlloc::flushICache(void *start, size_t len) { + sync_instruction_memory((char*)start, len); + } + +#elif defined AVMPLUS_UNIX + #ifdef ANDROID + void CodeAlloc::flushICache(void *start, size_t len) { + cacheflush((int)start, (int)start + len, 0); + } + #else + // fixme: __clear_cache is a libgcc feature, test for libgcc or gcc + void CodeAlloc::flushICache(void *start, size_t len) { + __clear_cache((char*)start, (char*)start + len); + } + #endif +#endif // AVMPLUS_MAC && NANOJIT_PPC + + void CodeAlloc::addBlock(CodeList* &blocks, CodeList* b) { + b->next = blocks; + blocks = b; + } + + CodeList* CodeAlloc::addMem(void *mem, size_t bytes) { + CodeList* b = (CodeList*)mem; + b->lower = 0; + b->end = (NIns*) (uintptr_t(mem) + bytes - sizeofMinBlock); + b->next = 0; + b->isFree = true; + + // create a tiny terminator block, add to fragmented list, this way + // all other blocks have a valid block at b->higher + CodeList* terminator = b->higher; + terminator->lower = b; + terminator->end = 0; // this is how we identify the terminator + terminator->isFree = false; + debug_only(sanity_check();) + + // add terminator to heapblocks list so we can track whole blocks + addBlock(heapblocks, terminator); + return b; + } + + CodeList* CodeAlloc::getBlock(NIns* start, NIns* end) { + CodeList* b = (CodeList*) (uintptr_t(start) - offsetof(CodeList, code)); + NanoAssert(b->end == end && b->next == 0); (void) end; + return b; + } + + CodeList* CodeAlloc::removeBlock(CodeList* &blocks) { + CodeList* b = blocks; + NanoAssert(b); + blocks = b->next; + b->next = 0; + return b; + } + + void CodeAlloc::add(CodeList* &blocks, NIns* start, NIns* end) { + addBlock(blocks, getBlock(start, end)); + } + + /** + * split a block by freeing the hole in the middle defined by [holeStart,holeEnd), + * and adding the used prefix and suffix parts to the blocks CodeList. + */ + void CodeAlloc::addRemainder(CodeList* &blocks, NIns* start, NIns* end, NIns* holeStart, NIns* holeEnd) { + NanoAssert(start < end && start <= holeStart && holeStart <= holeEnd && holeEnd <= end); + // shrink the hole by aligning holeStart forward and holeEnd backward + holeStart = (NIns*) ((uintptr_t(holeStart) + sizeof(NIns*)-1) & ~(sizeof(NIns*)-1)); + holeEnd = (NIns*) (uintptr_t(holeEnd) & ~(sizeof(NIns*)-1)); + size_t minHole = minAllocSize; + if (minHole < 2*sizeofMinBlock) + minHole = 2*sizeofMinBlock; + if (uintptr_t(holeEnd) - uintptr_t(holeStart) < minHole) { + // the hole is too small to make a new free block and a new used block. just keep + // the whole original block and don't free anything. + add(blocks, start, end); + } else if (holeStart == start && holeEnd == end) { + // totally empty block. free whole start-end range + this->free(start, end); + } else if (holeStart == start) { + // hole is lower-aligned with start, so just need one new block + // b1 b2 + CodeList* b1 = getBlock(start, end); + CodeList* b2 = (CodeList*) (uintptr_t(holeEnd) - offsetof(CodeList, code)); + b2->isFree = false; + b2->next = 0; + b2->higher = b1->higher; + b2->lower = b1; + b2->higher->lower = b2; + b1->higher = b2; + debug_only(sanity_check();) + this->free(b1->start(), b1->end); + addBlock(blocks, b2); + } else if (holeEnd == end) { + // hole is right-aligned with end, just need one new block + // todo + NanoAssert(false); + } else { + // there's enough space left to split into three blocks (two new ones) + CodeList* b1 = getBlock(start, end); + CodeList* b2 = (CodeList*) holeStart; + CodeList* b3 = (CodeList*) (uintptr_t(holeEnd) - offsetof(CodeList, code)); + b1->higher = b2; + b2->lower = b1; + b2->higher = b3; + b2->isFree = false; // redundant, since we're about to free, but good hygiene + b3->lower = b2; + b3->end = end; + b3->isFree = false; + b3->higher->lower = b3; + b2->next = 0; + b3->next = 0; + debug_only(sanity_check();) + this->free(b2->start(), b2->end); + addBlock(blocks, b3); + addBlock(blocks, b1); + } + } + + size_t CodeAlloc::size(const CodeList* blocks) { + size_t size = 0; + for (const CodeList* b = blocks; b != 0; b = b->next) + size += int((uintptr_t)b->end - (uintptr_t)b); + return size; + } + + size_t CodeAlloc::size() { + return totalAllocated; + } + + bool CodeAlloc::contains(const CodeList* blocks, NIns* p) { + for (const CodeList *b = blocks; b != 0; b = b->next) { + _nvprof("block contains",1); + if (b->contains(p)) + return true; + } + return false; + } + + void CodeAlloc::moveAll(CodeList* &blocks, CodeList* &other) { + if (other) { + CodeList* last = other; + while (last->next) + last = last->next; + last->next = blocks; + blocks = other; + other = 0; + } + } + + // figure out whether this is a pointer into allocated/free code, + // or something we don't manage. + CodeAlloc::CodePointerKind CodeAlloc::classifyPtr(NIns *p) { + for (CodeList* hb = heapblocks; hb != 0; hb = hb->next) { + CodeList* b = firstBlock(hb); + if (!containsPtr((NIns*)b, (NIns*)((uintptr_t)b + bytesPerAlloc), p)) + continue; + do { + if (b->contains(p)) + return b->isFree ? kFree : kUsed; + } while ((b = b->higher) != 0); + } + return kUnknown; + } + + // check that all block neighbors are correct + #ifdef _DEBUG + void CodeAlloc::sanity_check() { + for (CodeList* hb = heapblocks; hb != 0; hb = hb->next) { + NanoAssert(hb->higher == 0); + for (CodeList* b = hb->lower; b != 0; b = b->lower) { + NanoAssert(b->higher->lower == b); + } + } + for (CodeList* avail = this->availblocks; avail; avail = avail->next) { + NanoAssert(avail->isFree && avail->size() >= minAllocSize); + } + + #if CROSS_CHECK_FREE_LIST + for(CodeList* term = heapblocks; term; term = term->next) { + for(CodeList* hb = term->lower; hb; hb = hb->lower) { + if (hb->isFree && hb->size() >= minAllocSize) { + bool found_on_avail = false; + for (CodeList* avail = this->availblocks; !found_on_avail && avail; avail = avail->next) { + found_on_avail = avail == hb; + } + + NanoAssert(found_on_avail); + } + } + } + for (CodeList* avail = this->availblocks; avail; avail = avail->next) { + bool found_in_heapblocks = false; + for(CodeList* term = heapblocks; !found_in_heapblocks && term; term = term->next) { + for(CodeList* hb = term->lower; !found_in_heapblocks && hb; hb = hb->lower) { + found_in_heapblocks = hb == avail; + } + } + NanoAssert(found_in_heapblocks); + } + #endif /* CROSS_CHECK_FREE_LIST */ + } + #endif +} +#endif // FEATURE_NANOJIT diff --git a/ape-server/deps/js/src/nanojit/CodeAlloc.h b/ape-server/deps/js/src/nanojit/CodeAlloc.h new file mode 100755 index 0000000..bf473ba --- /dev/null +++ b/ape-server/deps/js/src/nanojit/CodeAlloc.h @@ -0,0 +1,204 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is [Open Source Virtual Machine]. + * + * The Initial Developer of the Original Code is + * Adobe System Incorporated. + * Portions created by the Initial Developer are Copyright (C) 2004-2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adobe AS3 Team + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef __nanojit_CodeAlloc__ +#define __nanojit_CodeAlloc__ + +namespace nanojit +{ + /** return true if ptr is in the range [start, end] */ + inline bool containsPtr(const NIns* start, const NIns* end, const NIns* ptr) { + return ptr >= start && ptr <= end; + } + + /** + * CodeList is a linked list of non-contigous blocks of code. Clients use CodeList* + * to point to a list, and each CodeList instance tracks a single contiguous + * block of code. + */ + class CodeList + { + friend class CodeAlloc; + + /** for making singly linked lists of blocks in any order */ + CodeList* next; + + /** adjacent block at lower address. This field plus higher + form a doubly linked list of blocks in address order, used + for splitting and coalescing blocks. */ + CodeList* lower; + + /** true if block is free, false otherwise */ + bool isFree; + union { + // this union is used in leu of pointer punning in code + // the end of this block is always the address of the next higher block + CodeList* higher; // adjacent block at higher address + NIns* end; // points just past the end + }; + + /** code holds this block's payload of binary code, from + here to this->end */ + NIns code[1]; // more follows + + /** return the starting address for this block only */ + NIns* start() { return &code[0]; } + + /** return just the usable size of this block */ + size_t size() const { return uintptr_t(end) - uintptr_t(&code[0]); } + + /** return the whole size of this block including overhead */ + size_t blockSize() const { return uintptr_t(end) - uintptr_t(this); } + + /** return true if just this block contains p */ + bool contains(NIns* p) const { return containsPtr(&code[0], end, p); } + }; + + /** + * Code memory allocator. + * Long lived manager for many code blocks, + * manages interaction with an underlying code memory allocator, + * setting page permissions, api's for allocating and freeing + * individual blocks of code memory (for methods, stubs, or compiled + * traces), and also static functions for managing lists of allocated + * code. + */ + class CodeAlloc + { + static const size_t sizeofMinBlock = offsetof(CodeList, code); + static const size_t minAllocSize = LARGEST_UNDERRUN_PROT; + + /** Terminator blocks. All active and free allocations + are reachable by traversing this chain and each + element's lower chain. */ + CodeList* heapblocks; + + /** Reusable blocks. */ + CodeList* availblocks; + size_t totalAllocated; + + /** remove one block from a list */ + static CodeList* removeBlock(CodeList* &list); + + /** add one block to a list */ + static void addBlock(CodeList* &blocks, CodeList* b); + + /** compute the CodeList pointer from a [start, end) range */ + static CodeList* getBlock(NIns* start, NIns* end); + + /** add raw memory to the free list */ + CodeList* addMem(void* mem, size_t bytes); + + /** make sure all the higher/lower pointers are correct for every block */ + void sanity_check(); + + /** find the beginning of the heapblock terminated by term */ + static CodeList* firstBlock(CodeList* term); + + // + // CodeAlloc's SPI. Implementations must be defined by nanojit embedder. + // allocation failures should cause an exception or longjmp; nanojit + // intentionally does not check for null. + // + + /** allocate nbytes of memory to hold code. Never return null! */ + void* allocCodeChunk(size_t nbytes); + + /** free a block previously allocated by allocCodeMem. nbytes will + * match the previous allocCodeMem, but is provided here as well + * to mirror the mmap()/munmap() api. */ + void freeCodeChunk(void* addr, size_t nbytes); + + public: + CodeAlloc(); + ~CodeAlloc(); + + /** return all the memory allocated through this allocator to the gcheap. */ + void reset(); + + /** allocate some memory for code, return pointers to the region. */ + void alloc(NIns* &start, NIns* &end); + + /** free a block of memory previously returned by alloc() */ + void free(NIns* start, NIns* end); + + /** free several blocks */ + void freeAll(CodeList* &code); + + /** flush the icache for all code in the list, before executing */ + static void flushICache(CodeList* &blocks); + + /** flush the icache for a specific extent */ + static void flushICache(void *start, size_t len); + + /** add the ranges [start, holeStart) and [holeEnd, end) to code, and + free [holeStart, holeEnd) if the hole is >= minsize */ + void addRemainder(CodeList* &code, NIns* start, NIns* end, NIns* holeStart, NIns* holeEnd); + + /** add a block previously returned by alloc(), to code */ + static void add(CodeList* &code, NIns* start, NIns* end); + + /** move all the code in list "from" to list "to", and leave from empty. */ + static void moveAll(CodeList* &to, CodeList* &from); + + /** return true if any block in list "code" contains the code pointer p */ + static bool contains(const CodeList* code, NIns* p); + + /** return the number of bytes in all the code blocks in "code", including block overhead */ + static size_t size(const CodeList* code); + + /** return the total number of bytes held by this CodeAlloc. */ + size_t size(); + + /** print out stats about heap usage */ + void logStats(); + + enum CodePointerKind { + kUnknown, kFree, kUsed + }; + + /** determine whether the given address is not code, or is allocated or free */ + CodePointerKind classifyPtr(NIns *p); + + /** return any completely empty pages */ + void sweep(); + }; +} + +#endif // __nanojit_CodeAlloc__ diff --git a/ape-server/deps/js/src/nanojit/Containers.cpp b/ape-server/deps/js/src/nanojit/Containers.cpp new file mode 100755 index 0000000..ecbe042 --- /dev/null +++ b/ape-server/deps/js/src/nanojit/Containers.cpp @@ -0,0 +1,95 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is [Open Source Virtual Machine]. + * + * The Initial Developer of the Original Code is + * Adobe System Incorporated. + * Portions created by the Initial Developer are Copyright (C) 2004-2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adobe AS3 Team + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nanojit.h" + +#ifdef FEATURE_NANOJIT + +namespace nanojit +{ + BitSet::BitSet(Allocator& allocator, int nbits) + : allocator(allocator) + , cap((nbits+63)>>6) + , bits((int64_t*)allocator.alloc(cap * sizeof(int64_t))) + { + reset(); + } + + void BitSet::reset() + { + for (int i=0, n=cap; i < n; i++) + bits[i] = 0; + } + + bool BitSet::setFrom(BitSet& other) + { + int c = other.cap; + if (c > cap) + grow(c); + int64_t *bits = this->bits; + int64_t *otherbits = other.bits; + int64_t newbits = 0; + for (int i=0; i < c; i++) { + int64_t b = bits[i]; + int64_t b2 = otherbits[i]; + newbits |= b2 & ~b; // bits in b2 that are not in b + bits[i] = b|b2; + } + return newbits != 0; + } + + /** keep doubling the bitset length until w fits */ + void BitSet::grow(int w) + { + int cap2 = cap; + do { + cap2 <<= 1; + } while (w > cap2); + int64_t *bits2 = (int64_t*) allocator.alloc(cap2 * sizeof(int64_t)); + int j=0; + for (; j < cap; j++) + bits2[j] = bits[j]; + for (; j < cap2; j++) + bits2[j] = 0; + cap = cap2; + bits = bits2; + } +} + +#endif // FEATURE_NANOJIT diff --git a/ape-server/deps/js/src/nanojit/Containers.h b/ape-server/deps/js/src/nanojit/Containers.h new file mode 100755 index 0000000..63707c9 --- /dev/null +++ b/ape-server/deps/js/src/nanojit/Containers.h @@ -0,0 +1,463 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is [Open Source Virtual Machine]. + * + * The Initial Developer of the Original Code is + * Adobe System Incorporated. + * Portions created by the Initial Developer are Copyright (C) 2004-2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adobe AS3 Team + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef __nanojit_Containers__ +#define __nanojit_Containers__ + +namespace nanojit +{ + /** simple linear bit array, memory taken from Allocator + * warning: when bit array grows, old memory is wasted since it + * was allocated from Allocator. pre-size the bitmap when possible + * by passing nbits to the constructor. */ + class BitSet { + Allocator &allocator; + int cap; + int64_t *bits; + static const int64_t ONE = 1; + static const int SHIFT = 6; + + inline int bitnum2word(int i) { + return i >> 6; + } + inline int64_t bitnum2mask(int i) { + return ONE << (i & 63); + } + + /** keep doubling array to fit at least w words */ + void grow(int w); + + public: + BitSet(Allocator& allocator, int nbits=128); + + /** clear all bits */ + void reset(); + + /** perform a bitwise or with BitSet other, return true if + * this bitset was modified */ + bool setFrom(BitSet& other); + + /** return bit i as a bool */ + bool get(int i) { + NanoAssert(i >= 0); + int w = bitnum2word(i); + if (w < cap) + return (bits[w] & bitnum2mask(i)) != 0; + return false; + } + + /** set bit i */ + void set(int i) { + NanoAssert(i >= 0); + int w = bitnum2word(i); + if (w >= cap) + grow(w); + bits[w] |= bitnum2mask(i); + } + + /** clear bit i */ + void clear(int i) { + NanoAssert(i >= 0); + int w = bitnum2word(i); + if (w < cap) + bits[w] &= ~bitnum2mask(i); + } + }; + + /** Seq is a single node in a linked list */ + template class Seq { + public: + Seq(T head, Seq* tail=NULL) : head(head), tail(tail) {} + T head; + Seq* tail; + }; + + /** SeqBuilder is used to create a linked list of Seq by inserting + * nodes either at the beginning, with insert(), or at the end, with + * add(). Once built, the actual list can be retained while this + * SeqBuilder can be discarded. */ + template class SeqBuilder { + public: + SeqBuilder(Allocator& allocator) + : allocator(allocator) + , items(NULL) + , last(NULL) + { } + + /** add item to beginning of list */ + void insert(T item) { + Seq* e = new (allocator) Seq(item, items); + if (last == NULL) + last = e; + items = e; + } + + /** add item to end of list */ + void add(T item) { + Seq* e = new (allocator) Seq(item); + if (last == NULL) + items = e; + else + last->tail = e; + last = e; + } + + /** return first item in sequence */ + Seq* get() const { + return items; + } + + /** self explanitory */ + bool isEmpty() const { + return items == NULL; + } + + /** de-reference all items */ + void clear() { + items = last = NULL; + } + + private: + Allocator& allocator; + Seq* items; + Seq* last; + }; + +#ifdef NANOJIT_64BIT + static inline size_t murmurhash(const void *key, size_t len) { + const uint64_t m = 0xc6a4a7935bd1e995; + const int r = 47; + uint64_t h = 0; + + const uint64_t *data = (const uint64_t*)key; + const uint64_t *end = data + (len/8); + + while(data != end) + { + uint64_t k = *data++; + + k *= m; + k ^= k >> r; + k *= m; + + h ^= k; + h *= m; + } + + const unsigned char *data2 = (const unsigned char*)data; + + switch(len & 7) { + case 7: h ^= uint64_t(data2[6]) << 48; + case 6: h ^= uint64_t(data2[5]) << 40; + case 5: h ^= uint64_t(data2[4]) << 32; + case 4: h ^= uint64_t(data2[3]) << 24; + case 3: h ^= uint64_t(data2[2]) << 16; + case 2: h ^= uint64_t(data2[1]) << 8; + case 1: h ^= uint64_t(data2[0]); + h *= m; + }; + + h ^= h >> r; + h *= m; + h ^= h >> r; + + return (size_t)h; + } +#else + static inline size_t murmurhash(const void * key, size_t len) { + const uint32_t m = 0x5bd1e995; + const int r = 24; + uint32_t h = 0; + + const unsigned char * data = (const unsigned char *)key; + while(len >= 4) { + uint32_t k = *(size_t *)(void*)data; + + k *= m; + k ^= k >> r; + k *= m; + + h *= m; + h ^= k; + + data += 4; + len -= 4; + } + + switch(len) { + case 3: h ^= data[2] << 16; + case 2: h ^= data[1] << 8; + case 1: h ^= data[0]; + h *= m; + }; + + h ^= h >> 13; + h *= m; + h ^= h >> 15; + + return (size_t)h; + } +#endif + + template struct DefaultHash { + static size_t hash(const K &k) { + return murmurhash(&k, sizeof(K)); + } + }; + + template struct DefaultHash { + static size_t hash(K* k) { + uintptr_t h = (uintptr_t) k; + // move the low 3 bits higher up since they're often 0 + h = (h>>3) ^ (h<<((sizeof(uintptr_t) * 8) - 3)); + return (size_t) h; + } + }; + + /** Bucket hashtable with a fixed # of buckets (never rehash) + * Intended for use when a reasonable # of buckets can be estimated ahead of time. + */ + template > class HashMap { + Allocator& allocator; + size_t nbuckets; + class Node { + public: + K key; + T value; + Node(K k, T v) : key(k), value(v) { } + }; + Seq** buckets; + + /** return the node containing K, and the bucket index, or NULL if not found */ + Node* find(K k, size_t &i) { + i = H::hash(k) % nbuckets; + for (Seq* p = buckets[i]; p != NULL; p = p->tail) { + if (p->head.key == k) + return &p->head; + } + return NULL; + } + public: + HashMap(Allocator& a, size_t nbuckets = 16) + : allocator(a) + , nbuckets(nbuckets) + , buckets(new (a) Seq*[nbuckets]) + { + NanoAssert(nbuckets > 0); + clear(); + } + + /** clear all buckets. Since we allocate all memory from Allocator, + * nothing needs to be freed. */ + void clear() { + VMPI_memset(buckets, 0, sizeof(Seq*) * nbuckets); + } + + /** add (k,v) to the map. If k is already in the map, replace the value */ + void put(const K& k, const T& v) { + size_t i; + Node* n = find(k, i); + if (n) { + n->value = v; + return; + } + buckets[i] = new (allocator) Seq(Node(k,v), buckets[i]); + } + + /** return v for element k, or T(0) if k is not present */ + T get(const K& k) { + size_t i; + Node* n = find(k, i); + return n ? n->value : 0; + } + + /** returns true if k is in the map. */ + bool containsKey(const K& k) { + size_t i; + return find(k, i) != 0; + } + + /** remove k from the map, if it is present. if not, remove() + * silently returns */ + void remove(const K& k) { + size_t i = H::hash(k) % nbuckets; + Seq** prev = &buckets[i]; + for (Seq* p = buckets[i]; p != NULL; p = p->tail) { + if (p->head.key == k) { + (*prev) = p->tail; + return; + } + prev = &p->tail; + } + } + + /** Iter is an iterator for HashMap, intended to be instantiated on + * the stack. Iteration order is undefined. Mutating the hashmap + * while iteration is in progress gives undefined results. All iteration + * state is in class Iter, so multiple iterations can be in progress + * at the same time. for example: + * + * HashMap::Iter iter(map); + * while (iter.next()) { + * K *k = iter.key(); + * T *t = iter.value(); + * } + */ + class Iter { + friend class HashMap; + const HashMap ↦ + int bucket; + const Seq* current; + + public: + Iter(HashMap& map) : map(map), bucket((int)map.nbuckets-1), current(NULL) + { } + + /** return true if more (k,v) remain to be visited */ + bool next() { + if (current) + current = current->tail; + while (bucket >= 0 && !current) + current = map.buckets[bucket--]; + return current != NULL; + } + + /** return the current key */ + const K& key() const { + NanoAssert(current != NULL); + return current->head.key; + } + + /** return the current value */ + const T& value() const { + NanoAssert(current != NULL); + return current->head.value; + } + }; + + /** return true if the hashmap has no elements */ + bool isEmpty() { + Iter iter(*this); + return !iter.next(); + } + }; + + /** + * Simple binary tree. No balancing is performed under the assumption + * that the only users of this structure are not performance critical. + */ + template class TreeMap { + Allocator& alloc; + class Node { + public: + Node* left; + Node* right; + K key; + T value; + Node(K k, T v) : left(NULL), right(NULL), key(k), value(v) + { } + }; + Node* root; + + /** + * helper method to recursively insert (k,v) below Node n or a child + * of n so that the binary search tree remains well formed. + */ + void insert(Node* &n, K k, T v) { + if (!n) + n = new (alloc) Node(k, v); + else if (k == n->key) + n->value = v; + else if (k < n->key) + insert(n->left, k, v); + else + insert(n->right, k, v); + } + + /** + * search for key k below Node n and return n if found, or the + * closest parent n where k should be inserted. + */ + Node* find(Node* n, K k) { + if (!n) + return NULL; + if (k == n->key) + return n; + if (k < n->key) + return find(n->left, k); + if (n->right) + return find(n->right, k); + return n; + } + + public: + TreeMap(Allocator& alloc) : alloc(alloc), root(NULL) + { } + + /** set k = v in the map. if k already exists, replace its value */ + void put(K k, T v) { + insert(root, k, v); + } + + /** return the closest key that is <= k, or NULL if k + is smaller than every key in the Map. */ + K findNear(K k) { + Node* n = find(root, k); + return n ? n->key : 0; + } + + /** returns the value for k or NULL */ + T get(K k) { + Node* n = find(root, k); + return (n && n->key == k) ? n->value : 0; + } + + /** returns true iff k is in the Map. */ + bool containsKey(K k) { + Node* n = find(root, k); + return n && n->key == k; + } + + /** make the tree empty. trivial since we dont manage elements */ + void clear() { + root = NULL; + } + }; +} +#endif // __nanojit_Containers__ diff --git a/ape-server/deps/js/src/nanojit/Fragmento.cpp b/ape-server/deps/js/src/nanojit/Fragmento.cpp new file mode 100755 index 0000000..6bf35cf --- /dev/null +++ b/ape-server/deps/js/src/nanojit/Fragmento.cpp @@ -0,0 +1,80 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is [Open Source Virtual Machine]. + * + * The Initial Developer of the Original Code is + * Adobe System Incorporated. + * Portions created by the Initial Developer are Copyright (C) 2004-2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adobe AS3 Team + * Mozilla TraceMonkey Team + * Asko Tontti + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nanojit.h" + +namespace nanojit +{ + #ifdef FEATURE_NANOJIT + + using namespace avmplus; + + // + // Fragment + // + Fragment::Fragment(const void* _ip + verbose_only(, uint32_t profFragID)) + : + lirbuf(NULL), + lastIns(NULL), + ip(_ip), + recordAttempts(0), + fragEntry(NULL), + verbose_only( loopLabel(NULL), ) + verbose_only( profFragID(profFragID), ) + verbose_only( profCount(0), ) + verbose_only( nStaticExits(0), ) + verbose_only( nCodeBytes(0), ) + verbose_only( nExitBytes(0), ) + verbose_only( guardNumberer(1), ) + verbose_only( guardsForFrag(NULL), ) + _code(NULL), + _hits(0) + { + // when frag profiling is enabled, profFragID should be >= 1, + // else it should be zero. However, there's no way to assert + // that here since there's no way to determine whether frag + // profiling is enabled. + } + #endif /* FEATURE_NANOJIT */ +} + + diff --git a/ape-server/deps/js/src/nanojit/Fragmento.h b/ape-server/deps/js/src/nanojit/Fragmento.h new file mode 100755 index 0000000..f4bbe7a --- /dev/null +++ b/ape-server/deps/js/src/nanojit/Fragmento.h @@ -0,0 +1,135 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is [Open Source Virtual Machine]. + * + * The Initial Developer of the Original Code is + * Adobe System Incorporated. + * Portions created by the Initial Developer are Copyright (C) 2004-2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adobe AS3 Team + * Mozilla TraceMonkey Team + * Asko Tontti + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + + +#ifndef __nanojit_Fragmento__ +#define __nanojit_Fragmento__ + +namespace nanojit +{ + struct GuardRecord; + + /** + * Fragments are linear sequences of native code that have a single entry + * point at the start of the fragment and may have one or more exit points + * + * It may turn out that that this arrangement causes too much traffic + * between d and i-caches and that we need to carve up the structure differently. + */ + class Fragment + { + public: + Fragment(const void* + verbose_only(, uint32_t profFragID)); + + NIns* code() { return _code; } + void setCode(NIns* codee) { _code = codee; } + int32_t& hits() { return _hits; } + + LirBuffer* lirbuf; + LIns* lastIns; + + const void* ip; + uint32_t recordAttempts; + NIns* fragEntry; + + // for fragment entry and exit profiling. See detailed + // how-to-use comment below. + verbose_only( LIns* loopLabel; ) // where's the loop top? + verbose_only( uint32_t profFragID; ) + verbose_only( uint32_t profCount; ) + verbose_only( uint32_t nStaticExits; ) + verbose_only( size_t nCodeBytes; ) + verbose_only( size_t nExitBytes; ) + verbose_only( uint32_t guardNumberer; ) + verbose_only( GuardRecord* guardsForFrag; ) + + private: + NIns* _code; // ptr to start of code + int32_t _hits; + }; +} + +/* + * How to use fragment profiling + * + * Fragprofiling adds code to count how many times each fragment is + * entered, and how many times each guard (exit) is taken. Using this + * it's possible to easily find which fragments are hot, which ones + * typically exit early, etc. The fragprofiler also gathers some + * simple static info: for each fragment, the number of code bytes, + * number of exit-block bytes, and number of guards (exits). + * + * Fragments and guards are given unique IDs (FragID, GuardID) which + * are shown in debug printouts, so as to facilitate navigating from + * the accumulated statistics to the associated bits of code. + * GuardIDs are issued automatically, but FragIDs you must supply when + * calling Fragment::Fragment. Supply values >= 1, and supply a + * different value for each new fragment (doesn't matter what, they + * just have to be unique and >= 1); else + * js_FragProfiling_FragFinalizer will assert. + * + * How to use/embed: + * + * - use a debug build (one with NJ_VERBOSE). Without it, none of + * this code is compiled in. + * + * - set LC_FragProfile in the lcbits of the LogControl* object handed + * to Nanojit + * + * When enabled, Fragment::profCount is incremented every time the + * fragment is entered, and GuardRecord::profCount is incremented + * every time that guard exits. However, NJ has no way to know where + * the fragment entry/loopback point is. So you must set + * Fragment::loopLabel before running the assembler, so as to indicate + * where the fragment-entry counter increment should be placed. If + * the fragment does not naturally have a loop label then you will + * need to artificially add one. + * + * It is the embedder's problem to fish out, collate and present the + * accumulated stats at the end of the Fragment's lifetime. A + * Fragment contains stats indicating its entry count and static code + * sizes. It also has a ::guardsForFrag field, which is a linked list + * of GuardRecords, and by traversing them you can get hold of the + * exit counts. + */ + +#endif // __nanojit_Fragmento__ diff --git a/ape-server/deps/js/src/nanojit/LIR.cpp b/ape-server/deps/js/src/nanojit/LIR.cpp new file mode 100755 index 0000000..15a4e1c --- /dev/null +++ b/ape-server/deps/js/src/nanojit/LIR.cpp @@ -0,0 +1,2401 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is [Open Source Virtual Machine]. + * + * The Initial Developer of the Original Code is + * Adobe System Incorporated. + * Portions created by the Initial Developer are Copyright (C) 2004-2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adobe AS3 Team + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nanojit.h" + +namespace nanojit +{ + using namespace avmplus; + #ifdef FEATURE_NANOJIT + + const uint8_t repKinds[] = { +#define OPDEF(op, number, repkind) \ + LRK_##repkind, +#define OPD64(op, number, repkind) \ + LRK_##repkind, +#include "LIRopcode.tbl" +#undef OPDEF +#undef OPD64 + 0 + }; + + // LIR verbose specific + #ifdef NJ_VERBOSE + + const char* lirNames[] = { +#define OPDEF(op, number, repkind) \ + #op, +#define OPD64(op, number, repkind) \ + #op, +#include "LIRopcode.tbl" +#undef OPDEF +#undef OPD64 + NULL + }; + + #endif /* NANOJIT_VEBROSE */ + + // implementation +#ifdef NJ_VERBOSE + /* A listing filter for LIR, going through backwards. It merely + passes its input to its output, but notes it down too. When + destructed, prints out what went through. Is intended to be + used to print arbitrary intermediate transformation stages of + LIR. */ + class ReverseLister : public LirFilter + { + Allocator& _alloc; + LirNameMap* _names; + const char* _title; + StringList _strs; + LogControl* _logc; + public: + ReverseLister(LirFilter* in, Allocator& alloc, + LirNameMap* names, LogControl* logc, const char* title) + : LirFilter(in) + , _alloc(alloc) + , _names(names) + , _title(title) + , _strs(alloc) + , _logc(logc) + { } + + void finish() + { + _logc->printf("\n"); + _logc->printf("=== BEGIN %s ===\n", _title); + int j = 0; + for (Seq* p = _strs.get(); p != NULL; p = p->tail) + _logc->printf(" %02d: %s\n", j++, p->head); + _logc->printf("=== END %s ===\n", _title); + _logc->printf("\n"); + } + + LInsp read() + { + LInsp i = in->read(); + const char* str = _names->formatIns(i); + char* cpy = new (_alloc) char[strlen(str)+1]; + VMPI_strcpy(cpy, str); + _strs.insert(cpy); + return i; + } + }; +#endif + +#ifdef NJ_PROFILE + // @todo fixup move to nanojit.h + #undef counter_value + #define counter_value(x) x +#endif /* NJ_PROFILE */ + + // LCompressedBuffer + LirBuffer::LirBuffer(Allocator& alloc) : +#ifdef NJ_VERBOSE + names(NULL), +#endif + abi(ABI_FASTCALL), state(NULL), param1(NULL), sp(NULL), rp(NULL), + _allocator(alloc) + { + clear(); + } + + void LirBuffer::clear() + { + // clear the stats, etc + _unused = 0; + _limit = 0; + _bytesAllocated = 0; + _stats.lir = 0; + for (int i = 0; i < NumSavedRegs; ++i) + savedRegs[i] = NULL; + chunkAlloc(); + } + + void LirBuffer::chunkAlloc() + { + _unused = (uintptr_t) _allocator.alloc(CHUNK_SZB); + NanoAssert(_unused != 0); // Allocator.alloc() never returns null. See Allocator.h + _limit = _unused + CHUNK_SZB; + } + + int32_t LirBuffer::insCount() + { + return _stats.lir; + } + + size_t LirBuffer::byteCount() + { + return _bytesAllocated - (_limit - _unused); + } + + // Allocate a new page, and write the first instruction to it -- a skip + // linking to last instruction of the previous page. + void LirBuffer::moveToNewChunk(uintptr_t addrOfLastLInsOnCurrentChunk) + { + chunkAlloc(); + // Link LIR stream back to prior instruction. + // Unlike all the ins*() functions, we don't call makeRoom() here + // because we know we have enough space, having just started a new + // page. + LInsSk* insSk = (LInsSk*)_unused; + LIns* ins = insSk->getLIns(); + ins->initLInsSk((LInsp)addrOfLastLInsOnCurrentChunk); + _unused += sizeof(LInsSk); + verbose_only(_stats.lir++); + } + + // Make room for a single instruction. + uintptr_t LirBuffer::makeRoom(size_t szB) + { + // Make sure the size is ok + NanoAssert(0 == szB % sizeof(void*)); + NanoAssert(sizeof(LIns) <= szB && szB <= sizeof(LInsSti)); // LInsSti is the biggest one + NanoAssert(_unused < _limit); + + debug_only( bool moved = false; ) + + // If the instruction won't fit on the current chunk, get a new chunk + if (_unused + szB > _limit) { + uintptr_t addrOfLastLInsOnChunk = _unused - sizeof(LIns); + moveToNewChunk(addrOfLastLInsOnChunk); + debug_only( moved = true; ) + } + + // We now know that we are on a chunk that has the requested amount of + // room: record the starting address of the requested space and bump + // the pointer. + uintptr_t startOfRoom = _unused; + _unused += szB; + verbose_only(_stats.lir++); // count the instruction + + // If there's no more space on this chunk, move to a new one. + // (This will only occur if the asked-for size filled up exactly to + // the end of the chunk.) This ensures that next time we enter this + // function, _unused won't be pointing one byte past the end of + // the chunk, which would break everything. + if (_unused >= _limit) { + // Check we used exactly the remaining space + NanoAssert(_unused == _limit); + NanoAssert(!moved); // shouldn't need to moveToNewChunk twice + uintptr_t addrOfLastLInsOnChunk = _unused - sizeof(LIns); + moveToNewChunk(addrOfLastLInsOnChunk); + } + + // Make sure it's word-aligned. + NanoAssert(0 == startOfRoom % sizeof(void*)); + return startOfRoom; + } + + LInsp LirBufWriter::insStorei(LInsp val, LInsp base, int32_t d) + { + LOpcode op = val->isQuad() ? LIR_stqi : LIR_sti; + base = insDisp(op, base, d); + LInsSti* insSti = (LInsSti*)_buf->makeRoom(sizeof(LInsSti)); + LIns* ins = insSti->getLIns(); + ins->initLInsSti(op, val, base, d); + return ins; + } + + LInsp LirBufWriter::ins0(LOpcode op) + { + LInsOp0* insOp0 = (LInsOp0*)_buf->makeRoom(sizeof(LInsOp0)); + LIns* ins = insOp0->getLIns(); + ins->initLInsOp0(op); + return ins; + } + + LInsp LirBufWriter::ins1(LOpcode op, LInsp o1) + { + LInsOp1* insOp1 = (LInsOp1*)_buf->makeRoom(sizeof(LInsOp1)); + LIns* ins = insOp1->getLIns(); + ins->initLInsOp1(op, o1); + return ins; + } + + LInsp LirBufWriter::ins2(LOpcode op, LInsp o1, LInsp o2) + { + LInsOp2* insOp2 = (LInsOp2*)_buf->makeRoom(sizeof(LInsOp2)); + LIns* ins = insOp2->getLIns(); + ins->initLInsOp2(op, o1, o2); + return ins; + } + + LInsp LirBufWriter::ins3(LOpcode op, LInsp o1, LInsp o2, LInsp o3) + { + LInsOp3* insOp3 = (LInsOp3*)_buf->makeRoom(sizeof(LInsOp3)); + LIns* ins = insOp3->getLIns(); + ins->initLInsOp3(op, o1, o2, o3); + return ins; + } + + LInsp LirBufWriter::insLoad(LOpcode op, LInsp base, int32_t d) + { + base = insDisp(op, base, d); + LInsLd* insLd = (LInsLd*)_buf->makeRoom(sizeof(LInsLd)); + LIns* ins = insLd->getLIns(); + ins->initLInsLd(op, base, d); + return ins; + } + + LInsp LirBufWriter::insGuard(LOpcode op, LInsp c, GuardRecord *gr) + { + debug_only( if (LIR_x == op || LIR_xbarrier == op) NanoAssert(!c); ) + return ins2(op, c, (LIns*)gr); + } + + LInsp LirBufWriter::insBranch(LOpcode op, LInsp condition, LInsp toLabel) + { + NanoAssert((op == LIR_j && !condition) || + ((op == LIR_jf || op == LIR_jt) && condition)); + return ins2(op, condition, toLabel); + } + + LIns* LirBufWriter::insJtbl(LIns* index, uint32_t size) + { + LInsJtbl* insJtbl = (LInsJtbl*) _buf->makeRoom(sizeof(LInsJtbl)); + LIns** table = new (_buf->_allocator) LIns*[size]; + LIns* ins = insJtbl->getLIns(); + VMPI_memset(table, 0, size * sizeof(LIns*)); + ins->initLInsJtbl(index, size, table); + return ins; + } + + LInsp LirBufWriter::insAlloc(int32_t size) + { + size = (size+3)>>2; // # of required 32bit words + LInsI* insI = (LInsI*)_buf->makeRoom(sizeof(LInsI)); + LIns* ins = insI->getLIns(); + ins->initLInsI(LIR_alloc, size); + return ins; + } + + LInsp LirBufWriter::insParam(int32_t arg, int32_t kind) + { + LInsP* insP = (LInsP*)_buf->makeRoom(sizeof(LInsP)); + LIns* ins = insP->getLIns(); + ins->initLInsP(arg, kind); + if (kind) { + NanoAssert(arg < NumSavedRegs); + _buf->savedRegs[arg] = ins; + } + return ins; + } + + LInsp LirBufWriter::insImm(int32_t imm) + { + LInsI* insI = (LInsI*)_buf->makeRoom(sizeof(LInsI)); + LIns* ins = insI->getLIns(); + ins->initLInsI(LIR_int, imm); + return ins; + } + + LInsp LirBufWriter::insImmq(uint64_t imm) + { + LInsI64* insI64 = (LInsI64*)_buf->makeRoom(sizeof(LInsI64)); + LIns* ins = insI64->getLIns(); + ins->initLInsI64(LIR_quad, imm); + return ins; + } + + LInsp LirBufWriter::insImmf(double d) + { + LInsI64* insI64 = (LInsI64*)_buf->makeRoom(sizeof(LInsI64)); + LIns* ins = insI64->getLIns(); + union { + double d; + uint64_t q; + } u; + u.d = d; + ins->initLInsI64(LIR_float, u.q); + return ins; + } + + // Reads the next non-skip instruction. + LInsp LirReader::read() + { + static const uint8_t insSizes[] = { + // LIR_start is treated specially -- see below. +#define OPDEF(op, number, repkind) \ + ((number) == LIR_start ? 0 : sizeof(LIns##repkind)), +#define OPD64(op, number, repkind) \ + OPDEF(op, number, repkind) +#include "LIRopcode.tbl" +#undef OPDEF +#undef OPD64 + 0 + }; + + // Check the invariant: _i never points to a skip. + NanoAssert(_i && !_i->isop(LIR_skip)); + + // Step back one instruction. Use a table lookup rather than a switch + // to avoid branch mispredictions. LIR_start is given a special size + // of zero so that we don't step back past the start of the block. + // (Callers of this function should stop once they see a LIR_start.) + LInsp ret = _i; + _i = (LInsp)(uintptr_t(_i) - insSizes[_i->opcode()]); + + // Ensure _i doesn't end up pointing to a skip. + while (_i->isop(LIR_skip)) { + NanoAssert(_i->prevLIns() != _i); + _i = _i->prevLIns(); + } + + return ret; + } + + // This is never called, but that's ok because it contains only static + // assertions. + void LIns::staticSanityCheck() + { + // LIns must be word-sized. + NanoStaticAssert(sizeof(LIns) == 1*sizeof(void*)); + + // LInsXYZ have expected sizes too. + NanoStaticAssert(sizeof(LInsOp0) == 1*sizeof(void*)); + NanoStaticAssert(sizeof(LInsOp1) == 2*sizeof(void*)); + NanoStaticAssert(sizeof(LInsOp2) == 3*sizeof(void*)); + NanoStaticAssert(sizeof(LInsOp3) == 4*sizeof(void*)); + NanoStaticAssert(sizeof(LInsLd) == 3*sizeof(void*)); + NanoStaticAssert(sizeof(LInsSti) == 4*sizeof(void*)); + NanoStaticAssert(sizeof(LInsSk) == 2*sizeof(void*)); + NanoStaticAssert(sizeof(LInsC) == 3*sizeof(void*)); + NanoStaticAssert(sizeof(LInsP) == 2*sizeof(void*)); + NanoStaticAssert(sizeof(LInsI) == 2*sizeof(void*)); + #if defined NANOJIT_64BIT + NanoStaticAssert(sizeof(LInsI64) == 2*sizeof(void*)); + #else + NanoStaticAssert(sizeof(LInsI64) == 3*sizeof(void*)); + #endif + + // oprnd_1 must be in the same position in LIns{Op1,Op2,Op3,Ld,Sti} + // because oprnd1() is used for all of them. + NanoStaticAssert( (offsetof(LInsOp1, ins) - offsetof(LInsOp1, oprnd_1)) == + (offsetof(LInsOp2, ins) - offsetof(LInsOp2, oprnd_1)) ); + NanoStaticAssert( (offsetof(LInsOp2, ins) - offsetof(LInsOp2, oprnd_1)) == + (offsetof(LInsOp3, ins) - offsetof(LInsOp3, oprnd_1)) ); + NanoStaticAssert( (offsetof(LInsOp3, ins) - offsetof(LInsOp3, oprnd_1)) == + (offsetof(LInsLd, ins) - offsetof(LInsLd, oprnd_1)) ); + NanoStaticAssert( (offsetof(LInsLd, ins) - offsetof(LInsLd, oprnd_1)) == + (offsetof(LInsSti, ins) - offsetof(LInsSti, oprnd_1)) ); + + // oprnd_2 must be in the same position in LIns{Op2,Op3,Sti} + // because oprnd2() is used for both of them. + NanoStaticAssert( (offsetof(LInsOp2, ins) - offsetof(LInsOp2, oprnd_2)) == + (offsetof(LInsOp3, ins) - offsetof(LInsOp3, oprnd_2)) ); + NanoStaticAssert( (offsetof(LInsOp3, ins) - offsetof(LInsOp3, oprnd_2)) == + (offsetof(LInsSti, ins) - offsetof(LInsSti, oprnd_2)) ); + } + + bool LIns::isFloat() const { + switch (opcode()) { + default: + return false; + case LIR_fadd: + case LIR_fsub: + case LIR_fmul: + case LIR_fdiv: + case LIR_fneg: + case LIR_fcall: + case LIR_i2f: + case LIR_u2f: + return true; + } + } + + LIns* LirWriter::ins2i(LOpcode v, LIns* oprnd1, int32_t imm) + { + return ins2(v, oprnd1, insImm(imm)); + } + + bool insIsS16(LInsp i) + { + if (i->isconst()) { + int c = i->imm32(); + return isS16(c); + } + if (i->isop(LIR_cmov) || i->isop(LIR_qcmov)) { + return insIsS16(i->oprnd2()) && insIsS16(i->oprnd3()); + } + if (i->isCmp()) + return true; + // many other possibilities too. + return false; + } + + LIns* ExprFilter::ins1(LOpcode v, LIns* i) + { + switch (v) { + case LIR_qlo: + if (i->isconstq()) + return insImm(i->imm64_0()); + if (i->isop(LIR_qjoin)) + return i->oprnd1(); + break; + case LIR_qhi: + if (i->isconstq()) + return insImm(i->imm64_1()); + if (i->isop(LIR_qjoin)) + return i->oprnd2(); + break; + case LIR_not: + if (i->isconst()) + return insImm(~i->imm32()); + involution: + if (v == i->opcode()) + return i->oprnd1(); + break; + case LIR_neg: + if (i->isconst()) + return insImm(-i->imm32()); + if (i->isop(LIR_sub)) // -(a-b) = b-a + return out->ins2(LIR_sub, i->oprnd2(), i->oprnd1()); + goto involution; + case LIR_fneg: + if (i->isconstq()) + return insImmf(-i->imm64f()); + if (i->isop(LIR_fsub)) + return out->ins2(LIR_fsub, i->oprnd2(), i->oprnd1()); + goto involution; + case LIR_i2f: + if (i->isconst()) + return insImmf(i->imm32()); + break; + case LIR_u2f: + if (i->isconst()) + return insImmf(uint32_t(i->imm32())); + break; + default: + ; + } + + return out->ins1(v, i); + } + + LIns* ExprFilter::ins2(LOpcode v, LIns* oprnd1, LIns* oprnd2) + { + NanoAssert(oprnd1 && oprnd2); + if (oprnd1 == oprnd2) + { + switch (v) { + case LIR_xor: + case LIR_sub: + case LIR_ult: + case LIR_ugt: + case LIR_gt: + case LIR_lt: + return insImm(0); + case LIR_or: + case LIR_and: + return oprnd1; + case LIR_le: + case LIR_ule: + case LIR_ge: + case LIR_uge: + // x <= x == 1; x >= x == 1 + return insImm(1); + default: + ; + } + } + if (oprnd1->isconst() && oprnd2->isconst()) + { + int32_t c1 = oprnd1->imm32(); + int32_t c2 = oprnd2->imm32(); + double d; + int32_t r; + uint64_t q; + + switch (v) { + case LIR_qjoin: + q = c1 | uint64_t(c2)<<32; + return insImmq(q); + case LIR_eq: + return insImm(c1 == c2); + case LIR_ov: + return insImm((c2 != 0) && ((c1 + c2) <= c1)); + case LIR_lt: + return insImm(c1 < c2); + case LIR_gt: + return insImm(c1 > c2); + case LIR_le: + return insImm(c1 <= c2); + case LIR_ge: + return insImm(c1 >= c2); + case LIR_ult: + return insImm(uint32_t(c1) < uint32_t(c2)); + case LIR_ugt: + return insImm(uint32_t(c1) > uint32_t(c2)); + case LIR_ule: + return insImm(uint32_t(c1) <= uint32_t(c2)); + case LIR_uge: + return insImm(uint32_t(c1) >= uint32_t(c2)); + case LIR_rsh: + return insImm(int32_t(c1) >> int32_t(c2)); + case LIR_lsh: + return insImm(int32_t(c1) << int32_t(c2)); + case LIR_ush: + return insImm(uint32_t(c1) >> int32_t(c2)); + case LIR_or: + return insImm(uint32_t(c1) | int32_t(c2)); + case LIR_and: + return insImm(uint32_t(c1) & int32_t(c2)); + case LIR_xor: + return insImm(uint32_t(c1) ^ int32_t(c2)); + case LIR_add: + d = double(c1) + double(c2); + fold: + r = int32_t(d); + if (r == d) + return insImm(r); + break; + case LIR_sub: + d = double(c1) - double(c2); + goto fold; + case LIR_mul: + d = double(c1) * double(c2); + goto fold; + case LIR_div: + case LIR_mod: + // We can't easily fold div and mod, since folding div makes it + // impossible to calculate the mod that refers to it. The + // frontend shouldn't emit div and mod with constant operands. + NanoAssert(0); + default: + ; + } + } + else if (oprnd1->isconstq() && oprnd2->isconstq()) + { + double c1 = oprnd1->imm64f(); + double c2 = oprnd2->imm64f(); + switch (v) { + case LIR_feq: + return insImm(c1 == c2); + case LIR_flt: + return insImm(c1 < c2); + case LIR_fgt: + return insImm(c1 > c2); + case LIR_fle: + return insImm(c1 <= c2); + case LIR_fge: + return insImm(c1 >= c2); + case LIR_fadd: + return insImmf(c1 + c2); + case LIR_fsub: + return insImmf(c1 - c2); + case LIR_fmul: + return insImmf(c1 * c2); + case LIR_fdiv: + return insImmf(c1 / c2); + default: + ; + } + } + else if (oprnd1->isconst() && !oprnd2->isconst()) + { + LIns* t; + switch (v) { + case LIR_add: + case LIR_iaddp: + case LIR_qaddp: + case LIR_mul: + case LIR_fadd: + case LIR_fmul: + case LIR_xor: + case LIR_or: + case LIR_and: + case LIR_eq: + // move const to rhs + t = oprnd2; + oprnd2 = oprnd1; + oprnd1 = t; + break; + default: + if (v >= LIR_lt && v <= LIR_uge) { + NanoStaticAssert((LIR_lt ^ 1) == LIR_gt); + NanoStaticAssert((LIR_le ^ 1) == LIR_ge); + NanoStaticAssert((LIR_ult ^ 1) == LIR_ugt); + NanoStaticAssert((LIR_ule ^ 1) == LIR_uge); + + // move const to rhs, swap the operator + LIns *t = oprnd2; + oprnd2 = oprnd1; + oprnd1 = t; + v = LOpcode(v^1); + } + break; + } + } + + if (oprnd2->isconst()) + { + int c = oprnd2->imm32(); + switch (v) { + case LIR_add: + if (oprnd1->isop(LIR_add) && oprnd1->oprnd2()->isconst()) { + // add(add(x,c1),c2) => add(x,c1+c2) + c += oprnd1->oprnd2()->imm32(); + oprnd2 = insImm(c); + oprnd1 = oprnd1->oprnd1(); + } + break; + case LIR_sub: + if (oprnd1->isop(LIR_add) && oprnd1->oprnd2()->isconst()) { + // sub(add(x,c1),c2) => add(x,c1-c2) + c = oprnd1->oprnd2()->imm32() - c; + oprnd2 = insImm(c); + oprnd1 = oprnd1->oprnd1(); + v = LIR_add; + } + break; + case LIR_rsh: + if (c == 16 && oprnd1->isop(LIR_lsh) && + oprnd1->oprnd2()->isconstval(16) && + insIsS16(oprnd1->oprnd1())) { + // rsh(lhs(x,16),16) == x, if x is S16 + return oprnd1->oprnd1(); + } + break; + default: + ; + } + + if (c == 0) { + switch (v) { + case LIR_add: + case LIR_iaddp: + case LIR_or: + case LIR_xor: + case LIR_sub: + case LIR_lsh: + case LIR_rsh: + case LIR_ush: + return oprnd1; + case LIR_and: + case LIR_mul: + return oprnd2; + case LIR_eq: + if (oprnd1->isop(LIR_or) && + oprnd1->oprnd2()->isconst() && + oprnd1->oprnd2()->imm32() != 0) { + // (x or c) != 0 if c != 0 + return insImm(0); + } + default: + ; + } + } else if (c == -1 || (c == 1 && oprnd1->isCmp())) { + switch (v) { + case LIR_or: + // x | -1 = -1, cmp | 1 = 1 + return oprnd2; + case LIR_and: + // x & -1 = x, cmp & 1 = cmp + return oprnd1; + default: + ; + } + } + } + + LInsp i; + if (v == LIR_qjoin && oprnd1->isop(LIR_qlo) && oprnd2->isop(LIR_qhi) && + (i = oprnd1->oprnd1()) == oprnd2->oprnd1()) { + // qjoin(qlo(x),qhi(x)) == x + return i; + } + + return out->ins2(v, oprnd1, oprnd2); + } + + LIns* ExprFilter::ins3(LOpcode v, LIns* oprnd1, LIns* oprnd2, LIns* oprnd3) + { + NanoAssert(oprnd1 && oprnd2 && oprnd3); + NanoAssert(v == LIR_cmov || v == LIR_qcmov); + if (oprnd2 == oprnd3) { + // c ? a : a => a + return oprnd2; + } + if (oprnd1->isconst()) { + // const ? x : y => return x or y depending on const + return oprnd1->imm32() ? oprnd2 : oprnd3; + } + if (oprnd1->isop(LIR_eq) && + ((oprnd1->oprnd2() == oprnd2 && oprnd1->oprnd1() == oprnd3) || + (oprnd1->oprnd1() == oprnd2 && oprnd1->oprnd2() == oprnd3))) { + // (y == x) ? x : y => y + // (x == y) ? x : y => y + return oprnd3; + } + + return out->ins3(v, oprnd1, oprnd2, oprnd3); + } + + LIns* ExprFilter::insGuard(LOpcode v, LInsp c, GuardRecord *gr) + { + if (v == LIR_xt || v == LIR_xf) { + if (c->isconst()) { + if ((v == LIR_xt && !c->imm32()) || (v == LIR_xf && c->imm32())) { + return 0; // no guard needed + } + else { +#ifdef JS_TRACER + // We're emitting a guard that will always fail. Any code + // emitted after this guard is dead code. We could + // silently optimize out the rest of the emitted code, but + // this could indicate a performance problem or other bug, + // so assert in debug builds. + NanoAssertMsg(0, "Constantly false guard detected"); +#endif + return out->insGuard(LIR_x, NULL, gr); + } + } + else { + NanoStaticAssert((LIR_xt ^ 1) == LIR_xf); + while (c->isop(LIR_eq) && c->oprnd1()->isCmp() && + c->oprnd2()->isconstval(0)) { + // xt(eq(cmp,0)) => xf(cmp) or xf(eq(cmp,0)) => xt(cmp) + v = LOpcode(v^1); + c = c->oprnd1(); + } + } + } + return out->insGuard(v, c, gr); + } + + LIns* ExprFilter::insBranch(LOpcode v, LIns *c, LIns *t) + { + switch (v) { + case LIR_jt: + case LIR_jf: + while (c->isop(LIR_eq) && c->oprnd1()->isCmp() && c->oprnd2()->isconstval(0)) { + // jt(eq(cmp,0)) => jf(cmp) or jf(eq(cmp,0)) => jt(cmp) + v = LOpcode(v ^ 1); + c = c->oprnd1(); + } + break; + default: + ; + } + return out->insBranch(v, c, t); + } + + LIns* ExprFilter::insLoad(LOpcode op, LIns* base, int32_t off) { + if (base->isconstp() && !isS8(off)) { + // if the effective address is constant, then transform: + // ld const[bigconst] => ld (const+bigconst)[0] + // note: we don't do this optimization for <8bit field offsets, + // under the assumption that we're more likely to CSE-match the + // constant base address if we dont const-fold small offsets. + uintptr_t p = (uintptr_t)base->constvalp() + off; + return out->insLoad(op, insImmPtr((void*)p), 0); + } + return out->insLoad(op, base, off); + } + + LIns* LirWriter::ins_eq0(LIns* oprnd1) + { + return ins2i(LIR_eq, oprnd1, 0); + } + + LIns* LirWriter::ins_peq0(LIns* oprnd1) + { + return ins2(LIR_peq, oprnd1, insImmWord(0)); + } + + LIns* LirWriter::ins_i2p(LIns* intIns) + { +#ifdef NANOJIT_64BIT + return ins1(LIR_i2q, intIns); +#else + return intIns; +#endif + } + + LIns* LirWriter::ins_u2p(LIns* uintIns) + { +#ifdef NANOJIT_64BIT + return ins1(LIR_u2q, uintIns); +#else + return uintIns; +#endif + } + + LIns* LirWriter::qjoin(LInsp lo, LInsp hi) + { + return ins2(LIR_qjoin, lo, hi); + } + + LIns* LirWriter::insImmWord(intptr_t value) + { +#ifdef NANOJIT_64BIT + return insImmq(value); +#else + return insImm(value); +#endif + } + + LIns* LirWriter::insImmPtr(const void *ptr) + { +#ifdef NANOJIT_64BIT + return insImmq((uint64_t)ptr); +#else + return insImm((int32_t)ptr); +#endif + } + + LIns* LirWriter::ins_choose(LIns* cond, LIns* iftrue, LIns* iffalse, bool use_cmov) + { + // if not a conditional, make it implicitly an ==0 test (then flop results) + if (!cond->isCmp()) + { + cond = ins_eq0(cond); + LInsp tmp = iftrue; + iftrue = iffalse; + iffalse = tmp; + } + + if (use_cmov) + return ins3((iftrue->isQuad() || iffalse->isQuad()) ? LIR_qcmov : LIR_cmov, cond, iftrue, iffalse); + + LInsp ncond = ins1(LIR_neg, cond); // cond ? -1 : 0 + return ins2(LIR_or, + ins2(LIR_and, iftrue, ncond), + ins2(LIR_and, iffalse, ins1(LIR_not, ncond))); + } + + LIns* LirBufWriter::insCall(const CallInfo *ci, LInsp args[]) + { + static const LOpcode k_callmap[] = { + // ARGSIZE_NONE ARGSIZE_F ARGSIZE_LO ARGSIZE_Q (4) (5) ARGSIZE_U (7) + LIR_pcall, LIR_fcall, LIR_icall, LIR_qcall, LIR_pcall, LIR_pcall, LIR_icall, LIR_pcall + }; + + uint32_t argt = ci->_argtypes; + LOpcode op = k_callmap[argt & ARGSIZE_MASK_ANY]; + NanoAssert(op != LIR_skip); // LIR_skip here is just an error condition + + int32_t argc = ci->count_args(); + NanoAssert(argc <= (int)MAXARGS); + + if (!ARM_VFP && (op == LIR_fcall || op == LIR_qcall)) + op = LIR_callh; + + // Allocate space for and copy the arguments. We use the same + // allocator as the normal LIR buffers so it has the same lifetime. + // Nb: this must be kept in sync with arg(). + LInsp* args2 = (LInsp*)_buf->_allocator.alloc(argc * sizeof(LInsp)); + memcpy(args2, args, argc * sizeof(LInsp)); + + // Allocate and write the call instruction. + LInsC* insC = (LInsC*)_buf->makeRoom(sizeof(LInsC)); + LIns* ins = insC->getLIns(); +#ifndef NANOJIT_64BIT + ins->initLInsC(op==LIR_callh ? LIR_icall : op, args2, ci); +#else + ins->initLInsC(op, args2, ci); +#endif + return ins; + } + + using namespace avmplus; + + StackFilter::StackFilter(LirFilter *in, Allocator& alloc, LirBuffer *lirbuf, LInsp sp, LInsp rp) + : LirFilter(in), lirbuf(lirbuf), sp(sp), rp(rp), spStk(alloc), rpStk(alloc), + spTop(0), rpTop(0) + {} + + bool StackFilter::ignoreStore(LInsp ins, int top, BitSet* stk) + { + bool ignore = false; + int d = ins->disp() >> 2; + if (d >= top) { + ignore = true; + } else { + d = top - d; + if (ins->oprnd1()->isQuad()) { + // storing 8 bytes + if (stk->get(d) && stk->get(d-1)) { + ignore = true; + } else { + stk->set(d); + stk->set(d-1); + } + } + else { + // storing 4 bytes + if (stk->get(d)) { + ignore = true; + } else { + stk->set(d); + } + } + } + return ignore; + } + + LInsp StackFilter::read() + { + for (;;) + { + LInsp i = in->read(); + if (i->isStore()) + { + LInsp base = i->oprnd2(); + + if (base == sp) { + if (ignoreStore(i, spTop, &spStk)) + continue; + + } else if (base == rp) { + if (ignoreStore(i, rpTop, &rpStk)) + continue; + } + } + /* + * NB: If there is a backward branch other than the loop-restart branch, this is + * going to be wrong. Unfortunately there doesn't seem to be an easy way to detect + * such branches. Just do not create any. + */ + else if (i->isGuard()) + { + spStk.reset(); + rpStk.reset(); + getTops(i, spTop, rpTop); + spTop >>= 2; + rpTop >>= 2; + } + + return i; + } + } + + // + // inlined/separated version of SuperFastHash + // This content is copyrighted by Paul Hsieh, For reference see : http://www.azillionmonkeys.com/qed/hash.html + // + inline uint32_t _hash8(uint32_t hash, const uint8_t data) + { + hash += data; + hash ^= hash << 10; + hash += hash >> 1; + return hash; + } + + inline uint32_t _hash32(uint32_t hash, const uint32_t data) + { + const uint32_t dlo = data & 0xffff; + const uint32_t dhi = data >> 16; + hash += dlo; + const uint32_t tmp = (dhi << 11) ^ hash; + hash = (hash << 16) ^ tmp; + hash += hash >> 11; + return hash; + } + + inline uint32_t _hashptr(uint32_t hash, const void* data) + { +#ifdef NANOJIT_64BIT + hash = _hash32(hash, uint32_t(uintptr_t(data) >> 32)); + hash = _hash32(hash, uint32_t(uintptr_t(data))); + return hash; +#else + return _hash32(hash, uint32_t(data)); +#endif + } + + inline uint32_t _hashfinish(uint32_t hash) + { + /* Force "avalanching" of final 127 bits */ + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; + } + + LInsHashSet::LInsHashSet(Allocator& alloc, uint32_t kInitialCaps[]) : alloc(alloc) + { + for (LInsHashKind kind = LInsFirst; kind <= LInsLast; kind = nextKind(kind)) { + m_cap[kind] = kInitialCaps[kind]; + m_list[kind] = new (alloc) LInsp[m_cap[kind]]; + } + clear(); + m_find[LInsImm] = &LInsHashSet::findImm; + m_find[LInsImmq] = &LInsHashSet::findImmq; + m_find[LInsImmf] = &LInsHashSet::findImmf; + m_find[LIns1] = &LInsHashSet::find1; + m_find[LIns2] = &LInsHashSet::find2; + m_find[LIns3] = &LInsHashSet::find3; + m_find[LInsLoad] = &LInsHashSet::findLoad; + m_find[LInsCall] = &LInsHashSet::findCall; + } + + void LInsHashSet::clear() { + for (LInsHashKind kind = LInsFirst; kind <= LInsLast; kind = nextKind(kind)) { + VMPI_memset(m_list[kind], 0, sizeof(LInsp)*m_cap[kind]); + m_used[kind] = 0; + } + } + + + inline uint32_t LInsHashSet::hashImm(int32_t a) { + return _hashfinish(_hash32(0,a)); + } + + inline uint32_t LInsHashSet::hashImmq(uint64_t a) { + uint32_t hash = _hash32(0, uint32_t(a >> 32)); + return _hashfinish(_hash32(hash, uint32_t(a))); + } + + inline uint32_t LInsHashSet::hashImmf(double d) { + union { + double d; + uint64_t u64; + } u; + u.d = d; + return hashImmq(u.u64); + } + + inline uint32_t LInsHashSet::hash1(LOpcode op, LInsp a) { + uint32_t hash = _hash8(0,uint8_t(op)); + return _hashfinish(_hashptr(hash, a)); + } + + inline uint32_t LInsHashSet::hash2(LOpcode op, LInsp a, LInsp b) { + uint32_t hash = _hash8(0,uint8_t(op)); + hash = _hashptr(hash, a); + return _hashfinish(_hashptr(hash, b)); + } + + inline uint32_t LInsHashSet::hash3(LOpcode op, LInsp a, LInsp b, LInsp c) { + uint32_t hash = _hash8(0,uint8_t(op)); + hash = _hashptr(hash, a); + hash = _hashptr(hash, b); + return _hashfinish(_hashptr(hash, c)); + } + + inline uint32_t LInsHashSet::hashLoad(LOpcode op, LInsp a, int32_t d) { + uint32_t hash = _hash8(0,uint8_t(op)); + hash = _hashptr(hash, a); + return _hashfinish(_hash32(hash, d)); + } + + inline uint32_t LInsHashSet::hashCall(const CallInfo *ci, uint32_t argc, LInsp args[]) { + uint32_t hash = _hashptr(0, ci); + for (int32_t j=argc-1; j >= 0; j--) + hash = _hashptr(hash,args[j]); + return _hashfinish(hash); + } + + void LInsHashSet::grow(LInsHashKind kind) + { + const uint32_t oldcap = m_cap[kind]; + m_cap[kind] <<= 1; + LInsp *oldlist = m_list[kind]; + m_list[kind] = new (alloc) LInsp[m_cap[kind]]; + VMPI_memset(m_list[kind], 0, m_cap[kind] * sizeof(LInsp)); + find_t find = m_find[kind]; + for (uint32_t i = 0; i < oldcap; i++) { + LInsp ins = oldlist[i]; + if (!ins) continue; + uint32_t j = (this->*find)(ins); + m_list[kind][j] = ins; + } + } + + LInsp LInsHashSet::add(LInsHashKind kind, LInsp ins, uint32_t k) + { + NanoAssert(!m_list[kind][k]); + m_used[kind]++; + m_list[kind][k] = ins; + // This is relatively short-lived so let's try a more aggressive load + // factor in the interest of improving performance. + if ((m_used[kind] << 1) >= m_cap[kind]) { // 0.50 + grow(kind); + } + return ins; + } + + LInsp LInsHashSet::findImm(int32_t a, uint32_t &k) + { + LInsHashKind kind = LInsImm; + const uint32_t bitmask = (m_cap[kind] - 1) & ~0x1; + uint32_t hash = hashImm(a) & bitmask; + uint32_t n = 7 << 1; + LInsp ins; + while ((ins = m_list[kind][hash]) != NULL && + (ins->imm32() != a)) + { + NanoAssert(ins->isconst()); + hash = (hash + (n += 2)) & bitmask; // quadratic probe + } + k = hash; + return ins; + } + + uint32_t LInsHashSet::findImm(LInsp ins) + { + uint32_t k; + findImm(ins->imm32(), k); + return k; + } + + LInsp LInsHashSet::findImmq(uint64_t a, uint32_t &k) + { + LInsHashKind kind = LInsImmq; + const uint32_t bitmask = (m_cap[kind] - 1) & ~0x1; + uint32_t hash = hashImmq(a) & bitmask; + uint32_t n = 7 << 1; + LInsp ins; + while ((ins = m_list[kind][hash]) != NULL && + (ins->imm64() != a)) + { + NanoAssert(ins->isconstq()); + hash = (hash + (n += 2)) & bitmask; // quadratic probe + } + k = hash; + return ins; + } + + uint32_t LInsHashSet::findImmq(LInsp ins) + { + uint32_t k; + findImmq(ins->imm64(), k); + return k; + } + + LInsp LInsHashSet::findImmf(double a, uint32_t &k) + { + // We must pun 'a' as a uint64_t otherwise 0 and -0 will be treated as + // equal, which breaks things (see bug 527288). + union { + double d; + uint64_t u64; + } u; + u.d = a; + LInsHashKind kind = LInsImmf; + const uint32_t bitmask = (m_cap[kind] - 1) & ~0x1; + uint32_t hash = hashImmf(a) & bitmask; + uint32_t n = 7 << 1; + LInsp ins; + while ((ins = m_list[kind][hash]) != NULL && + (ins->imm64() != u.u64)) + { + NanoAssert(ins->isconstf()); + hash = (hash + (n += 2)) & bitmask; // quadratic probe + } + k = hash; + return ins; + } + + uint32_t LInsHashSet::findImmf(LInsp ins) + { + uint32_t k; + findImmf(ins->imm64f(), k); + return k; + } + + LInsp LInsHashSet::find1(LOpcode op, LInsp a, uint32_t &k) + { + LInsHashKind kind = LIns1; + const uint32_t bitmask = (m_cap[kind] - 1) & ~0x1; + uint32_t hash = hash1(op,a) & bitmask; + uint32_t n = 7 << 1; + LInsp ins; + while ((ins = m_list[kind][hash]) != NULL && + (ins->opcode() != op || ins->oprnd1() != a)) + { + hash = (hash + (n += 2)) & bitmask; // quadratic probe + } + k = hash; + return ins; + } + + uint32_t LInsHashSet::find1(LInsp ins) + { + uint32_t k; + find1(ins->opcode(), ins->oprnd1(), k); + return k; + } + + LInsp LInsHashSet::find2(LOpcode op, LInsp a, LInsp b, uint32_t &k) + { + LInsHashKind kind = LIns2; + const uint32_t bitmask = (m_cap[kind] - 1) & ~0x1; + uint32_t hash = hash2(op,a,b) & bitmask; + uint32_t n = 7 << 1; + LInsp ins; + while ((ins = m_list[kind][hash]) != NULL && + (ins->opcode() != op || ins->oprnd1() != a || ins->oprnd2() != b)) + { + hash = (hash + (n += 2)) & bitmask; // quadratic probe + } + k = hash; + return ins; + } + + uint32_t LInsHashSet::find2(LInsp ins) + { + uint32_t k; + find2(ins->opcode(), ins->oprnd1(), ins->oprnd2(), k); + return k; + } + + LInsp LInsHashSet::find3(LOpcode op, LInsp a, LInsp b, LInsp c, uint32_t &k) + { + LInsHashKind kind = LIns3; + const uint32_t bitmask = (m_cap[kind] - 1) & ~0x1; + uint32_t hash = hash3(op,a,b,c) & bitmask; + uint32_t n = 7 << 1; + LInsp ins; + while ((ins = m_list[kind][hash]) != NULL && + (ins->opcode() != op || ins->oprnd1() != a || ins->oprnd2() != b || ins->oprnd3() != c)) + { + hash = (hash + (n += 2)) & bitmask; // quadratic probe + } + k = hash; + return ins; + } + + uint32_t LInsHashSet::find3(LInsp ins) + { + uint32_t k; + find3(ins->opcode(), ins->oprnd1(), ins->oprnd2(), ins->oprnd3(), k); + return k; + } + + LInsp LInsHashSet::findLoad(LOpcode op, LInsp a, int32_t d, uint32_t &k) + { + LInsHashKind kind = LInsLoad; + const uint32_t bitmask = (m_cap[kind] - 1) & ~0x1; + uint32_t hash = hashLoad(op,a,d) & bitmask; + uint32_t n = 7 << 1; + LInsp ins; + while ((ins = m_list[kind][hash]) != NULL && + (ins->opcode() != op || ins->oprnd1() != a || ins->disp() != d)) + { + hash = (hash + (n += 2)) & bitmask; // quadratic probe + } + k = hash; + return ins; + } + + uint32_t LInsHashSet::findLoad(LInsp ins) + { + uint32_t k; + findLoad(ins->opcode(), ins->oprnd1(), ins->disp(), k); + return k; + } + + bool argsmatch(LInsp ins, uint32_t argc, LInsp args[]) + { + for (uint32_t j=0; j < argc; j++) + if (ins->arg(j) != args[j]) + return false; + return true; + } + + LInsp LInsHashSet::findCall(const CallInfo *ci, uint32_t argc, LInsp args[], uint32_t &k) + { + LInsHashKind kind = LInsCall; + const uint32_t bitmask = (m_cap[kind] - 1) & ~0x1; + uint32_t hash = hashCall(ci, argc, args) & bitmask; + uint32_t n = 7 << 1; + LInsp ins; + while ((ins = m_list[kind][hash]) != NULL && + (!ins->isCall() || ins->callInfo() != ci || !argsmatch(ins, argc, args))) + { + hash = (hash + (n += 2)) & bitmask; // quadratic probe + } + k = hash; + return ins; + } + + uint32_t LInsHashSet::findCall(LInsp ins) + { + LInsp args[MAXARGS]; + uint32_t argc = ins->argc(); + NanoAssert(argc < MAXARGS); + for (uint32_t j=0; j < argc; j++) + args[j] = ins->arg(j); + uint32_t k; + findCall(ins->callInfo(), argc, args, k); + return k; + } + +#ifdef NJ_VERBOSE + class RetiredEntry + { + public: + Seq* live; + LIns* i; + RetiredEntry(): live(NULL), i(NULL) {} + }; + + class LiveTable + { + Allocator& alloc; + public: + HashMap live; + SeqBuilder retired; + int retiredCount; + int maxlive; + LiveTable(Allocator& alloc) + : alloc(alloc) + , live(alloc) + , retired(alloc) + , retiredCount(0) + , maxlive(0) + { } + + void add(LInsp i, LInsp use) { + if (!i->isconst() && !i->isconstq() && !live.containsKey(i)) { + NanoAssert(size_t(i->opcode()) < sizeof(lirNames) / sizeof(lirNames[0])); + live.put(i,use); + } + } + + void retire(LInsp i) { + RetiredEntry *e = new (alloc) RetiredEntry(); + e->i = i; + SeqBuilder livelist(alloc); + HashMap::Iter iter(live); + int live_count = 0; + while (iter.next()) { + LIns* ins = iter.key(); + if (!ins->isStore() && !ins->isGuard()) { + live_count++; + livelist.insert(ins); + } + } + e->live = livelist.get(); + if (live_count > maxlive) + maxlive = live_count; + + live.remove(i); + retired.insert(e); + retiredCount++; + } + + bool contains(LInsp i) { + return live.containsKey(i); + } + }; + + /* + * traverse the LIR buffer and discover which instructions are live + * by starting from instructions with side effects (stores, calls, branches) + * and marking instructions used by them. Works bottom-up, in one pass. + * if showLiveRefs == true, also print the set of live expressions next to + * each instruction + */ + void live(Allocator& alloc, Fragment *frag, LogControl *logc) + { + // traverse backwards to find live exprs and a few other stats. + + LiveTable live(alloc); + uint32_t exits = 0; + LirReader br(frag->lastIns); + StackFilter sf(&br, alloc, frag->lirbuf, frag->lirbuf->sp, frag->lirbuf->rp); + int total = 0; + if (frag->lirbuf->state) + live.add(frag->lirbuf->state, sf.pos()); + for (LInsp ins = sf.read(); !ins->isop(LIR_start); ins = sf.read()) + { + total++; + + // first handle side-effect instructions + if (ins->isStmt()) + { + live.add(ins, 0); + if (ins->isGuard()) + exits++; + } + + // now propagate liveness + if (live.contains(ins)) + { + live.retire(ins); + + switch (ins->opcode()) { + case LIR_skip: + NanoAssertMsg(0, "Shouldn't see LIR_skip"); + break; + + case LIR_start: + case LIR_regfence: + case LIR_iparam: + case LIR_qparam: + case LIR_ialloc: + case LIR_qalloc: + case LIR_x: + case LIR_xbarrier: + case LIR_j: + case LIR_label: + case LIR_int: + case LIR_quad: + case LIR_float: + // No operands, do nothing. + break; + + case LIR_ld: + case LIR_ldc: + case LIR_ldq: + case LIR_ldqc: + case LIR_ldcb: + case LIR_ldcs: + case LIR_ret: + case LIR_fret: + case LIR_live: + case LIR_flive: + case LIR_xt: + case LIR_xf: + case LIR_xtbl: + case LIR_jt: + case LIR_jf: + case LIR_jtbl: + case LIR_neg: + case LIR_fneg: + case LIR_not: + case LIR_qlo: + case LIR_qhi: + case LIR_ov: + case LIR_i2q: + case LIR_u2q: + case LIR_i2f: + case LIR_u2f: + live.add(ins->oprnd1(), ins); + break; + + case LIR_sti: + case LIR_stqi: + case LIR_eq: + case LIR_lt: + case LIR_gt: + case LIR_le: + case LIR_ge: + case LIR_ult: + case LIR_ugt: + case LIR_ule: + case LIR_uge: + case LIR_feq: + case LIR_flt: + case LIR_fgt: + case LIR_fle: + case LIR_fge: + case LIR_qeq: + case LIR_qlt: + case LIR_qgt: + case LIR_qle: + case LIR_qge: + case LIR_qult: + case LIR_qugt: + case LIR_qule: + case LIR_quge: + case LIR_lsh: + case LIR_rsh: + case LIR_ush: + case LIR_qilsh: + case LIR_qirsh: + case LIR_qursh: + case LIR_iaddp: + case LIR_qaddp: + case LIR_add: + case LIR_sub: + case LIR_mul: + case LIR_div: + case LIR_mod: + case LIR_fadd: + case LIR_fsub: + case LIR_fmul: + case LIR_fdiv: + case LIR_fmod: + case LIR_qiadd: + case LIR_and: + case LIR_or: + case LIR_xor: + case LIR_qiand: + case LIR_qior: + case LIR_qxor: + case LIR_qjoin: + case LIR_file: + case LIR_line: + live.add(ins->oprnd1(), ins); + live.add(ins->oprnd2(), ins); + break; + + case LIR_cmov: + case LIR_qcmov: + live.add(ins->oprnd1(), ins); + live.add(ins->oprnd2(), ins); + live.add(ins->oprnd3(), ins); + break; + + case LIR_icall: + case LIR_fcall: + case LIR_qcall: + for (int i = 0, argc = ins->argc(); i < argc; i++) + live.add(ins->arg(i), ins); + break; + + case LIR_callh: + live.add(ins->oprnd1(), ins); + break; + + default: + NanoAssertMsgf(0, "unhandled opcode: %d", ins->opcode()); + break; + } + } + } + + logc->printf(" Live instruction count %d, total %u, max pressure %d\n", + live.retiredCount, total, live.maxlive); + if (exits > 0) + logc->printf(" Side exits %u\n", exits); + logc->printf(" Showing LIR instructions with live-after variables\n"); + logc->printf("\n"); + + // print live exprs, going forwards + LirNameMap *names = frag->lirbuf->names; + bool newblock = true; + for (Seq* p = live.retired.get(); p != NULL; p = p->tail) { + RetiredEntry* e = p->head; + char livebuf[4000], *s=livebuf; + *s = 0; + if (!newblock && e->i->isop(LIR_label)) { + logc->printf("\n"); + } + newblock = false; + for (Seq* p = e->live; p != NULL; p = p->tail) { + VMPI_strcpy(s, names->formatRef(p->head)); + s += VMPI_strlen(s); + *s++ = ' '; *s = 0; + NanoAssert(s < livebuf+sizeof(livebuf)); + } + /* If the LIR insn is pretty short, print it and its + live-after set on the same line. If not, put + live-after set on a new line, suitably indented. */ + const char* insn_text = names->formatIns(e->i); + if (VMPI_strlen(insn_text) >= 30-2) { + logc->printf(" %-30s\n %-30s %s\n", names->formatIns(e->i), "", livebuf); + } else { + logc->printf(" %-30s %s\n", names->formatIns(e->i), livebuf); + } + + if (e->i->isGuard() || e->i->isBranch() || e->i->isRet()) { + logc->printf("\n"); + newblock = true; + } + } + } + + void LirNameMap::addName(LInsp i, const char* name) { + if (!names.containsKey(i)) { + char *copy = new (alloc) char[VMPI_strlen(name)+1]; + VMPI_strcpy(copy, name); + Entry *e = new (alloc) Entry(copy); + names.put(i, e); + } + } + + void LirNameMap::copyName(LInsp i, const char *s, int suffix) { + char s2[200]; + if (VMPI_isdigit(s[VMPI_strlen(s)-1])) { + // if s ends with a digit, add '_' to clarify the suffix + VMPI_sprintf(s2,"%s_%d", s, suffix); + } else { + VMPI_sprintf(s2,"%s%d", s, suffix); + } + addName(i, s2); + } + + void LirNameMap::formatImm(int32_t c, char *buf) { + if (c >= 10000 || c <= -10000) + VMPI_sprintf(buf,"#%s",labels->format((void*)c)); + else + VMPI_sprintf(buf,"%d", c); + } + + const char* LirNameMap::formatRef(LIns *ref) + { + char buffer[200], *buf=buffer; + buf[0]=0; + if (names.containsKey(ref)) { + const char* name = names.get(ref)->name; + VMPI_strcat(buf, name); + } + else if (ref->isconstf()) { + VMPI_sprintf(buf, "%g", ref->imm64f()); + } + else if (ref->isconstq()) { + int64_t c = ref->imm64(); + if (c >= 10000 || c <= -10000) + VMPI_sprintf(buf, "#0x%llxLL", (long long unsigned int) c); + else + VMPI_sprintf(buf, "%dLL", (int)c); + } + else if (ref->isconst()) { + formatImm(ref->imm32(), buf); + } + else { + if (ref->isCall()) { +#if !defined NANOJIT_64BIT + if (ref->isop(LIR_callh)) { + // we've presumably seen the other half already + ref = ref->oprnd1(); + } else { +#endif + copyName(ref, ref->callInfo()->_name, funccounts.add(ref->callInfo())); +#if !defined NANOJIT_64BIT + } +#endif + } else { + NanoAssert(size_t(ref->opcode()) < sizeof(lirNames) / sizeof(lirNames[0])); + copyName(ref, lirNames[ref->opcode()], lircounts.add(ref->opcode())); + } + const char* name = names.get(ref)->name; + VMPI_strcat(buf, name); + } + return labels->dup(buffer); + } + + const char* LirNameMap::formatIns(LIns* i) + { + char sbuf[4096]; + char *s = sbuf; + LOpcode op = i->opcode(); + switch(op) + { + case LIR_int: + { + VMPI_sprintf(s, "%s = %s %d", formatRef(i), lirNames[op], i->imm32()); + break; + } + + case LIR_alloc: { + VMPI_sprintf(s, "%s = %s %d", formatRef(i), lirNames[op], i->size()); + break; + } + + case LIR_quad: + { + VMPI_sprintf(s, "%s = %s #%X:%X /* %g */", formatRef(i), lirNames[op], + i->imm64_1(), i->imm64_0(), i->imm64f()); + break; + } + + case LIR_float: + { + VMPI_sprintf(s, "%s = %s #%g", formatRef(i), lirNames[op], i->imm64f()); + break; + } + + case LIR_start: + case LIR_regfence: + VMPI_sprintf(s, "%s", lirNames[op]); + break; + + case LIR_qcall: + case LIR_fcall: + case LIR_icall: { + const CallInfo* call = i->callInfo(); + int32_t argc = i->argc(); + if (call->isIndirect()) + VMPI_sprintf(s, "%s = %s [%s] ( ", formatRef(i), lirNames[op], formatRef(i->arg(--argc))); + else + VMPI_sprintf(s, "%s = %s #%s ( ", formatRef(i), lirNames[op], call->_name); + for (int32_t j = argc - 1; j >= 0; j--) { + s += VMPI_strlen(s); + VMPI_sprintf(s, "%s ",formatRef(i->arg(j))); + } + s += VMPI_strlen(s); + VMPI_sprintf(s, ")"); + break; + } + + case LIR_jtbl: { + VMPI_sprintf(s, "%s %s [ ", lirNames[op], formatRef(i->oprnd1())); + for (uint32_t j = 0, n = i->getTableSize(); j < n; j++) { + if (VMPI_strlen(sbuf) + 50 > sizeof(sbuf)) { + s += VMPI_strlen(s); + VMPI_sprintf(s, "... "); + break; + } + LIns* target = i->getTarget(j); + s += VMPI_strlen(s); + VMPI_sprintf(s, "%s ", target ? formatRef(target) : "unpatched"); + } + s += VMPI_strlen(s); + VMPI_sprintf(s, "]"); + break; + } + + case LIR_param: { + uint32_t arg = i->paramArg(); + if (!i->paramKind()) { + if (arg < sizeof(Assembler::argRegs)/sizeof(Assembler::argRegs[0])) { + VMPI_sprintf(s, "%s = %s %d %s", formatRef(i), lirNames[op], + arg, gpn(Assembler::argRegs[arg])); + } else { + VMPI_sprintf(s, "%s = %s %d", formatRef(i), lirNames[op], arg); + } + } else { + VMPI_sprintf(s, "%s = %s %d %s", formatRef(i), lirNames[op], + arg, gpn(Assembler::savedRegs[arg])); + } + break; + } + + case LIR_label: + VMPI_sprintf(s, "%s:", formatRef(i)); + break; + + case LIR_jt: + case LIR_jf: + VMPI_sprintf(s, "%s %s -> %s", lirNames[op], formatRef(i->oprnd1()), + i->oprnd2() ? formatRef(i->oprnd2()) : "unpatched"); + break; + + case LIR_j: + VMPI_sprintf(s, "%s -> %s", lirNames[op], + i->oprnd2() ? formatRef(i->oprnd2()) : "unpatched"); + break; + + case LIR_live: + case LIR_ret: + case LIR_fret: + VMPI_sprintf(s, "%s %s", lirNames[op], formatRef(i->oprnd1())); + break; + + case LIR_callh: + case LIR_neg: + case LIR_fneg: + case LIR_i2f: + case LIR_u2f: + case LIR_qlo: + case LIR_qhi: + case LIR_ov: + case LIR_not: + case LIR_mod: + case LIR_i2q: + case LIR_u2q: + VMPI_sprintf(s, "%s = %s %s", formatRef(i), lirNames[op], formatRef(i->oprnd1())); + break; + + case LIR_x: + case LIR_xt: + case LIR_xf: + case LIR_xbarrier: + case LIR_xtbl: + formatGuard(i, s); + break; + + case LIR_add: case LIR_qiadd: + case LIR_iaddp: case LIR_qaddp: + case LIR_sub: + case LIR_mul: + case LIR_div: + case LIR_fadd: + case LIR_fsub: + case LIR_fmul: + case LIR_fdiv: + case LIR_and: case LIR_qiand: + case LIR_or: case LIR_qior: + case LIR_xor: case LIR_qxor: + case LIR_lsh: case LIR_qilsh: + case LIR_rsh: case LIR_qirsh: + case LIR_ush: case LIR_qursh: + case LIR_eq: case LIR_qeq: + case LIR_lt: case LIR_qlt: + case LIR_le: case LIR_qle: + case LIR_gt: case LIR_qgt: + case LIR_ge: case LIR_qge: + case LIR_ult: case LIR_qult: + case LIR_ule: case LIR_qule: + case LIR_ugt: case LIR_qugt: + case LIR_uge: case LIR_quge: + case LIR_feq: + case LIR_flt: + case LIR_fle: + case LIR_fgt: + case LIR_fge: + VMPI_sprintf(s, "%s = %s %s, %s", formatRef(i), lirNames[op], + formatRef(i->oprnd1()), + formatRef(i->oprnd2())); + break; + + case LIR_qjoin: + VMPI_sprintf(s, "%s (%s), %s", lirNames[op], + formatRef(i->oprnd1()), + formatRef(i->oprnd2())); + break; + + case LIR_qcmov: + case LIR_cmov: + VMPI_sprintf(s, "%s = %s %s ? %s : %s", formatRef(i), lirNames[op], + formatRef(i->oprnd1()), + formatRef(i->oprnd2()), + formatRef(i->oprnd3())); + break; + + case LIR_ld: + case LIR_ldc: + case LIR_ldq: + case LIR_ldqc: + case LIR_ldcb: + case LIR_ldcs: + VMPI_sprintf(s, "%s = %s %s[%d]", formatRef(i), lirNames[op], + formatRef(i->oprnd1()), + i->disp()); + break; + + case LIR_sti: + case LIR_stqi: + VMPI_sprintf(s, "%s %s[%d] = %s", lirNames[op], + formatRef(i->oprnd2()), + i->disp(), + formatRef(i->oprnd1())); + break; + + default: + VMPI_sprintf(s, "?"); + break; + } + NanoAssert(VMPI_strlen(sbuf) < sizeof(sbuf)-1); + return labels->dup(sbuf); + } +#endif + + + CseFilter::CseFilter(LirWriter *out, Allocator& alloc) + : LirWriter(out) + { + uint32_t kInitialCaps[LInsLast + 1]; + kInitialCaps[LInsImm] = 128; + kInitialCaps[LInsImmq] = 16; + kInitialCaps[LInsImmf] = 16; + kInitialCaps[LIns1] = 256; + kInitialCaps[LIns2] = 512; + kInitialCaps[LIns3] = 16; + kInitialCaps[LInsLoad] = 16; + kInitialCaps[LInsCall] = 64; + exprs = new (alloc) LInsHashSet(alloc, kInitialCaps); + } + + LIns* CseFilter::insImm(int32_t imm) + { + uint32_t k; + LInsp ins = exprs->findImm(imm, k); + if (ins) + return ins; + return exprs->add(LInsImm, out->insImm(imm), k); + } + + LIns* CseFilter::insImmq(uint64_t q) + { + uint32_t k; + LInsp ins = exprs->findImmq(q, k); + if (ins) + return ins; + return exprs->add(LInsImmq, out->insImmq(q), k); + } + + LIns* CseFilter::insImmf(double d) + { + uint32_t k; + LInsp ins = exprs->findImmf(d, k); + if (ins) + return ins; + return exprs->add(LInsImmf, out->insImmf(d), k); + } + + LIns* CseFilter::ins0(LOpcode v) + { + if (v == LIR_label) + exprs->clear(); + return out->ins0(v); + } + + LIns* CseFilter::ins1(LOpcode v, LInsp a) + { + if (isCseOpcode(v)) { + uint32_t k; + LInsp ins = exprs->find1(v, a, k); + if (ins) + return ins; + return exprs->add(LIns1, out->ins1(v,a), k); + } + return out->ins1(v,a); + } + + LIns* CseFilter::ins2(LOpcode v, LInsp a, LInsp b) + { + if (isCseOpcode(v)) { + uint32_t k; + LInsp ins = exprs->find2(v, a, b, k); + if (ins) + return ins; + return exprs->add(LIns2, out->ins2(v,a,b), k); + } + return out->ins2(v,a,b); + } + + LIns* CseFilter::ins3(LOpcode v, LInsp a, LInsp b, LInsp c) + { + NanoAssert(isCseOpcode(v)); + uint32_t k; + LInsp ins = exprs->find3(v, a, b, c, k); + if (ins) + return ins; + return exprs->add(LIns3, out->ins3(v,a,b,c), k); + } + + LIns* CseFilter::insLoad(LOpcode v, LInsp base, int32_t disp) + { + if (isCseOpcode(v)) { + uint32_t k; + LInsp ins = exprs->findLoad(v, base, disp, k); + if (ins) + return ins; + return exprs->add(LInsLoad, out->insLoad(v,base,disp), k); + } + return out->insLoad(v,base,disp); + } + + LInsp CseFilter::insGuard(LOpcode v, LInsp c, GuardRecord *gr) + { + // LIR_xt and LIR_xf guards are CSEable. Note that we compare the + // opcode and condition when determining if two guards are equivalent + // -- in find1() and hash1() -- but we do *not* compare the + // GuardRecord. This works because: + // - If guard 1 is taken (exits) then guard 2 is never reached, so + // guard 2 can be removed. + // - If guard 1 is not taken then neither is guard 2, so guard 2 can + // be removed. + // + // The underlying assumptions that are required for this to be safe: + // - There's never a path from the side exit of guard 1 back to guard + // 2; for tree-shaped fragments this should be true. + // - GuardRecords do not contain information other than what is needed + // to execute a successful exit. That is currently true. + // - The CSE algorithm will always keep guard 1 and remove guard 2 + // (not vice versa). The current algorithm does this. + // + if (isCseOpcode(v)) { + // conditional guard + uint32_t k; + LInsp ins = exprs->find1(v, c, k); + if (ins) + return 0; + return exprs->add(LIns1, out->insGuard(v,c,gr), k); + } + return out->insGuard(v, c, gr); + } + + LInsp CseFilter::insCall(const CallInfo *ci, LInsp args[]) + { + if (ci->_cse) { + uint32_t k; + uint32_t argc = ci->count_args(); + LInsp ins = exprs->findCall(ci, argc, args, k); + if (ins) + return ins; + return exprs->add(LInsCall, out->insCall(ci, args), k); + } + return out->insCall(ci, args); + } + + void compile(Assembler* assm, Fragment* frag, Allocator& alloc verbose_only(, LabelMap* labels)) + { + verbose_only( + LogControl *logc = assm->_logc; + bool anyVerb = (logc->lcbits & 0xFFFF & ~LC_FragProfile) > 0; + bool asmVerb = (logc->lcbits & 0xFFFF & LC_Assembly) > 0; + bool liveVerb = (logc->lcbits & 0xFFFF & LC_Liveness) > 0; + ) + + /* BEGIN decorative preamble */ + verbose_only( + if (anyVerb) { + logc->printf("========================================" + "========================================\n"); + logc->printf("=== BEGIN LIR::compile(%p, %p)\n", + (void*)assm, (void*)frag); + logc->printf("===\n"); + }) + /* END decorative preamble */ + + verbose_only( if (liveVerb) { + logc->printf("\n"); + logc->printf("=== Results of liveness analysis:\n"); + logc->printf("===\n"); + live(alloc, frag, logc); + }) + + /* Set up the generic text output cache for the assembler */ + verbose_only( StringList asmOutput(alloc); ) + verbose_only( assm->_outputCache = &asmOutput; ) + + assm->beginAssembly(frag); + if (assm->error()) + return; + + //logc->printf("recompile trigger %X kind %d\n", (int)frag, frag->kind); + + verbose_only( if (anyVerb) { + logc->printf("=== Translating LIR fragments into assembly:\n"); + }) + + // now the the main trunk + verbose_only( if (anyVerb) { + logc->printf("=== -- Compile trunk %s: begin\n", + labels->format(frag)); + }) + + // Used for debug printing, if needed + verbose_only( + ReverseLister *pp_init = NULL; + ReverseLister *pp_after_sf = NULL; + ) + + // set up backwards pipeline: assembler -> StackFilter -> LirReader + LirReader bufreader(frag->lastIns); + + // Used to construct the pipeline + LirFilter* prev = &bufreader; + + // The LIR passes through these filters as listed in this + // function, viz, top to bottom. + + // INITIAL PRINTING + verbose_only( if (assm->_logc->lcbits & LC_ReadLIR) { + pp_init = new (alloc) ReverseLister(prev, alloc, frag->lirbuf->names, assm->_logc, + "Initial LIR"); + prev = pp_init; + }) + + // STACKFILTER + StackFilter stackfilter(prev, alloc, frag->lirbuf, frag->lirbuf->sp, frag->lirbuf->rp); + prev = &stackfilter; + + verbose_only( if (assm->_logc->lcbits & LC_AfterSF) { + pp_after_sf = new (alloc) ReverseLister(prev, alloc, frag->lirbuf->names, assm->_logc, + "After StackFilter"); + prev = pp_after_sf; + }) + + assm->assemble(frag, prev); + + // If we were accumulating debug info in the various ReverseListers, + // call finish() to emit whatever contents they have accumulated. + verbose_only( + if (pp_init) pp_init->finish(); + if (pp_after_sf) pp_after_sf->finish(); + ) + + verbose_only( if (anyVerb) { + logc->printf("=== -- Compile trunk %s: end\n", + labels->format(frag)); + }) + + verbose_only( + if (asmVerb) + assm->outputf("## compiling trunk %s", + labels->format(frag)); + ) + assm->endAssembly(frag); + + // reverse output so that assembly is displayed low-to-high + // Up to this point, assm->_outputCache has been non-NULL, and so + // has been accumulating output. Now we set it to NULL, traverse + // the entire list of stored strings, and hand them a second time + // to assm->output. Since _outputCache is now NULL, outputf just + // hands these strings directly onwards to logc->printf. + verbose_only( if (anyVerb) { + logc->printf("\n"); + logc->printf("=== Aggregated assembly output: BEGIN\n"); + logc->printf("===\n"); + assm->_outputCache = 0; + for (Seq* p = asmOutput.get(); p != NULL; p = p->tail) { + char *str = p->head; + assm->outputf(" %s", str); + } + logc->printf("===\n"); + logc->printf("=== Aggregated assembly output: END\n"); + }); + + if (assm->error()) + frag->fragEntry = 0; + + verbose_only( frag->nCodeBytes += assm->codeBytes; ) + verbose_only( frag->nExitBytes += assm->exitBytes; ) + + /* BEGIN decorative postamble */ + verbose_only( if (anyVerb) { + logc->printf("\n"); + logc->printf("===\n"); + logc->printf("=== END LIR::compile(%p, %p)\n", + (void*)assm, (void*)frag); + logc->printf("========================================" + "========================================\n"); + logc->printf("\n"); + }); + /* END decorative postamble */ + } + + LInsp LoadFilter::insLoad(LOpcode v, LInsp base, int32_t disp) + { + if (base != sp && base != rp && (v == LIR_ld || v == LIR_ldq)) { + uint32_t k; + LInsp ins = exprs->findLoad(v, base, disp, k); + if (ins) + return ins; + return exprs->add(LInsLoad, out->insLoad(v,base,disp), k); + } + return out->insLoad(v, base, disp); + } + + void LoadFilter::clear(LInsp p) + { + if (p != sp && p != rp) + exprs->clear(); + } + + LInsp LoadFilter::insStorei(LInsp v, LInsp b, int32_t d) + { + clear(b); + return out->insStorei(v, b, d); + } + + LInsp LoadFilter::insCall(const CallInfo *ci, LInsp args[]) + { + if (!ci->_cse) + exprs->clear(); + return out->insCall(ci, args); + } + + LInsp LoadFilter::ins0(LOpcode op) + { + if (op == LIR_label) + exprs->clear(); + return out->ins0(op); + } + + #endif /* FEATURE_NANOJIT */ + +#if defined(NJ_VERBOSE) + LabelMap::LabelMap(Allocator& a, LogControl *logc) + : allocator(a), names(a), logc(logc), end(buf) + {} + + void LabelMap::add(const void *p, size_t size, size_t align, const char *name) + { + if (!this || names.containsKey(p)) + return; + char* copy = new (allocator) char[VMPI_strlen(name)+1]; + VMPI_strcpy(copy, name); + Entry *e = new (allocator) Entry(copy, size << align, align); + names.put(p, e); + } + + const char *LabelMap::format(const void *p) + { + char b[200]; + + const void *start = names.findNear(p); + if (start != NULL) { + Entry *e = names.get(start); + const void *end = (const char*)start + e->size; + const char *name = e->name; + if (p == start) { + if (!(logc->lcbits & LC_NoCodeAddrs)) + VMPI_sprintf(b,"%p %s",p,name); + else + VMPI_strcpy(b, name); + return dup(b); + } + else if (p > start && p < end) { + int32_t d = int32_t(intptr_t(p)-intptr_t(start)) >> e->align; + if (!(logc->lcbits & LC_NoCodeAddrs)) + VMPI_sprintf(b, "%p %s+%d", p, name, d); + else + VMPI_sprintf(b,"%s+%d", name, d); + return dup(b); + } + else { + VMPI_sprintf(b, "%p", p); + return dup(b); + } + } + VMPI_sprintf(b, "%p", p); + return dup(b); + } + + const char *LabelMap::dup(const char *b) + { + size_t need = VMPI_strlen(b)+1; + NanoAssert(need <= sizeof(buf)); + char *s = end; + end += need; + if (end > buf+sizeof(buf)) { + s = buf; + end = s+need; + } + VMPI_strcpy(s, b); + return s; + } + + // --------------------------------------------------------------- + // START debug-logging definitions + // --------------------------------------------------------------- + + void LogControl::printf( const char* format, ... ) + { + va_list vargs; + va_start(vargs, format); + vfprintf(stdout, format, vargs); + va_end(vargs); + } + +#endif // NJ_VERBOSE + + +#ifdef FEATURE_NANOJIT +#ifdef DEBUG + LIns* SanityFilter::ins1(LOpcode v, LIns* s0) + { + switch (v) + { + case LIR_fneg: + case LIR_fret: + case LIR_qlo: + case LIR_qhi: + NanoAssert(s0->isQuad()); + break; + case LIR_not: + case LIR_neg: + case LIR_u2f: + case LIR_i2f: + case LIR_i2q: case LIR_u2q: + NanoAssert(!s0->isQuad()); + break; + default: + break; + } + return out->ins1(v, s0); + } + + LIns* SanityFilter::ins2(LOpcode v, LIns* s0, LIns* s1) + { + switch (v) { + case LIR_fadd: + case LIR_fsub: + case LIR_fmul: + case LIR_fdiv: + case LIR_feq: + case LIR_flt: + case LIR_fgt: + case LIR_fle: + case LIR_fge: + case LIR_qaddp: + case LIR_qior: + case LIR_qxor: + case LIR_qiand: + case LIR_qiadd: + case LIR_qeq: + case LIR_qlt: case LIR_qult: + case LIR_qgt: case LIR_qugt: + case LIR_qle: case LIR_quge: + NanoAssert(s0->isQuad() && s1->isQuad()); + break; + case LIR_add: + case LIR_iaddp: + case LIR_sub: + case LIR_mul: + case LIR_and: + case LIR_or: + case LIR_xor: + case LIR_lsh: + case LIR_rsh: + case LIR_ush: + case LIR_eq: + case LIR_lt: case LIR_ult: + case LIR_gt: case LIR_ugt: + case LIR_le: case LIR_ule: + case LIR_ge: case LIR_uge: + NanoAssert(!s0->isQuad() && !s1->isQuad()); + break; + case LIR_qilsh: + case LIR_qirsh: + case LIR_qursh: + NanoAssert(s0->isQuad() && !s1->isQuad()); + break; + default: + break; + } + return out->ins2(v, s0, s1); + } + + LIns* SanityFilter::ins3(LOpcode v, LIns* s0, LIns* s1, LIns* s2) + { + switch (v) + { + case LIR_cmov: + NanoAssert(s0->isCond() || s0->isconst()); + NanoAssert(!s1->isQuad() && !s2->isQuad()); + break; + case LIR_qcmov: + NanoAssert(s0->isCond() || s0->isconst()); + NanoAssert(s1->isQuad() && s2->isQuad()); + break; + default: + break; + } + return out->ins3(v, s0, s1, s2); + } +#endif +#endif + +} diff --git a/ape-server/deps/js/src/nanojit/LIR.h b/ape-server/deps/js/src/nanojit/LIR.h new file mode 100755 index 0000000..a6a4de9 --- /dev/null +++ b/ape-server/deps/js/src/nanojit/LIR.h @@ -0,0 +1,1503 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is [Open Source Virtual Machine]. + * + * The Initial Developer of the Original Code is + * Adobe System Incorporated. + * Portions created by the Initial Developer are Copyright (C) 2004-2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adobe AS3 Team + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef __nanojit_LIR__ +#define __nanojit_LIR__ + +/** + * Fundamentally, the arguments to the various operands can be grouped along + * two dimensions. One dimension is size: can the arguments fit into a 32-bit + * register, or not? The other dimension is whether the argument is an integer + * (including pointers) or a floating-point value. In all comments below, + * "integer" means integer of any size, including 64-bit, unless otherwise + * specified. All floating-point values are always 64-bit. Below, "quad" is + * used for a 64-bit value that might be either integer or floating-point. + */ +namespace nanojit +{ + struct GuardRecord; + struct SideExit; + + enum AbiKind { + ABI_FASTCALL, + ABI_THISCALL, + ABI_STDCALL, + ABI_CDECL + }; + + enum ArgSize { + ARGSIZE_NONE = 0, + ARGSIZE_F = 1, // double (64bit) + ARGSIZE_I = 2, // int32_t + ARGSIZE_Q = 3, // uint64_t + ARGSIZE_U = 6, // uint32_t + ARGSIZE_MASK_ANY = 7, + ARGSIZE_MASK_INT = 2, + ARGSIZE_SHIFT = 3, + + // aliases + ARGSIZE_P = PTR_SIZE(ARGSIZE_I, ARGSIZE_Q), // pointer + ARGSIZE_LO = ARGSIZE_I, // int32_t + ARGSIZE_B = ARGSIZE_I, // bool + ARGSIZE_V = ARGSIZE_NONE // void + }; + + enum IndirectCall { + CALL_INDIRECT = 0 + }; + + struct CallInfo + { + uintptr_t _address; + uint32_t _argtypes:27; // 9 3-bit fields indicating arg type, by ARGSIZE above (including ret type): a1 a2 a3 a4 a5 ret + uint8_t _cse:1; // true if no side effects + uint8_t _fold:1; // true if no side effects + AbiKind _abi:3; + verbose_only ( const char* _name; ) + + uint32_t _count_args(uint32_t mask) const; + uint32_t get_sizes(ArgSize*) const; + + inline bool isIndirect() const { + return _address < 256; + } + inline uint32_t count_args() const { + return _count_args(ARGSIZE_MASK_ANY); + } + inline uint32_t count_iargs() const { + return _count_args(ARGSIZE_MASK_INT); + } + // fargs = args - iargs + }; + + /* + * Record for extra data used to compile switches as jump tables. + */ + struct SwitchInfo + { + NIns** table; // Jump table; a jump address is NIns* + uint32_t count; // Number of table entries + // Index value at last execution of the switch. The index value + // is the offset into the jump table. Thus it is computed as + // (switch expression) - (lowest case value). + uint32_t index; + }; + + inline bool isCseOpcode(LOpcode op) { + op = LOpcode(op & ~LIR64); + return op >= LIR_int && op <= LIR_uge; + } + inline bool isRetOpcode(LOpcode op) { + return (op & ~LIR64) == LIR_ret; + } + + // Array holding the 'repkind' field from LIRopcode.tbl. + extern const uint8_t repKinds[]; + + //----------------------------------------------------------------------- + // Low-level instructions. This is a bit complicated, because we have a + // variable-width representation to minimise space usage. + // + // - Instruction size is always an integral multiple of word size. + // + // - Every instruction has at least one word, holding the opcode and the + // reservation info. That word is in class LIns. + // + // - Beyond that, most instructions have 1, 2 or 3 extra words. These + // extra words are in classes LInsOp1, LInsOp2, etc (collectively called + // "LInsXYZ" in what follows). Each LInsXYZ class also contains an LIns, + // accessible by the 'ins' member, which holds the LIns data. + // + // - LIR is written forward, but read backwards. When reading backwards, + // in order to find the opcode, it must be in a predictable place in the + // LInsXYZ isn't affected by instruction width. Therefore, the LIns + // word (which contains the opcode) is always the *last* word in an + // instruction. + // + // - Each instruction is created by casting pre-allocated bytes from a + // LirBuffer to the LInsXYZ type. Therefore there are no constructors + // for LIns or LInsXYZ. + // + // - The standard handle for an instruction is a LIns*. This actually + // points to the LIns word, ie. to the final word in the instruction. + // This is a bit odd, but it allows the instruction's opcode to be + // easily accessed. Once you've looked at the opcode and know what kind + // of instruction it is, if you want to access any of the other words, + // you need to use toLInsXYZ(), which takes the LIns* and gives you an + // LInsXYZ*, ie. the pointer to the actual start of the instruction's + // bytes. From there you can access the instruction-specific extra + // words. + // + // - However, from outside class LIns, LInsXYZ isn't visible, nor is + // toLInsXYZ() -- from outside LIns, all LIR instructions are handled + // via LIns pointers and get/set methods are used for all LIns/LInsXYZ + // accesses. In fact, all data members in LInsXYZ are private and can + // only be accessed by LIns, which is a friend class. The only thing + // anyone outside LIns can do with a LInsXYZ is call getLIns(). + // + // - An example Op2 instruction and the likely pointers to it (each line + // represents a word, and pointers to a line point to the start of the + // word on that line): + // + // [ oprnd_2 <-- LInsOp2* insOp2 == toLInsOp2(ins) + // oprnd_1 + // opcode + resv ] <-- LIns* ins + // + // - LIR_skip instructions are used to link code chunks. If the first + // instruction on a chunk isn't a LIR_start, it will be a skip, and the + // skip's operand will point to the last LIns on the preceding chunk. + // LInsSk has the same layout as LInsOp1, but we represent it as a + // different class because there are some places where we treat + // skips specially and so having it separate seems like a good idea. + // + // - Various things about the size and layout of LIns and LInsXYZ are + // statically checked in staticSanityCheck(). In particular, this is + // worthwhile because there's nothing that guarantees that all the + // LInsXYZ classes have a size that is a multiple of word size (but in + // practice all sane compilers use a layout that results in this). We + // also check that every LInsXYZ is word-aligned in + // LirBuffer::makeRoom(); this seems sensible to avoid potential + // slowdowns due to misalignment. It relies on chunks themselves being + // word-aligned, which is extremely likely. + // + // - There is an enum, LInsRepKind, with one member for each of the + // LInsXYZ kinds. Each opcode is categorised with its LInsRepKind value + // in LIRopcode.tbl, and this is used in various places. + //----------------------------------------------------------------------- + + enum LInsRepKind { + // LRK_XYZ corresponds to class LInsXYZ. + LRK_Op0, + LRK_Op1, + LRK_Op2, + LRK_Op3, + LRK_Ld, + LRK_Sti, + LRK_Sk, + LRK_C, + LRK_P, + LRK_I, + LRK_I64, + LRK_Jtbl, + LRK_None // this one is used for unused opcode numbers + }; + + class LInsOp0; + class LInsOp1; + class LInsOp2; + class LInsOp3; + class LInsLd; + class LInsSti; + class LInsSk; + class LInsC; + class LInsP; + class LInsI; + class LInsI64; + class LInsJtbl; + + class LIns + { + private: + // LastWord: fields shared by all LIns kinds. The .arIndex, .reg and + // .used fields form a "reservation" that is used temporarily during + // assembly to record information relating to register allocation. + // See class RegAlloc for more details. + struct LastWord { + uint32_t arIndex:16; // index into stack frame. displ is -4*arIndex + Register reg:7; // register UnknownReg implies not in register + uint32_t used:1; // when set, the reservation is active + + LOpcode opcode:8; // instruction's opcode + }; + + union { + LastWord lastWord; + // Force sizeof(LIns)==8 and 8-byte alignment on 64-bit machines. + // This is necessary because sizeof(LastWord)==4 and we want all + // instances of LIns to be pointer-aligned. + void* dummy; + }; + + // LIns-to-LInsXYZ converters. + inline LInsOp0* toLInsOp0() const; + inline LInsOp1* toLInsOp1() const; + inline LInsOp2* toLInsOp2() const; + inline LInsOp3* toLInsOp3() const; + inline LInsLd* toLInsLd() const; + inline LInsSti* toLInsSti() const; + inline LInsSk* toLInsSk() const; + inline LInsC* toLInsC() const; + inline LInsP* toLInsP() const; + inline LInsI* toLInsI() const; + inline LInsI64* toLInsI64() const; + inline LInsJtbl*toLInsJtbl()const; + + void staticSanityCheck(); + + public: + // LIns initializers. + inline void initLInsOp0(LOpcode opcode); + inline void initLInsOp1(LOpcode opcode, LIns* oprnd1); + inline void initLInsOp2(LOpcode opcode, LIns* oprnd1, LIns* oprnd2); + inline void initLInsOp3(LOpcode opcode, LIns* oprnd1, LIns* oprnd2, LIns* oprnd3); + inline void initLInsLd(LOpcode opcode, LIns* val, int32_t d); + inline void initLInsSti(LOpcode opcode, LIns* val, LIns* base, int32_t d); + inline void initLInsSk(LIns* prevLIns); + // Nb: args[] must be allocated and initialised before being passed in; + // initLInsC() just copies the pointer into the LInsC. + inline void initLInsC(LOpcode opcode, LIns** args, const CallInfo* ci); + inline void initLInsP(int32_t arg, int32_t kind); + inline void initLInsI(LOpcode opcode, int32_t imm32); + inline void initLInsI64(LOpcode opcode, int64_t imm64); + inline void initLInsJtbl(LIns* index, uint32_t size, LIns** table); + + LOpcode opcode() const { return lastWord.opcode; } + + void markAsUsed() { + lastWord.reg = UnknownReg; + lastWord.arIndex = 0; + lastWord.used = 1; + } + void markAsClear() { + lastWord.used = 0; + } + bool isUsed() { + return lastWord.used; + } + bool hasKnownReg() { + NanoAssert(isUsed()); + return getReg() != UnknownReg; + } + Register getReg() { + NanoAssert(isUsed()); + return lastWord.reg; + } + void setReg(Register r) { + NanoAssert(isUsed()); + lastWord.reg = r; + } + uint32_t getArIndex() { + NanoAssert(isUsed()); + return lastWord.arIndex; + } + void setArIndex(uint32_t arIndex) { + NanoAssert(isUsed()); + lastWord.arIndex = arIndex; + } + bool isUnusedOrHasUnknownReg() { + return !isUsed() || !hasKnownReg(); + } + + // For various instruction kinds. + inline LIns* oprnd1() const; + inline LIns* oprnd2() const; + inline LIns* oprnd3() const; + + // For branches. + inline LIns* getTarget() const; + inline void setTarget(LIns* label); + + // For guards. + inline GuardRecord* record() const; + + // Displacement for LInsLd/LInsSti + inline int32_t disp() const; + + // For LInsSk. + inline LIns* prevLIns() const; + + // For LInsP. + inline uint8_t paramArg() const; + inline uint8_t paramKind() const; + + // For LInsI. + inline int32_t imm32() const; + + // For LInsI64. + inline int32_t imm64_0() const; + inline int32_t imm64_1() const; + inline uint64_t imm64() const; + inline double imm64f() const; + + // For LIR_alloc. + inline int32_t size() const; + inline void setSize(int32_t nbytes); + + // For LInsC. + inline LIns* arg(uint32_t i) const; + inline uint32_t argc() const; + inline LIns* callArgN(uint32_t n) const; + inline const CallInfo* callInfo() const; + + // For LIR_jtbl + inline uint32_t getTableSize() const; + inline LIns* getTarget(uint32_t index) const; + inline void setTarget(uint32_t index, LIns* label) const; + + // isLInsXYZ() returns true if the instruction has the LInsXYZ form. + // Note that there is some overlap with other predicates, eg. + // isStore()==isLInsSti(), isCall()==isLInsC(), but that's ok; these + // ones are used mostly to check that opcodes are appropriate for + // instruction layouts, the others are used for non-debugging + // purposes. + bool isLInsOp0() const { + NanoAssert(LRK_None != repKinds[opcode()]); + return LRK_Op0 == repKinds[opcode()]; + } + bool isLInsOp1() const { + NanoAssert(LRK_None != repKinds[opcode()]); + return LRK_Op1 == repKinds[opcode()]; + } + bool isLInsOp2() const { + NanoAssert(LRK_None != repKinds[opcode()]); + return LRK_Op2 == repKinds[opcode()]; + } + bool isLInsOp3() const { + NanoAssert(LRK_None != repKinds[opcode()]); + return LRK_Op3 == repKinds[opcode()]; + } + bool isLInsLd() const { + NanoAssert(LRK_None != repKinds[opcode()]); + return LRK_Ld == repKinds[opcode()]; + } + bool isLInsSti() const { + NanoAssert(LRK_None != repKinds[opcode()]); + return LRK_Sti == repKinds[opcode()]; + } + bool isLInsSk() const { + NanoAssert(LRK_None != repKinds[opcode()]); + return LRK_Sk == repKinds[opcode()]; + } + bool isLInsC() const { + NanoAssert(LRK_None != repKinds[opcode()]); + return LRK_C == repKinds[opcode()]; + } + bool isLInsP() const { + NanoAssert(LRK_None != repKinds[opcode()]); + return LRK_P == repKinds[opcode()]; + } + bool isLInsI() const { + NanoAssert(LRK_None != repKinds[opcode()]); + return LRK_I == repKinds[opcode()]; + } + bool isLInsI64() const { + NanoAssert(LRK_None != repKinds[opcode()]); + return LRK_I64 == repKinds[opcode()]; + } + bool isLInsJtbl() const { + NanoAssert(LRK_None != repKinds[opcode()]); + return LRK_Jtbl == repKinds[opcode()]; + } + + // LIns predicates. + bool isCse() const { + return isCseOpcode(opcode()) || (isCall() && callInfo()->_cse); + } + bool isRet() const { + return isRetOpcode(opcode()); + } + bool isop(LOpcode o) const { + return opcode() == o; + } + bool isQuad() const { + LOpcode op = opcode(); +#ifdef NANOJIT_64BIT + // callh in 64bit cpu's means a call that returns an int64 in a single register + return (!(op >= LIR_qeq && op <= LIR_quge) && (op & LIR64) != 0) || + op == LIR_callh; +#else + // callh in 32bit cpu's means the 32bit MSW of an int64 result in 2 registers + return (op & LIR64) != 0; +#endif + } + bool isCond() const { + LOpcode op = opcode(); + return (op == LIR_ov) || isCmp(); + } + bool isFloat() const; // not inlined because it contains a switch + bool isCmp() const { + LOpcode op = opcode(); + return (op >= LIR_eq && op <= LIR_uge) || + (op >= LIR_qeq && op <= LIR_quge) || + (op >= LIR_feq && op <= LIR_fge); + } + bool isCall() const { + LOpcode op = opcode(); + return (op & ~LIR64) == LIR_icall || op == LIR_qcall; + } + bool isStore() const { + LOpcode op = LOpcode(opcode() & ~LIR64); + return op == LIR_sti; + } + bool isLoad() const { + LOpcode op = opcode(); + return op == LIR_ldq || op == LIR_ld || op == LIR_ldc || + op == LIR_ldqc || op == LIR_ldcs || op == LIR_ldcb; + } + bool isGuard() const { + LOpcode op = opcode(); + return op == LIR_x || op == LIR_xf || op == LIR_xt || + op == LIR_xbarrier || op == LIR_xtbl; + } + // True if the instruction is a 32-bit or smaller constant integer. + bool isconst() const { + return opcode() == LIR_int; + } + // True if the instruction is a 32-bit or smaller constant integer and + // has the value val when treated as a 32-bit signed integer. + bool isconstval(int32_t val) const { + return isconst() && imm32()==val; + } + // True if the instruction is a constant quad value. + bool isconstq() const { + return opcode() == LIR_quad || opcode() == LIR_float; + } + // True if the instruction is a constant pointer value. + bool isconstp() const + { +#ifdef NANOJIT_64BIT + return isconstq(); +#else + return isconst(); +#endif + } + // True if the instruction is a constant float value. + bool isconstf() const { + return opcode() == LIR_float; + } + + bool isBranch() const { + return isop(LIR_jt) || isop(LIR_jf) || isop(LIR_j) || isop(LIR_jtbl); + } + + bool isPtr() { +#ifdef NANOJIT_64BIT + return isQuad(); +#else + return !isQuad(); +#endif + } + + // Return true if removal of 'ins' from a LIR fragment could + // possibly change the behaviour of that fragment, even if any + // value computed by 'ins' is not used later in the fragment. + // In other words, can 'ins' possible alter control flow or memory? + // Note, this assumes that loads will never fault and hence cannot + // affect the control flow. + bool isStmt() { + return isGuard() || isBranch() || + (isCall() && !isCse()) || + isStore() || + isop(LIR_label) || isop(LIR_live) || isop(LIR_flive) || + isop(LIR_regfence) || + isRet(); + } + + inline void* constvalp() const + { + #ifdef NANOJIT_64BIT + return (void*)imm64(); + #else + return (void*)imm32(); + #endif + } + }; + + typedef LIns* LInsp; + typedef SeqBuilder InsList; + + + // 0-operand form. Used for LIR_start and LIR_label. + class LInsOp0 + { + private: + friend class LIns; + + LIns ins; + + public: + LIns* getLIns() { return &ins; }; + }; + + // 1-operand form. Used for LIR_ret, LIR_ov, unary arithmetic/logic ops, + // etc. + class LInsOp1 + { + private: + friend class LIns; + + LIns* oprnd_1; + + LIns ins; + + public: + LIns* getLIns() { return &ins; }; + }; + + // 2-operand form. Used for loads, guards, branches, comparisons, binary + // arithmetic/logic ops, etc. + class LInsOp2 + { + private: + friend class LIns; + + LIns* oprnd_2; + + LIns* oprnd_1; + + LIns ins; + + public: + LIns* getLIns() { return &ins; }; + }; + + // 3-operand form. Used for conditional moves. + class LInsOp3 + { + private: + friend class LIns; + + LIns* oprnd_3; + + LIns* oprnd_2; + + LIns* oprnd_1; + + LIns ins; + + public: + LIns* getLIns() { return &ins; }; + }; + + // Used for all loads. + class LInsLd + { + private: + friend class LIns; + + int32_t disp; + + LIns* oprnd_1; + + LIns ins; + + public: + LIns* getLIns() { return &ins; }; + }; + + // Used for LIR_sti and LIR_stqi. + class LInsSti + { + private: + friend class LIns; + + int32_t disp; + + LIns* oprnd_2; + + LIns* oprnd_1; + + LIns ins; + + public: + LIns* getLIns() { return &ins; }; + }; + + // Used for LIR_skip. + class LInsSk + { + private: + friend class LIns; + + LIns* prevLIns; + + LIns ins; + + public: + LIns* getLIns() { return &ins; }; + }; + + // Used for all variants of LIR_call. + class LInsC + { + private: + friend class LIns; + + // Arguments in reverse order, just like insCall() (ie. args[0] holds + // the rightmost arg). The array should be allocated by the same + // allocator as the LIR buffers, because it has the same lifetime. + LIns** args; + + const CallInfo* ci; + + LIns ins; + + public: + LIns* getLIns() { return &ins; }; + }; + + // Used for LIR_iparam. + class LInsP + { + private: + friend class LIns; + + uintptr_t arg:8; + uintptr_t kind:8; + + LIns ins; + + public: + LIns* getLIns() { return &ins; }; + }; + + // Used for LIR_int and LIR_ialloc. + class LInsI + { + private: + friend class LIns; + + int32_t imm32; + + LIns ins; + + public: + LIns* getLIns() { return &ins; }; + }; + + // Used for LIR_quad. + class LInsI64 + { + private: + friend class LIns; + + int32_t imm64_0; + + int32_t imm64_1; + + LIns ins; + + public: + LIns* getLIns() { return &ins; }; + }; + + // Used for LIR_jtbl. oprnd_1 must be a uint32_t index in + // the range 0 <= index < size; no range check is performed. + class LInsJtbl + { + private: + friend class LIns; + + uint32_t size; // number of entries in table + LIns** table; // pointer to table[size] with same lifetime as this LInsJtbl + LIns* oprnd_1; // uint32_t index expression + + LIns ins; + + public: + LIns* getLIns() { return &ins; } + }; + + // Used only as a placeholder for OPDEF macros for unused opcodes in + // LIRopcode.tbl. + class LInsNone + { + }; + + LInsOp0* LIns::toLInsOp0() const { return (LInsOp0*)( uintptr_t(this+1) - sizeof(LInsOp0) ); } + LInsOp1* LIns::toLInsOp1() const { return (LInsOp1*)( uintptr_t(this+1) - sizeof(LInsOp1) ); } + LInsOp2* LIns::toLInsOp2() const { return (LInsOp2*)( uintptr_t(this+1) - sizeof(LInsOp2) ); } + LInsOp3* LIns::toLInsOp3() const { return (LInsOp3*)( uintptr_t(this+1) - sizeof(LInsOp3) ); } + LInsLd* LIns::toLInsLd() const { return (LInsLd* )( uintptr_t(this+1) - sizeof(LInsLd ) ); } + LInsSti* LIns::toLInsSti() const { return (LInsSti*)( uintptr_t(this+1) - sizeof(LInsSti) ); } + LInsSk* LIns::toLInsSk() const { return (LInsSk* )( uintptr_t(this+1) - sizeof(LInsSk ) ); } + LInsC* LIns::toLInsC() const { return (LInsC* )( uintptr_t(this+1) - sizeof(LInsC ) ); } + LInsP* LIns::toLInsP() const { return (LInsP* )( uintptr_t(this+1) - sizeof(LInsP ) ); } + LInsI* LIns::toLInsI() const { return (LInsI* )( uintptr_t(this+1) - sizeof(LInsI ) ); } + LInsI64* LIns::toLInsI64() const { return (LInsI64*)( uintptr_t(this+1) - sizeof(LInsI64) ); } + LInsJtbl*LIns::toLInsJtbl()const { return (LInsJtbl*)(uintptr_t(this+1) - sizeof(LInsJtbl)); } + + void LIns::initLInsOp0(LOpcode opcode) { + markAsClear(); + lastWord.opcode = opcode; + NanoAssert(isLInsOp0()); + } + void LIns::initLInsOp1(LOpcode opcode, LIns* oprnd1) { + markAsClear(); + lastWord.opcode = opcode; + toLInsOp1()->oprnd_1 = oprnd1; + NanoAssert(isLInsOp1()); + } + void LIns::initLInsOp2(LOpcode opcode, LIns* oprnd1, LIns* oprnd2) { + markAsClear(); + lastWord.opcode = opcode; + toLInsOp2()->oprnd_1 = oprnd1; + toLInsOp2()->oprnd_2 = oprnd2; + NanoAssert(isLInsOp2()); + } + void LIns::initLInsOp3(LOpcode opcode, LIns* oprnd1, LIns* oprnd2, LIns* oprnd3) { + markAsClear(); + lastWord.opcode = opcode; + toLInsOp3()->oprnd_1 = oprnd1; + toLInsOp3()->oprnd_2 = oprnd2; + toLInsOp3()->oprnd_3 = oprnd3; + NanoAssert(isLInsOp3()); + } + void LIns::initLInsLd(LOpcode opcode, LIns* val, int32_t d) { + markAsClear(); + lastWord.opcode = opcode; + toLInsLd()->oprnd_1 = val; + toLInsLd()->disp = d; + NanoAssert(isLInsLd()); + } + void LIns::initLInsSti(LOpcode opcode, LIns* val, LIns* base, int32_t d) { + markAsClear(); + lastWord.opcode = opcode; + toLInsSti()->oprnd_1 = val; + toLInsSti()->oprnd_2 = base; + toLInsSti()->disp = d; + NanoAssert(isLInsSti()); + } + void LIns::initLInsSk(LIns* prevLIns) { + markAsClear(); + lastWord.opcode = LIR_skip; + toLInsSk()->prevLIns = prevLIns; + NanoAssert(isLInsSk()); + } + void LIns::initLInsC(LOpcode opcode, LIns** args, const CallInfo* ci) { + markAsClear(); + lastWord.opcode = opcode; + toLInsC()->args = args; + toLInsC()->ci = ci; + NanoAssert(isLInsC()); + } + void LIns::initLInsP(int32_t arg, int32_t kind) { + markAsClear(); + lastWord.opcode = LIR_param; + NanoAssert(isU8(arg) && isU8(kind)); + toLInsP()->arg = arg; + toLInsP()->kind = kind; + NanoAssert(isLInsP()); + } + void LIns::initLInsI(LOpcode opcode, int32_t imm32) { + markAsClear(); + lastWord.opcode = opcode; + toLInsI()->imm32 = imm32; + NanoAssert(isLInsI()); + } + void LIns::initLInsI64(LOpcode opcode, int64_t imm64) { + markAsClear(); + lastWord.opcode = opcode; + toLInsI64()->imm64_0 = int32_t(imm64); + toLInsI64()->imm64_1 = int32_t(imm64 >> 32); + NanoAssert(isLInsI64()); + } + void LIns::initLInsJtbl(LIns* index, uint32_t size, LIns** table) { + markAsClear(); + lastWord.opcode = LIR_jtbl; + toLInsJtbl()->oprnd_1 = index; + toLInsJtbl()->table = table; + toLInsJtbl()->size = size; + NanoAssert(isLInsJtbl()); + } + + LIns* LIns::oprnd1() const { + NanoAssert(isLInsOp1() || isLInsOp2() || isLInsOp3() || isLInsLd() || isLInsSti() || isLInsJtbl()); + return toLInsOp2()->oprnd_1; + } + LIns* LIns::oprnd2() const { + NanoAssert(isLInsOp2() || isLInsOp3() || isLInsSti()); + return toLInsOp2()->oprnd_2; + } + LIns* LIns::oprnd3() const { + NanoAssert(isLInsOp3()); + return toLInsOp3()->oprnd_3; + } + + LIns* LIns::getTarget() const { + NanoAssert(isBranch() && !isop(LIR_jtbl)); + return oprnd2(); + } + + void LIns::setTarget(LIns* label) { + NanoAssert(label && label->isop(LIR_label)); + NanoAssert(isBranch() && !isop(LIR_jtbl)); + toLInsOp2()->oprnd_2 = label; + } + + LIns* LIns::getTarget(uint32_t index) const { + NanoAssert(isop(LIR_jtbl)); + NanoAssert(index < toLInsJtbl()->size); + return toLInsJtbl()->table[index]; + } + + void LIns::setTarget(uint32_t index, LIns* label) const { + NanoAssert(label && label->isop(LIR_label)); + NanoAssert(isop(LIR_jtbl)); + NanoAssert(index < toLInsJtbl()->size); + toLInsJtbl()->table[index] = label; + } + + GuardRecord *LIns::record() const { + NanoAssert(isGuard()); + return (GuardRecord*)oprnd2(); + } + + int32_t LIns::disp() const { + if (isLInsSti()) { + return toLInsSti()->disp; + } else { + NanoAssert(isLInsLd()); + return toLInsLd()->disp; + } + } + + LIns* LIns::prevLIns() const { + NanoAssert(isLInsSk()); + return toLInsSk()->prevLIns; + } + + inline uint8_t LIns::paramArg() const { NanoAssert(isop(LIR_param)); return toLInsP()->arg; } + inline uint8_t LIns::paramKind() const { NanoAssert(isop(LIR_param)); return toLInsP()->kind; } + + inline int32_t LIns::imm32() const { NanoAssert(isconst()); return toLInsI()->imm32; } + + inline int32_t LIns::imm64_0() const { NanoAssert(isconstq()); return toLInsI64()->imm64_0; } + inline int32_t LIns::imm64_1() const { NanoAssert(isconstq()); return toLInsI64()->imm64_1; } + uint64_t LIns::imm64() const { + NanoAssert(isconstq()); + return (uint64_t(toLInsI64()->imm64_1) << 32) | uint32_t(toLInsI64()->imm64_0); + } + double LIns::imm64f() const { + union { + double f; + uint64_t q; + } u; + u.q = imm64(); + return u.f; + } + + int32_t LIns::size() const { + NanoAssert(isop(LIR_alloc)); + return toLInsI()->imm32 << 2; + } + + void LIns::setSize(int32_t nbytes) { + NanoAssert(isop(LIR_alloc)); + NanoAssert(nbytes > 0); + toLInsI()->imm32 = (nbytes+3)>>2; // # of required 32bit words + } + + // Index args in reverse order, i.e. arg(0) returns the rightmost arg. + // Nb: this must be kept in sync with insCall(). + LIns* LIns::arg(uint32_t i) const + { + NanoAssert(isCall()); + NanoAssert(i < callInfo()->count_args()); + return toLInsC()->args[i]; // args[] is in right-to-left order as well + } + + uint32_t LIns::argc() const { + return callInfo()->count_args(); + } + + LIns* LIns::callArgN(uint32_t n) const + { + return arg(argc()-n-1); + } + + const CallInfo* LIns::callInfo() const + { + NanoAssert(isCall()); + return toLInsC()->ci; + } + + uint32_t LIns::getTableSize() const + { + NanoAssert(isLInsJtbl()); + return toLInsJtbl()->size; + } + + class LirWriter + { + protected: + LInsp insDisp(LOpcode op, LInsp base, int32_t& d) { + if (!isValidDisplacement(op, d)) { + base = ins2i(LIR_piadd, base, d); + d = 0; + } + return base; + } + public: + LirWriter *out; + + LirWriter(LirWriter* out) + : out(out) {} + virtual ~LirWriter() {} + + virtual LInsp ins0(LOpcode v) { + return out->ins0(v); + } + virtual LInsp ins1(LOpcode v, LIns* a) { + return out->ins1(v, a); + } + virtual LInsp ins2(LOpcode v, LIns* a, LIns* b) { + return out->ins2(v, a, b); + } + virtual LInsp ins3(LOpcode v, LIns* a, LIns* b, LIns* c) { + return out->ins3(v, a, b, c); + } + virtual LInsp insGuard(LOpcode v, LIns *c, GuardRecord *gr) { + return out->insGuard(v, c, gr); + } + virtual LInsp insBranch(LOpcode v, LInsp condition, LInsp to) { + return out->insBranch(v, condition, to); + } + // arg: 0=first, 1=second, ... + // kind: 0=arg 1=saved-reg + virtual LInsp insParam(int32_t arg, int32_t kind) { + return out->insParam(arg, kind); + } + virtual LInsp insImm(int32_t imm) { + return out->insImm(imm); + } + virtual LInsp insImmq(uint64_t imm) { + return out->insImmq(imm); + } + virtual LInsp insImmf(double d) { + return out->insImmf(d); + } + virtual LInsp insLoad(LOpcode op, LIns* base, int32_t d) { + return out->insLoad(op, base, d); + } + virtual LInsp insStorei(LIns* value, LIns* base, int32_t d) { + return out->insStorei(value, base, d); + } + // args[] is in reverse order, ie. args[0] holds the rightmost arg. + virtual LInsp insCall(const CallInfo *call, LInsp args[]) { + return out->insCall(call, args); + } + virtual LInsp insAlloc(int32_t size) { + NanoAssert(size != 0); + return out->insAlloc(size); + } + virtual LInsp insJtbl(LIns* index, uint32_t size) { + return out->insJtbl(index, size); + } + + // convenience functions + + // Inserts a conditional to execute and branches to execute if + // the condition is true and false respectively. + LIns* ins_choose(LIns* cond, LIns* iftrue, LIns* iffalse, bool use_cmov); + // Inserts an integer comparison to 0 + LIns* ins_eq0(LIns* oprnd1); + // Inserts a pointer comparison to 0 + LIns* ins_peq0(LIns* oprnd1); + // Inserts a binary operation where the second operand is an + // integer immediate. + LIns* ins2i(LOpcode op, LIns *oprnd1, int32_t); + LIns* qjoin(LInsp lo, LInsp hi); + LIns* insImmPtr(const void *ptr); + LIns* insImmWord(intptr_t ptr); + // Sign or zero extend integers to native integers. On 32-bit this is a no-op. + LIns* ins_i2p(LIns* intIns); + LIns* ins_u2p(LIns* uintIns); + }; + + +#ifdef NJ_VERBOSE + extern const char* lirNames[]; + + /** + * map address ranges to meaningful names. + */ + class LabelMap + { + Allocator& allocator; + class Entry + { + public: + Entry(int) : name(0), size(0), align(0) {} + Entry(char *n, size_t s, size_t a) : name(n),size(s),align(a) {} + char* name; + size_t size:29, align:3; + }; + TreeMap names; + LogControl *logc; + char buf[5000], *end; + void formatAddr(const void *p, char *buf); + public: + LabelMap(Allocator& allocator, LogControl* logc); + void add(const void *p, size_t size, size_t align, const char *name); + const char *dup(const char *); + const char *format(const void *p); + }; + + class LirNameMap + { + Allocator& alloc; + + template + class CountMap: public HashMap { + public: + CountMap(Allocator& alloc) : HashMap(alloc) {} + int add(Key k) { + int c = 1; + if (containsKey(k)) { + c = 1+get(k); + } + put(k,c); + return c; + } + }; + CountMap lircounts; + CountMap funccounts; + + class Entry + { + public: + Entry(int) : name(0) {} + Entry(char* n) : name(n) {} + char* name; + }; + HashMap names; + LabelMap *labels; + void formatImm(int32_t c, char *buf); + public: + + LirNameMap(Allocator& alloc, LabelMap *lm) + : alloc(alloc), + lircounts(alloc), + funccounts(alloc), + names(alloc), + labels(lm) + {} + + void addName(LInsp i, const char *s); + void copyName(LInsp i, const char *s, int suffix); + const char *formatRef(LIns *ref); + const char *formatIns(LInsp i); + void formatGuard(LInsp i, char *buf); + }; + + + class VerboseWriter : public LirWriter + { + InsList code; + LirNameMap* names; + LogControl* logc; + public: + VerboseWriter(Allocator& alloc, LirWriter *out, + LirNameMap* names, LogControl* logc) + : LirWriter(out), code(alloc), names(names), logc(logc) + {} + + LInsp add(LInsp i) { + if (i) + code.add(i); + return i; + } + + LInsp add_flush(LInsp i) { + if ((i = add(i)) != 0) + flush(); + return i; + } + + void flush() + { + if (!code.isEmpty()) { + int32_t count = 0; + for (Seq* p = code.get(); p != NULL; p = p->tail) { + logc->printf(" %s\n",names->formatIns(p->head)); + count++; + } + code.clear(); + if (count > 1) + logc->printf("\n"); + } + } + + LIns* insGuard(LOpcode op, LInsp cond, GuardRecord *gr) { + return add_flush(out->insGuard(op,cond,gr)); + } + + LIns* insBranch(LOpcode v, LInsp condition, LInsp to) { + return add_flush(out->insBranch(v, condition, to)); + } + + LIns* insJtbl(LIns* index, uint32_t size) { + return add_flush(out->insJtbl(index, size)); + } + + LIns* ins0(LOpcode v) { + if (v == LIR_label || v == LIR_start) { + flush(); + } + return add(out->ins0(v)); + } + + LIns* ins1(LOpcode v, LInsp a) { + return isRetOpcode(v) ? add_flush(out->ins1(v, a)) : add(out->ins1(v, a)); + } + LIns* ins2(LOpcode v, LInsp a, LInsp b) { + return add(out->ins2(v, a, b)); + } + LIns* ins3(LOpcode v, LInsp a, LInsp b, LInsp c) { + return add(out->ins3(v, a, b, c)); + } + LIns* insCall(const CallInfo *call, LInsp args[]) { + return add_flush(out->insCall(call, args)); + } + LIns* insParam(int32_t i, int32_t kind) { + return add(out->insParam(i, kind)); + } + LIns* insLoad(LOpcode v, LInsp base, int32_t disp) { + return add(out->insLoad(v, base, disp)); + } + LIns* insStorei(LInsp v, LInsp b, int32_t d) { + return add(out->insStorei(v, b, d)); + } + LIns* insAlloc(int32_t size) { + return add(out->insAlloc(size)); + } + LIns* insImm(int32_t imm) { + return add(out->insImm(imm)); + } + LIns* insImmq(uint64_t imm) { + return add(out->insImmq(imm)); + } + LIns* insImmf(double d) { + return add(out->insImmf(d)); + } + }; + +#endif + + class ExprFilter: public LirWriter + { + public: + ExprFilter(LirWriter *out) : LirWriter(out) {} + LIns* ins1(LOpcode v, LIns* a); + LIns* ins2(LOpcode v, LIns* a, LIns* b); + LIns* ins3(LOpcode v, LIns* a, LIns* b, LIns* c); + LIns* insGuard(LOpcode, LIns *cond, GuardRecord *); + LIns* insBranch(LOpcode, LIns *cond, LIns *target); + LIns* insLoad(LOpcode op, LInsp base, int32_t off); + }; + + enum LInsHashKind { + // We divide instruction kinds into groups for the use of LInsHashSet. + // LIns0 isn't present because we don't need to record any 0-ary + // instructions. + LInsImm = 0, + LInsImmq = 1, + LInsImmf = 2, + LIns1 = 3, + LIns2 = 4, + LIns3 = 5, + LInsLoad = 6, + LInsCall = 7, + + LInsFirst = 0, + LInsLast = 7 + }; + #define nextKind(kind) LInsHashKind(kind+1) + + // @todo, this could be replaced by a generic HashMap or HashSet, if we had one + class LInsHashSet + { + // Must be a power of 2. + // Don't start too small, or we'll waste time growing and rehashing. + // Don't start too large, will waste memory. + static const uint32_t kInitialCap[LInsLast + 1]; + + // There is one list for each instruction kind. This lets us size the + // lists appropriately (some instructions are more common than others). + // It also lets us have kind-specific find/add/grow functions, which + // are faster than generic versions. + LInsp *m_list[LInsLast + 1]; + uint32_t m_cap[LInsLast + 1]; + uint32_t m_used[LInsLast + 1]; + typedef uint32_t (LInsHashSet::*find_t)(LInsp); + find_t m_find[LInsLast + 1]; + Allocator& alloc; + + static uint32_t hashImm(int32_t); + static uint32_t hashImmq(uint64_t); + static uint32_t hashImmf(double); + static uint32_t hash1(LOpcode v, LInsp); + static uint32_t hash2(LOpcode v, LInsp, LInsp); + static uint32_t hash3(LOpcode v, LInsp, LInsp, LInsp); + static uint32_t hashLoad(LOpcode v, LInsp, int32_t); + static uint32_t hashCall(const CallInfo *call, uint32_t argc, LInsp args[]); + + // These private versions are used after an LIns has been created; + // they are used for rehashing after growing. + uint32_t findImm(LInsp ins); + uint32_t findImmq(LInsp ins); + uint32_t findImmf(LInsp ins); + uint32_t find1(LInsp ins); + uint32_t find2(LInsp ins); + uint32_t find3(LInsp ins); + uint32_t findLoad(LInsp ins); + uint32_t findCall(LInsp ins); + + void grow(LInsHashKind kind); + + public: + // kInitialCaps[i] holds the initial size for m_list[i]. + LInsHashSet(Allocator&, uint32_t kInitialCaps[]); + + // These public versions are used before an LIns has been created. + LInsp findImm(int32_t a, uint32_t &k); + LInsp findImmq(uint64_t a, uint32_t &k); + LInsp findImmf(double d, uint32_t &k); + LInsp find1(LOpcode v, LInsp a, uint32_t &k); + LInsp find2(LOpcode v, LInsp a, LInsp b, uint32_t &k); + LInsp find3(LOpcode v, LInsp a, LInsp b, LInsp c, uint32_t &k); + LInsp findLoad(LOpcode v, LInsp a, int32_t b, uint32_t &k); + LInsp findCall(const CallInfo *call, uint32_t argc, LInsp args[], uint32_t &k); + + // 'k' is the index found by findXYZ(). + LInsp add(LInsHashKind kind, LInsp ins, uint32_t k); + + void clear(); + }; + + class CseFilter: public LirWriter + { + private: + LInsHashSet* exprs; + + public: + CseFilter(LirWriter *out, Allocator&); + + LIns* insImm(int32_t imm); + LIns* insImmq(uint64_t q); + LIns* insImmf(double d); + LIns* ins0(LOpcode v); + LIns* ins1(LOpcode v, LInsp); + LIns* ins2(LOpcode v, LInsp, LInsp); + LIns* ins3(LOpcode v, LInsp, LInsp, LInsp); + LIns* insLoad(LOpcode op, LInsp cond, int32_t d); + LIns* insCall(const CallInfo *call, LInsp args[]); + LIns* insGuard(LOpcode op, LInsp cond, GuardRecord *gr); + }; + + class LirBuffer + { + public: + LirBuffer(Allocator& alloc); + void clear(); + uintptr_t makeRoom(size_t szB); // make room for an instruction + + debug_only (void validate() const;) + verbose_only(LirNameMap* names;) + + int32_t insCount(); + size_t byteCount(); + + // stats + struct + { + uint32_t lir; // # instructions + } + _stats; + + AbiKind abi; + LInsp state,param1,sp,rp; + LInsp savedRegs[NumSavedRegs]; + + protected: + friend class LirBufWriter; + + /** Each chunk is just a raw area of LIns instances, with no header + and no more than 8-byte alignment. The chunk size is somewhat arbitrary. */ + static const size_t CHUNK_SZB = 8000; + + /** Get CHUNK_SZB more memory for LIR instructions. */ + void chunkAlloc(); + void moveToNewChunk(uintptr_t addrOfLastLInsOnCurrentChunk); + + Allocator& _allocator; + uintptr_t _unused; // next unused instruction slot in the current LIR chunk + uintptr_t _limit; // one past the last usable byte of the current LIR chunk + size_t _bytesAllocated; + }; + + class LirBufWriter : public LirWriter + { + LirBuffer* _buf; // underlying buffer housing the instructions + + public: + LirBufWriter(LirBuffer* buf) + : LirWriter(0), _buf(buf) { + } + + // LirWriter interface + LInsp insLoad(LOpcode op, LInsp base, int32_t disp); + LInsp insStorei(LInsp o1, LInsp o2, int32_t disp); + LInsp ins0(LOpcode op); + LInsp ins1(LOpcode op, LInsp o1); + LInsp ins2(LOpcode op, LInsp o1, LInsp o2); + LInsp ins3(LOpcode op, LInsp o1, LInsp o2, LInsp o3); + LInsp insParam(int32_t i, int32_t kind); + LInsp insImm(int32_t imm); + LInsp insImmq(uint64_t imm); + LInsp insImmf(double d); + LInsp insCall(const CallInfo *call, LInsp args[]); + LInsp insGuard(LOpcode op, LInsp cond, GuardRecord *gr); + LInsp insBranch(LOpcode v, LInsp condition, LInsp to); + LInsp insAlloc(int32_t size); + LInsp insJtbl(LIns* index, uint32_t size); + }; + + class LirFilter + { + public: + LirFilter *in; + LirFilter(LirFilter *in) : in(in) {} + virtual ~LirFilter(){} + + virtual LInsp read() { + return in->read(); + } + virtual LInsp pos() { + return in->pos(); + } + }; + + // concrete + class LirReader : public LirFilter + { + LInsp _i; // next instruction to be read; invariant: is never a skip + + public: + LirReader(LInsp i) : LirFilter(0), _i(i) + { + // The last instruction for a fragment shouldn't be a skip. + // (Actually, if the last *inserted* instruction exactly fills up + // a chunk, a new chunk will be created, and thus the last *written* + // instruction will be a skip -- the one needed for the + // cross-chunk link. But the last *inserted* instruction is what + // is recorded and used to initialise each LirReader, and that is + // what is seen here, and therefore this assertion holds.) + NanoAssert(i && !i->isop(LIR_skip)); + } + virtual ~LirReader() {} + + // Returns next instruction and advances to the prior instruction. + // Invariant: never returns a skip. + LInsp read(); + + // Returns next instruction. Invariant: never returns a skip. + LInsp pos() { + return _i; + } + }; + + class Assembler; + + void compile(Assembler *assm, Fragment *frag, Allocator& alloc verbose_only(, LabelMap*)); + verbose_only(void live(Allocator& alloc, Fragment* frag, LogControl*);) + + class StackFilter: public LirFilter + { + LirBuffer *lirbuf; + LInsp sp; + LInsp rp; + BitSet spStk; + BitSet rpStk; + int spTop; + int rpTop; + void getTops(LInsp br, int& spTop, int& rpTop); + + public: + StackFilter(LirFilter *in, Allocator& alloc, LirBuffer *lirbuf, LInsp sp, LInsp rp); + bool ignoreStore(LInsp ins, int top, BitSet* stk); + LInsp read(); + }; + + // eliminate redundant loads by watching for stores & mutator calls + class LoadFilter: public LirWriter + { + public: + LInsp sp, rp; + LInsHashSet* exprs; + + void clear(LInsp p); + + public: + LoadFilter(LirWriter *out, Allocator& alloc) + : LirWriter(out), sp(NULL), rp(NULL) + { + uint32_t kInitialCaps[LInsLast + 1]; + kInitialCaps[LInsImm] = 1; + kInitialCaps[LInsImmq] = 1; + kInitialCaps[LInsImmf] = 1; + kInitialCaps[LIns1] = 1; + kInitialCaps[LIns2] = 1; + kInitialCaps[LIns3] = 1; + kInitialCaps[LInsLoad] = 64; + kInitialCaps[LInsCall] = 1; + exprs = new (alloc) LInsHashSet(alloc, kInitialCaps); + } + + LInsp ins0(LOpcode); + LInsp insLoad(LOpcode, LInsp base, int32_t disp); + LInsp insStorei(LInsp v, LInsp b, int32_t d); + LInsp insCall(const CallInfo *call, LInsp args[]); + }; + +#ifdef DEBUG + class SanityFilter : public LirWriter + { + public: + SanityFilter(LirWriter* out) : LirWriter(out) + { } + public: + LIns* ins1(LOpcode v, LIns* s0); + LIns* ins2(LOpcode v, LIns* s0, LIns* s1); + LIns* ins3(LOpcode v, LIns* s0, LIns* s1, LIns* s2); + }; +#endif +} +#endif // __nanojit_LIR__ diff --git a/ape-server/deps/js/src/nanojit/LIRopcode.tbl b/ape-server/deps/js/src/nanojit/LIRopcode.tbl new file mode 100755 index 0000000..52fbe1f --- /dev/null +++ b/ape-server/deps/js/src/nanojit/LIRopcode.tbl @@ -0,0 +1,258 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=0 ft=C: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is SpiderMonkey nanojit. + * + * The Initial Developer of the Original Code is + * the Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Jeff Walden + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Definitions of LIR opcodes. If you need to allocate an opcode, look + * for a name beginning with "__" and claim it. + * + * Includers must define OPDEF and OPD64 macros of the following forms: + * + * #define OPDEF(op,val,repkind) ... + * #define OPD64(op,val,repkind) ... + * + * Selected arguments can then be used within the macro expansions. + * - op Bytecode name, token-pasted after "LIR_" to form an LOpcode. + * - val Bytecode value, which is the LOpcode enumerator value. + * - repkind Indicates how the instruction is represented in memory; XYZ + * corresponds to LInsXYZ and LRK_XYZ. + * + * This file is best viewed with 128 columns: +12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678 + * + * Aliases for pointer-sized operations that choose 32bit or 64bit instructions + * are given in the LOpcode enum in LIR.h just after including LIRopcodes.tbl. + */ + +/* op val name operands */ + +/* special operations (must be 0..N) */ +OPDEF(start, 0, Op0) // start of a fragment +OPDEF(regfence, 1, Op0) // register fence, no register allocation is allowed across this meta instruction +OPDEF(skip, 2, Sk) // holds blobs ("payloads") of data; also links pages +OPDEF(__3, 3, None) +OPDEF(__4, 4, None) +OPDEF(__5, 5, None) +OPDEF(__6, 6, None) + +/* non-pure operations */ +OPDEF(iaddp, 7, Op2) // integer addition for temporary pointer calculations (32bit only) +OPDEF(iparam, 8, P) // load a parameter (32bit register or stk location) +OPDEF(__9, 9, None) +OPDEF(ld, 10, Ld) // 32-bit load +OPDEF(ialloc, 11, I) // alloc some stack space (value is 32bit address) +OPDEF(sti, 12, Sti) // 32-bit store +OPDEF(ret, 13, Op1) // return a word-sized value +OPDEF(live, 14, Op1) // extend live range of reference +OPDEF(flive, 15, Op1) // extend live range of a floating point value reference +OPDEF(icall, 16, C) // subroutine call returning a 32-bit value +OPDEF(__17, 17, None) + +/* guards */ +OPDEF(x, 18, Op2) // exit always + +/* branches */ +OPDEF(j, 19, Op2) // jump always +OPDEF(jt, 20, Op2) // jump if true +OPDEF(jf, 21, Op2) // jump if false +OPDEF(label, 22, Op0) // a jump target (no machine code is emitted for this) +OPDEF(jtbl, 23, Jtbl) // jump to address in table + +/* operators */ + +/* + * NB: Opcodes LIR_int through LIR_uge must remain continuous to aid in + * common-subexpression-elimination detection code. + */ + +OPDEF(int, 24, I) // constant 32-bit integer +OPDEF(cmov, 25, Op3) // conditional move +OPDEF(callh, 26, Op1) // get the high 32 bits of a call returning a 64-bit value in two 32bit registers + +/* + * feq though fge must only be used on float arguments. They return integers. + * For all except feq, (op ^ 1) is the op which flips the + * left and right sides of the comparison, so (lt ^ 1) == gt, or the operator + * "<" is xored with 1 to get ">". Similarly, (op ^ 3) is the complement of + * op, so (lt ^ 1) == ge, or the complement of the operator "<" is ">=" xored + * with 3. NB: These opcodes must remain continuous so that comparison-opcode + * detection works correctly. + */ +OPDEF(feq, 27, Op2) // floating-point equality +OPDEF(flt, 28, Op2) // floating-point less-than +OPDEF(fgt, 29, Op2) // floating-point greater-than +OPDEF(fle, 30, Op2) // floating-point less-than-or-equal +OPDEF(fge, 31, Op2) // floating-point greater-than-or-equal + +OPDEF(ldcb, 32, Ld) // non-volatile 8-bit load +OPDEF(ldcs, 33, Ld) // non-volatile 16-bit load +OPDEF(ldc, 34, Ld) // non-volatile 32-bit load + +OPDEF(neg, 35, Op1) // integer negation +OPDEF(add, 36, Op2) // integer addition +OPDEF(sub, 37, Op2) // integer subtraction +OPDEF(mul, 38, Op2) // integer multiplication +OPDEF(div, 39, Op2) // integer division +OPDEF(mod, 40, Op1) // hack: get the modulus from a LIR_div result, for x86 only + +OPDEF(and, 41, Op2) // 32-bit bitwise AND +OPDEF(or, 42, Op2) // 32-bit bitwise OR +OPDEF(xor, 43, Op2) // 32-bit bitwise XOR +OPDEF(not, 44, Op1) // 32-bit bitwise NOT +OPDEF(lsh, 45, Op2) // 32-bit left shift +OPDEF(rsh, 46, Op2) // 32-bit right shift with sign-extend (>>) +OPDEF(ush, 47, Op2) // 32-bit unsigned right shift (>>>) + +// conditional guards, op^1 to complement. Only things that are +// isCond() can be passed to these. +OPDEF(xt, 48, Op2) // exit if true (0x30 0011 0000) +OPDEF(xf, 49, Op2) // exit if false (0x31 0011 0001) + +OPDEF(qlo, 50, Op1) // get the low 32 bits of a 64-bit value +OPDEF(qhi, 51, Op1) // get the high 32 bits of a 64-bit value + +OPDEF(__52, 52, None) +OPDEF(__53, 53, None) + +// This must be right before LIR_eq, so (op&~LIR64 - LIR_ov) can be indexed +// into a convenient table. +OPDEF(ov, 54, Op1) // test for overflow; value must have just been computed + +// Integer (32 bit) relational operators. (op ^ 1) is the op which flips the +// left and right sides of the comparison, so (lt ^ 1) == gt, or the operator +// "<" is xored with 1 to get ">". Similarly, (op ^ 3) is the complement of +// op, so (lt ^ 1) == ge, or the complement of the operator "<" is ">=" xored +// with 3. 'u' prefix indicates the unsigned integer variant. +// NB: These opcodes must remain continuous so that comparison-opcode detection +// works correctly. +OPDEF(eq, 55, Op2) // integer equality +OPDEF(lt, 56, Op2) // signed integer less-than (0x38 0011 1000) +OPDEF(gt, 57, Op2) // signed integer greater-than (0x39 0011 1001) +OPDEF(le, 58, Op2) // signed integer less-than-or-equal (0x3A 0011 1010) +OPDEF(ge, 59, Op2) // signed integer greater-than-or-equal (0x3B 0011 1011) +OPDEF(ult, 60, Op2) // unsigned integer less-than (0x3C 0011 1100) +OPDEF(ugt, 61, Op2) // unsigned integer greater-than (0x3D 0011 1101) +OPDEF(ule, 62, Op2) // unsigned integer less-than-or-equal (0x3E 0011 1110) +OPDEF(uge, 63, Op2) // unsigned integer greater-than-or-equal (0x3F 0011 1111) + +OPD64(__0_64, 0, None) + +OPD64(file, 1, Op1) // source filename for debug symbols +OPD64(line, 2, Op1) // source line number for debug symbols +OPD64(xbarrier, 3, Op2) // memory barrier; doesn't exit, but flushes all values to the stack +OPD64(xtbl, 4, Op2) // exit via indirect jump + +OPD64(__5_64, 5, None) +OPD64(__6_64, 6, None) +OPD64(qaddp, LIR_iaddp, Op2) // integer addition for temp pointer calculations (64bit only) +OPD64(qparam, LIR_iparam, P) // load a parameter (64bit register or stk location) +OPD64(__9_64, 9, None) + +OPD64(ldq, LIR_ld, Ld) // 64-bit (quad) load + +OPD64(qalloc, LIR_ialloc, I) // allocate some stack space (value is 64bit address) + +OPD64(stqi, LIR_sti, Sti) // 64-bit (quad) store +OPD64(fret, LIR_ret, Op1) + +OPD64(__14_64, 14, None) +OPD64(__15_64, 15, None) + +OPD64(fcall, LIR_icall, C) // subroutine call returning 64-bit (quad) double value +OPD64(qcall, 17, C) // subroutine call returning 64-bit (quad) integer value + +OPD64(__18_64, 18, None) +OPD64(__19_64, 19, None) +OPD64(__20_64, 20, None) +OPD64(__21_64, 21, None) +OPD64(__22_64, 22, None) +OPD64(__23_64, 23, None) + +// We strip off the 64 bit flag and compare that the opcode is between LIR_int +// and LIR_uge to decide whether we can CSE the opcode. All opcodes below +// this marker are subject to CSE. + +OPD64(quad, LIR_int, I64) // 64-bit (quad) constant value +OPD64(qcmov, LIR_cmov, Op3) // 64-bit conditional move + +OPD64(i2q, 26, Op1) // sign-extend i32 to i64 +OPD64(u2q, 27, Op1) // zero-extend u32 to u64 +OPD64(i2f, 28, Op1) // convert a signed 32-bit integer to a float +OPD64(u2f, 29, Op1) // convert an unsigned 32-bit integer to a float + +OPD64(__30_64, 30, None) +OPD64(__31_64, 31, None) +OPD64(__32_64, 32, None) +OPD64(__33_64, 33, None) + +OPD64(ldqc, LIR_ldc, Ld) // non-volatile 64-bit load + +OPD64(fneg, LIR_neg, Op1) // floating-point negation +OPD64(fadd, LIR_add, Op2) // floating-point addition +OPD64(fsub, LIR_sub, Op2) // floating-point subtraction +OPD64(fmul, LIR_mul, Op2) // floating-point multiplication +OPD64(fdiv, LIR_div, Op2) // floating-point division +OPD64(fmod, LIR_mod, Op2) // floating-point modulus(?) + +OPD64(qiand, 41, Op2) // 64-bit bitwise AND +OPD64(qior, 42, Op2) // 64-bit bitwise OR +OPD64(qxor, 43, Op2) // 64-bit bitwise XOR +OPD64(__44_64, 44, None) +OPD64(qilsh, 45, Op2) // 64-bit left shift +OPD64(qirsh, 46, Op2) // 64-bit signed right shift +OPD64(qursh, 47, Op2) // 64-bit unsigned right shift +OPD64(qiadd, 48, Op2) // 64-bit bitwise ADD + +OPD64(__49_64, 49, None) +OPD64(qjoin, 50, Op2) // join two 32-bit values (1st arg is low bits, 2nd is high) +OPD64(__51_64, 51, None) +OPD64(__52_64, 52, None) +OPD64(__53_64, 53, None) +OPD64(float, 54, I64) + +// 64bit equivalents for integer comparisons +OPD64(qeq, LIR_eq, Op2) // integer equality +OPD64(qlt, LIR_lt, Op2) // signed integer less-than (0x78 0111 1000) +OPD64(qgt, LIR_gt, Op2) // signed integer greater-than (0x79 0111 1001) +OPD64(qle, LIR_le, Op2) // signed integer less-than-or-equal (0x7A 0111 1010) +OPD64(qge, LIR_ge, Op2) // signed integer greater-than-or-equal (0x7B 0111 1011) +OPD64(qult, LIR_ult, Op2) // unsigned integer less-than (0x7C 0111 1100) +OPD64(qugt, LIR_ugt, Op2) // unsigned integer greater-than (0x7D 0111 1101) +OPD64(qule, LIR_ule, Op2) // unsigned integer less-than-or-equal (0x7E 0111 1110) +OPD64(quge, LIR_uge, Op2) // unsigned integer greater-than-or-equal (0x7F 0111 1111) diff --git a/ape-server/deps/js/src/nanojit/Native.h b/ape-server/deps/js/src/nanojit/Native.h new file mode 100755 index 0000000..8085c07 --- /dev/null +++ b/ape-server/deps/js/src/nanojit/Native.h @@ -0,0 +1,209 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is [Open Source Virtual Machine]. + * + * The Initial Developer of the Original Code is + * Adobe System Incorporated. + * Portions created by the Initial Developer are Copyright (C) 2004-2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adobe AS3 Team + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + + +#ifndef __nanojit_Native__ +#define __nanojit_Native__ + +// define PEDANTIC=1 to ignore specialized forms, force general forms +// for everything, far branches, extra page-linking, etc. This will +// flush out many corner cases. + +#define PEDANTIC 0 +#if PEDANTIC +# define UNLESS_PEDANTIC(...) +# define IF_PEDANTIC(...) __VA_ARGS__ +#else +# define UNLESS_PEDANTIC(...) __VA_ARGS__ +# define IF_PEDANTIC(...) +#endif + +namespace nanojit { + enum LOpcode +#if defined(_MSC_VER) && _MSC_VER >= 1400 +#pragma warning(disable:4480) // nonstandard extension used: specifying underlying type for enum + : unsigned +#endif + { + // flags; upper bits reserved + LIR64 = 0x40, // result is double or quad + +#define OPDEF(op, number, repkind) \ + LIR_##op = (number), +#define OPD64(op, number, repkind) \ + LIR_##op = ((number) | LIR64), +#include "LIRopcode.tbl" + LIR_sentinel, +#undef OPDEF +#undef OPD64 + +#ifdef NANOJIT_64BIT +# define PTR_SIZE(a,b) b +#else +# define PTR_SIZE(a,b) a +#endif + + // pointer op aliases + LIR_ldp = PTR_SIZE(LIR_ld, LIR_ldq), + LIR_ldcp = PTR_SIZE(LIR_ldc, LIR_ldqc), + LIR_stpi = PTR_SIZE(LIR_sti, LIR_stqi), + LIR_piadd = PTR_SIZE(LIR_add, LIR_qiadd), + LIR_piand = PTR_SIZE(LIR_and, LIR_qiand), + LIR_pilsh = PTR_SIZE(LIR_lsh, LIR_qilsh), + LIR_pirsh = PTR_SIZE(LIR_rsh, LIR_qirsh), + LIR_pursh = PTR_SIZE(LIR_ush, LIR_qursh), + LIR_pcmov = PTR_SIZE(LIR_cmov, LIR_qcmov), + LIR_pior = PTR_SIZE(LIR_or, LIR_qior), + LIR_pxor = PTR_SIZE(LIR_xor, LIR_qxor), + LIR_addp = PTR_SIZE(LIR_iaddp, LIR_qaddp), + LIR_peq = PTR_SIZE(LIR_eq, LIR_qeq), + LIR_plt = PTR_SIZE(LIR_lt, LIR_qlt), + LIR_pgt = PTR_SIZE(LIR_gt, LIR_qgt), + LIR_ple = PTR_SIZE(LIR_le, LIR_qle), + LIR_pge = PTR_SIZE(LIR_ge, LIR_qge), + LIR_pult = PTR_SIZE(LIR_ult, LIR_qult), + LIR_pugt = PTR_SIZE(LIR_ugt, LIR_qugt), + LIR_pule = PTR_SIZE(LIR_ule, LIR_qule), + LIR_puge = PTR_SIZE(LIR_uge, LIR_quge), + LIR_alloc = PTR_SIZE(LIR_ialloc, LIR_qalloc), + LIR_pcall = PTR_SIZE(LIR_icall, LIR_qcall), + LIR_param = PTR_SIZE(LIR_iparam, LIR_qparam) + }; +} + +#ifdef NANOJIT_IA32 +#include "Nativei386.h" +#elif defined(NANOJIT_ARM) +#include "NativeARM.h" +#elif defined(NANOJIT_PPC) +#include "NativePPC.h" +#elif defined(NANOJIT_SPARC) +#include "NativeSparc.h" +#elif defined(NANOJIT_X64) +#include "NativeX64.h" +#else +#error "unknown nanojit architecture" +#endif + +#ifndef NJ_JTBL_SUPPORTED +# define NJ_JTBL_SUPPORTED 0 +#endif + +namespace nanojit { + + inline Register nextreg(Register r) { + return Register(r+1); + } + + inline Register prevreg(Register r) { + return Register(r-1); + } + + + class Fragment; + struct SideExit; + struct SwitchInfo; + + struct GuardRecord + { + void* jmp; + GuardRecord* next; + SideExit* exit; + // profiling stuff + verbose_only( uint32_t profCount; ) + verbose_only( uint32_t profGuardID; ) + verbose_only( GuardRecord* nextInFrag; ) + }; + + struct SideExit + { + GuardRecord* guards; + Fragment* from; + Fragment* target; + SwitchInfo* switchInfo; + + void addGuard(GuardRecord* gr) + { + NanoAssert(gr->next == NULL); + NanoAssert(guards != gr); + gr->next = guards; + guards = gr; + } + }; +} + + #ifdef NJ_STACK_GROWTH_UP + #define stack_direction(n) n + #else + #define stack_direction(n) -n + #endif + + #define isSPorFP(r) ( (r)==SP || (r)==FP ) + + #ifdef NJ_NO_VARIADIC_MACROS + static void asm_output(const char *f, ...) {} + #define gpn(r) regNames[(r)] + #define fpn(r) regNames[(r)] + #elif defined(NJ_VERBOSE) + // Used for printing native instructions. Like Assembler::outputf(), + // but only outputs if LC_Assembly is set. Also prepends the output + // with the address of the current native instruction if + // LC_NoCodeAddrs is not set. + #define asm_output(...) do { \ + counter_increment(native); \ + if (_logc->lcbits & LC_Assembly) { \ + outline[0]='\0'; \ + if (outputAddr) \ + VMPI_sprintf(outline, "%010lx ", (unsigned long)_nIns); \ + else \ + VMPI_memset(outline, (int)' ', 10+3); \ + sprintf(&outline[13], ##__VA_ARGS__); \ + output(); \ + outputAddr=(_logc->lcbits & LC_NoCodeAddrs) ? false : true; \ + } \ + } while (0) /* no semi */ + #define gpn(r) regNames[(r)] + #define fpn(r) regNames[(r)] + #else + #define asm_output(...) + #define gpn(r) + #define fpn(r) + #endif /* NJ_VERBOSE */ + +#endif // __nanojit_Native__ diff --git a/ape-server/deps/js/src/nanojit/NativeARM.cpp b/ape-server/deps/js/src/nanojit/NativeARM.cpp new file mode 100755 index 0000000..dc9f43f --- /dev/null +++ b/ape-server/deps/js/src/nanojit/NativeARM.cpp @@ -0,0 +1,2627 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is [Open Source Virtual Machine]. + * + * The Initial Developer of the Original Code is + * Adobe System Incorporated. + * Portions created by the Initial Developer are Copyright (C) 2004-2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adobe AS3 Team + * Vladimir Vukicevic + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nanojit.h" + +#ifdef UNDER_CE +#include +extern "C" bool blx_lr_broken(); +#endif + +#if defined(FEATURE_NANOJIT) && defined(NANOJIT_ARM) + +namespace nanojit +{ + +#ifdef NJ_VERBOSE +const char* regNames[] = {"r0","r1","r2","r3","r4","r5","r6","r7","r8","r9","r10","fp","ip","sp","lr","pc", + "d0","d1","d2","d3","d4","d5","d6","d7","s14"}; +const char* condNames[] = {"eq","ne","cs","cc","mi","pl","vs","vc","hi","ls","ge","lt","gt","le",""/*al*/,"nv"}; +const char* shiftNames[] = { "lsl", "lsl", "lsr", "lsr", "asr", "asr", "ror", "ror" }; +#endif + +const Register Assembler::argRegs[] = { R0, R1, R2, R3 }; +const Register Assembler::retRegs[] = { R0, R1 }; +const Register Assembler::savedRegs[] = { R4, R5, R6, R7, R8, R9, R10 }; + +// -------------------------------- +// ARM-specific utility functions. +// -------------------------------- + +#ifdef DEBUG +// Return true if enc is a valid Operand 2 encoding and thus can be used as-is +// in an ARM arithmetic operation that accepts such encoding. +// +// This utility does not know (or determine) the actual value that the encoded +// value represents, and thus cannot be used to ensure the correct operation of +// encOp2Imm, but it does ensure that the encoded value can be used to encode a +// valid ARM instruction. decOp2Imm can be used if you also need to check that +// a literal is correctly encoded (and thus that encOp2Imm is working +// correctly). +inline bool +Assembler::isOp2Imm(uint32_t enc) +{ + return ((enc & 0xfff) == enc); +} + +// Decodes operand 2 immediate values (for debug output and assertions). +inline uint32_t +Assembler::decOp2Imm(uint32_t enc) +{ + NanoAssert(isOp2Imm(enc)); + + uint32_t imm8 = enc & 0xff; + uint32_t rot = 32 - ((enc >> 7) & 0x1e); + + return imm8 << (rot & 0x1f); +} +#endif + +// Calculate the number of leading zeroes in data. +inline uint32_t +Assembler::CountLeadingZeroes(uint32_t data) +{ + uint32_t leading_zeroes; + + // We can't do CLZ on anything earlier than ARMv5. Architectures as early + // as that aren't supported, but assert that we aren't running on one + // anyway. + // If ARMv4 support is required in the future for some reason, we can do a + // run-time check on config.arch and fall back to the C routine, but for + // now we can avoid the cost of the check as we don't intend to support + // ARMv4 anyway. + NanoAssert(ARM_ARCH >= 5); + +#if defined(__ARMCC__) + // ARMCC can do this with an intrinsic. + leading_zeroes = __clz(data); + +// current Android GCC compiler incorrectly refuses to compile 'clz' for armv5 +// (even though this is a legal instruction there). Since we currently only compile for ARMv5 +// for emulation, we don't care too much (but we DO care for ARMv6+ since those are "real" +// devices). +#elif defined(__GNUC__) && !(defined(ANDROID) && __ARM_ARCH__ <= 5) + // GCC can use inline assembler to insert a CLZ instruction. + __asm ( + " clz %0, %1 \n" + : "=r" (leading_zeroes) + : "r" (data) + ); +#elif defined(WINCE) + // WinCE can do this with an intrinsic. + leading_zeroes = _CountLeadingZeros(data); +#else + // Other platforms must fall back to a C routine. This won't be as + // efficient as the CLZ instruction, but it is functional. + uint32_t try_shift; + + leading_zeroes = 0; + + // This loop does a bisection search rather than the obvious rotation loop. + // This should be faster, though it will still be no match for CLZ. + for (try_shift = 16; try_shift != 0; try_shift /= 2) { + uint32_t shift = leading_zeroes + try_shift; + if (((data << shift) >> shift) == data) { + leading_zeroes = shift; + } + } +#endif + + // Assert that the operation worked! + NanoAssert(((0xffffffff >> leading_zeroes) & data) == data); + + return leading_zeroes; +} + +// The ARM instruction set allows some flexibility to the second operand of +// most arithmetic operations. When operand 2 is an immediate value, it takes +// the form of an 8-bit value rotated by an even value in the range 0-30. +// +// Some values that can be encoded this scheme — such as 0xf000000f — are +// probably fairly rare in practice and require extra code to detect, so this +// function implements a fast CLZ-based heuristic to detect any value that can +// be encoded using just a shift, and not a full rotation. For example, +// 0xff000000 and 0x000000ff are both detected, but 0xf000000f is not. +// +// This function will return true to indicate that the encoding was successful, +// or false to indicate that the literal could not be encoded as an operand 2 +// immediate. If successful, the encoded value will be written to *enc. +inline bool +Assembler::encOp2Imm(uint32_t literal, uint32_t * enc) +{ + // The number of leading zeroes in the literal. This is used to calculate + // the rotation component of the encoding. + uint32_t leading_zeroes; + + // Components of the operand 2 encoding. + int32_t rot; + uint32_t imm8; + + // Check the literal to see if it is a simple 8-bit value. I suspect that + // most literals are in fact small values, so doing this check early should + // give a decent speed-up. + if (literal < 256) + { + *enc = literal; + return true; + } + + // Determine the number of leading zeroes in the literal. This is used to + // calculate the required rotation. + leading_zeroes = CountLeadingZeroes(literal); + + // We've already done a check to see if the literal is an 8-bit value, so + // leading_zeroes must be less than (and not equal to) (32-8)=24. However, + // if it is greater than 24, this algorithm will break, so debug code + // should use an assertion here to check that we have a value that we + // expect. + NanoAssert(leading_zeroes < 24); + + // Assuming that we have a field of no more than 8 bits for a valid + // literal, we can calculate the required rotation by subtracting + // leading_zeroes from (32-8): + // + // Example: + // 0: Known to be zero. + // 1: Known to be one. + // X: Either zero or one. + // .: Zero in a valid operand 2 literal. + // + // Literal: [ 1XXXXXXX ........ ........ ........ ] + // leading_zeroes = 0 + // Therefore rot (left) = 24. + // Encoded 8-bit literal: [ 1XXXXXXX ] + // + // Literal: [ ........ ..1XXXXX XX...... ........ ] + // leading_zeroes = 10 + // Therefore rot (left) = 14. + // Encoded 8-bit literal: [ 1XXXXXXX ] + // + // Note, however, that we can only encode even shifts, and so + // "rot=24-leading_zeroes" is not sufficient by itself. By ignoring + // zero-bits in odd bit positions, we can ensure that we get a valid + // encoding. + // + // Example: + // Literal: [ 01XXXXXX ........ ........ ........ ] + // leading_zeroes = 1 + // Therefore rot (left) = round_up(23) = 24. + // Encoded 8-bit literal: [ 01XXXXXX ] + rot = 24 - (leading_zeroes & ~1); + + // The imm8 component of the operand 2 encoding can be calculated from the + // rot value. + imm8 = literal >> rot; + + // The validity of the literal can be checked by reversing the + // calculation. It is much easier to decode the immediate than it is to + // encode it! + if (literal != (imm8 << rot)) { + // The encoding is not valid, so report the failure. Calling code + // should use some other method of loading the value (such as LDR). + return false; + } + + // The operand is valid, so encode it. + // Note that the ARM encoding is actually described by a rotate to the + // _right_, so rot must be negated here. Calculating a left shift (rather + // than calculating a right rotation) simplifies the above code. + *enc = ((-rot << 7) & 0xf00) | imm8; + + // Assert that the operand was properly encoded. + NanoAssert(decOp2Imm(*enc) == literal); + + return true; +} + +// Encode "rd = rn + imm" using an appropriate instruction sequence. +// Set stat to 1 to update the status flags. Otherwise, set it to 0 or omit it. +// (The declaration in NativeARM.h defines the default value of stat as 0.) +// +// It is not valid to call this function if: +// (rd == IP) AND (rn == IP) AND !encOp2Imm(imm) AND !encOp2Imm(-imm) +// Where: if (encOp2Imm(imm)), imm can be encoded as an ARM operand 2 using the +// encOp2Imm method. +void +Assembler::asm_add_imm(Register rd, Register rn, int32_t imm, int stat /* =0 */) +{ + // Operand 2 encoding of the immediate. + uint32_t op2imm; + + NanoAssert(IsGpReg(rd)); + NanoAssert(IsGpReg(rn)); + NanoAssert((stat & 1) == stat); + + // Try to encode the value directly as an operand 2 immediate value, then + // fall back to loading the value into a register. + if (encOp2Imm(imm, &op2imm)) { + ADDis(rd, rn, op2imm, stat); + } else if (encOp2Imm(-imm, &op2imm)) { + // We could not encode the value for ADD, so try to encode it for SUB. + // Note that this is valid even if stat is set, _unless_ imm is 0, but + // that case is caught above. + NanoAssert(imm != 0); + SUBis(rd, rn, op2imm, stat); + } else { + // We couldn't encode the value directly, so use an intermediate + // register to encode the value. We will use IP to do this unless rn is + // IP; in that case we can reuse rd. This allows every case other than + // "ADD IP, IP, =#imm". + Register rm = (rn == IP) ? (rd) : (IP); + NanoAssert(rn != rm); + + ADDs(rd, rn, rm, stat); + asm_ld_imm(rm, imm); + } +} + +// Encode "rd = rn - imm" using an appropriate instruction sequence. +// Set stat to 1 to update the status flags. Otherwise, set it to 0 or omit it. +// (The declaration in NativeARM.h defines the default value of stat as 0.) +// +// It is not valid to call this function if: +// (rd == IP) AND (rn == IP) AND !encOp2Imm(imm) AND !encOp2Imm(-imm) +// Where: if (encOp2Imm(imm)), imm can be encoded as an ARM operand 2 using the +// encOp2Imm method. +void +Assembler::asm_sub_imm(Register rd, Register rn, int32_t imm, int stat /* =0 */) +{ + // Operand 2 encoding of the immediate. + uint32_t op2imm; + + NanoAssert(IsGpReg(rd)); + NanoAssert(IsGpReg(rn)); + NanoAssert((stat & 1) == stat); + + // Try to encode the value directly as an operand 2 immediate value, then + // fall back to loading the value into a register. + if (encOp2Imm(imm, &op2imm)) { + SUBis(rd, rn, op2imm, stat); + } else if (encOp2Imm(-imm, &op2imm)) { + // We could not encode the value for SUB, so try to encode it for ADD. + // Note that this is valid even if stat is set, _unless_ imm is 0, but + // that case is caught above. + NanoAssert(imm != 0); + ADDis(rd, rn, op2imm, stat); + } else { + // We couldn't encode the value directly, so use an intermediate + // register to encode the value. We will use IP to do this unless rn is + // IP; in that case we can reuse rd. This allows every case other than + // "SUB IP, IP, =#imm". + Register rm = (rn == IP) ? (rd) : (IP); + NanoAssert(rn != rm); + + SUBs(rd, rn, rm, stat); + asm_ld_imm(rm, imm); + } +} + +// Encode "rd = rn & imm" using an appropriate instruction sequence. +// Set stat to 1 to update the status flags. Otherwise, set it to 0 or omit it. +// (The declaration in NativeARM.h defines the default value of stat as 0.) +// +// It is not valid to call this function if: +// (rd == IP) AND (rn == IP) AND !encOp2Imm(imm) AND !encOp2Imm(~imm) +// Where: if (encOp2Imm(imm)), imm can be encoded as an ARM operand 2 using the +// encOp2Imm method. +void +Assembler::asm_and_imm(Register rd, Register rn, int32_t imm, int stat /* =0 */) +{ + // Operand 2 encoding of the immediate. + uint32_t op2imm; + + NanoAssert(IsGpReg(rd)); + NanoAssert(IsGpReg(rn)); + NanoAssert((stat & 1) == stat); + + // Try to encode the value directly as an operand 2 immediate value, then + // fall back to loading the value into a register. + if (encOp2Imm(imm, &op2imm)) { + ANDis(rd, rn, op2imm, stat); + } else if (encOp2Imm(~imm, &op2imm)) { + // Use BIC with the inverted immediate. + BICis(rd, rn, op2imm, stat); + } else { + // We couldn't encode the value directly, so use an intermediate + // register to encode the value. We will use IP to do this unless rn is + // IP; in that case we can reuse rd. This allows every case other than + // "AND IP, IP, =#imm". + Register rm = (rn == IP) ? (rd) : (IP); + NanoAssert(rn != rm); + + ANDs(rd, rn, rm, stat); + asm_ld_imm(rm, imm); + } +} + +// Encode "rd = rn | imm" using an appropriate instruction sequence. +// Set stat to 1 to update the status flags. Otherwise, set it to 0 or omit it. +// (The declaration in NativeARM.h defines the default value of stat as 0.) +// +// It is not valid to call this function if: +// (rd == IP) AND (rn == IP) AND !encOp2Imm(imm) +// Where: if (encOp2Imm(imm)), imm can be encoded as an ARM operand 2 using the +// encOp2Imm method. +void +Assembler::asm_orr_imm(Register rd, Register rn, int32_t imm, int stat /* =0 */) +{ + // Operand 2 encoding of the immediate. + uint32_t op2imm; + + NanoAssert(IsGpReg(rd)); + NanoAssert(IsGpReg(rn)); + NanoAssert((stat & 1) == stat); + + // Try to encode the value directly as an operand 2 immediate value, then + // fall back to loading the value into a register. + if (encOp2Imm(imm, &op2imm)) { + ORRis(rd, rn, op2imm, stat); + } else { + // We couldn't encode the value directly, so use an intermediate + // register to encode the value. We will use IP to do this unless rn is + // IP; in that case we can reuse rd. This allows every case other than + // "ORR IP, IP, =#imm". + Register rm = (rn == IP) ? (rd) : (IP); + NanoAssert(rn != rm); + + ORRs(rd, rn, rm, stat); + asm_ld_imm(rm, imm); + } +} + +// Encode "rd = rn ^ imm" using an appropriate instruction sequence. +// Set stat to 1 to update the status flags. Otherwise, set it to 0 or omit it. +// (The declaration in NativeARM.h defines the default value of stat as 0.) +// +// It is not valid to call this function if: +// (rd == IP) AND (rn == IP) AND !encOp2Imm(imm) +// Where: if (encOp2Imm(imm)), imm can be encoded as an ARM operand 2 using the +// encOp2Imm method. +void +Assembler::asm_eor_imm(Register rd, Register rn, int32_t imm, int stat /* =0 */) +{ + // Operand 2 encoding of the immediate. + uint32_t op2imm; + + NanoAssert(IsGpReg(rd)); + NanoAssert(IsGpReg(rn)); + NanoAssert((stat & 1) == stat); + + // Try to encode the value directly as an operand 2 immediate value, then + // fall back to loading the value into a register. + if (encOp2Imm(imm, &op2imm)) { + EORis(rd, rn, op2imm, stat); + } else { + // We couldn't encoder the value directly, so use an intermediate + // register to encode the value. We will use IP to do this unless rn is + // IP; in that case we can reuse rd. This allows every case other than + // "EOR IP, IP, =#imm". + Register rm = (rn == IP) ? (rd) : (IP); + NanoAssert(rn != rm); + + EORs(rd, rn, rm, stat); + asm_ld_imm(rm, imm); + } +} + +// -------------------------------- +// Assembler functions. +// -------------------------------- + +void +Assembler::nInit(AvmCore*) +{ +#ifdef UNDER_CE + blx_lr_bug = blx_lr_broken(); +#else + blx_lr_bug = 0; +#endif +} + +void Assembler::nBeginAssembly() +{ + max_out_args = 0; +} + +NIns* +Assembler::genPrologue() +{ + /** + * Prologue + */ + + // NJ_RESV_OFFSET is space at the top of the stack for us + // to use for parameter passing (8 bytes at the moment) + uint32_t stackNeeded = max_out_args + STACK_GRANULARITY * _activation.tos; + uint32_t savingCount = 2; + + uint32_t savingMask = rmask(FP) | rmask(LR); + + // so for alignment purposes we've pushed return addr and fp + uint32_t stackPushed = STACK_GRANULARITY * savingCount; + uint32_t aligned = alignUp(stackNeeded + stackPushed, NJ_ALIGN_STACK); + int32_t amt = aligned - stackPushed; + + // Make room on stack for what we are doing + if (amt) + asm_sub_imm(SP, SP, amt); + + verbose_only( asm_output("## %p:",(void*)_nIns); ) + verbose_only( asm_output("## patch entry"); ) + NIns *patchEntry = _nIns; + + MOV(FP, SP); + PUSH_mask(savingMask); + return patchEntry; +} + +void +Assembler::nFragExit(LInsp guard) +{ + SideExit * exit = guard->record()->exit; + Fragment * frag = exit->target; + + bool target_is_known = frag && frag->fragEntry; + + if (target_is_known) { + // The target exists so we can simply emit a branch to its location. + JMP_far(frag->fragEntry); + } else { + // The target doesn't exit yet, so emit a jump to the epilogue. If the + // target is created later on, the jump will be patched. + + GuardRecord *gr = guard->record(); + + if (!_epilogue) + _epilogue = genEpilogue(); + + // Jump to the epilogue. This may get patched later, but JMP_far always + // emits two instructions even when only one is required, so patching + // will work correctly. + JMP_far(_epilogue); + + // In the future you may want to move this further down so that we can + // overwrite the r0 guard record load during a patch to a different + // fragment with some assumed input-register state. Not today though. + gr->jmp = _nIns; + + // NB: this is a workaround for the fact that, by patching a + // fragment-exit jump, we could be changing the *meaning* of the R0 + // register we're passing to the jump target. If we jump to the + // epilogue, ideally R0 means "return value when exiting fragment". + // If we patch this to jump to another fragment however, R0 means + // "incoming 0th parameter". This is just a quirk of ARM ABI. So + // we compromise by passing "return value" to the epilogue in IP, + // not R0, and have the epilogue MOV(R0, IP) first thing. + + asm_ld_imm(IP, int(gr)); + } + +#ifdef NJ_VERBOSE + if (config.show_stats) { + // load R1 with Fragment *fromFrag, target fragment + // will make use of this when calling fragenter(). + int fromfrag = int((Fragment*)_thisfrag); + asm_ld_imm(argRegs[1], fromfrag); + } +#endif + + // profiling for the exit + verbose_only( + if (_logc->lcbits & LC_FragProfile) { + asm_inc_m32( &guard->record()->profCount ); + } + ) + + // Pop the stack frame. + MOV(SP, FP); +} + +NIns* +Assembler::genEpilogue() +{ + // On ARMv5+, loading directly to PC correctly handles interworking. + // Note that we don't support anything older than ARMv5. + NanoAssert(ARM_ARCH >= 5); + + RegisterMask savingMask = rmask(FP) | rmask(PC); + + POP_mask(savingMask); // regs + + // NB: this is the later half of the dual-nature patchable exit branch + // workaround noted above in nFragExit. IP has the "return value" + // incoming, we need to move it to R0. + MOV(R0, IP); + + return _nIns; +} + +/* + * asm_arg will encode the specified argument according to the current ABI, and + * will update r and stkd as appropriate so that the next argument can be + * encoded. + * + * Linux has used ARM's EABI for some time. Windows CE uses the legacy ABI. + * + * Under EABI: + * - doubles are 64-bit aligned both in registers and on the stack. + * If the next available argument register is R1, it is skipped + * and the double is placed in R2:R3. If R0:R1 or R2:R3 are not + * available, the double is placed on the stack, 64-bit aligned. + * - 32-bit arguments are placed in registers and 32-bit aligned + * on the stack. + * + * Under legacy ABI: + * - doubles are placed in subsequent arg registers; if the next + * available register is r3, the low order word goes into r3 + * and the high order goes on the stack. + * - 32-bit arguments are placed in the next available arg register, + * - both doubles and 32-bit arguments are placed on stack with 32-bit + * alignment. + */ +void +Assembler::asm_arg(ArgSize sz, LInsp arg, Register& r, int& stkd) +{ + // The stack pointer must always be at least aligned to 4 bytes. + NanoAssert((stkd & 3) == 0); + + if (sz == ARGSIZE_F) { + // This task is fairly complex and so is delegated to asm_arg_64. + asm_arg_64(arg, r, stkd); + } else if (sz & ARGSIZE_MASK_INT) { + // pre-assign registers R0-R3 for arguments (if they fit) + if (r < R4) { + asm_regarg(sz, arg, r); + r = nextreg(r); + } else { + asm_stkarg(arg, stkd); + stkd += 4; + } + } else { + NanoAssert(sz == ARGSIZE_Q); + // shouldn't have 64 bit int params on ARM + NanoAssert(false); + } +} + +// Encode a 64-bit floating-point argument using the appropriate ABI. +// This function operates in the same way as asm_arg, except that it will only +// handle arguments where (ArgSize)sz == ARGSIZE_F. +void +Assembler::asm_arg_64(LInsp arg, Register& r, int& stkd) +{ + // The stack pointer must always be at least aligned to 4 bytes. + NanoAssert((stkd & 3) == 0); + // The only use for this function when we are using soft floating-point + // is for LIR_qjoin. + NanoAssert(ARM_VFP || arg->isop(LIR_qjoin)); + + Register fp_reg = UnknownReg; + + if (ARM_VFP) { + fp_reg = findRegFor(arg, FpRegs); + NanoAssert(isKnownReg(fp_reg)); + } + +#ifdef NJ_ARM_EABI + // EABI requires that 64-bit arguments are aligned on even-numbered + // registers, as R0:R1 or R2:R3. If the register base is at an + // odd-numbered register, advance it. Note that this will push r past + // R3 if r is R3 to start with, and will force the argument to go on + // the stack. + if ((r == R1) || (r == R3)) { + r = nextreg(r); + } +#endif + + if (r < R3) { + Register ra = r; + Register rb = nextreg(r); + r = nextreg(rb); + +#ifdef NJ_ARM_EABI + // EABI requires that 64-bit arguments are aligned on even-numbered + // registers, as R0:R1 or R2:R3. + NanoAssert( ((ra == R0) && (rb == R1)) || ((ra == R2) && (rb == R3)) ); +#endif + + // Put the argument in ra and rb. If the argument is in a VFP register, + // use FMRRD to move it to ra and rb. Otherwise, let asm_regarg deal + // with the argument as if it were two 32-bit arguments. + if (ARM_VFP) { + FMRRD(ra, rb, fp_reg); + } else { + asm_regarg(ARGSIZE_LO, arg->oprnd1(), ra); + asm_regarg(ARGSIZE_LO, arg->oprnd2(), rb); + } + +#ifndef NJ_ARM_EABI + } else if (r == R3) { + // We only have one register left, but the legacy ABI requires that we + // put 32 bits of the argument in the register (R3) and the remaining + // 32 bits on the stack. + Register ra = r; + r = nextreg(r); + + // This really just checks that nextreg() works properly, as we know + // that r was previously R3. + NanoAssert(r == R4); + + // We're splitting the argument between registers and the stack. This + // must be the first time that the stack is used, so stkd must be at 0. + NanoAssert(stkd == 0); + + if (ARM_VFP) { + // TODO: We could optimize the this to store directly from + // the VFP register to memory using "FMRRD ra, fp_reg[31:0]" and + // "STR fp_reg[63:32], [SP, #stkd]". + + // Load from the floating-point register as usual, but use IP + // as a swap register. + STR(IP, SP, 0); + stkd += 4; + FMRRD(ra, IP, fp_reg); + } else { + // Without VFP, we can simply use asm_regarg and asm_stkarg to + // encode the two 32-bit words as we don't need to load from a VFP + // register. + asm_regarg(ARGSIZE_LO, arg->oprnd1(), ra); + asm_stkarg(arg->oprnd2(), 0); + stkd += 4; + } +#endif + } else { + // The argument won't fit in registers, so pass on to asm_stkarg. +#ifdef NJ_ARM_EABI + // EABI requires that 64-bit arguments are 64-bit aligned. + if ((stkd & 7) != 0) { + // stkd will always be aligned to at least 4 bytes; this was + // asserted on entry to this function. + stkd += 4; + } +#endif + asm_stkarg(arg, stkd); + stkd += 8; + } +} + +void +Assembler::asm_regarg(ArgSize sz, LInsp p, Register r) +{ + NanoAssert(isKnownReg(r)); + if (sz & ARGSIZE_MASK_INT) + { + // arg goes in specific register + if (p->isconst()) { + asm_ld_imm(r, p->imm32()); + } else { + if (p->isUsed()) { + if (!p->hasKnownReg()) { + // load it into the arg reg + int d = findMemFor(p); + if (p->isop(LIR_alloc)) { + asm_add_imm(r, FP, d, 0); + } else { + LDR(r, FP, d); + } + } else { + // it must be in a saved reg + MOV(r, p->getReg()); + } + } + else { + // this is the last use, so fine to assign it + // to the scratch reg, it's dead after this point. + findSpecificRegFor(p, r); + } + } + } + else if (sz == ARGSIZE_Q) { + // 64 bit integer argument - should never happen on ARM + NanoAssert(false); + } + else + { + NanoAssert(sz == ARGSIZE_F); + // fpu argument in register - should never happen since FPU + // args are converted to two 32-bit ints on ARM + NanoAssert(false); + } +} + +void +Assembler::asm_stkarg(LInsp arg, int stkd) +{ + bool isQuad = arg->isQuad(); + + Register rr; + if (arg->isUsed() && (rr = arg->getReg(), isKnownReg(rr))) { + // The argument resides somewhere in registers, so we simply need to + // push it onto the stack. + if (!ARM_VFP || !isQuad) { + NanoAssert(IsGpReg(rr)); + + STR(rr, SP, stkd); + } else { + // According to the comments in asm_arg_64, LIR_qjoin + // can have a 64-bit argument even if VFP is disabled. However, + // asm_arg_64 will split the argument and issue two 32-bit + // arguments to asm_stkarg so we can ignore that case here and + // assert that we will never get 64-bit arguments unless VFP is + // available. + NanoAssert(ARM_VFP); + NanoAssert(IsFpReg(rr)); + +#ifdef NJ_ARM_EABI + // EABI requires that 64-bit arguments are 64-bit aligned. + NanoAssert((stkd & 7) == 0); +#endif + + FSTD(rr, SP, stkd); + } + } else { + // The argument does not reside in registers, so we need to get some + // memory for it and then copy it onto the stack. + int d = findMemFor(arg); + if (!isQuad) { + STR(IP, SP, stkd); + if (arg->isop(LIR_alloc)) { + asm_add_imm(IP, FP, d); + } else { + LDR(IP, FP, d); + } + } else { +#ifdef NJ_ARM_EABI + // EABI requires that 64-bit arguments are 64-bit aligned. + NanoAssert((stkd & 7) == 0); +#endif + + STR(IP, SP, stkd+4); + LDR(IP, FP, d+4); + STR(IP, SP, stkd); + LDR(IP, FP, d); + } + } +} + +void +Assembler::asm_call(LInsp ins) +{ + const CallInfo* call = ins->callInfo(); + ArgSize sizes[MAXARGS]; + uint32_t argc = call->get_sizes(sizes); + bool indirect = call->isIndirect(); + + // If we aren't using VFP, assert that the LIR operation is an integer + // function call. + NanoAssert(ARM_VFP || ins->isop(LIR_icall)); + + // If we're using VFP, and the return type is a double, it'll come back in + // R0/R1. We need to either place it in the result fp reg, or store it. + // See comments in asm_prep_fcall() for more details as to why this is + // necessary here for floating point calls, but not for integer calls. + if (ARM_VFP && ins->isUsed()) { + // Determine the size (and type) of the instruction result. + ArgSize rsize = (ArgSize)(call->_argtypes & ARGSIZE_MASK_ANY); + + // If the result size is a floating-point value, treat the result + // specially, as described previously. + if (rsize == ARGSIZE_F) { + Register rr = ins->getReg(); + + NanoAssert(ins->opcode() == LIR_fcall); + + if (!isKnownReg(rr)) { + int d = disp(ins); + NanoAssert(d != 0); + freeRsrcOf(ins, false); + + // The result doesn't have a register allocated, so store the + // result (in R0,R1) directly to its stack slot. + STR(R0, FP, d+0); + STR(R1, FP, d+4); + } else { + NanoAssert(IsFpReg(rr)); + + // Copy the result to the (VFP) result register. + prepResultReg(ins, rmask(rr)); + FMDRR(rr, R0, R1); + } + } + } + + // Emit the branch. + if (!indirect) { + verbose_only(if (_logc->lcbits & LC_Assembly) + outputf(" %p:", _nIns); + ) + + // Direct call: on v5 and above (where the calling sequence doesn't + // corrupt LR until the actual branch instruction), we can avoid an + // interlock in the "long" branch sequence by manually loading the + // target address into LR ourselves before setting up the parameters + // in other registers. + BranchWithLink((NIns*)call->_address); + } else { + // Indirect call: we assign the address arg to LR since it's not + // used for regular arguments, and is otherwise scratch since it's + // clobberred by the call. On v4/v4T, where we have to manually do + // the equivalent of a BLX, move LR into IP before corrupting LR + // with the return address. + if (blx_lr_bug) { + // workaround for msft device emulator bug (blx lr emulated as no-op) + underrunProtect(8); + BLX(IP); + MOV(IP,LR); + } else { + BLX(LR); + } + asm_regarg(ARGSIZE_LO, ins->arg(--argc), LR); + } + + // Encode the arguments, starting at R0 and with an empty argument stack. + Register r = R0; + int stkd = 0; + + // Iterate through the argument list and encode each argument according to + // the ABI. + // Note that we loop through the arguments backwards as LIR specifies them + // in reverse order. + uint32_t i = argc; + while(i--) { + asm_arg(sizes[i], ins->arg(i), r, stkd); + } + + if (stkd > max_out_args) { + max_out_args = stkd; + } +} + +Register +Assembler::nRegisterAllocFromSet(RegisterMask set) +{ + NanoAssert(set != 0); + + // The CountLeadingZeroes function will use the CLZ instruction where + // available. In other cases, it will fall back to a (slower) C + // implementation. + Register r = (Register)(31-CountLeadingZeroes(set)); + _allocator.free &= ~rmask(r); + + NanoAssert(IsGpReg(r) || IsFpReg(r)); + NanoAssert((rmask(r) & set) == rmask(r)); + + return r; +} + +void +Assembler::nRegisterResetAll(RegAlloc& a) +{ + // add scratch registers to our free list for the allocator + a.clear(); + a.free = + rmask(R0) | rmask(R1) | rmask(R2) | rmask(R3) | rmask(R4) | + rmask(R5) | rmask(R6) | rmask(R7) | rmask(R8) | rmask(R9) | + rmask(R10) | rmask(LR); + if (ARM_VFP) + a.free |= FpRegs; + + debug_only(a.managed = a.free); +} + +static inline ConditionCode +get_cc(NIns *ins) +{ + return ConditionCode((*ins >> 28) & 0xF); +} + +static inline bool +branch_is_B(NIns* branch) +{ + return (*branch & 0x0E000000) == 0x0A000000; +} + +static inline bool +branch_is_LDR_PC(NIns* branch) +{ + return (*branch & 0x0F7FF000) == 0x051FF000; +} + +// Is this an instruction of the form ldr/str reg, [fp, #-imm] ? +static inline bool +is_ldstr_reg_fp_minus_imm(/*OUT*/uint32_t* isLoad, /*OUT*/uint32_t* rX, + /*OUT*/uint32_t* immX, NIns i1) +{ + if ((i1 & 0xFFEF0000) != 0xE50B0000) + return false; + *isLoad = (i1 >> 20) & 1; + *rX = (i1 >> 12) & 0xF; + *immX = i1 & 0xFFF; + return true; +} + +// Is this an instruction of the form ldmdb/stmdb fp, regset ? +static inline bool +is_ldstmdb_fp(/*OUT*/uint32_t* isLoad, /*OUT*/uint32_t* regSet, NIns i1) +{ + if ((i1 & 0xFFEF0000) != 0xE90B0000) + return false; + *isLoad = (i1 >> 20) & 1; + *regSet = i1 & 0xFFFF; + return true; +} + +// Make an instruction of the form ldmdb/stmdb fp, regset +static inline NIns +mk_ldstmdb_fp(uint32_t isLoad, uint32_t regSet) +{ + return 0xE90B0000 | (regSet & 0xFFFF) | ((isLoad & 1) << 20); +} + +// Compute the number of 1 bits in the lowest 16 bits of regSet +static inline uint32_t +size_of_regSet(uint32_t regSet) +{ + uint32_t x = regSet; + x = (x & 0x5555) + ((x >> 1) & 0x5555); + x = (x & 0x3333) + ((x >> 2) & 0x3333); + x = (x & 0x0F0F) + ((x >> 4) & 0x0F0F); + x = (x & 0x00FF) + ((x >> 8) & 0x00FF); + return x; +} + +// See if two ARM instructions, i1 and i2, can be combined into one +static bool +do_peep_2_1(/*OUT*/NIns* merged, NIns i1, NIns i2) +{ + uint32_t rX, rY, immX, immY, isLoadX, isLoadY, regSet; + /* ld/str rX, [fp, #-8] + ld/str rY, [fp, #-4] + ==> + ld/stmdb fp, {rX, rY} + when + X < Y and X != fp and Y != fp and X != 15 and Y != 15 + */ + if (is_ldstr_reg_fp_minus_imm(&isLoadX, &rX, &immX, i1) && + is_ldstr_reg_fp_minus_imm(&isLoadY, &rY, &immY, i2) && + immX == 8 && immY == 4 && rX < rY && + isLoadX == isLoadY && + rX != FP && rY != FP && + rX != 15 && rY != 15) { + *merged = mk_ldstmdb_fp(isLoadX, (1 << rX) | (1< + ld/stmdb fp, union(regset,{rX}) + when + regset is nonempty + X < all elements of regset + N == 4 * (1 + card(regset)) + X != fp and X != 15 + */ + if (is_ldstr_reg_fp_minus_imm(&isLoadX, &rX, &immX, i1) && + is_ldstmdb_fp(&isLoadY, ®Set, i2) && + regSet != 0 && + (regSet & ((1 << (rX + 1)) - 1)) == 0 && + immX == 4 * (1 + size_of_regSet(regSet)) && + isLoadX == isLoadY && + rX != FP && rX != 15) { + *merged = mk_ldstmdb_fp(isLoadX, regSet | (1 << rX)); + return true; + } + return false; +} + +// Determine whether or not it's safe to look at _nIns[1]. +// Necessary condition for safe peepholing with do_peep_2_1. +static inline bool +does_next_instruction_exist(NIns* _nIns, NIns* codeStart, NIns* codeEnd, + NIns* exitStart, NIns* exitEnd) +{ + return (exitStart <= _nIns && _nIns+1 < exitEnd) || + (codeStart <= _nIns && _nIns+1 < codeEnd); +} + +void +Assembler::nPatchBranch(NIns* branch, NIns* target) +{ + // Patch the jump in a loop + + // + // There are two feasible cases here, the first of which has 2 sub-cases: + // + // (1) We are patching a patchable unconditional jump emitted by + // JMP_far. All possible encodings we may be looking at with + // involve 2 words, though we *may* have to change from 1 word to + // 2 or vice verse. + // + // 1a: B ±32MB ; BKPT + // 1b: LDR PC [PC, #-4] ; $imm + // + // (2) We are patching a patchable conditional jump emitted by + // B_cond_chk. Short conditional jumps are non-patchable, so we + // won't have one here; will only ever have an instruction of the + // following form: + // + // LDRcc PC [PC, #lit] ... + // + // We don't actually know whether the lit-address is in the + // constant pool or in-line of the instruction stream, following + // the insn (with a jump over it) and we don't need to. For our + // purposes here, cases 2, 3 and 4 all look the same. + // + // For purposes of handling our patching task, we group cases 1b and 2 + // together, and handle case 1a on its own as it might require expanding + // from a short-jump to a long-jump. + // + // We do not handle contracting from a long-jump to a short-jump, though + // this is a possible future optimisation for case 1b. For now it seems + // not worth the trouble. + // + + if (branch_is_B(branch)) { + // Case 1a + // A short B branch, must be unconditional. + NanoAssert(get_cc(branch) == AL); + + int32_t offset = PC_OFFSET_FROM(target, branch); + if (isS24(offset>>2)) { + // We can preserve the existing form, just rewrite its offset. + NIns cond = *branch & 0xF0000000; + *branch = (NIns)( cond | (0xA<<24) | ((offset>>2) & 0xFFFFFF) ); + } else { + // We need to expand the existing branch to a long jump. + // make sure the next instruction is a dummy BKPT + NanoAssert(*(branch+1) == BKPT_insn); + + // Set the branch instruction to LDRcc pc, [pc, #-4] + NIns cond = *branch & 0xF0000000; + *branch++ = (NIns)( cond | (0x51<<20) | (PC<<16) | (PC<<12) | (4)); + *branch++ = (NIns)target; + } + } else { + // Case 1b & 2 + // Not a B branch, must be LDR, might be any kind of condition. + NanoAssert(branch_is_LDR_PC(branch)); + + NIns *addr = branch+2; + int offset = (*branch & 0xFFF) / sizeof(NIns); + if (*branch & (1<<23)) { + addr += offset; + } else { + addr -= offset; + } + + // Just redirect the jump target, leave the insn alone. + *addr = (NIns) target; + } +} + +RegisterMask +Assembler::hint(LIns* i, RegisterMask allow /* = ~0 */) +{ + uint32_t op = i->opcode(); + int prefer = ~0; + if (op==LIR_icall) + prefer = rmask(R0); + else if (op == LIR_callh) + prefer = rmask(R1); + else if (op == LIR_param) { + if (i->paramArg() < 4) + prefer = rmask(argRegs[i->paramArg()]); + } + if (_allocator.free & allow & prefer) + allow &= prefer; + return allow; +} + +void +Assembler::asm_qjoin(LIns *ins) +{ + int d = findMemFor(ins); + NanoAssert(d); + LIns* lo = ins->oprnd1(); + LIns* hi = ins->oprnd2(); + + Register r = findRegFor(hi, GpRegs); + STR(r, FP, d+4); + + // okay if r gets recycled. + r = findRegFor(lo, GpRegs); + STR(r, FP, d); + freeRsrcOf(ins, false); // if we had a reg in use, emit a ST to flush it to mem +} + +void +Assembler::asm_store32(LIns *value, int dr, LIns *base) +{ + Register ra, rb; + if (base->isop(LIR_alloc)) { + rb = FP; + dr += findMemFor(base); + ra = findRegFor(value, GpRegs); + } else { + findRegFor2(GpRegs, value, ra, base, rb); + } + + if (!isS12(dr)) { + STR(ra, IP, 0); + asm_add_imm(IP, rb, dr); + } else { + STR(ra, rb, dr); + } +} + +void +Assembler::asm_restore(LInsp i, Register r) +{ + if (i->isop(LIR_alloc)) { + asm_add_imm(r, FP, disp(i)); + } else if (i->isconst()) { + if (!i->getArIndex()) { + i->markAsClear(); + } + asm_ld_imm(r, i->imm32()); + } + else { + // We can't easily load immediate values directly into FP registers, so + // ensure that memory is allocated for the constant and load it from + // memory. + int d = findMemFor(i); + if (ARM_VFP && IsFpReg(r)) { + if (isS8(d >> 2)) { + FLDD(r, FP, d); + } else { + FLDD(r, IP, 0); + asm_add_imm(IP, FP, d); + } + } else { + NIns merged; + LDR(r, FP, d); + // See if we can merge this load into an immediately following + // one, by creating or extending an LDM instruction. + if (/* is it safe to poke _nIns[1] ? */ + does_next_instruction_exist(_nIns, codeStart, codeEnd, + exitStart, exitEnd) + && /* can we merge _nIns[0] into _nIns[1] ? */ + do_peep_2_1(&merged, _nIns[0], _nIns[1])) { + _nIns[1] = merged; + _nIns++; + verbose_only( asm_output("merge next into LDMDB"); ) + } + } + } +} + +void +Assembler::asm_spill(Register rr, int d, bool pop, bool quad) +{ + (void) pop; + (void) quad; + if (d) { + if (ARM_VFP && IsFpReg(rr)) { + if (isS8(d >> 2)) { + FSTD(rr, FP, d); + } else { + FSTD(rr, IP, 0); + asm_add_imm(IP, FP, d); + } + } else { + NIns merged; + STR(rr, FP, d); + // See if we can merge this store into an immediately following one, + // one, by creating or extending a STM instruction. + if (/* is it safe to poke _nIns[1] ? */ + does_next_instruction_exist(_nIns, codeStart, codeEnd, + exitStart, exitEnd) + && /* can we merge _nIns[0] into _nIns[1] ? */ + do_peep_2_1(&merged, _nIns[0], _nIns[1])) { + _nIns[1] = merged; + _nIns++; + verbose_only( asm_output("merge next into STMDB"); ) + } + } + } +} + +void +Assembler::asm_load64(LInsp ins) +{ + //asm_output("<<< load64"); + + NanoAssert(ins->isQuad()); + + LIns* base = ins->oprnd1(); + int offset = ins->disp(); + + Register rr = ins->getReg(); + int d = disp(ins); + + Register rb = findRegFor(base, GpRegs); + NanoAssert(IsGpReg(rb)); + freeRsrcOf(ins, false); + + //outputf("--- load64: Finished register allocation."); + + if (ARM_VFP && isKnownReg(rr)) { + // VFP is enabled and the result will go into a register. + NanoAssert(IsFpReg(rr)); + + if (!isS8(offset >> 2) || (offset&3) != 0) { + FLDD(rr,IP,0); + asm_add_imm(IP, rb, offset); + } else { + FLDD(rr,rb,offset); + } + } else { + // Either VFP is not available or the result needs to go into memory; + // in either case, VFP instructions are not required. Note that the + // result will never be loaded into registers if VFP is not available. + NanoAssert(!isKnownReg(rr)); + NanoAssert(d != 0); + + // Check that the offset is 8-byte (64-bit) aligned. + NanoAssert((d & 0x7) == 0); + + // *(uint64_t*)(FP+d) = *(uint64_t*)(rb+offset) + asm_mmq(FP, d, rb, offset); + } + + //asm_output(">>> load64"); +} + +void +Assembler::asm_store64(LInsp value, int dr, LInsp base) +{ + //asm_output("<<< store64 (dr: %d)", dr); + + if (ARM_VFP) { + Register rb = findRegFor(base, GpRegs); + + if (value->isconstq()) { + underrunProtect(LD32_size*2 + 8); + + // XXX use another reg, get rid of dependency + STR(IP, rb, dr); + asm_ld_imm(IP, value->imm64_0(), false); + STR(IP, rb, dr+4); + asm_ld_imm(IP, value->imm64_1(), false); + + return; + } + + Register rv = findRegFor(value, FpRegs); + + NanoAssert(isKnownReg(rb)); + NanoAssert(isKnownReg(rv)); + + Register baseReg = rb; + intptr_t baseOffset = dr; + + if (!isS8(dr)) { + baseReg = IP; + baseOffset = 0; + } + + FSTD(rv, baseReg, baseOffset); + + if (!isS8(dr)) { + asm_add_imm(IP, rb, dr); + } + + // if it's a constant, make sure our baseReg/baseOffset location + // has the right value + if (value->isconstq()) { + underrunProtect(4*4); + asm_quad_nochk(rv, value->imm64_0(), value->imm64_1()); + } + } else { + int da = findMemFor(value); + Register rb = findRegFor(base, GpRegs); + // *(uint64_t*)(rb+dr) = *(uint64_t*)(FP+da) + asm_mmq(rb, dr, FP, da); + } + + //asm_output(">>> store64"); +} + +// stick a quad into register rr, where p points to the two +// 32-bit parts of the quad, optinally also storing at FP+d +void +Assembler::asm_quad_nochk(Register rr, int32_t imm64_0, int32_t imm64_1) +{ + // We're not going to use a slot, because it might be too far + // away. Instead, we're going to stick a branch in the stream to + // jump over the constants, and then load from a short PC relative + // offset. + + // stream should look like: + // branch A + // imm64_0 + // imm64_1 + // A: FLDD PC-16 + + FLDD(rr, PC, -16); + + *(--_nIns) = (NIns) imm64_1; + *(--_nIns) = (NIns) imm64_0; + + B_nochk(_nIns+2); +} + +void +Assembler::asm_quad(LInsp ins) +{ + //asm_output(">>> asm_quad"); + + int d = disp(ins); + Register rr = ins->getReg(); + + freeRsrcOf(ins, false); + + if (ARM_VFP && isKnownReg(rr)) + { + asm_spill(rr, d, false, true); + + underrunProtect(4*4); + asm_quad_nochk(rr, ins->imm64_0(), ins->imm64_1()); + } else { + NanoAssert(d); + // asm_mmq might spill a reg, so don't call it; + // instead do the equivalent directly. + //asm_mmq(FP, d, PC, -16); + + STR(IP, FP, d+4); + asm_ld_imm(IP, ins->imm64_1()); + STR(IP, FP, d); + asm_ld_imm(IP, ins->imm64_0()); + } + + //asm_output("<<< asm_quad"); +} + +void +Assembler::asm_nongp_copy(Register r, Register s) +{ + if (ARM_VFP && IsFpReg(r) && IsFpReg(s)) { + // fp->fp + FCPYD(r, s); + } else { + // We can't move a double-precision FP register into a 32-bit GP + // register, so assert that no calling code is trying to do that. + NanoAssert(0); + } +} + +Register +Assembler::asm_binop_rhs_reg(LInsp) +{ + return UnknownReg; +} + +/** + * copy 64 bits: (rd+dd) <- (rs+ds) + */ +void +Assembler::asm_mmq(Register rd, int dd, Register rs, int ds) +{ + // The value is either a 64bit struct or maybe a float that isn't live in + // an FPU reg. Either way, don't put it in an FPU reg just to load & store + // it. + // This operation becomes a simple 64-bit memcpy. + + // In order to make the operation optimal, we will require two GP + // registers. We can't allocate a register here because the caller may have + // called freeRsrcOf, and allocating a register here may cause something + // else to spill onto the stack which has just be conveniently freed by + // freeRsrcOf (resulting in stack corruption). + // + // Falling back to a single-register implementation of asm_mmq is better + // than adjusting the callers' behaviour (to allow us to allocate another + // register here) because spilling a register will end up being slower than + // just using the same register twice anyway. + // + // Thus, if there is a free register which we can borrow, we will emit the + // following code: + // LDR rr, [rs, #ds] + // LDR ip, [rs, #(ds+4)] + // STR rr, [rd, #dd] + // STR ip, [rd, #(dd+4)] + // (Where rr is the borrowed register.) + // + // If there is no free register, don't spill an existing allocation. Just + // do the following: + // LDR ip, [rs, #ds] + // STR ip, [rd, #dd] + // LDR ip, [rs, #(ds+4)] + // STR ip, [rd, #(dd+4)] + + // Ensure that the PC is not used as either base register. The instruction + // generation macros call underrunProtect, and a side effect of this is + // that we may be pushed onto another page, so the PC is not a reliable + // base register. + NanoAssert(rs != PC); + NanoAssert(rd != PC); + + // Find the list of free registers from the allocator's free list and the + // GpRegs mask. This excludes any floating-point registers that may be on + // the free list. + RegisterMask free = _allocator.free & AllowableFlagRegs; + + if (free) { + // There is at least one register on the free list, so grab one for + // temporary use. There is no need to allocate it explicitly because + // we won't need it after this function returns. + + // The CountLeadingZeroes can be used to quickly find a set bit in the + // free mask. + Register rr = (Register)(31-CountLeadingZeroes(free)); + + // Note: Not every register in GpRegs is usable here. However, these + // registers will never appear on the free list. + NanoAssert((free & rmask(PC)) == 0); + NanoAssert((free & rmask(LR)) == 0); + NanoAssert((free & rmask(SP)) == 0); + NanoAssert((free & rmask(IP)) == 0); + NanoAssert((free & rmask(FP)) == 0); + + // Emit the actual instruction sequence. + + STR(IP, rd, dd+4); + STR(rr, rd, dd); + LDR(IP, rs, ds+4); + LDR(rr, rs, ds); + } else { + // There are no free registers, so fall back to using IP twice. + STR(IP, rd, dd+4); + LDR(IP, rs, ds+4); + STR(IP, rd, dd); + LDR(IP, rs, ds); + } +} + +// Increment the 32-bit profiling counter at pCtr, without +// changing any registers. +verbose_only( +void Assembler::asm_inc_m32(uint32_t* pCtr) +{ + // We need to temporarily free up two registers to do this, so + // just push r0 and r1 on the stack. This assumes that the area + // at r13 - 8 .. r13 - 1 isn't being used for anything else at + // this point. This guaranteed us by the EABI; although the + // situation with the legacy ABI I'm not sure of. + // + // Plan: emit the following bit of code. It's not efficient, but + // this is for profiling debug builds only, and is self contained, + // except for above comment re stack use. + // + // E92D0003 push {r0,r1} + // E59F0000 ldr r0, [r15] ; pCtr + // EA000000 b .+8 ; jump over imm + // 12345678 .word 0x12345678 ; pCtr + // E5901000 ldr r1, [r0] + // E2811001 add r1, r1, #1 + // E5801000 str r1, [r0] + // E8BD0003 pop {r0,r1} + + // We need keep the 4 words beginning at "ldr r0, [r15]" + // together. Simplest to underrunProtect the whole thing. + underrunProtect(8*4); + IMM32(0xE8BD0003); // pop {r0,r1} + IMM32(0xE5801000); // str r1, [r0] + IMM32(0xE2811001); // add r1, r1, #1 + IMM32(0xE5901000); // ldr r1, [r0] + IMM32((uint32_t)pCtr); // .word pCtr + IMM32(0xEA000000); // b .+8 + IMM32(0xE59F0000); // ldr r0, [r15] + IMM32(0xE92D0003); // push {r0,r1} +} +) + +void +Assembler::nativePageReset() +{ + _nSlot = 0; + _nExitSlot = 0; +} + +void +Assembler::nativePageSetup() +{ + NanoAssert(!_inExit); + if (!_nIns) + codeAlloc(codeStart, codeEnd, _nIns verbose_only(, codeBytes)); + if (!_nExitIns) + codeAlloc(exitStart, exitEnd, _nExitIns verbose_only(, exitBytes)); + + // constpool starts at top of page and goes down, + // code starts at bottom of page and moves up + if (!_nSlot) + _nSlot = codeStart; + if (!_nExitSlot) + _nExitSlot = exitStart; +} + + +void +Assembler::underrunProtect(int bytes) +{ + NanoAssertMsg(bytes<=LARGEST_UNDERRUN_PROT, "constant LARGEST_UNDERRUN_PROT is too small"); + NanoAssert(_nSlot != 0 && int(_nIns)-int(_nSlot) <= 4096); + uintptr_t top = uintptr_t(_nSlot); + uintptr_t pc = uintptr_t(_nIns); + if (pc - bytes < top) + { + verbose_only(verbose_outputf(" %p:", _nIns);) + NIns* target = _nIns; + // This may be in a normal code chunk or an exit code chunk. + codeAlloc(codeStart, codeEnd, _nIns verbose_only(, codeBytes)); + + _nSlot = codeStart; + + // _nSlot points to the first empty position in the new code block + // _nIns points just past the last empty position. + // Assume B_nochk won't ever try to write to _nSlot. See B_cond_chk macro. + B_nochk(target); + } +} + +void +Assembler::JMP_far(NIns* addr) +{ + // Even if a simple branch is all that is required, this function must emit + // two words so that the branch can be arbitrarily patched later on. + underrunProtect(8); + + intptr_t offs = PC_OFFSET_FROM(addr,_nIns-2); + + if (isS24(offs>>2)) { + // Emit a BKPT to ensure that we reserve enough space for a full 32-bit + // branch patch later on. The BKPT should never be executed. + BKPT_nochk(); + + asm_output("bkpt"); + + // B [PC+offs] + *(--_nIns) = (NIns)( COND_AL | (0xA<<24) | ((offs>>2) & 0xFFFFFF) ); + + asm_output("b %p", (void*)addr); + } else { + // Insert the target address as a constant in the instruction stream. + *(--_nIns) = (NIns)((addr)); + // ldr pc, [pc, #-4] // load the address into pc, reading it from [pc-4] (e.g., + // the next instruction) + *(--_nIns) = (NIns)( COND_AL | (0x51<<20) | (PC<<16) | (PC<<12) | (4)); + + asm_output("ldr pc, =%p", (void*)addr); + } +} + +// Perform a branch with link, and ARM/Thumb exchange if necessary. The actual +// BLX instruction is only available from ARMv5 onwards, but as we don't +// support anything older than that this function will not attempt to output +// pre-ARMv5 sequences. +// +// Note: This function is not designed to be used with branches which will be +// patched later, though it will work if the patcher knows how to patch the +// generated instruction sequence. +void +Assembler::BranchWithLink(NIns* addr) +{ + // Most branches emitted by TM are loaded through a register, so always + // reserve enough space for the LDR sequence. This should give us a slight + // net gain over reserving the exact amount required for shorter branches. + // This _must_ be called before PC_OFFSET_FROM as it can move _nIns! + underrunProtect(4+LD32_size); + + // Calculate the offset from the instruction that is about to be + // written (at _nIns-1) to the target. + intptr_t offs = PC_OFFSET_FROM(addr,_nIns-1); + + // ARMv5 and above can use BLX for branches within ±32MB of the + // PC and BLX Rm for long branches. + if (isS24(offs>>2)) { + // the value we need to stick in the instruction; masked, + // because it will be sign-extended back to 32 bits. + intptr_t offs2 = (offs>>2) & 0xffffff; + + if (((intptr_t)addr & 1) == 0) { + // The target is ARM, so just emit a BL. + + // BL target + *(--_nIns) = (NIns)( (COND_AL) | (0xB<<24) | (offs2) ); + asm_output("bl %p", (void*)addr); + } else { + // The target is Thumb, so emit a BLX. + + // We need to emit an ARMv5+ instruction, so assert that we have a + // suitable processor. Note that we don't support ARMv4(T), but + // this serves as a useful sanity check. + NanoAssert(ARM_ARCH >= 5); + + // The (pre-shifted) value of the "H" bit in the BLX encoding. + uint32_t H = (offs & 0x2) << 23; + + // BLX addr + *(--_nIns) = (NIns)( (0xF << 28) | (0x5<<25) | (H) | (offs2) ); + asm_output("blx %p", (void*)addr); + } + } else { + // Load the target address into IP and branch to that. We've already + // done underrunProtect, so we can skip that here. + BLX(IP, false); + + // LDR IP, =addr + asm_ld_imm(IP, (int32_t)addr, false); + } +} + +// This is identical to BranchWithLink(NIns*) but emits a branch to an address +// held in a register rather than a literal address. +inline void +Assembler::BLX(Register addr, bool chk /* = true */) +{ + // We need to emit an ARMv5+ instruction, so assert that we have a suitable + // processor. Note that we don't support ARMv4(T), but this serves as a + // useful sanity check. + NanoAssert(ARM_ARCH >= 5); + + NanoAssert(IsGpReg(addr)); + // There is a bug in the WinCE device emulator which stops "BLX LR" from + // working as expected. Assert that we never do that! + if (blx_lr_bug) { NanoAssert(addr != LR); } + + if (chk) { + underrunProtect(4); + } + + // BLX IP + *(--_nIns) = (NIns)( (COND_AL) | (0x12<<20) | (0xFFF<<8) | (0x3<<4) | (addr) ); + asm_output("blx ip"); +} + +// Emit the code required to load a memory address into a register as follows: +// d = *(b+off) +// underrunProtect calls from this function can be disabled by setting chk to +// false. However, this function can use more than LD32_size bytes of space if +// the offset is out of the range of a LDR instruction; the maximum space this +// function requires for underrunProtect is 4+LD32_size. +void +Assembler::asm_ldr_chk(Register d, Register b, int32_t off, bool chk) +{ + if (ARM_VFP && IsFpReg(d)) { + FLDD_chk(d,b,off,chk); + return; + } + + NanoAssert(IsGpReg(d)); + NanoAssert(IsGpReg(b)); + + // We can't use underrunProtect if the base register is the PC because + // underrunProtect might move the PC if there isn't enough space on the + // current page. + NanoAssert((b != PC) || (!chk)); + + if (isU12(off)) { + // LDR d, b, #+off + if (chk) underrunProtect(4); + *(--_nIns) = (NIns)( COND_AL | (0x59<<20) | (b<<16) | (d<<12) | off ); + } else if (isU12(-off)) { + // LDR d, b, #-off + if (chk) underrunProtect(4); + *(--_nIns) = (NIns)( COND_AL | (0x51<<20) | (b<<16) | (d<<12) | -off ); + } else { + // The offset is over 4096 (and outside the range of LDR), so we need + // to add a level of indirection to get the address into IP. + + // Because of that, we can't do a PC-relative load unless it fits within + // the single-instruction forms above. + + NanoAssert(b != PC); + NanoAssert(b != IP); + + if (chk) underrunProtect(4+LD32_size); + + *(--_nIns) = (NIns)( COND_AL | (0x79<<20) | (b<<16) | (d<<12) | IP ); + asm_ld_imm(IP, off, false); + } + + asm_output("ldr %s, [%s, #%d]",gpn(d),gpn(b),(off)); +} + +// Emit the code required to load an immediate value (imm) into general-purpose +// register d. Optimal (MOV-based) mechanisms are used if the immediate can be +// encoded using ARM's operand 2 encoding. Otherwise, a slot is used on the +// literal pool and LDR is used to load the value. +// +// chk can be explicitly set to false in order to disable underrunProtect calls +// from this function; this allows the caller to perform the check manually. +// This function guarantees not to use more than LD32_size bytes of space. +void +Assembler::asm_ld_imm(Register d, int32_t imm, bool chk /* = true */) +{ + uint32_t op2imm; + + NanoAssert(IsGpReg(d)); + + // Attempt to encode the immediate using the second operand of MOV or MVN. + // This is the simplest solution and generates the shortest and fastest + // code, but can only encode a limited set of values. + + if (encOp2Imm(imm, &op2imm)) { + // Use MOV to encode the literal. + MOVis(d, op2imm, 0); + return; + } + + if (encOp2Imm(~imm, &op2imm)) { + // Use MVN to encode the inverted literal. + MVNis(d, op2imm, 0); + return; + } + + // Try to use simple MOV, MVN or MOV(W|T) instructions to load the + // immediate. If this isn't possible, load it from memory. + // - We cannot use MOV(W|T) on cores older than the introduction of + // Thumb-2 or if the target register is the PC. + if (ARM_THUMB2 && (d != PC)) { + // ARMv6T2 and above have MOVW and MOVT. + uint32_t high_h = (uint32_t)imm >> 16; + uint32_t low_h = imm & 0xffff; + + if (high_h != 0) { + // Load the high half-word (if necessary). + MOVTi_chk(d, high_h, chk); + } + // Load the low half-word. This also zeroes the high half-word, and + // thus must execute _before_ MOVT, and is necessary even if low_h is 0 + // because MOVT will not change the existing low half-word. + MOVWi_chk(d, low_h, chk); + + return; + } + + // We couldn't encode the literal in the instruction stream, so load it + // from memory. + + // Because the literal pool is on the same page as the generated code, it + // will almost always be within the ±4096 range of a LDR. However, this may + // not be the case if _nSlot is at the start of the page and _nIns is at + // the end because the PC is 8 bytes ahead of _nIns. This is unlikely to + // happen, but if it does occur we can simply waste a word or two of + // literal space. + + // We must do the underrunProtect before PC_OFFSET_FROM as underrunProtect + // can move the PC if there isn't enough space on the current page! + if (chk) { + underrunProtect(LD32_size); + } + + int offset = PC_OFFSET_FROM(_nSlot, _nIns-1); + // If the offset is out of range, waste literal space until it is in range. + while (offset <= -4096) { + ++_nSlot; + offset += sizeof(_nSlot); + } + NanoAssert(isS12(offset) && (offset <= -8)); + + // Write the literal. + *(_nSlot++) = imm; + asm_output("## imm= 0x%x", imm); + + // Load the literal. + LDR_nochk(d,PC,offset); + NanoAssert(uintptr_t(_nIns) + 8 + offset == uintptr_t(_nSlot-1)); + NanoAssert(*((int32_t*)_nSlot-1) == imm); +} + +// Branch to target address _t with condition _c, doing underrun +// checks (_chk == 1) or skipping them (_chk == 0). +// +// Set the target address (_t) to 0 if the target is not yet known and the +// branch will be patched up later. +// +// If the jump is to a known address (with _t != 0) and it fits in a relative +// jump (±32MB), emit that. +// If the jump is unconditional, emit the dest address inline in +// the instruction stream and load it into pc. +// If the jump has a condition, but noone's mucked with _nIns and our _nSlot +// pointer is valid, stick the constant in the slot and emit a conditional +// load into pc. +// Otherwise, emit the conditional load into pc from a nearby constant, +// and emit a jump to jump over it it in case the condition fails. +// +// NB: B_nochk depends on this not calling samepage() when _c == AL +void +Assembler::B_cond_chk(ConditionCode _c, NIns* _t, bool _chk) +{ + int32_t offs = PC_OFFSET_FROM(_t,_nIns-1); + //nj_dprintf("B_cond_chk target: 0x%08x offset: %d @0x%08x\n", _t, offs, _nIns-1); + + // optimistically check if this will fit in 24 bits + if (_chk && isS24(offs>>2) && (_t != 0)) { + underrunProtect(4); + // recalculate the offset, because underrunProtect may have + // moved _nIns to a new page + offs = PC_OFFSET_FROM(_t,_nIns-1); + } + + // Emit one of the following patterns: + // + // --- Short branch. This can never be emitted if the branch target is not + // known. + // B(cc) ±32MB + // + // --- Long unconditional branch. + // LDR PC, #lit + // lit: #target + // + // --- Long conditional branch. Note that conditional branches will never + // be patched, so the nPatchBranch function doesn't need to know where + // the literal pool is located. + // LDRcc PC, #lit + // ; #lit is in the literal pool at _nSlot + // + // --- Long conditional branch (if the slot isn't on the same page as the instruction). + // LDRcc PC, #lit + // B skip ; Jump over the literal data. + // lit: #target + // skip: [...] + + if (isS24(offs>>2) && (_t != 0)) { + // The underrunProtect for this was done above (if required by _chk). + *(--_nIns) = (NIns)( ((_c)<<28) | (0xA<<24) | (((offs)>>2) & 0xFFFFFF) ); + asm_output("b%s %p", _c == AL ? "" : condNames[_c], (void*)(_t)); + } else if (_c == AL) { + if(_chk) underrunProtect(8); + *(--_nIns) = (NIns)(_t); + *(--_nIns) = (NIns)( COND_AL | (0x51<<20) | (PC<<16) | (PC<<12) | 0x4 ); + asm_output("b%s %p", _c == AL ? "" : condNames[_c], (void*)(_t)); + } else if (PC_OFFSET_FROM(_nSlot, _nIns-1) > -0x1000) { + if(_chk) underrunProtect(8); + *(_nSlot++) = (NIns)(_t); + offs = PC_OFFSET_FROM(_nSlot-1,_nIns-1); + NanoAssert(offs < 0); + *(--_nIns) = (NIns)( ((_c)<<28) | (0x51<<20) | (PC<<16) | (PC<<12) | ((-offs) & 0xFFF) ); + asm_output("ldr%s %s, [%s, #-%d]", condNames[_c], gpn(PC), gpn(PC), -offs); + NanoAssert(uintptr_t(_nIns)+8+offs == uintptr_t(_nSlot-1)); + } else { + if(_chk) underrunProtect(12); + // Emit a pointer to the target as a literal in the instruction stream. + *(--_nIns) = (NIns)(_t); + // Emit a branch to skip over the literal. The PC value is 8 bytes + // ahead of the executing instruction, so to branch two instructions + // forward this must branch 8-8=0 bytes. + *(--_nIns) = (NIns)( COND_AL | (0xA<<24) | 0x0 ); + // Emit the conditional branch. + *(--_nIns) = (NIns)( ((_c)<<28) | (0x51<<20) | (PC<<16) | (PC<<12) | 0x0 ); + asm_output("b%s %p", _c == AL ? "" : condNames[_c], (void*)(_t)); + } +} + +/* + * VFP + */ + +void +Assembler::asm_i2f(LInsp ins) +{ + Register rr = prepResultReg(ins, FpRegs); + Register srcr = findRegFor(ins->oprnd1(), GpRegs); + + // todo: support int value in memory, as per x86 + NanoAssert(isKnownReg(srcr)); + + FSITOD(rr, FpSingleScratch); + FMSR(FpSingleScratch, srcr); +} + +void +Assembler::asm_u2f(LInsp ins) +{ + Register rr = prepResultReg(ins, FpRegs); + Register sr = findRegFor(ins->oprnd1(), GpRegs); + + // todo: support int value in memory, as per x86 + NanoAssert(isKnownReg(sr)); + + FUITOD(rr, FpSingleScratch); + FMSR(FpSingleScratch, sr); +} + +void +Assembler::asm_fneg(LInsp ins) +{ + LInsp lhs = ins->oprnd1(); + Register rr = prepResultReg(ins, FpRegs); + + Register sr = ( lhs->isUnusedOrHasUnknownReg() + ? findRegFor(lhs, FpRegs) + : lhs->getReg() ); + + FNEGD(rr, sr); +} + +void +Assembler::asm_fop(LInsp ins) +{ + LInsp lhs = ins->oprnd1(); + LInsp rhs = ins->oprnd2(); + LOpcode op = ins->opcode(); + + NanoAssert(op >= LIR_fadd && op <= LIR_fdiv); + + // rr = ra OP rb + + Register rr = prepResultReg(ins, FpRegs); + + Register ra = findRegFor(lhs, FpRegs); + Register rb = (rhs == lhs) ? ra : findRegFor(rhs, FpRegs & ~rmask(ra)); + + // XXX special-case 1.0 and 0.0 + + switch (op) + { + case LIR_fadd: FADDD(rr,ra,rb); break; + case LIR_fsub: FSUBD(rr,ra,rb); break; + case LIR_fmul: FMULD(rr,ra,rb); break; + case LIR_fdiv: FDIVD(rr,ra,rb); break; + default: NanoAssert(0); break; + } +} + +void +Assembler::asm_fcmp(LInsp ins) +{ + LInsp lhs = ins->oprnd1(); + LInsp rhs = ins->oprnd2(); + LOpcode op = ins->opcode(); + + NanoAssert(op >= LIR_feq && op <= LIR_fge); + + Register ra, rb; + findRegFor2(FpRegs, lhs, ra, rhs, rb); + + int e_bit = (op != LIR_feq); + + // do the comparison and get results loaded in ARM status register + FMSTAT(); + FCMPD(ra, rb, e_bit); +} + +Register +Assembler::asm_prep_fcall(LInsp) +{ + /* Because ARM actually returns the result in (R0,R1), and not in a + * floating point register, the code to move the result into a correct + * register is at the beginning of asm_call(). This function does + * nothing. + * + * The reason being that if this function did something, the final code + * sequence we'd get would be something like: + * MOV {R0-R3},params [from asm_call()] + * BL function [from asm_call()] + * MOV {R0-R3},spilled data [from evictScratchRegs()] + * MOV Dx,{R0,R1} [from this function] + * which is clearly broken. + * + * This is not a problem for non-floating point calls, because the + * restoring of spilled data into R0 is done via a call to prepResultReg(R0) + * at the same point in the sequence as this function is called, meaning that + * evictScratchRegs() will not modify R0. However, prepResultReg is not aware + * of the concept of using a register pair (R0,R1) for the result of a single + * operation, so it can only be used here with the ultimate VFP register, and + * not R0/R1, which potentially allows for R0/R1 to get corrupted as described. + */ + return UnknownReg; +} + +/* Call this with targ set to 0 if the target is not yet known and the branch + * will be patched up later. + */ +NIns* +Assembler::asm_branch(bool branchOnFalse, LInsp cond, NIns* targ) +{ + LOpcode condop = cond->opcode(); + NanoAssert(cond->isCond()); + NanoAssert(ARM_VFP || ((condop < LIR_feq) || (condop > LIR_fge))); + + // The old "never" condition code has special meaning on newer ARM cores, + // so use "always" as a sensible default code. + ConditionCode cc = AL; + + // Detect whether or not this is a floating-point comparison. + bool fp_cond; + + // Because MUL can't set the V flag, we use SMULL and CMP to set the Z flag + // to detect overflow on multiply. Thus, if cond points to a LIR_ov which + // in turn points to a LIR_mul, we must be conditional on !Z, not V. + if ((condop == LIR_ov) && (cond->oprnd1()->isop(LIR_mul))) { + condop = LIR_eq; + branchOnFalse = !branchOnFalse; + } + + // Select the appropriate ARM condition code to match the LIR instruction. + switch (condop) + { + // Floating-point conditions. Note that the VFP LT/LE conditions + // require use of the unsigned condition codes, even though + // float-point comparisons are always signed. + case LIR_feq: cc = EQ; fp_cond = true; break; + case LIR_flt: cc = LO; fp_cond = true; break; + case LIR_fle: cc = LS; fp_cond = true; break; + case LIR_fge: cc = GE; fp_cond = true; break; + case LIR_fgt: cc = GT; fp_cond = true; break; + + // Standard signed and unsigned integer comparisons. + case LIR_eq: cc = EQ; fp_cond = false; break; + case LIR_ov: cc = VS; fp_cond = false; break; + case LIR_lt: cc = LT; fp_cond = false; break; + case LIR_le: cc = LE; fp_cond = false; break; + case LIR_gt: cc = GT; fp_cond = false; break; + case LIR_ge: cc = GE; fp_cond = false; break; + case LIR_ult: cc = LO; fp_cond = false; break; + case LIR_ule: cc = LS; fp_cond = false; break; + case LIR_ugt: cc = HI; fp_cond = false; break; + case LIR_uge: cc = HS; fp_cond = false; break; + + // Default case for invalid or unexpected LIR instructions. + default: cc = AL; fp_cond = false; break; + } + + // Invert the condition if required. + if (branchOnFalse) + cc = OppositeCond(cc); + + // Ensure that we got a sensible condition code. + NanoAssert((cc != AL) && (cc != NV)); + + // Ensure that we don't hit floating-point LIR codes if VFP is disabled. + NanoAssert(ARM_VFP || !fp_cond); + + // Emit a suitable branch instruction. + B_cond(cc, targ); + + // Store the address of the branch instruction so that we can return it. + // asm_[f]cmp will move _nIns so we must do this now. + NIns *at = _nIns; + + if (ARM_VFP && fp_cond) + asm_fcmp(cond); + else + asm_cmp(cond); + + return at; +} + +void +Assembler::asm_cmp(LIns *cond) +{ + LOpcode condop = cond->opcode(); + + // LIR_ov recycles the flags set by arithmetic ops + if ((condop == LIR_ov)) + return; + + LInsp lhs = cond->oprnd1(); + LInsp rhs = cond->oprnd2(); + + // Not supported yet. + NanoAssert(!lhs->isQuad() && !rhs->isQuad()); + + // ready to issue the compare + if (rhs->isconst()) { + int c = rhs->imm32(); + if (c == 0 && cond->isop(LIR_eq)) { + Register r = findRegFor(lhs, GpRegs); + TST(r,r); + // No 64-bit immediates so fall-back to below + } else if (!rhs->isQuad()) { + Register r = getBaseReg(condop, lhs, c, GpRegs); + asm_cmpi(r, c); + } else { + NanoAssert(0); + } + } else { + Register ra, rb; + findRegFor2(GpRegs, lhs, ra, rhs, rb); + CMP(ra, rb); + } +} + +void +Assembler::asm_cmpi(Register r, int32_t imm) +{ + if (imm < 0) { + if (imm > -256) { + ALUi(AL, cmn, 1, 0, r, -imm); + } else { + underrunProtect(4 + LD32_size); + CMP(r, IP); + asm_ld_imm(IP, imm); + } + } else { + if (imm < 256) { + ALUi(AL, cmp, 1, 0, r, imm); + } else { + underrunProtect(4 + LD32_size); + CMP(r, IP); + asm_ld_imm(IP, imm); + } + } +} + +void +Assembler::asm_fcond(LInsp ins) +{ + // only want certain regs + Register r = prepResultReg(ins, AllowableFlagRegs); + + switch (ins->opcode()) { + case LIR_feq: SETEQ(r); break; + case LIR_flt: SETLO(r); break; // } note: VFP LT/LE operations require use of + case LIR_fle: SETLS(r); break; // } unsigned LO/LS condition codes! + case LIR_fge: SETGE(r); break; + case LIR_fgt: SETGT(r); break; + default: NanoAssert(0); break; + } + + asm_fcmp(ins); +} + +void +Assembler::asm_cond(LInsp ins) +{ + Register r = prepResultReg(ins, AllowableFlagRegs); + LOpcode op = ins->opcode(); + + switch(op) + { + case LIR_eq: SETEQ(r); break; + case LIR_lt: SETLT(r); break; + case LIR_le: SETLE(r); break; + case LIR_gt: SETGT(r); break; + case LIR_ge: SETGE(r); break; + case LIR_ult: SETLO(r); break; + case LIR_ule: SETLS(r); break; + case LIR_ugt: SETHI(r); break; + case LIR_uge: SETHS(r); break; + case LIR_ov: + // Because MUL can't set the V flag, we use SMULL and CMP to set + // the Z flag to detect overflow on multiply. Thus, if ins points + // to a LIR_ov which in turn points to a LIR_mul, we must be + // conditional on !Z, not V. + if (!ins->oprnd1()->isop(LIR_mul)) { + SETVS(r); + } else { + SETNE(r); + } + break; + default: NanoAssert(0); break; + } + asm_cmp(ins); +} + +void +Assembler::asm_arith(LInsp ins) +{ + LOpcode op = ins->opcode(); + LInsp lhs = ins->oprnd1(); + LInsp rhs = ins->oprnd2(); + + RegisterMask allow = GpRegs; + + // We always need the result register and the first operand register. + Register rr = prepResultReg(ins, allow); + + // If this is the last use of lhs in reg, we can re-use the result reg. + // Else, lhs already has a register assigned. + Register ra = ( lhs->isUnusedOrHasUnknownReg() + ? findSpecificRegFor(lhs, rr) + : lhs->getReg() ); + + // Don't re-use the registers we've already allocated. + NanoAssert(isKnownReg(rr)); + NanoAssert(isKnownReg(ra)); + allow &= ~rmask(rr); + allow &= ~rmask(ra); + + // If the rhs is constant, we can use the instruction-specific code to + // determine if the value can be encoded in an ARM instruction. If the + // value cannot be encoded, it will be loaded into a register. + // + // Note that the MUL instruction can never take an immediate argument so + // even if the argument is constant, we must allocate a register for it. + // + // Note: It is possible to use a combination of the barrel shifter and the + // basic arithmetic instructions to generate constant multiplications. + // However, LIR_mul is never invoked with a constant during + // trace-tests.js so it is very unlikely to be worthwhile implementing it. + if (rhs->isconst() && op != LIR_mul) + { + if ((op == LIR_add || op == LIR_iaddp) && lhs->isop(LIR_ialloc)) { + // Add alloc+const. The result should be the address of the + // allocated space plus a constant. + Register rs = prepResultReg(ins, allow); + int d = findMemFor(lhs) + rhs->imm32(); + + NanoAssert(isKnownReg(rs)); + asm_add_imm(rs, FP, d); + } + + int32_t imm32 = rhs->imm32(); + + switch (op) + { + case LIR_iaddp: asm_add_imm(rr, ra, imm32); break; + case LIR_add: asm_add_imm(rr, ra, imm32, 1); break; + case LIR_sub: asm_sub_imm(rr, ra, imm32, 1); break; + case LIR_and: asm_and_imm(rr, ra, imm32); break; + case LIR_or: asm_orr_imm(rr, ra, imm32); break; + case LIR_xor: asm_eor_imm(rr, ra, imm32); break; + case LIR_lsh: LSLi(rr, ra, imm32); break; + case LIR_rsh: ASRi(rr, ra, imm32); break; + case LIR_ush: LSRi(rr, ra, imm32); break; + + default: + NanoAssertMsg(0, "Unsupported"); + break; + } + + // We've already emitted an instruction, so return now. + return; + } + + // The rhs is either a register or cannot be encoded as a constant. + + Register rb; + if (lhs == rhs) { + rb = ra; + } else { + rb = asm_binop_rhs_reg(ins); + if (!isKnownReg(rb)) + rb = findRegFor(rhs, allow); + allow &= ~rmask(rb); + } + NanoAssert(isKnownReg(rb)); + + switch (op) + { + case LIR_iaddp: ADDs(rr, ra, rb, 0); break; + case LIR_add: ADDs(rr, ra, rb, 1); break; + case LIR_sub: SUBs(rr, ra, rb, 1); break; + case LIR_and: ANDs(rr, ra, rb, 0); break; + case LIR_or: ORRs(rr, ra, rb, 0); break; + case LIR_xor: EORs(rr, ra, rb, 0); break; + + case LIR_mul: + // ARMv5 and earlier cores cannot do a MUL where the first operand + // is also the result, so we need a special case to handle that. + // + // We try to use rb as the first operand by default because it is + // common for (rr == ra) and is thus likely to be the most + // efficient method. + + if ((ARM_ARCH > 5) || (rr != rb)) { + // IP is used to temporarily store the high word of the result from + // SMULL, so we make use of this to perform an overflow check, as + // ARM's MUL instruction can't set the overflow flag by itself. + // We can check for overflow using the following: + // SMULL rr, ip, ra, rb + // CMP ip, rr, ASR #31 + // An explanation can be found in bug 521161. This sets Z if we did + // _not_ overflow, and clears it if we did. + ALUr_shi(AL, cmp, 1, IP, IP, rr, ASR_imm, 31); + SMULL(rr, IP, rb, ra); + } else { + // ARM_ARCH is ARMv5 (or below) and rr == rb, so we must + // find a different way to encode the instruction. + + // If possible, swap the arguments to avoid the restriction. + if (rr != ra) { + // We know that rr == rb, so this will be something like + // rX = rY * rX. + // Other than swapping ra and rb, this works in the same as + // as the ARMv6+ case, above. + ALUr_shi(AL, cmp, 1, IP, IP, rr, ASR_imm, 31); + SMULL(rr, IP, ra, rb); + } else { + // We're trying to do rX = rX * rX, but we also need to + // check for overflow so we would need two extra registers + // on ARMv5 and below. We achieve this by observing the + // following: + // - abs(rX)*abs(rX) = rX*rX, so we force the input to be + // positive to simplify the detection logic. + // - Any argument greater than 0xffff will _always_ + // overflow, and we can easily check that the top 16 + // bits are zero. + // - Any argument lower than (or equal to) 0xffff that + // also overflows is guaranteed to set output bit 31. + // + // Thus, we know we have _not_ overflowed if: + // abs(rX)&0xffff0000 == 0 AND result[31] == 0 + // + // The following instruction sequence will be emitted: + // MOVS IP, rX // Put abs(rX) into IP. + // RSBMI IP, IP, #0 // ... + // MUL rX, IP, IP // Do the actual multiplication. + // MOVS IP, IP, LSR #16 // Check that abs(arg)<=0xffff + // CMPEQ IP, rX, ASR #31 // Check that result[31] == 0 + + NanoAssert(rr != IP); + + ALUr_shi(AL, cmp, 1, IP, rr, rr, ASR_imm, 31); + ALUr_shi(AL, mov, 1, IP, IP, IP, LSR_imm, 16); + MUL(rr, IP, IP); + ALUi(MI, rsb, 0, IP, IP, 0); + ALUr(AL, mov, 1, IP, ra, ra); + } + } + break; + + // The shift operations need a mask to match the JavaScript + // specification because the ARM architecture allows a greater shift + // range than JavaScript. + case LIR_lsh: + LSL(rr, ra, IP); + ANDi(IP, rb, 0x1f); + break; + case LIR_rsh: + ASR(rr, ra, IP); + ANDi(IP, rb, 0x1f); + break; + case LIR_ush: + LSR(rr, ra, IP); + ANDi(IP, rb, 0x1f); + break; + default: + NanoAssertMsg(0, "Unsupported"); + break; + } +} + +void +Assembler::asm_neg_not(LInsp ins) +{ + LOpcode op = ins->opcode(); + Register rr = prepResultReg(ins, GpRegs); + + LIns* lhs = ins->oprnd1(); + // If this is the last use of lhs in reg, we can re-use result reg. + // Else, lhs already has a register assigned. + Register ra = ( lhs->isUnusedOrHasUnknownReg() + ? findSpecificRegFor(lhs, rr) + : lhs->getReg() ); + NanoAssert(isKnownReg(ra)); + + if (op == LIR_not) + MVN(rr, ra); + else + RSBS(rr, ra); +} + +void +Assembler::asm_ld(LInsp ins) +{ + LOpcode op = ins->opcode(); + LIns* base = ins->oprnd1(); + int d = ins->disp(); + + Register rr = prepResultReg(ins, GpRegs); + Register ra = getBaseReg(op, base, d, GpRegs); + + // these will always be 4-byte aligned + if (op == LIR_ld || op == LIR_ldc) { + LDR(rr, ra, d); + return; + } + + // these will be 2 or 4-byte aligned + if (op == LIR_ldcs) { + LDRH(rr, ra, d); + return; + } + + // aaand this is just any byte. + if (op == LIR_ldcb) { + LDRB(rr, ra, d); + return; + } + + NanoAssertMsg(0, "Unsupported instruction in asm_ld"); +} + +void +Assembler::asm_cmov(LInsp ins) +{ + NanoAssert(ins->opcode() == LIR_cmov); + LIns* condval = ins->oprnd1(); + LIns* iftrue = ins->oprnd2(); + LIns* iffalse = ins->oprnd3(); + + NanoAssert(condval->isCmp()); + NanoAssert(!iftrue->isQuad() && !iffalse->isQuad()); + + const Register rr = prepResultReg(ins, GpRegs); + + // this code assumes that neither LD nor MR nor MRcc set any of the condition flags. + // (This is true on Intel, is it true on all architectures?) + const Register iffalsereg = findRegFor(iffalse, GpRegs & ~rmask(rr)); + switch (condval->opcode()) { + // note that these are all opposites... + case LIR_eq: MOVNE(rr, iffalsereg); break; + case LIR_lt: MOVGE(rr, iffalsereg); break; + case LIR_le: MOVGT(rr, iffalsereg); break; + case LIR_gt: MOVLE(rr, iffalsereg); break; + case LIR_ge: MOVLT(rr, iffalsereg); break; + case LIR_ult: MOVHS(rr, iffalsereg); break; + case LIR_ule: MOVHI(rr, iffalsereg); break; + case LIR_ugt: MOVLS(rr, iffalsereg); break; + case LIR_uge: MOVLO(rr, iffalsereg); break; + case LIR_ov: + // Because MUL can't set the V flag, we use SMULL and CMP to set + // the Z flag to detect overflow on multiply. Thus, if ins points + // to a LIR_ov which in turn points to a LIR_mul, we must be + // conditional on !Z, not V. + if (!condval->oprnd1()->isop(LIR_mul)) { + MOVVC(rr, iffalsereg); + } else { + MOVEQ(rr, iffalsereg); + } + break; + default: debug_only( NanoAssert(0) ); break; + } + /*const Register iftruereg =*/ findSpecificRegFor(iftrue, rr); + asm_cmp(condval); +} + +void +Assembler::asm_qhi(LInsp ins) +{ + Register rr = prepResultReg(ins, GpRegs); + LIns *q = ins->oprnd1(); + int d = findMemFor(q); + LDR(rr, FP, d+4); +} + +void +Assembler::asm_qlo(LInsp ins) +{ + Register rr = prepResultReg(ins, GpRegs); + LIns *q = ins->oprnd1(); + int d = findMemFor(q); + LDR(rr, FP, d); +} + +void +Assembler::asm_param(LInsp ins) +{ + uint32_t a = ins->paramArg(); + uint32_t kind = ins->paramKind(); + if (kind == 0) { + // ordinary param + AbiKind abi = _thisfrag->lirbuf->abi; + uint32_t abi_regcount = abi == ABI_CDECL ? 4 : abi == ABI_FASTCALL ? 2 : abi == ABI_THISCALL ? 1 : 0; + if (a < abi_regcount) { + // incoming arg in register + prepResultReg(ins, rmask(argRegs[a])); + } else { + // incoming arg is on stack, and EBP points nearby (see genPrologue) + Register r = prepResultReg(ins, GpRegs); + int d = (a - abi_regcount) * sizeof(intptr_t) + 8; + LDR(r, FP, d); + } + } else { + // saved param + prepResultReg(ins, rmask(savedRegs[a])); + } +} + +void +Assembler::asm_int(LInsp ins) +{ + Register rr = prepResultReg(ins, GpRegs); + asm_ld_imm(rr, ins->imm32()); +} + +void +Assembler::asm_ret(LIns *ins) +{ + genEpilogue(); + + // NB: our contract with genEpilogue is actually that the return value + // we are intending for R0 is currently IP, not R0. This has to do with + // the strange dual-nature of the patchable jump in a side-exit. See + // nPatchBranch. + + MOV(IP, R0); + + // Pop the stack frame. + MOV(SP,FP); + + assignSavedRegs(); + LIns *value = ins->oprnd1(); + if (ins->isop(LIR_ret)) { + findSpecificRegFor(value, R0); + } + else { + NanoAssert(ins->isop(LIR_fret)); + if (ARM_VFP) { + Register reg = findRegFor(value, FpRegs); + FMRRD(R0, R1, reg); + } else { + NanoAssert(value->isop(LIR_qjoin)); + findSpecificRegFor(value->oprnd1(), R0); // lo + findSpecificRegFor(value->oprnd2(), R1); // hi + } + } +} + +void +Assembler::asm_promote(LIns *ins) +{ + /* The LIR opcodes that result in a call to asm_promote are only generated + * if NANOJIT_64BIT is #define'd, which it never is for ARM. + */ + (void)ins; + NanoAssert(0); +} + +void +Assembler::asm_jtbl(LIns* ins, NIns** table) +{ + Register indexreg = findRegFor(ins->oprnd1(), GpRegs); + Register tmp = registerAllocTmp(GpRegs & ~rmask(indexreg)); + LDR_scaled(PC, tmp, indexreg, 2); // LDR PC, [tmp + index*4] + asm_ld_imm(tmp, (int32_t)table); // tmp = #table +} + +void Assembler::swapCodeChunks() { + SWAP(NIns*, _nIns, _nExitIns); + SWAP(NIns*, _nSlot, _nExitSlot); // this one is ARM-specific + SWAP(NIns*, codeStart, exitStart); + SWAP(NIns*, codeEnd, exitEnd); + verbose_only( SWAP(size_t, codeBytes, exitBytes); ) +} + +} +#endif /* FEATURE_NANOJIT */ diff --git a/ape-server/deps/js/src/nanojit/NativeARM.h b/ape-server/deps/js/src/nanojit/NativeARM.h new file mode 100755 index 0000000..c00083a --- /dev/null +++ b/ape-server/deps/js/src/nanojit/NativeARM.h @@ -0,0 +1,974 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is [Open Source Virtual Machine]. + * + * The Initial Developer of the Original Code is + * Adobe System Incorporated. + * Portions created by the Initial Developer are Copyright (C) 2004-2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adobe AS3 Team + * Vladimir Vukicevic + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + + +#ifndef __nanojit_NativeARM__ +#define __nanojit_NativeARM__ + + +#ifdef PERFM +#include "../vprof/vprof.h" +#define count_instr() _nvprof("arm",1) +#define count_prolog() _nvprof("arm-prolog",1); count_instr(); +#define count_imt() _nvprof("arm-imt",1) count_instr() +#else +#define count_instr() +#define count_prolog() +#define count_imt() +#endif + +namespace nanojit +{ +#if defined VMCFG_DOUBLE_MSW_FIRST || defined _MSC_VER +# undef NJ_ARM_EABI +#else +# define NJ_ARM_EABI 1 +#endif + +// default to ARMv5 +#if !defined(ARM_ARCH) +# define ARM_ARCH 5 +#endif + +// default to no-thumb2 +#if !defined(ARM_THUMB2) +# define ARM_THUMB2 0 +#endif + +// only d0-d6 are actually used; we'll use d7 as s14-s15 for i2f/u2f/etc. +#define NJ_VFP_MAX_REGISTERS 8 +#define NJ_MAX_REGISTERS (11 + NJ_VFP_MAX_REGISTERS) +#define NJ_MAX_STACK_ENTRY 256 +#define NJ_MAX_PARAMETERS 16 +#define NJ_ALIGN_STACK 8 +#define NJ_JTBL_SUPPORTED 1 + +#define NJ_CONSTANT_POOLS +const int NJ_MAX_CPOOL_OFFSET = 4096; +const int NJ_CPOOL_SIZE = 16; + +const int LARGEST_UNDERRUN_PROT = 32; // largest value passed to underrunProtect + +typedef int NIns; + +// Bytes of icache to flush after Assembler::patch +const size_t LARGEST_BRANCH_PATCH = 2 * sizeof(NIns); + +/* ARM registers */ +typedef enum { + R0 = 0, + R1 = 1, + R2 = 2, + R3 = 3, + R4 = 4, + R5 = 5, + R6 = 6, + R7 = 7, + R8 = 8, + R9 = 9, + R10 = 10, + FP = 11, + IP = 12, + SP = 13, + LR = 14, + PC = 15, + + // VFP regs (we currently only use D0-D6 and S14) + D0 = 16, + D1 = 17, + D2 = 18, + D3 = 19, + D4 = 20, + D5 = 21, + D6 = 22, + // S14 overlaps with D7 and is hard-coded into i2f and u2f operations, but + // D7 is still listed here for completeness and to facilitate assertions. + D7 = 23, + + FirstFloatReg = D0, + LastFloatReg = D6, + + FirstReg = R0, + LastReg = D6, + UnknownReg = 32, + + // special value referring to S14 + FpSingleScratch = 24 +} Register; + +/* ARM condition codes */ +typedef enum { + EQ = 0x0, // Equal + NE = 0x1, // Not Equal + CS = 0x2, // Carry Set (or HS) + HS = 0x2, + CC = 0x3, // Carry Clear (or LO) + LO = 0x3, + MI = 0x4, // MInus + PL = 0x5, // PLus + VS = 0x6, // oVerflow Set + VC = 0x7, // oVerflow Clear + HI = 0x8, // HIgher + LS = 0x9, // Lower or Same + GE = 0xA, // Greater or Equal + LT = 0xB, // Less Than + GT = 0xC, // Greater Than + LE = 0xD, // Less or Equal + AL = 0xE, // ALways + + // Note that condition code NV is unpredictable on ARMv3 and ARMv4, and has + // special meaning for ARMv5 onwards. As such, it should never be used in + // an instruction encoding unless the special (ARMv5+) meaning is required. + NV = 0xF // NeVer +} ConditionCode; +#define IsCond(cc) (((cc) >= EQ) && ((cc) <= AL)) + +// Bit 0 of the condition code can be flipped to obtain the opposite condition. +// However, this won't work for AL because its opposite — NV — has special +// meaning. +#define OppositeCond(cc) ((ConditionCode)((unsigned int)(cc)^0x1)) + +typedef int RegisterMask; +typedef struct _FragInfo { + RegisterMask needRestoring; + NIns* epilogue; +} FragInfo; + +// D0-D6 are not saved; D7-D15 are, but we don't use those, +// so we don't have to worry about saving/restoring them +static const RegisterMask SavedFpRegs = 0; +static const RegisterMask SavedRegs = 1<= 0) ? isU8(d) : isU8(-d); + return isS12(d); +} + +#define IsFpReg(_r) ((rmask((Register)_r) & (FpRegs)) != 0) +#define IsGpReg(_r) ((rmask((Register)_r) & (GpRegs)) != 0) +#define FpRegNum(_fpr) ((_fpr) - FirstFloatReg) + +#define firstreg() R0 +// only good for normal regs +#define imm2register(c) (Register)(c-1) + +verbose_only( extern const char* regNames[]; ) +verbose_only( extern const char* condNames[]; ) +verbose_only( extern const char* shiftNames[]; ) + +// abstract to platform specific calls +#define nExtractPlatformFlags(x) 0 + +#define DECLARE_PLATFORM_STATS() + +#define DECLARE_PLATFORM_REGALLOC() + +#ifdef DEBUG +# define DECLARE_PLATFORM_ASSEMBLER_DEBUG() \ + inline bool isOp2Imm(uint32_t literal); \ + inline uint32_t decOp2Imm(uint32_t enc); +#else +// define stubs, for code that defines NJ_VERBOSE without DEBUG +# define DECLARE_PLATFORM_ASSEMBLER_DEBUG() \ + inline bool isOp2Imm(uint32_t ) { return true; } \ + inline uint32_t decOp2Imm(uint32_t ) { return 0; } +#endif + +#define DECLARE_PLATFORM_ASSEMBLER() \ + \ + DECLARE_PLATFORM_ASSEMBLER_DEBUG() \ + \ + const static Register argRegs[4], retRegs[2]; \ + \ + void BranchWithLink(NIns* addr); \ + inline void BLX(Register addr, bool chk = true); \ + void JMP_far(NIns*); \ + void B_cond_chk(ConditionCode, NIns*, bool); \ + void underrunProtect(int bytes); \ + void nativePageReset(); \ + void nativePageSetup(); \ + void asm_quad_nochk(Register, int32_t, int32_t); \ + void asm_regarg(ArgSize, LInsp, Register); \ + void asm_stkarg(LInsp p, int stkd); \ + void asm_cmpi(Register, int32_t imm); \ + void asm_ldr_chk(Register d, Register b, int32_t off, bool chk); \ + void asm_cmp(LIns *cond); \ + void asm_fcmp(LIns *cond); \ + void asm_ld_imm(Register d, int32_t imm, bool chk = true); \ + void asm_arg(ArgSize sz, LInsp arg, Register& r, int& stkd); \ + void asm_arg_64(LInsp arg, Register& r, int& stkd); \ + void asm_add_imm(Register rd, Register rn, int32_t imm, int stat = 0); \ + void asm_sub_imm(Register rd, Register rn, int32_t imm, int stat = 0); \ + void asm_and_imm(Register rd, Register rn, int32_t imm, int stat = 0); \ + void asm_orr_imm(Register rd, Register rn, int32_t imm, int stat = 0); \ + void asm_eor_imm(Register rd, Register rn, int32_t imm, int stat = 0); \ + inline bool encOp2Imm(uint32_t literal, uint32_t * enc); \ + inline uint32_t CountLeadingZeroes(uint32_t data); \ + int * _nSlot; \ + int * _nExitSlot; \ + bool blx_lr_bug; \ + int max_out_args; /* bytes */ + +#define IMM32(imm) *(--_nIns) = (NIns)((imm)); + +#define OP_IMM (1<<25) +#define OP_STAT (1<<20) + +#define COND_AL ((uint32_t)AL<<28) + +typedef enum { + LSL_imm = 0, // LSL #c - Logical Shift Left + LSL_reg = 1, // LSL Rc - Logical Shift Left + LSR_imm = 2, // LSR #c - Logical Shift Right + LSR_reg = 3, // LSR Rc - Logical Shift Right + ASR_imm = 4, // ASR #c - Arithmetic Shift Right + ASR_reg = 5, // ASR Rc - Arithmetic Shift Right + ROR_imm = 6, // Rotate Right (c != 0) + RRX = 6, // Rotate Right one bit with extend (c == 0) + ROR_reg = 7 // Rotate Right +} ShiftOperator; +#define IsShift(sh) (((sh) >= LSL_imm) && ((sh) <= ROR_reg)) + +#define LD32_size 8 + +#define BEGIN_NATIVE_CODE(x) \ + { DWORD* _nIns = (uint8_t*)x + +#define END_NATIVE_CODE(x) \ + (x) = (dictwordp*)_nIns; } + +// BX +#define BX(_r) do { \ + underrunProtect(4); \ + NanoAssert(IsGpReg(_r)); \ + *(--_nIns) = (NIns)( COND_AL | (0x12<<20) | (0xFFF<<8) | (1<<4) | (_r)); \ + asm_output("bx %s", gpn(_r)); } while(0) + +/* + * ALU operations + */ + +enum { + ARM_and = 0, + ARM_eor = 1, + ARM_sub = 2, + ARM_rsb = 3, + ARM_add = 4, + ARM_adc = 5, + ARM_sbc = 6, + ARM_rsc = 7, + ARM_tst = 8, + ARM_teq = 9, + ARM_cmp = 10, + ARM_cmn = 11, + ARM_orr = 12, + ARM_mov = 13, + ARM_bic = 14, + ARM_mvn = 15 +}; +#define IsOp(op) (((ARM_##op) >= ARM_and) && ((ARM_##op) <= ARM_mvn)) + +// ALU operation with register and 8-bit immediate arguments +// S - bit, 0 or 1, whether the CPSR register is updated +// rd - destination register +// rl - first (left) operand register +// op2imm - operand 2 immediate. Use encOp2Imm (from NativeARM.cpp) to calculate this. +#define ALUi(cond, op, S, rd, rl, op2imm) ALUi_chk(cond, op, S, rd, rl, op2imm, 1) +#define ALUi_chk(cond, op, S, rd, rl, op2imm, chk) do {\ + if (chk) underrunProtect(4);\ + NanoAssert(IsCond(cond));\ + NanoAssert(IsOp(op));\ + NanoAssert(((S)==0) || ((S)==1));\ + NanoAssert(IsGpReg(rd) && IsGpReg(rl));\ + NanoAssert(isOp2Imm(op2imm));\ + *(--_nIns) = (NIns) ((cond)<<28 | OP_IMM | (ARM_##op)<<21 | (S)<<20 | (rl)<<16 | (rd)<<12 | (op2imm));\ + if (ARM_##op == ARM_mov || ARM_##op == ARM_mvn) { \ + asm_output("%s%s%s %s, #0x%X", #op, condNames[cond], (S)?"s":"", gpn(rd), decOp2Imm(op2imm));\ + } else if (ARM_##op >= ARM_tst && ARM_##op <= ARM_cmn) { \ + NanoAssert(S==1);\ + asm_output("%s%s %s, #0x%X", #op, condNames[cond], gpn(rl), decOp2Imm(op2imm));\ + } else { \ + asm_output("%s%s%s %s, %s, #0x%X", #op, condNames[cond], (S)?"s":"", gpn(rd), gpn(rl), decOp2Imm(op2imm));\ + }\ + } while (0) + +// ALU operation with two register arguments +// S - bit, 0 or 1, whether the CPSR register is updated +// rd - destination register +// rl - first (left) operand register +// rr - first (left) operand register +#define ALUr(cond, op, S, rd, rl, rr) ALUr_chk(cond, op, S, rd, rl, rr, 1) +#define ALUr_chk(cond, op, S, rd, rl, rr, chk) do {\ + if (chk) underrunProtect(4);\ + NanoAssert(IsCond(cond));\ + NanoAssert(IsOp(op));\ + NanoAssert(((S)==0) || ((S)==1));\ + NanoAssert(IsGpReg(rd) && IsGpReg(rl) && IsGpReg(rr));\ + *(--_nIns) = (NIns) ((cond)<<28 |(ARM_##op)<<21 | (S)<<20 | (rl)<<16 | (rd)<<12 | (rr));\ + if (ARM_##op == ARM_mov || ARM_##op == ARM_mvn) { \ + asm_output("%s%s%s %s, %s", #op, condNames[cond], (S)?"s":"", gpn(rd), gpn(rr));\ + } else if (ARM_##op >= ARM_tst && ARM_##op <= ARM_cmn) { \ + NanoAssert(S==1);\ + asm_output("%s%s %s, %s", #op, condNames[cond], gpn(rl), gpn(rr));\ + } else { \ + asm_output("%s%s%s %s, %s, %s", #op, condNames[cond], (S)?"s":"", gpn(rd), gpn(rl), gpn(rr));\ + }\ + } while (0) + +// ALU operation with two register arguments, with rr operated on by a shift and shift immediate +// S - bit, 0 or 1, whether the CPSR register is updated +// rd - destination register +// rl - first (left) operand register +// rr - first (left) operand register +// sh - a ShiftOperator +// imm - immediate argument to shift operator, 5 bits (0..31) +#define ALUr_shi(cond, op, S, rd, rl, rr, sh, imm) do {\ + underrunProtect(4);\ + NanoAssert(IsCond(cond));\ + NanoAssert(IsOp(op));\ + NanoAssert(((S)==0) || ((S)==1));\ + NanoAssert(IsGpReg(rd) && IsGpReg(rl) && IsGpReg(rr));\ + NanoAssert(IsShift(sh));\ + NanoAssert((imm)>=0 && (imm)<32);\ + *(--_nIns) = (NIns) ((cond)<<28 |(ARM_##op)<<21 | (S)<<20 | (rl)<<16 | (rd)<<12 | (imm)<<7 | (sh)<<4 | (rr));\ + if (ARM_##op == ARM_mov || ARM_##op == ARM_mvn) { \ + asm_output("%s%s%s %s, %s, %s #%d", #op, condNames[cond], (S)?"s":"", gpn(rd), gpn(rr), shiftNames[sh], (imm));\ + } else if (ARM_##op >= ARM_tst && ARM_##op <= ARM_cmn) { \ + NanoAssert(S==1);\ + asm_output("%s%s %s, %s, %s #%d", #op, condNames[cond], gpn(rl), gpn(rr), shiftNames[sh], (imm));\ + } else { \ + asm_output("%s%s%s %s, %s, %s, %s #%d", #op, condNames[cond], (S)?"s":"", gpn(rd), gpn(rl), gpn(rr), shiftNames[sh], (imm));\ + }\ + } while (0) + +// ALU operation with two register arguments, with rr operated on by a shift and shift register +// S - bit, 0 or 1, whether the CPSR register is updated +// rd - destination register +// rl - first (left) operand register +// rr - first (left) operand register +// sh - a ShiftOperator +// rs - shift operand register +#define ALUr_shr(cond, op, S, rd, rl, rr, sh, rs) do {\ + underrunProtect(4);\ + NanoAssert(IsCond(cond));\ + NanoAssert(IsOp(op));\ + NanoAssert(((S)==0) || ((S)==1));\ + NanoAssert(IsGpReg(rd) && IsGpReg(rl) && IsGpReg(rr) && IsGpReg(rs));\ + NanoAssert(IsShift(sh));\ + *(--_nIns) = (NIns) ((cond)<<28 |(ARM_##op)<<21 | (S)<<20 | (rl)<<16 | (rd)<<12 | (rs)<<8 | (sh)<<4 | (rr));\ + if (ARM_##op == ARM_mov || ARM_##op == ARM_mvn) { \ + asm_output("%s%s%s %s, %s, %s %s", #op, condNames[cond], (S)?"s":"", gpn(rd), gpn(rr), shiftNames[sh], gpn(rs));\ + } else if (ARM_##op >= ARM_tst && ARM_##op <= ARM_cmn) { \ + NanoAssert(S==1);\ + asm_output("%s%s %s, %s, %s %s", #op, condNames[cond], gpn(rl), gpn(rr), shiftNames[sh], gpn(rs));\ + } else { \ + asm_output("%s%s%s %s, %s, %s, %s %s", #op, condNames[cond], (S)?"s":"", gpn(rd), gpn(rl), gpn(rr), shiftNames[sh], gpn(rs));\ + }\ + } while (0) + +// -------- +// Basic arithmetic operations. +// -------- +// Argument naming conventions for these macros: +// _d Destination register. +// _l First (left) operand. +// _r Second (right) operand. +// _op2imm An operand 2 immediate value. Use encOp2Imm to calculate this. +// _s Set to 1 to update the status flags (for subsequent conditional +// tests). Otherwise, set to 0. + +// _d = _l + decOp2Imm(_op2imm) +#define ADDis(_d,_l,_op2imm,_s) ALUi(AL, add, _s, _d, _l, _op2imm) +#define ADDi(_d,_l,_op2imm) ALUi(AL, add, 0, _d, _l, _op2imm) + +// _d = _l & ~decOp2Imm(_op2imm) +#define BICis(_d,_l,_op2imm,_s) ALUi(AL, bic, _s, _d, _l, _op2imm) +#define BICi(_d,_l,_op2imm) ALUi(AL, bic, 0, _d, _l, _op2imm) + +// _d = _l - decOp2Imm(_op2imm) +#define SUBis(_d,_l,_op2imm,_s) ALUi(AL, sub, _s, _d, _l, _op2imm) +#define SUBi(_d,_l,_op2imm) ALUi(AL, sub, 0, _d, _l, _op2imm) + +// _d = _l & decOp2Imm(_op2imm) +#define ANDis(_d,_l,_op2imm,_s) ALUi(AL, and, _s, _d, _l, _op2imm) +#define ANDi(_d,_l,_op2imm) ALUi(AL, and, 0, _d, _l, _op2imm) + +// _d = _l | decOp2Imm(_op2imm) +#define ORRis(_d,_l,_op2imm,_s) ALUi(AL, orr, _s, _d, _l, _op2imm) +#define ORRi(_d,_l,_op2imm) ALUi(AL, orr, 0, _d, _l, _op2imm) + +// _d = _l ^ decOp2Imm(_op2imm) +#define EORis(_d,_l,_op2imm,_s) ALUi(AL, eor, _s, _d, _l, _op2imm) +#define EORi(_d,_l,_op2imm) ALUi(AL, eor, 0, _d, _l, _op2imm) + +// _d = _l | _r +#define ORRs(_d,_l,_r,_s) ALUr(AL, orr, _s, _d, _l, _r) +#define ORR(_d,_l,_r) ALUr(AL, orr, 0, _d, _l, _r) + +// _d = _l & _r +#define ANDs(_d,_l,_r,_s) ALUr(AL, and, _s, _d, _l, _r) +#define AND(_d,_l,_r) ALUr(AL, and, 0, _d, _l, _r) + +// _d = _l ^ _r +#define EORs(_d,_l,_r,_s) ALUr(AL, eor, _s, _d, _l, _r) +#define EOR(_d,_l,_r) ALUr(AL, eor, 0, _d, _l, _r) + +// _d = _l + _r +#define ADDs(_d,_l,_r,_s) ALUr(AL, add, _s, _d, _l, _r) +#define ADD(_d,_l,_r) ALUr(AL, add, 0, _d, _l, _r) + +// _d = _l - _r +#define SUBs(_d,_l,_r,_s) ALUr(AL, sub, _s, _d, _l, _r) +#define SUB(_d,_l,_r) ALUr(AL, sub, 0, _d, _l, _r) + +// -------- +// Other operations. +// -------- + +// [_d_hi,_d] = _l * _r +#define SMULL_dont_check_op1(_d, _d_hi, _l, _r) do { \ + underrunProtect(4); \ + NanoAssert((ARM_ARCH >= 6) || ((_d) != (_l))); \ + NanoAssert(IsGpReg(_d) && IsGpReg(_d_hi) && IsGpReg(_l) && IsGpReg(_r)); \ + NanoAssert(((_d) != PC) && ((_d_hi) != PC) && ((_l) != PC) && ((_r) != PC));\ + *(--_nIns) = (NIns)( COND_AL | 0xc00090 | (_d_hi)<<16 | (_d)<<12 | (_r)<<8 | (_l) );\ + asm_output("smull %s, %s, %s, %s",gpn(_d),gpn(_d_hi),gpn(_l),gpn(_r)); \ +} while(0) + +#if NJ_ARM_ARCH >= NJ_ARM_V6 +#define SMULL(_d, _d_hi, _l, _r) SMULL_dont_check_op1(_d, _d_hi, _l, _r) +#else +#define SMULL(_d, _d_hi, _l, _r) do { \ + NanoAssert( (_d)!=(_l)); \ + NanoAssert((_d_hi)!=(_l)); \ + SMULL_dont_check_op1(_d, _d_hi, _l, _r); \ + } while(0) +#endif + +// _d = _l * _r +#define MUL_dont_check_op1(_d, _l, _r) do { \ + underrunProtect(4); \ + NanoAssert((ARM_ARCH >= 6) || ((_d) != (_l))); \ + NanoAssert(IsGpReg(_d) && IsGpReg(_l) && IsGpReg(_r)); \ + NanoAssert(((_d) != PC) && ((_l) != PC) && ((_r) != PC)); \ + *(--_nIns) = (NIns)( COND_AL | (_d)<<16 | (_r)<<8 | 0x90 | (_l) ); \ + asm_output("mul %s, %s, %s",gpn(_d),gpn(_l),gpn(_r)); } while(0) + +#if NJ_ARM_ARCH >= NJ_ARM_V6 +#define MUL(_d, _l, _r) MUL_dont_check_op1(_d, _l, _r) +#else +#define MUL(_d, _l, _r) do { \ + NanoAssert((_d)!=(_l)); \ + MUL_dont_check_op1(_d, _l, _r); \ + } while(0) +#endif + +// RSBS _d, _r +// _d = 0 - _r +#define RSBS(_d,_r) ALUi(AL, rsb, 1, _d, _r, 0) + +// MVN +// _d = ~_r (one's compliment) +#define MVN(_d,_r) ALUr(AL, mvn, 0, _d, 0, _r) +#define MVNis_chk(_d,_op2imm,_stat,_chk) ALUi_chk(AL, mvn, _stat, _d, 0, op2imm, _chk) +#define MVNis(_d,_op2imm,_stat) MVNis_chk(_d,_op2imm,_stat,1); + +// Logical Shift Right (LSR) rotates the bits without maintaining sign extensions. +// MOVS _d, _r, LSR <_s> +// _d = _r >> _s +#define LSR(_d,_r,_s) ALUr_shr(AL, mov, 1, _d, 0, _r, LSR_reg, _s) + +// Logical Shift Right (LSR) rotates the bits without maintaining sign extensions. +// MOVS _d, _r, LSR #(_imm & 0x1f) +// _d = _r >> (_imm & 0x1f) +#define LSRi(_d,_r,_imm) ALUr_shi(AL, mov, 1, _d, 0, _r, LSR_imm, (_imm & 0x1f)) + +// Arithmetic Shift Right (ASR) maintains the sign extension. +// MOVS _d, _r, ASR <_s> +// _d = _r >> _s +#define ASR(_d,_r,_s) ALUr_shr(AL, mov, 1, _d, 0, _r, ASR_reg, _s) + +// Arithmetic Shift Right (ASR) maintains the sign extension. +// MOVS _r, _r, ASR #(_imm & 0x1f) +// _d = _r >> (_imm & 0x1f) +#define ASRi(_d,_r,_imm) ALUr_shi(AL, mov, 1, _d, 0, _r, ASR_imm, (_imm & 0x1f)) + +// Logical Shift Left (LSL). +// MOVS _d, _r, LSL <_s> +// _d = _r << _s +#define LSL(_d, _r, _s) ALUr_shr(AL, mov, 1, _d, 0, _r, LSL_reg, _s) + +// Logical Shift Left (LSL). +// MOVS _d, _r, LSL #(_imm & 0x1f) +// _d = _r << (_imm & 0x1f) +#define LSLi(_d, _r, _imm) ALUr_shi(AL, mov, 1, _d, 0, _r, LSL_imm, (_imm & 0x1f)) + +// TST +#define TST(_l,_r) ALUr(AL, tst, 1, 0, _l, _r) +#define TSTi(_d,_imm) ALUi(AL, tst, 1, 0, _d, _imm) + +// CMP +#define CMP(_l,_r) ALUr(AL, cmp, 1, 0, _l, _r) +#define CMN(_l,_r) ALUr(AL, cmn, 1, 0, _l, _r) + +// MOV +#define MOVis_chk(_d,_op2imm,_stat,_chk) ALUi_chk(AL, mov, _stat, _d, 0, op2imm, _chk) +#define MOVis(_d,_op2imm,_stat) MOVis_chk(_d,_op2imm,_stat,1) +#define MOVi(_d,_op2imm) MOVis(_d,_op2imm,0); + +#define MOV_cond(_cond,_d,_s) ALUr(_cond, mov, 0, _d, 0, _s) + +#define MOV(dr,sr) MOV_cond(AL, dr, sr) +#define MOVEQ(dr,sr) MOV_cond(EQ, dr, sr) +#define MOVNE(dr,sr) MOV_cond(NE, dr, sr) +#define MOVLT(dr,sr) MOV_cond(LT, dr, sr) +#define MOVLE(dr,sr) MOV_cond(LE, dr, sr) +#define MOVGT(dr,sr) MOV_cond(GT, dr, sr) +#define MOVGE(dr,sr) MOV_cond(GE, dr, sr) +#define MOVLO(dr,sr) MOV_cond(LO, dr, sr) // Equivalent to MOVCC +#define MOVCC(dr,sr) MOV_cond(CC, dr, sr) // Equivalent to MOVLO +#define MOVLS(dr,sr) MOV_cond(LS, dr, sr) +#define MOVHI(dr,sr) MOV_cond(HI, dr, sr) +#define MOVHS(dr,sr) MOV_cond(HS, dr, sr) // Equivalent to MOVCS +#define MOVCS(dr,sr) MOV_cond(CS, dr, sr) // Equivalent to MOVHS +#define MOVVC(dr,sr) MOV_cond(VC, dr, sr) // overflow clear + +// _d = [_b+off] +#define LDR(_d,_b,_off) asm_ldr_chk(_d,_b,_off,1) +#define LDR_nochk(_d,_b,_off) asm_ldr_chk(_d,_b,_off,0) + +// _d = [_b + _x<<_s] +#define LDR_scaled(_d, _b, _x, _s) do { \ + NanoAssert(((_s)&31) == _s);\ + NanoAssert(IsGpReg(_d) && IsGpReg(_b) && IsGpReg(_x));\ + underrunProtect(4);\ + *(--_nIns) = (NIns)(COND_AL | (0x79<<20) | ((_b)<<16) | ((_d)<<12) | ((_s)<<7) | (_x));\ + asm_output("ldr %s, [%s, +%s, LSL #%d]", gpn(_d), gpn(_b), gpn(_x), (_s));\ + } while (0) + +// _d = #_imm +#define LDi(_d,_imm) asm_ld_imm(_d,_imm) + +// MOVW and MOVT are ARMv6T2 or newer only + +// MOVW -- writes _imm into _d, zero-extends. +#define MOVWi_cond_chk(_cond,_d,_imm,_chk) do { \ + NanoAssert(isU16(_imm)); \ + NanoAssert(IsGpReg(_d)); \ + NanoAssert(IsCond(_cond)); \ + if (_chk) underrunProtect(4); \ + *(--_nIns) = (NIns)( (_cond)<<28 | 3<<24 | 0<<20 | (((_imm)>>12)&0xf)<<16 | (_d)<<12 | ((_imm)&0xfff) ); \ + asm_output("movw%s %s, #0x%x", condNames[_cond], gpn(_d), (_imm)); \ + } while (0) + +#define MOVWi(_d,_imm) MOVWi_cond_chk(AL, _d, _imm, 1) +#define MOVWi_chk(_d,_imm,_chk) MOVWi_cond_chk(AL, _d, _imm, _chk) +#define MOVWi_cond(_cond,_d,_imm) MOVWi_cond_chk(_cond, _d, _imm, 1) + +// MOVT -- writes _imm into top halfword of _d, does not affect bottom halfword +#define MOVTi_cond_chk(_cond,_d,_imm,_chk) do { \ + NanoAssert(isU16(_imm)); \ + NanoAssert(IsGpReg(_d)); \ + NanoAssert(IsCond(_cond)); \ + if (_chk) underrunProtect(4); \ + *(--_nIns) = (NIns)( (_cond)<<28 | 3<<24 | 4<<20 | (((_imm)>>12)&0xf)<<16 | (_d)<<12 | ((_imm)&0xfff) ); \ + asm_output("movt%s %s, #0x%x", condNames[_cond], gpn(_d), (_imm)); \ + } while (0) + +#define MOVTi(_d,_imm) MOVTi_cond_chk(AL, _d, _imm, 1) +#define MOVTi_chk(_d,_imm,_chk) MOVTi_cond_chk(AL, _d, _imm, _chk) +#define MOVTi_cond(_cond,_d,_imm) MOVTi_cond_chk(_cond, _d, _imm, 1) + +// i386 compat, for Assembler.cpp +#define MR(d,s) MOV(d,s) +#define ST(base,offset,reg) STR(reg,base,offset) + +// Load a byte (8 bits). The offset range is ±4095. +#define LDRB(_d,_n,_off) do { \ + NanoAssert(IsGpReg(_d) && IsGpReg(_n)); \ + underrunProtect(4); \ + if (_off < 0) { \ + NanoAssert(isU12(-_off)); \ + *(--_nIns) = (NIns)( COND_AL | (0x55<<20) | ((_n)<<16) | ((_d)<<12) | ((-_off)&0xfff) ); \ + } else { \ + NanoAssert(isU12(_off)); \ + *(--_nIns) = (NIns)( COND_AL | (0x5D<<20) | ((_n)<<16) | ((_d)<<12) | ((_off)&0xfff) ); \ + } \ + asm_output("ldrb %s, [%s,#%d]", gpn(_d),gpn(_n),(_off)); \ + } while(0) + +// Load and sign-extend a half word (16 bits). The offset range is ±255, and +// must be aligned to two bytes on some architectures, but we never make +// unaligned accesses so a simple assertion is sufficient here. +#define LDRH(_d,_n,_off) do { \ + /* TODO: This is actually LDRSH. Is this correct? */ \ + NanoAssert(IsGpReg(_d) && IsGpReg(_n)); \ + NanoAssert(((_off) & ~1) == (_off)); \ + underrunProtect(4); \ + if (_off < 0) { \ + NanoAssert(isU8(-_off)); \ + *(--_nIns) = (NIns)( COND_AL | (0x15<<20) | ((_n)<<16) | ((_d)<<12) | ((0xB)<<4) | (((-_off)&0xf0)<<4) | ((-_off)&0xf) ); \ + } else { \ + NanoAssert(isU8(_off)); \ + *(--_nIns) = (NIns)( COND_AL | (0x1D<<20) | ((_n)<<16) | ((_d)<<12) | ((0xB)<<4) | (((_off)&0xf0)<<4) | ((_off)&0xf) ); \ + } \ + asm_output("ldrsh %s, [%s,#%d]", gpn(_d),gpn(_n),(_off)); \ + } while(0) + +#define STR(_d,_n,_off) do { \ + NanoAssert(IsGpReg(_d) && IsGpReg(_n)); \ + NanoAssert(isS12(_off)); \ + underrunProtect(4); \ + if ((_off)<0) *(--_nIns) = (NIns)( COND_AL | (0x50<<20) | ((_n)<<16) | ((_d)<<12) | ((-(_off))&0xFFF) ); \ + else *(--_nIns) = (NIns)( COND_AL | (0x58<<20) | ((_n)<<16) | ((_d)<<12) | ((_off)&0xFFF) ); \ + asm_output("str %s, [%s, #%d]", gpn(_d), gpn(_n), (_off)); \ + } while(0) + +// Encode a breakpoint. The ID is not important and is ignored by the +// processor, but it can be useful as a marker when debugging emitted code. +#define BKPT_insn ((NIns)( COND_AL | (0x12<<20) | (0x7<<4) )) +#define BKPTi_insn(id) ((NIns)(BKPT_insn | ((id << 4) & 0xfff00) | (id & 0xf))); + +#define BKPT_nochk() BKPTi_nochk(0) +#define BKPTi_nochk(id) do { \ + NanoAssert((id & 0xffff) == id); \ + *(--_nIns) = BKPTi_insn(id); \ + } while (0) + +// STMFD SP!, {reg} +#define PUSHr(_r) do { \ + underrunProtect(4); \ + NanoAssert(IsGpReg(_r)); \ + *(--_nIns) = (NIns)( COND_AL | (0x92<<20) | (SP<<16) | rmask(_r) ); \ + asm_output("push %s",gpn(_r)); } while (0) + +// STMFD SP!,{reglist} +#define PUSH_mask(_mask) do { \ + underrunProtect(4); \ + NanoAssert(isU16(_mask)); \ + *(--_nIns) = (NIns)( COND_AL | (0x92<<20) | (SP<<16) | (_mask) ); \ + asm_output("push %x", (_mask));} while (0) + +// LDMFD SP!,{reg} +#define POPr(_r) do { \ + underrunProtect(4); \ + NanoAssert(IsGpReg(_r)); \ + *(--_nIns) = (NIns)( COND_AL | (0x8B<<20) | (SP<<16) | rmask(_r) ); \ + asm_output("pop %s",gpn(_r));} while (0) + +// LDMFD SP!,{reglist} +#define POP_mask(_mask) do { \ + underrunProtect(4); \ + NanoAssert(isU16(_mask)); \ + *(--_nIns) = (NIns)( COND_AL | (0x8B<<20) | (SP<<16) | (_mask) ); \ + asm_output("pop %x", (_mask));} while (0) + +// PC always points to current instruction + 8, so when calculating pc-relative +// offsets, use PC+8. +#define PC_OFFSET_FROM(target,frompc) ((intptr_t)(target) - ((intptr_t)(frompc) + 8)) + +#define B_cond(_c,_t) \ + B_cond_chk(_c,_t,1) + +#define B_nochk(_t) \ + B_cond_chk(AL,_t,0) + +#define B(t) B_cond(AL,t) +#define BHI(t) B_cond(HI,t) +#define BLS(t) B_cond(LS,t) +#define BHS(t) B_cond(HS,t) +#define BLO(t) B_cond(LO,t) +#define BEQ(t) B_cond(EQ,t) +#define BNE(t) B_cond(NE,t) +#define BLT(t) B_cond(LT,t) +#define BGE(t) B_cond(GE,t) +#define BLE(t) B_cond(LE,t) +#define BGT(t) B_cond(GT,t) +#define BVS(t) B_cond(VS,t) +#define BVC(t) B_cond(VC,t) +#define BCC(t) B_cond(CC,t) +#define BCS(t) B_cond(CS,t) + +#define JMP(t) B(t) +#define JMP_nochk(t) B_nochk(t) + +// MOV(cond) _r, #1 +// MOV(!cond) _r, #0 +#define SET(_r,_cond) do { \ + ConditionCode _opp = OppositeCond(_cond); \ + underrunProtect(8); \ + *(--_nIns) = (NIns)( ( _opp<<28) | (0x3A<<20) | ((_r)<<12) | (0) ); \ + *(--_nIns) = (NIns)( (_cond<<28) | (0x3A<<20) | ((_r)<<12) | (1) ); \ + asm_output("mov%s %s, #1", condNames[_cond], gpn(_r)); \ + asm_output("mov%s %s, #0", condNames[_opp], gpn(_r)); \ + } while (0) + +#define SETEQ(r) SET(r,EQ) +#define SETNE(r) SET(r,NE) +#define SETLT(r) SET(r,LT) +#define SETLE(r) SET(r,LE) +#define SETGT(r) SET(r,GT) +#define SETGE(r) SET(r,GE) +#define SETLO(r) SET(r,LO) +#define SETLS(r) SET(r,LS) +#define SETHI(r) SET(r,HI) +#define SETHS(r) SET(r,HS) +#define SETVS(r) SET(r,VS) +#define SETCS(r) SET(r,CS) + +// Load and sign extend a 16-bit value into a reg +#define MOVSX(_d,_off,_b) do { \ + if ((_off)>=0) { \ + if ((_off)<256) { \ + underrunProtect(4); \ + *(--_nIns) = (NIns)( COND_AL | (0x1D<<20) | ((_b)<<16) | ((_d)<<12) | ((((_off)>>4)&0xF)<<8) | (0xF<<4) | ((_off)&0xF) ); \ + } else if ((_off)<=510) { \ + underrunProtect(8); \ + int rem = (_off) - 255; \ + NanoAssert(rem<256); \ + *(--_nIns) = (NIns)( COND_AL | (0x1D<<20) | ((_d)<<16) | ((_d)<<12) | ((((rem)>>4)&0xF)<<8) | (0xF<<4) | ((rem)&0xF) ); \ + *(--_nIns) = (NIns)( COND_AL | OP_IMM | (1<<23) | ((_b)<<16) | ((_d)<<12) | (0xFF) ); \ + } else { \ + underrunProtect(16); \ + int rem = (_off) & 3; \ + *(--_nIns) = (NIns)( COND_AL | (0x19<<20) | ((_b)<<16) | ((_d)<<12) | (0xF<<4) | (_d) ); \ + asm_output("ldrsh %s,[%s, #%d]",gpn(_d), gpn(_b), (_off)); \ + *(--_nIns) = (NIns)( COND_AL | OP_IMM | (1<<23) | ((_d)<<16) | ((_d)<<12) | rem ); \ + *(--_nIns) = (NIns)( COND_AL | (0x1A<<20) | ((_d)<<12) | (2<<7)| (_d) ); \ + *(--_nIns) = (NIns)( COND_AL | (0x3B<<20) | ((_d)<<12) | (((_off)>>2)&0xFF) ); \ + asm_output("mov %s,%d",gpn(_d),(_off)); \ + } \ + } else { \ + if ((_off)>-256) { \ + underrunProtect(4); \ + *(--_nIns) = (NIns)( COND_AL | (0x15<<20) | ((_b)<<16) | ((_d)<<12) | ((((-(_off))>>4)&0xF)<<8) | (0xF<<4) | ((-(_off))&0xF) ); \ + asm_output("ldrsh %s,[%s, #%d]",gpn(_d), gpn(_b), (_off)); \ + } else if ((_off)>=-510){ \ + underrunProtect(8); \ + int rem = -(_off) - 255; \ + NanoAssert(rem<256); \ + *(--_nIns) = (NIns)( COND_AL | (0x15<<20) | ((_d)<<16) | ((_d)<<12) | ((((rem)>>4)&0xF)<<8) | (0xF<<4) | ((rem)&0xF) ); \ + *(--_nIns) = (NIns)( COND_AL | OP_IMM | (1<<22) | ((_b)<<16) | ((_d)<<12) | (0xFF) ); \ + } else NanoAssert(0); \ + } \ + } while(0) + +#define STMIA(_b, _mask) do { \ + underrunProtect(4); \ + NanoAssert(IsGpReg(_b)); \ + NanoAssert(((_mask)&rmask(_b))==0 && isU8(_mask)); \ + *(--_nIns) = (NIns)(COND_AL | (0x8A<<20) | ((_b)<<16) | (_mask)&0xFF); \ + asm_output("stmia %s!,{0x%x}", gpn(_b), _mask); \ + } while (0) + +#define LDMIA(_b, _mask) do { \ + underrunProtect(4); \ + NanoAssert(IsGpReg(_b)); \ + NanoAssert(((_mask)&rmask(_b))==0 && isU8(_mask)); \ + *(--_nIns) = (NIns)(COND_AL | (0x8B<<20) | ((_b)<<16) | (_mask)&0xFF); \ + asm_output("ldmia %s!,{0x%x}", gpn(_b), (_mask)); \ + } while (0) + +/* + * VFP + */ + +#define FMDRR(_Dm,_Rd,_Rn) do { \ + underrunProtect(4); \ + NanoAssert(ARM_VFP); \ + NanoAssert(IsFpReg(_Dm) && IsGpReg(_Rd) && IsGpReg(_Rn)); \ + *(--_nIns) = (NIns)( COND_AL | (0xC4<<20) | ((_Rn)<<16) | ((_Rd)<<12) | (0xB1<<4) | (FpRegNum(_Dm)) ); \ + asm_output("fmdrr %s,%s,%s", gpn(_Dm), gpn(_Rd), gpn(_Rn)); \ + } while (0) + +#define FMRRD(_Rd,_Rn,_Dm) do { \ + underrunProtect(4); \ + NanoAssert(ARM_VFP); \ + NanoAssert(IsGpReg(_Rd) && IsGpReg(_Rn) && IsFpReg(_Dm)); \ + *(--_nIns) = (NIns)( COND_AL | (0xC5<<20) | ((_Rn)<<16) | ((_Rd)<<12) | (0xB1<<4) | (FpRegNum(_Dm)) ); \ + asm_output("fmrrd %s,%s,%s", gpn(_Rd), gpn(_Rn), gpn(_Dm)); \ + } while (0) + +#define FMRDH(_Rd,_Dn) do { \ + underrunProtect(4); \ + NanoAssert(ARM_VFP); \ + NanoAssert(IsGpReg(_Rd) && IsFpReg(_Dn)); \ + *(--_nIns) = (NIns)( COND_AL | (0xE3<<20) | (FpRegNum(_Dn)<<16) | ((_Rd)<<12) | (0xB<<8) | (1<<4) ); \ + asm_output("fmrdh %s,%s", gpn(_Rd), gpn(_Dn)); \ + } while (0) + +#define FMRDL(_Rd,_Dn) do { \ + underrunProtect(4); \ + NanoAssert(ARM_VFP); \ + NanoAssert(IsGpReg(_Rd) && IsFpReg(_Dn)); \ + *(--_nIns) = (NIns)( COND_AL | (0xE1<<20) | (FpRegNum(_Dn)<<16) | ((_Rd)<<12) | (0xB<<8) | (1<<4) ); \ + asm_output("fmrdh %s,%s", gpn(_Rd), gpn(_Dn)); \ + } while (0) + +#define FSTD(_Dd,_Rn,_offs) do { \ + underrunProtect(4); \ + NanoAssert(ARM_VFP); \ + NanoAssert((((_offs) & 3) == 0) && isS8((_offs) >> 2)); \ + NanoAssert(IsFpReg(_Dd) && !IsFpReg(_Rn)); \ + int negflag = 1<<23; \ + intptr_t offs = (_offs); \ + if (_offs < 0) { \ + negflag = 0<<23; \ + offs = -(offs); \ + } \ + *(--_nIns) = (NIns)( COND_AL | (0xD0<<20) | ((_Rn)<<16) | (FpRegNum(_Dd)<<12) | (0xB<<8) | negflag | ((offs>>2)&0xff) ); \ + asm_output("fstd %s,%s(%d)", gpn(_Dd), gpn(_Rn), _offs); \ + } while (0) + +#define FLDD_chk(_Dd,_Rn,_offs,_chk) do { \ + if(_chk) underrunProtect(4); \ + NanoAssert(ARM_VFP); \ + NanoAssert((((_offs) & 3) == 0) && isS8((_offs) >> 2)); \ + NanoAssert(IsFpReg(_Dd) && !IsFpReg(_Rn)); \ + int negflag = 1<<23; \ + intptr_t offs = (_offs); \ + if (_offs < 0) { \ + negflag = 0<<23; \ + offs = -(offs); \ + } \ + *(--_nIns) = (NIns)( COND_AL | (0xD1<<20) | ((_Rn)<<16) | (FpRegNum(_Dd)<<12) | (0xB<<8) | negflag | ((offs>>2)&0xff) ); \ + asm_output("fldd %s,%s(%d)", gpn(_Dd), gpn(_Rn), _offs); \ + } while (0) +#define FLDD(_Dd,_Rn,_offs) FLDD_chk(_Dd,_Rn,_offs,1) + +#define FSITOD(_Dd,_Sm) do { \ + underrunProtect(4); \ + NanoAssert(ARM_VFP); \ + NanoAssert(IsFpReg(_Dd) && ((_Sm) == FpSingleScratch)); \ + *(--_nIns) = (NIns)( COND_AL | (0xEB8<<16) | (FpRegNum(_Dd)<<12) | (0x2F<<6) | (0<<5) | (0x7) ); \ + asm_output("fsitod %s,%s", gpn(_Dd), gpn(_Sm)); \ + } while (0) + + +#define FUITOD(_Dd,_Sm) do { \ + underrunProtect(4); \ + NanoAssert(ARM_VFP); \ + NanoAssert(IsFpReg(_Dd) && ((_Sm) == FpSingleScratch)); \ + *(--_nIns) = (NIns)( COND_AL | (0xEB8<<16) | (FpRegNum(_Dd)<<12) | (0x2D<<6) | (0<<5) | (0x7) ); \ + asm_output("fuitod %s,%s", gpn(_Dd), gpn(_Sm)); \ + } while (0) + +#define FMSR(_Sn,_Rd) do { \ + underrunProtect(4); \ + NanoAssert(ARM_VFP); \ + NanoAssert(((_Sn) == FpSingleScratch) && IsGpReg(_Rd)); \ + *(--_nIns) = (NIns)( COND_AL | (0xE0<<20) | (0x7<<16) | ((_Rd)<<12) | (0xA<<8) | (0<<7) | (0x1<<4) ); \ + asm_output("fmsr %s,%s", gpn(_Sn), gpn(_Rd)); \ + } while (0) + +#define FNEGD(_Dd,_Dm) do { \ + underrunProtect(4); \ + NanoAssert(ARM_VFP); \ + NanoAssert(IsFpReg(_Dd) && IsFpReg(_Dm)); \ + *(--_nIns) = (NIns)( COND_AL | (0xEB1<<16) | (FpRegNum(_Dd)<<12) | (0xB4<<4) | (FpRegNum(_Dm)) ); \ + asm_output("fnegd %s,%s", gpn(_Dd), gpn(_Dm)); \ + } while (0) + +#define FADDD(_Dd,_Dn,_Dm) do { \ + underrunProtect(4); \ + NanoAssert(ARM_VFP); \ + NanoAssert(IsFpReg(_Dd) && IsFpReg(_Dn) && IsFpReg(_Dm)); \ + *(--_nIns) = (NIns)( COND_AL | (0xE3<<20) | (FpRegNum(_Dn)<<16) | (FpRegNum(_Dd)<<12) | (0xB0<<4) | (FpRegNum(_Dm)) ); \ + asm_output("faddd %s,%s,%s", gpn(_Dd), gpn(_Dn), gpn(_Dm)); \ + } while (0) + +#define FSUBD(_Dd,_Dn,_Dm) do { \ + underrunProtect(4); \ + NanoAssert(ARM_VFP); \ + NanoAssert(IsFpReg(_Dd) && IsFpReg(_Dn) && IsFpReg(_Dm)); \ + *(--_nIns) = (NIns)( COND_AL | (0xE3<<20) | (FpRegNum(_Dn)<<16) | (FpRegNum(_Dd)<<12) | (0xB4<<4) | (FpRegNum(_Dm)) ); \ + asm_output("fsubd %s,%s,%s", gpn(_Dd), gpn(_Dn), gpn(_Dm)); \ + } while (0) + +#define FMULD(_Dd,_Dn,_Dm) do { \ + underrunProtect(4); \ + NanoAssert(ARM_VFP); \ + NanoAssert(IsFpReg(_Dd) && IsFpReg(_Dn) && IsFpReg(_Dm)); \ + *(--_nIns) = (NIns)( COND_AL | (0xE2<<20) | (FpRegNum(_Dn)<<16) | (FpRegNum(_Dd)<<12) | (0xB0<<4) | (FpRegNum(_Dm)) ); \ + asm_output("fmuld %s,%s,%s", gpn(_Dd), gpn(_Dn), gpn(_Dm)); \ + } while (0) + +#define FDIVD(_Dd,_Dn,_Dm) do { \ + underrunProtect(4); \ + NanoAssert(ARM_VFP); \ + NanoAssert(IsFpReg(_Dd) && IsFpReg(_Dn) && IsFpReg(_Dm)); \ + *(--_nIns) = (NIns)( COND_AL | (0xE8<<20) | (FpRegNum(_Dn)<<16) | (FpRegNum(_Dd)<<12) | (0xB0<<4) | (FpRegNum(_Dm)) ); \ + asm_output("fmuld %s,%s,%s", gpn(_Dd), gpn(_Dn), gpn(_Dm)); \ + } while (0) + +#define FMSTAT() do { \ + underrunProtect(4); \ + NanoAssert(ARM_VFP); \ + *(--_nIns) = (NIns)( COND_AL | 0x0EF1FA10); \ + asm_output("fmstat"); \ + } while (0) + +#define FCMPD(_Dd,_Dm,_E) do { \ + underrunProtect(4); \ + NanoAssert(ARM_VFP); \ + NanoAssert(IsFpReg(_Dd) && IsFpReg(_Dm)); \ + NanoAssert(((_E)==0) || ((_E)==1)); \ + *(--_nIns) = (NIns)( COND_AL | (0xEB4<<16) | (FpRegNum(_Dd)<<12) | (0xB<<8) | ((_E)<<7) | (0x4<<4) | (FpRegNum(_Dm)) ); \ + asm_output("fcmp%sd %s,%s", (((_E)==1)?"e":""), gpn(_Dd), gpn(_Dm)); \ + } while (0) + +#define FCPYD(_Dd,_Dm) do { \ + underrunProtect(4); \ + NanoAssert(ARM_VFP); \ + NanoAssert(IsFpReg(_Dd) && IsFpReg(_Dm)); \ + *(--_nIns) = (NIns)( COND_AL | (0xEB0<<16) | (FpRegNum(_Dd)<<12) | (0xB4<<4) | (FpRegNum(_Dm)) ); \ + asm_output("fcpyd %s,%s", gpn(_Dd), gpn(_Dm)); \ + } while (0) +} +#endif // __nanojit_NativeARM__ diff --git a/ape-server/deps/js/src/nanojit/NativePPC.cpp b/ape-server/deps/js/src/nanojit/NativePPC.cpp new file mode 100755 index 0000000..0d8340c --- /dev/null +++ b/ape-server/deps/js/src/nanojit/NativePPC.cpp @@ -0,0 +1,1327 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is [Open Source Virtual Machine]. + * + * The Initial Developer of the Original Code is + * Adobe System Incorporated. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adobe AS3 Team + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nanojit.h" + +#if defined FEATURE_NANOJIT && defined NANOJIT_PPC + +namespace nanojit +{ + const Register Assembler::retRegs[] = { R3, R4 }; // high=R3, low=R4 + const Register Assembler::argRegs[] = { R3, R4, R5, R6, R7, R8, R9, R10 }; + + const Register Assembler::savedRegs[] = { + #if !defined NANOJIT_64BIT + R13, + #endif + R14, R15, R16, R17, R18, R19, R20, R21, R22, + R23, R24, R25, R26, R27, R28, R29, R30 + }; + + const char *regNames[] = { + "r0", "sp", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", + "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", + "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", + "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", + "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31" + }; + + const char *bitNames[] = { "lt", "gt", "eq", "so" }; + + #define TODO(x) do{ avmplus::AvmLog(#x); NanoAssertMsgf(false, "%s", #x); } while(0) + + /* + * see http://developer.apple.com/documentation/developertools/Conceptual/LowLevelABI/index.html + * stack layout (higher address going down) + * sp -> out linkage area + * out parameter area + * local variables + * saved registers + * sp' -> in linkage area + * in parameter area + * + * linkage area layout: + * PPC32 PPC64 + * sp+0 sp+0 saved sp + * sp+4 sp+8 saved cr + * sp+8 sp+16 saved lr + * sp+12 sp+24 reserved + */ + + const int linkage_size = 6*sizeof(void*); + const int lr_offset = 2*sizeof(void*); // linkage.lr + const int cr_offset = 1*sizeof(void*); // linkage.cr + + NIns* Assembler::genPrologue() { + // mflr r0 + // stw r0, lr_offset(sp) + // stwu sp, -framesize(sp) + + // activation frame is 4 bytes per entry even on 64bit machines + uint32_t stackNeeded = max_param_size + linkage_size + _activation.tos * 4; + uint32_t aligned = alignUp(stackNeeded, NJ_ALIGN_STACK); + + UNLESS_PEDANTIC( if (isS16(aligned)) { + STPU(SP, -aligned, SP); // *(sp-aligned) = sp; sp -= aligned + } else ) { + STPUX(SP, SP, R0); + asm_li(R0, -aligned); + } + + NIns *patchEntry = _nIns; + MR(FP,SP); // save SP to use as a FP + STP(FP, cr_offset, SP); // cheat and save our FP in linkage.cr + STP(R0, lr_offset, SP); // save LR in linkage.lr + MFLR(R0); + + return patchEntry; + } + + NIns* Assembler::genEpilogue() { + BLR(); + MTLR(R0); + LP(R0, lr_offset, SP); + LP(FP, cr_offset, SP); // restore FP from linkage.cr + MR(SP,FP); + return _nIns; + } + + void Assembler::asm_qjoin(LIns *ins) { + int d = findMemFor(ins); + NanoAssert(d && isS16(d)); + LIns* lo = ins->oprnd1(); + LIns* hi = ins->oprnd2(); + + Register r = findRegFor(hi, GpRegs); + STW(r, d+4, FP); + + // okay if r gets recycled. + r = findRegFor(lo, GpRegs); + STW(r, d, FP); + freeRsrcOf(ins, false); // if we had a reg in use, emit a ST to flush it to mem + } + + void Assembler::asm_ld(LIns *ins) { + LIns* base = ins->oprnd1(); + int d = ins->disp(); + Register rr = prepResultReg(ins, GpRegs); + Register ra = getBaseReg(ins->opcode(), base, d, GpRegs); + + #if !PEDANTIC + if (isS16(d)) { + if (ins->isop(LIR_ldcb)) { + LBZ(rr, d, ra); + } else { + LWZ(rr, d, ra); + } + return; + } + #endif + + // general case + underrunProtect(12); + LWZX(rr, ra, R0); // rr = [ra+R0] + asm_li(R0,d); + } + + void Assembler::asm_store32(LIns *value, int32_t dr, LIns *base) { + Register rs = findRegFor(value, GpRegs); + Register ra = value == base ? rs : getBaseReg(LIR_sti, base, dr, GpRegs & ~rmask(rs)); + + #if !PEDANTIC + if (isS16(dr)) { + STW(rs, dr, ra); + return; + } + #endif + + // general case store, any offset size + STWX(rs, ra, R0); + asm_li(R0, dr); + } + + void Assembler::asm_load64(LIns *ins) { + LIns* base = ins->oprnd1(); + #ifdef NANOJIT_64BIT + Register rr = ins->getReg(); + if (isKnownReg(rr) && (rmask(rr) & FpRegs)) { + // FPR already assigned, fine, use it + freeRsrcOf(ins, false); + } else { + // use a GPR register; its okay to copy doubles with GPR's + // but *not* okay to copy non-doubles with FPR's + rr = prepResultReg(ins, GpRegs); + } + #else + Register rr = prepResultReg(ins, FpRegs); + #endif + + int dr = ins->disp(); + Register ra = getBaseReg(ins->opcode(), base, dr, GpRegs); + + #ifdef NANOJIT_64BIT + if (rmask(rr) & GpRegs) { + #if !PEDANTIC + if (isS16(dr)) { + LD(rr, dr, ra); + return; + } + #endif + // general case 64bit GPR load + LDX(rr, ra, R0); + asm_li(R0, dr); + return; + } + #endif + + // FPR + #if !PEDANTIC + if (isS16(dr)) { + LFD(rr, dr, ra); + return; + } + #endif + + // general case FPR load + LFDX(rr, ra, R0); + asm_li(R0, dr); + } + + void Assembler::asm_li(Register r, int32_t imm) { + #if !PEDANTIC + if (isS16(imm)) { + LI(r, imm); + return; + } + if ((imm & 0xffff) == 0) { + imm = uint32_t(imm) >> 16; + LIS(r, imm); + return; + } + #endif + asm_li32(r, imm); + } + + void Assembler::asm_li32(Register r, int32_t imm) { + // general case + // TODO use ADDI instead of ORI if r != r0, impl might have 3way adder + ORI(r, r, imm); + LIS(r, imm>>16); // on ppc64, this sign extends + } + + void Assembler::asm_li64(Register r, uint64_t imm) { + underrunProtect(5*sizeof(NIns)); // must be contiguous to be patchable + ORI(r,r,uint16_t(imm)); // r[0:15] = imm[0:15] + ORIS(r,r,uint16_t(imm>>16)); // r[16:31] = imm[16:31] + SLDI(r,r,32); // r[32:63] = r[0:31], r[0:31] = 0 + asm_li32(r, int32_t(imm>>32)); // r[0:31] = imm[32:63] + } + + void Assembler::asm_store64(LIns *value, int32_t dr, LIns *base) { + NanoAssert(value->isQuad()); + Register ra = getBaseReg(LIR_stqi, base, dr, GpRegs); + + #if !PEDANTIC && !defined NANOJIT_64BIT + if (value->isop(LIR_quad) && isS16(dr) && isS16(dr+4)) { + // quad constant and short offset + uint64_t q = value->imm64(); + STW(R0, dr, ra); // hi + asm_li(R0, int32_t(q>>32)); // hi + STW(R0, dr+4, ra); // lo + asm_li(R0, int32_t(q)); // lo + return; + } + if (value->isop(LIR_qjoin) && isS16(dr) && isS16(dr+4)) { + // short offset and qjoin(lo,hi) - store lo & hi separately + RegisterMask allow = GpRegs & ~rmask(ra); + LIns *lo = value->oprnd1(); + Register rlo = findRegFor(lo, allow); + LIns *hi = value->oprnd2(); + Register rhi = hi == lo ? rlo : findRegFor(hi, allow & ~rmask(rlo)); + STW(rhi, dr, ra); // hi + STW(rlo, dr+4, ra); // lo + return; + } + #endif // !PEDANTIC + + // general case for any value + #if !defined NANOJIT_64BIT + // on 32bit cpu's, we only use store64 for doubles + Register rs = findRegFor(value, FpRegs); + #else + // if we have to choose a register, use a GPR + Register rs = ( value->isUnusedOrHasUnknownReg() + ? findRegFor(value, GpRegs & ~rmask(ra)) + : value->getReg() ); + + if (rmask(rs) & GpRegs) { + #if !PEDANTIC + if (isS16(dr)) { + // short offset + STD(rs, dr, ra); + return; + } + #endif + // general case store 64bit GPR + STDX(rs, ra, R0); + asm_li(R0, dr); + return; + } + #endif // NANOJIT_64BIT + + #if !PEDANTIC + if (isS16(dr)) { + // short offset + STFD(rs, dr, ra); + return; + } + #endif + + // general case for any offset + STFDX(rs, ra, R0); + asm_li(R0, dr); + } + + void Assembler::asm_cond(LIns *ins) { + LOpcode op = ins->opcode(); + LIns *a = ins->oprnd1(); + LIns *b = ins->oprnd2(); + ConditionRegister cr = CR7; + Register r = prepResultReg(ins, GpRegs); + switch (op) { + case LIR_eq: case LIR_feq: + case LIR_qeq: + EXTRWI(r, r, 1, 4*cr+COND_eq); // extract CR7.eq + MFCR(r); + break; + case LIR_lt: case LIR_ult: + case LIR_flt: case LIR_fle: + case LIR_qlt: case LIR_qult: + EXTRWI(r, r, 1, 4*cr+COND_lt); // extract CR7.lt + MFCR(r); + break; + case LIR_gt: case LIR_ugt: + case LIR_fgt: case LIR_fge: + case LIR_qgt: case LIR_qugt: + EXTRWI(r, r, 1, 4*cr+COND_gt); // extract CR7.gt + MFCR(r); + break; + case LIR_le: case LIR_ule: + case LIR_qle: case LIR_qule: + EXTRWI(r, r, 1, 4*cr+COND_eq); // extract CR7.eq + MFCR(r); + CROR(CR7, eq, lt, eq); + break; + case LIR_ge: case LIR_uge: + case LIR_qge: case LIR_quge: + EXTRWI(r, r, 1, 4*cr+COND_eq); // select CR7.eq + MFCR(r); + CROR(CR7, eq, gt, eq); + break; + default: + debug_only(outputf("%s",lirNames[ins->opcode()]);) + TODO(asm_cond); + break; + } + asm_cmp(op, a, b, cr); + } + + void Assembler::asm_fcond(LIns *ins) { + asm_cond(ins); + } + + // cause 32bit sign extension to test bits + #define isS14(i) ((int32_t(bd<<18)>>18) == (i)) + + NIns* Assembler::asm_branch(bool onfalse, LIns *cond, NIns * const targ) { + LOpcode condop = cond->opcode(); + NanoAssert(cond->isCond()); + + // powerpc offsets are based on the address of the branch instruction + NIns *patch; + #if !PEDANTIC + ptrdiff_t bd = targ - (_nIns-1); + if (targ && isS24(bd)) + patch = asm_branch_near(onfalse, cond, targ); + else + #endif + patch = asm_branch_far(onfalse, cond, targ); + asm_cmp(condop, cond->oprnd1(), cond->oprnd2(), CR7); + return patch; + } + + NIns* Assembler::asm_branch_near(bool onfalse, LIns *cond, NIns * const targ) { + NanoAssert(targ != 0); + underrunProtect(4); + ptrdiff_t bd = targ - (_nIns-1); + NIns *patch = 0; + if (!isS14(bd)) { + underrunProtect(8); + bd = targ - (_nIns-1); + if (isS24(bd)) { + // can't fit conditional branch offset into 14 bits, but + // we can fit in 24, so invert the condition and branch + // around an unconditional jump + verbose_only(verbose_outputf("%p:", _nIns);) + NIns *skip = _nIns; + B(bd); + patch = _nIns; // this is the patchable branch to the given target + onfalse = !onfalse; + bd = skip - (_nIns-1); + NanoAssert(isS14(bd)); + verbose_only(verbose_outputf("branch24");) + } + else { + // known far target + return asm_branch_far(onfalse, cond, targ); + } + } + ConditionRegister cr = CR7; + switch (cond->opcode()) { + case LIR_eq: + case LIR_feq: + case LIR_qeq: + if (onfalse) BNE(cr,bd); else BEQ(cr,bd); + break; + case LIR_lt: case LIR_ult: + case LIR_flt: case LIR_fle: + case LIR_qlt: case LIR_qult: + if (onfalse) BNL(cr,bd); else BLT(cr,bd); + break; + case LIR_le: case LIR_ule: + case LIR_qle: case LIR_qule: + if (onfalse) BGT(cr,bd); else BLE(cr,bd); + break; + case LIR_gt: case LIR_ugt: + case LIR_fgt: case LIR_fge: + case LIR_qgt: case LIR_qugt: + if (onfalse) BNG(cr,bd); else BGT(cr,bd); + break; + case LIR_ge: case LIR_uge: + case LIR_qge: case LIR_quge: + if (onfalse) BLT(cr,bd); else BGE(cr,bd); + break; + default: + debug_only(outputf("%s",lirNames[cond->opcode()]);) + TODO(unknown_cond); + } + if (!patch) + patch = _nIns; + return patch; + } + + // general case branch to any address (using CTR) + NIns *Assembler::asm_branch_far(bool onfalse, LIns *cond, NIns * const targ) { + LOpcode condop = cond->opcode(); + ConditionRegister cr = CR7; + underrunProtect(16); + switch (condop) { + case LIR_eq: + case LIR_feq: + case LIR_qeq: + if (onfalse) BNECTR(cr); else BEQCTR(cr); + break; + case LIR_lt: case LIR_ult: + case LIR_qlt: case LIR_qult: + case LIR_flt: case LIR_fle: + if (onfalse) BNLCTR(cr); else BLTCTR(cr); + break; + case LIR_le: case LIR_ule: + case LIR_qle: case LIR_qule: + if (onfalse) BGTCTR(cr); else BLECTR(cr); + break; + case LIR_gt: case LIR_ugt: + case LIR_qgt: case LIR_qugt: + case LIR_fgt: case LIR_fge: + if (onfalse) BNGCTR(cr); else BGTCTR(cr); + break; + case LIR_ge: case LIR_uge: + case LIR_qge: case LIR_quge: + if (onfalse) BLTCTR(cr); else BGECTR(cr); + break; + default: + debug_only(outputf("%s",lirNames[condop]);) + TODO(unknown_cond); + } + + #if !defined NANOJIT_64BIT + MTCTR(R0); + asm_li32(R0, (int)targ); + #else + MTCTR(R0); + if (!targ || !isU32(uintptr_t(targ))) { + asm_li64(R0, uint64_t(targ)); + } else { + asm_li32(R0, uint32_t(uintptr_t(targ))); + } + #endif + return _nIns; + } + + void Assembler::asm_cmp(LOpcode condop, LIns *a, LIns *b, ConditionRegister cr) { + RegisterMask allow = condop >= LIR_feq && condop <= LIR_fge ? FpRegs : GpRegs; + Register ra = findRegFor(a, allow); + + #if !PEDANTIC + if (b->isconst()) { + int32_t d = b->imm32(); + if (isS16(d)) { + if (condop >= LIR_eq && condop <= LIR_ge) { + CMPWI(cr, ra, d); + return; + } + if (condop >= LIR_qeq && condop <= LIR_qge) { + CMPDI(cr, ra, d); + TODO(cmpdi); + return; + } + } + if (isU16(d)) { + if ((condop == LIR_eq || condop >= LIR_ult && condop <= LIR_uge)) { + CMPLWI(cr, ra, d); + return; + } + if ((condop == LIR_qeq || condop >= LIR_qult && condop <= LIR_quge)) { + CMPLDI(cr, ra, d); + TODO(cmpldi); + return; + } + } + } + #endif + + // general case + Register rb = b==a ? ra : findRegFor(b, allow & ~rmask(ra)); + if (condop >= LIR_eq && condop <= LIR_ge) { + CMPW(cr, ra, rb); + } else if (condop >= LIR_ult && condop <= LIR_uge) { + CMPLW(cr, ra, rb); + } else if (condop >= LIR_qeq && condop <= LIR_qge) { + CMPD(cr, ra, rb); + } + else if (condop >= LIR_qult && condop <= LIR_quge) { + CMPLD(cr, ra, rb); + } + else if (condop >= LIR_feq && condop <= LIR_fge) { + // set the lt/gt bit for fle/fge. We don't do this for + // int/uint because in those cases we can invert the branch condition. + // for float, we can't because of unordered comparisons + if (condop == LIR_fle) + CROR(cr, lt, lt, eq); // lt = lt|eq + else if (condop == LIR_fge) + CROR(cr, gt, gt, eq); // gt = gt|eq + FCMPU(cr, ra, rb); + } + else { + TODO(asm_cmp); + } + } + + void Assembler::asm_ret(LIns *ins) { + genEpilogue(); + assignSavedRegs(); + LIns *value = ins->oprnd1(); + Register r = ins->isop(LIR_ret) ? R3 : F1; + findSpecificRegFor(value, r); + } + + void Assembler::asm_nongp_copy(Register r, Register s) { + // PPC doesn't support any GPR<->FPR moves + NanoAssert((rmask(r) & FpRegs) && (rmask(s) & FpRegs)); + FMR(r, s); + } + + void Assembler::asm_restore(LIns *i, Register r) { + int d; + if (i->isop(LIR_alloc)) { + d = disp(i); + ADDI(r, FP, d); + } + else if (i->isconst()) { + if (!i->getArIndex()) { + i->markAsClear(); + } + asm_li(r, i->imm32()); + } + else { + d = findMemFor(i); + if (IsFpReg(r)) { + NanoAssert(i->isQuad()); + LFD(r, d, FP); + } else if (i->isQuad()) { + LD(r, d, FP); + } else { + LWZ(r, d, FP); + } + } + } + + Register Assembler::asm_prep_fcall(LIns *ins) { + return prepResultReg(ins, rmask(F1)); + } + + void Assembler::asm_int(LIns *ins) { + Register rr = prepResultReg(ins, GpRegs); + asm_li(rr, ins->imm32()); + } + + void Assembler::asm_fneg(LIns *ins) { + Register rr = prepResultReg(ins, FpRegs); + Register ra = findRegFor(ins->oprnd1(), FpRegs); + FNEG(rr,ra); + } + + void Assembler::asm_param(LIns *ins) { + uint32_t a = ins->paramArg(); + uint32_t kind = ins->paramKind(); + if (kind == 0) { + // ordinary param + // first eight args always in R3..R10 for PPC + if (a < 8) { + // incoming arg in register + prepResultReg(ins, rmask(argRegs[a])); + } else { + // todo: support stack based args, arg 0 is at [FP+off] where off + // is the # of regs to be pushed in genProlog() + TODO(asm_param_stk); + } + } + else { + // saved param + prepResultReg(ins, rmask(savedRegs[a])); + } + } + + void Assembler::asm_call(LIns *ins) { + const CallInfo* call = ins->callInfo(); + ArgSize sizes[MAXARGS]; + uint32_t argc = call->get_sizes(sizes); + + bool indirect; + if (!(indirect = call->isIndirect())) { + verbose_only(if (_logc->lcbits & LC_Assembly) + outputf(" %p:", _nIns); + ) + br((NIns*)call->_address, 1); + } else { + // Indirect call: we assign the address arg to R11 since it's not + // used for regular arguments, and is otherwise scratch since it's + // clobberred by the call. + underrunProtect(8); // underrunProtect might clobber CTR + BCTRL(); + MTCTR(R11); + asm_regarg(ARGSIZE_P, ins->arg(--argc), R11); + } + + int param_size = 0; + + Register r = R3; + Register fr = F1; + for(uint32_t i = 0; i < argc; i++) { + uint32_t j = argc - i - 1; + ArgSize sz = sizes[j]; + LInsp arg = ins->arg(j); + if (sz & ARGSIZE_MASK_INT) { + // GP arg + if (r <= R10) { + asm_regarg(sz, arg, r); + r = nextreg(r); + param_size += sizeof(void*); + } else { + // put arg on stack + TODO(stack_int32); + } + } else if (sz == ARGSIZE_F) { + // double + if (fr <= F13) { + asm_regarg(sz, arg, fr); + fr = nextreg(fr); + #ifdef NANOJIT_64BIT + r = nextreg(r); + #else + r = nextreg(nextreg(r)); // skip 2 gpr's + #endif + param_size += sizeof(double); + } else { + // put arg on stack + TODO(stack_double); + } + } else { + TODO(ARGSIZE_UNK); + } + } + if (param_size > max_param_size) + max_param_size = param_size; + } + + void Assembler::asm_regarg(ArgSize sz, LInsp p, Register r) + { + NanoAssert(r != UnknownReg); + if (sz & ARGSIZE_MASK_INT) + { + #ifdef NANOJIT_64BIT + if (sz == ARGSIZE_I) { + // sign extend 32->64 + EXTSW(r, r); + } else if (sz == ARGSIZE_U) { + // zero extend 32->64 + CLRLDI(r, r, 32); + } + #endif + // arg goes in specific register + if (p->isconst()) { + asm_li(r, p->imm32()); + } else { + if (p->isUsed()) { + if (!p->hasKnownReg()) { + // load it into the arg reg + int d = findMemFor(p); + if (p->isop(LIR_alloc)) { + NanoAssert(isS16(d)); + ADDI(r, FP, d); + } else if (p->isQuad()) { + LD(r, d, FP); + } else { + LWZ(r, d, FP); + } + } else { + // it must be in a saved reg + MR(r, p->getReg()); + } + } + else { + // this is the last use, so fine to assign it + // to the scratch reg, it's dead after this point. + findSpecificRegFor(p, r); + } + } + } + else if (sz == ARGSIZE_F) { + if (p->isUsed()) { + Register rp = p->getReg(); + if (!isKnownReg(rp) || !IsFpReg(rp)) { + // load it into the arg reg + int d = findMemFor(p); + LFD(r, d, FP); + } else { + // it must be in a saved reg + NanoAssert(IsFpReg(r) && IsFpReg(rp)); + FMR(r, rp); + } + } + else { + // this is the last use, so fine to assign it + // to the scratch reg, it's dead after this point. + findSpecificRegFor(p, r); + } + } + else { + TODO(ARGSIZE_UNK); + } + } + + void Assembler::asm_spill(Register rr, int d, bool /* pop */, bool quad) { + (void)quad; + if (d) { + if (IsFpReg(rr)) { + NanoAssert(quad); + STFD(rr, d, FP); + } + #ifdef NANOJIT_64BIT + else if (quad) { + STD(rr, d, FP); + } + #endif + else { + NanoAssert(!quad); + STW(rr, d, FP); + } + } + } + + void Assembler::asm_arith(LIns *ins) { + LOpcode op = ins->opcode(); + LInsp lhs = ins->oprnd1(); + LInsp rhs = ins->oprnd2(); + RegisterMask allow = GpRegs; + Register rr = prepResultReg(ins, allow); + Register ra = findRegFor(lhs, GpRegs); + + if (rhs->isconst()) { + int32_t rhsc = rhs->imm32(); + if (isS16(rhsc)) { + // ppc arith immediate ops sign-exted the imm16 value + switch (op) { + case LIR_add: + case LIR_iaddp: + IF_64BIT(case LIR_qiadd:) + IF_64BIT(case LIR_qaddp:) + ADDI(rr, ra, rhsc); + return; + case LIR_sub: + SUBI(rr, ra, rhsc); + return; + case LIR_mul: + MULLI(rr, ra, rhsc); + return; + } + } + if (isU16(rhsc)) { + // ppc logical immediate zero-extend the imm16 value + switch (op) { + IF_64BIT(case LIR_qior:) + case LIR_or: + ORI(rr, ra, rhsc); + return; + IF_64BIT(case LIR_qiand:) + case LIR_and: + ANDI(rr, ra, rhsc); + return; + IF_64BIT(case LIR_qxor:) + case LIR_xor: + XORI(rr, ra, rhsc); + return; + } + } + + // LIR shift ops only use last 5bits of shift const + switch (op) { + case LIR_lsh: + SLWI(rr, ra, rhsc&31); + return; + case LIR_ush: + SRWI(rr, ra, rhsc&31); + return; + case LIR_rsh: + SRAWI(rr, ra, rhsc&31); + return; + } + } + + // general case, put rhs in register + Register rb = rhs==lhs ? ra : findRegFor(rhs, GpRegs&~rmask(ra)); + switch (op) { + IF_64BIT(case LIR_qiadd:) + IF_64BIT(case LIR_qaddp:) + case LIR_add: + case LIR_iaddp: + ADD(rr, ra, rb); + break; + IF_64BIT(case LIR_qiand:) + case LIR_and: + AND(rr, ra, rb); + break; + IF_64BIT(case LIR_qior:) + case LIR_or: + OR(rr, ra, rb); + break; + IF_64BIT(case LIR_qxor:) + case LIR_xor: + XOR(rr, ra, rb); + break; + case LIR_sub: SUBF(rr, rb, ra); break; + case LIR_lsh: SLW(rr, ra, R0); ANDI(R0, rb, 31); break; + case LIR_rsh: SRAW(rr, ra, R0); ANDI(R0, rb, 31); break; + case LIR_ush: SRW(rr, ra, R0); ANDI(R0, rb, 31); break; + case LIR_mul: MULLW(rr, ra, rb); break; + #ifdef NANOJIT_64BIT + case LIR_qilsh: + SLD(rr, ra, R0); + ANDI(R0, rb, 63); + break; + case LIR_qursh: + SRD(rr, ra, R0); + ANDI(R0, rb, 63); + break; + case LIR_qirsh: + SRAD(rr, ra, R0); + ANDI(R0, rb, 63); + TODO(qirsh); + break; + #endif + default: + debug_only(outputf("%s",lirNames[op]);) + TODO(asm_arith); + } + } + + void Assembler::asm_fop(LIns *ins) { + LOpcode op = ins->opcode(); + LInsp lhs = ins->oprnd1(); + LInsp rhs = ins->oprnd2(); + RegisterMask allow = FpRegs; + Register rr = prepResultReg(ins, allow); + Register ra, rb; + findRegFor2(allow, lhs, ra, rhs, rb); + switch (op) { + case LIR_fadd: FADD(rr, ra, rb); break; + case LIR_fsub: FSUB(rr, ra, rb); break; + case LIR_fmul: FMUL(rr, ra, rb); break; + case LIR_fdiv: FDIV(rr, ra, rb); break; + default: + debug_only(outputf("%s",lirNames[op]);) + TODO(asm_fop); + } + } + + void Assembler::asm_i2f(LIns *ins) { + Register r = prepResultReg(ins, FpRegs); + Register v = findRegFor(ins->oprnd1(), GpRegs); + const int d = 16; // natural aligned + + #if defined NANOJIT_64BIT && !PEDANTIC + FCFID(r, r); // convert to double + LFD(r, d, SP); // load into fpu register + STD(v, d, SP); // save int64 + EXTSW(v, v); // extend sign destructively, ok since oprnd1 only is 32bit + #else + FSUB(r, r, F0); + LFD(r, d, SP); // scratch area in outgoing linkage area + STW(R0, d+4, SP); + XORIS(R0, v, 0x8000); + LFD(F0, d, SP); + STW(R0, d+4, SP); + LIS(R0, 0x8000); + STW(R0, d, SP); + LIS(R0, 0x4330); + #endif + } + + void Assembler::asm_u2f(LIns *ins) { + Register r = prepResultReg(ins, FpRegs); + Register v = findRegFor(ins->oprnd1(), GpRegs); + const int d = 16; + + #if defined NANOJIT_64BIT && !PEDANTIC + FCFID(r, r); // convert to double + LFD(r, d, SP); // load into fpu register + STD(v, d, SP); // save int64 + CLRLDI(v, v, 32); // zero-extend destructively + #else + FSUB(r, r, F0); + LFD(F0, d, SP); + STW(R0, d+4, SP); + LI(R0, 0); + LFD(r, d, SP); + STW(v, d+4, SP); + STW(R0, d, SP); + LIS(R0, 0x4330); + #endif + } + + void Assembler::asm_promote(LIns *ins) { + LOpcode op = ins->opcode(); + Register r = prepResultReg(ins, GpRegs); + Register v = findRegFor(ins->oprnd1(), GpRegs); + switch (op) { + default: + debug_only(outputf("%s",lirNames[op])); + TODO(asm_promote); + case LIR_u2q: + CLRLDI(r, v, 32); // clears the top 32 bits + break; + case LIR_i2q: + EXTSW(r, v); + break; + } + } + + void Assembler::asm_quad(LIns *ins) { + #ifdef NANOJIT_64BIT + Register r = ins->getReg(); + if (isKnownReg(r) && (rmask(r) & FpRegs)) { + // FPR already assigned, fine, use it + freeRsrcOf(ins, false); + } else { + // use a GPR register; its okay to copy doubles with GPR's + // but *not* okay to copy non-doubles with FPR's + r = prepResultReg(ins, GpRegs); + } + #else + Register r = prepResultReg(ins, FpRegs); + #endif + + if (rmask(r) & FpRegs) { + union { + double d; + struct { + int32_t hi, lo; + } w; + }; + d = ins->imm64f(); + LFD(r, 12, SP); + STW(R0, 12, SP); + asm_li(R0, w.hi); + STW(R0, 16, SP); + asm_li(R0, w.lo); + } + else { + int64_t q = ins->imm64(); + if (isS32(q)) { + asm_li(r, int32_t(q)); + return; + } + RLDIMI(r,R0,32,0); // or 32,32? + asm_li(R0, int32_t(q>>32)); // hi bits into R0 + asm_li(r, int32_t(q)); // lo bits into dest reg + } + } + + void Assembler::br(NIns* addr, int link) { + // destination unknown, then use maximum branch possible + if (!addr) { + br_far(addr,link); + return; + } + + // powerpc offsets are based on the address of the branch instruction + underrunProtect(4); // ensure _nIns is addr of Bx + ptrdiff_t offset = addr - (_nIns-1); // we want ptr diff's implicit >>2 here + + #if !PEDANTIC + if (isS24(offset)) { + Bx(offset, 0, link); // b addr or bl addr + return; + } + ptrdiff_t absaddr = addr - (NIns*)0; // ptr diff implies >>2 + if (isS24(absaddr)) { + Bx(absaddr, 1, link); // ba addr or bla addr + return; + } + #endif // !PEDANTIC + + br_far(addr,link); + } + + void Assembler::br_far(NIns* addr, int link) { + // far jump. + // can't have a page break in this sequence, because the break + // would also clobber ctr and r2. We use R2 here because it's not available + // to the register allocator, and we use R0 everywhere else as scratch, so using + // R2 here avoids clobbering anything else besides CTR. + #ifdef NANOJIT_64BIT + if (addr==0 || !isU32(uintptr_t(addr))) { + // really far jump to 64bit abs addr + underrunProtect(28); // 7 instructions + BCTR(link); + MTCTR(R2); + asm_li64(R2, uintptr_t(addr)); // 5 instructions + return; + } + #endif + underrunProtect(16); + BCTR(link); + MTCTR(R2); + asm_li32(R2, uint32_t(uintptr_t(addr))); // 2 instructions + } + + void Assembler::underrunProtect(int bytes) { + NanoAssertMsg(bytes<=LARGEST_UNDERRUN_PROT, "constant LARGEST_UNDERRUN_PROT is too small"); + int instr = (bytes + sizeof(NIns) - 1) / sizeof(NIns); + NIns *pc = _nIns; + NIns *top = codeStart; // this may be in a normal code chunk or an exit code chunk + + #if PEDANTIC + // pedanticTop is based on the last call to underrunProtect; any time we call + // underrunProtect and would use more than what's already protected, then insert + // a page break jump. Sometimes, this will be to a new page, usually it's just + // the next instruction and the only effect is to clobber R2 & CTR + + NanoAssert(pedanticTop >= top); + if (pc - instr < pedanticTop) { + // no page break required, but insert a far branch anyway just to be difficult + #ifdef NANOJIT_64BIT + const int br_size = 7; + #else + const int br_size = 4; + #endif + if (pc - instr - br_size < top) { + // really do need a page break + verbose_only(if (_logc->lcbits & LC_Assembly) outputf("newpage %p:", pc);) + codeAlloc(); + } + // now emit the jump, but make sure we won't need another page break. + // we're pedantic, but not *that* pedantic. + pedanticTop = _nIns - br_size; + br(pc, 0); + pedanticTop = _nIns - instr; + } + #else + if (pc - instr < top) { + verbose_only(if (_logc->lcbits & LC_Assembly) outputf("newpage %p:", pc);) + // This may be in a normal code chunk or an exit code chunk. + codeAlloc(codeStart, codeEnd, _nIns verbose_only(, codeBytes)); + // This jump will call underrunProtect again, but since we're on a new + // page, nothing will happen. + br(pc, 0); + } + #endif + } + + void Assembler::asm_cmov(LIns *ins) { + NanoAssert(ins->isop(LIR_cmov) || ins->isop(LIR_qcmov)); + LIns* cond = ins->oprnd1(); + LIns* iftrue = ins->oprnd2(); + LIns* iffalse = ins->oprnd3(); + + NanoAssert(cond->isCmp()); + NanoAssert(iftrue->isQuad() == iffalse->isQuad()); + + // fixme: we could handle fpu registers here, too, since we're just branching + Register rr = prepResultReg(ins, GpRegs); + findSpecificRegFor(iftrue, rr); + Register rf = findRegFor(iffalse, GpRegs & ~rmask(rr)); + NIns *after = _nIns; + verbose_only(if (_logc->lcbits & LC_Assembly) outputf("%p:",after);) + MR(rr, rf); + asm_branch(false, cond, after); + } + + RegisterMask Assembler::hint(LIns *i, RegisterMask allow) { + LOpcode op = i->opcode(); + RegisterMask prefer = ~0LL; + if (op == LIR_icall || op == LIR_qcall) + prefer = rmask(R3); + else if (op == LIR_fcall) + prefer = rmask(F1); + else if (op == LIR_param) { + if (i->paramArg() < 8) { + prefer = rmask(argRegs[i->paramArg()]); + } + } + // narrow the allow set to whatever is preferred and also free + if (_allocator.free & allow & prefer) + allow &= prefer; + return allow; + } + + void Assembler::asm_neg_not(LIns *ins) { + Register rr = prepResultReg(ins, GpRegs); + Register ra = findRegFor(ins->oprnd1(), GpRegs); + if (ins->isop(LIR_neg)) { + NEG(rr, ra); + } else { + NOT(rr, ra); + } + } + + void Assembler::asm_qlo(LIns *ins) { + Register rr = prepResultReg(ins, GpRegs); + int d = findMemFor(ins->oprnd1()); + LWZ(rr, d+4, FP); + } + + void Assembler::asm_qhi(LIns *ins) { + Register rr = prepResultReg(ins, GpRegs); + int d = findMemFor(ins->oprnd1()); + LWZ(rr, d, FP); + TODO(asm_qhi); + } + + void Assembler::nInit(AvmCore*) { + } + + void Assembler::nBeginAssembly() { + max_param_size = 0; + } + + void Assembler::nativePageSetup() { + NanoAssert(!_inExit); + if (!_nIns) { + codeAlloc(codeStart, codeEnd, _nIns verbose_only(, codeBytes)); + IF_PEDANTIC( pedanticTop = _nIns; ) + } + if (!_nExitIns) { + codeAlloc(exitStart, exitEnd, _nExitIns verbose_only(, exitBytes)); + } + } + + void Assembler::nativePageReset() + {} + + // Increment the 32-bit profiling counter at pCtr, without + // changing any registers. + verbose_only( + void Assembler::asm_inc_m32(uint32_t* /*pCtr*/) + { + } + ) + + void Assembler::nPatchBranch(NIns *branch, NIns *target) { + // ppc relative offsets are based on the addr of the branch instruction + ptrdiff_t bd = target - branch; + if (branch[0] == PPC_b) { + // unconditional, 24bit offset. Whoever generated the unpatched jump + // must have known the final size would fit in 24bits! otherwise the + // jump would be (lis,ori,mtctr,bctr) and we'd be patching the lis,ori. + NanoAssert(isS24(bd)); + branch[0] |= (bd & 0xffffff) << 2; + } + else if ((branch[0] & PPC_bc) == PPC_bc) { + // conditional, 14bit offset. Whoever generated the unpatched jump + // must have known the final size would fit in 14bits! otherwise the + // jump would be (lis,ori,mtctr,bcctr) and we'd be patching the lis,ori below. + NanoAssert(isS14(bd)); + NanoAssert(((branch[0] & 0x3fff)<<2) == 0); + branch[0] |= (bd & 0x3fff) << 2; + TODO(patch_bc); + } + #ifdef NANOJIT_64BIT + // patch 64bit branch + else if ((branch[0] & ~(31<<21)) == PPC_addis) { + // general branch, using lis,ori,sldi,oris,ori to load the const 64bit addr. + Register rd = Register((branch[0] >> 21) & 31); + NanoAssert(branch[1] == PPC_ori | GPR(rd)<<21 | GPR(rd)<<16); + NanoAssert(branch[3] == PPC_oris | GPR(rd)<<21 | GPR(rd)<<16); + NanoAssert(branch[4] == PPC_ori | GPR(rd)<<21 | GPR(rd)<<16); + uint64_t imm = uintptr_t(target); + uint32_t lo = uint32_t(imm); + uint32_t hi = uint32_t(imm>>32); + branch[0] = PPC_addis | GPR(rd)<<21 | uint16_t(hi>>16); + branch[1] = PPC_ori | GPR(rd)<<21 | GPR(rd)<<16 | uint16_t(hi); + branch[3] = PPC_oris | GPR(rd)<<21 | GPR(rd)<<16 | uint16_t(lo>>16); + branch[4] = PPC_ori | GPR(rd)<<21 | GPR(rd)<<16 | uint16_t(lo); + } + #else // NANOJIT_64BIT + // patch 32bit branch + else if ((branch[0] & ~(31<<21)) == PPC_addis) { + // general branch, using lis,ori to load the const addr. + // patch a lis,ori sequence with a 32bit value + Register rd = Register((branch[0] >> 21) & 31); + NanoAssert(branch[1] == PPC_ori | GPR(rd)<<21 | GPR(rd)<<16); + uint32_t imm = uint32_t(target); + branch[0] = PPC_addis | GPR(rd)<<21 | uint16_t(imm >> 16); // lis rd, imm >> 16 + branch[1] = PPC_ori | GPR(rd)<<21 | GPR(rd)<<16 | uint16_t(imm); // ori rd, rd, imm & 0xffff + } + #endif // !NANOJIT_64BIT + else { + TODO(unknown_patch); + } + } + + static int cntzlw(int set) { + // On PowerPC, prefer higher registers, to minimize + // size of nonvolatile area that must be saved. + register Register i; + #ifdef __GNUC__ + asm ("cntlzw %0,%1" : "=r" (i) : "r" (set)); + #else // __GNUC__ + # error("unsupported compiler") + #endif // __GNUC__ + return 31-i; + } + + Register Assembler::nRegisterAllocFromSet(RegisterMask set) { + Register i; + // note, deliberate truncation of 64->32 bits + if (set & 0xffffffff) { + i = Register(cntzlw(int(set))); // gp reg + } else { + i = Register(32+cntzlw(int(set>>32))); // fp reg + } + _allocator.free &= ~rmask(i); + return i; + } + + void Assembler::nRegisterResetAll(RegAlloc ®s) { + regs.clear(); + regs.free = SavedRegs | 0x1ff8 /* R3-12 */ | 0x3ffe00000000LL /* F1-13 */; + debug_only(regs.managed = regs.free); + } + +#ifdef NANOJIT_64BIT + void Assembler::asm_qbinop(LIns *ins) { + LOpcode op = ins->opcode(); + switch (op) { + case LIR_qaddp: + case LIR_qior: + case LIR_qiand: + case LIR_qursh: + case LIR_qirsh: + case LIR_qilsh: + case LIR_qxor: + case LIR_qiadd: + asm_arith(ins); + break; + default: + debug_only(outputf("%s",lirNames[op])); + TODO(asm_qbinop); + } + } +#endif // NANOJIT_64BIT + + void Assembler::nFragExit(LIns*) { + TODO(nFragExit); + } + + void Assembler::asm_jtbl(LIns* ins, NIns** native_table) + { + // R0 = index*4, R2 = table, CTR = computed address to jump to. + // must ensure no page breaks in here because R2 & CTR can get clobbered. + Register indexreg = findRegFor(ins->oprnd1(), GpRegs); +#ifdef NANOJIT_64BIT + underrunProtect(9*4); + BCTR(0); // jump to address in CTR + MTCTR(R2); // CTR = R2 + LDX(R2, R2, R0); // R2 = [table + index*8] + SLDI(R0, indexreg, 3); // R0 = index*8 + asm_li64(R2, uint64_t(native_table)); // R2 = table (5 instr) +#else // 64bit + underrunProtect(6*4); + BCTR(0); // jump to address in CTR + MTCTR(R2); // CTR = R2 + LWZX(R2, R2, R0); // R2 = [table + index*4] + SLWI(R0, indexreg, 2); // R0 = index*4 + asm_li(R2, int32_t(native_table)); // R2 = table (up to 2 instructions) +#endif // 64bit + } + + void Assembler::swapCodeChunks() { + SWAP(NIns*, _nIns, _nExitIns); + SWAP(NIns*, codeStart, exitStart); + SWAP(NIns*, codeEnd, exitEnd); + verbose_only( SWAP(size_t, codeBytes, exitBytes); ) + } + +} // namespace nanojit + +#endif // FEATURE_NANOJIT && NANOJIT_PPC diff --git a/ape-server/deps/js/src/nanojit/NativePPC.h b/ape-server/deps/js/src/nanojit/NativePPC.h new file mode 100755 index 0000000..f347365 --- /dev/null +++ b/ape-server/deps/js/src/nanojit/NativePPC.h @@ -0,0 +1,582 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is [Open Source Virtual Machine]. + * + * The Initial Developer of the Original Code is + * Adobe System Incorporated. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adobe AS3 Team + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef __nanojit_NativePPC__ +#define __nanojit_NativePPC__ + +#ifdef PERFM +#define DOPROF +#include "../vprof/vprof.h" +#define count_instr() _nvprof("ppc",1) +#define count_prolog() _nvprof("ppc-prolog",1); count_instr(); +#define count_imt() _nvprof("ppc-imt",1) count_instr() +#else +#define count_instr() +#define count_prolog() +#define count_imt() +#endif + +namespace nanojit +{ +#define NJ_MAX_STACK_ENTRY 256 +#define NJ_ALIGN_STACK 16 +#define NJ_JTBL_SUPPORTED 1 + + enum ConditionRegister { + CR0 = 0, + CR1 = 1, + CR2 = 2, + CR3 = 3, + CR4 = 4, + CR5 = 5, + CR6 = 6, + CR7 = 7, + }; + + enum ConditionBit { + COND_lt = 0, // msb of CR + COND_gt = 1, + COND_eq = 2, + COND_so = 3, // lsb of CR + COND_un = 3, + }; + + // this is the BO field in condition instructions + enum ConditionOption { + BO_true = 12, // branch if true + BO_false = 4, // branch if false + }; + + enum Register { + // general purpose 32bit regs + R0 = 0, // scratch or the value 0, excluded from regalloc + SP = 1, // stack pointer, excluded from regalloc + R2 = 2, // scratch on MacOSX, rtoc pointer elsewhere + R3 = 3, // this, return value, MSW of int64 return + R4 = 4, // param, LSW of int64 return + R5 = 5, // param + R6 = 6, // param + R7 = 7, // param + R8 = 8, // param + R9 = 9, // param + R10 = 10, // param + R11 = 11, // scratch in leaf funcs, outgoing arg ptr otherwise + R12 = 12, // scratch + R13 = 13, // ppc32: saved, ppc64: thread-specific storage + R14 = 14, // saved + R15 = 15, + R16 = 16, + R17 = 17, + R18 = 18, + R19 = 19, + R20 = 20, + R21 = 21, + R22 = 22, + R23 = 23, + R24 = 24, + R25 = 25, + R26 = 26, + R27 = 27, + R28 = 28, + R29 = 29, + R30 = 30, + R31 = 31, // excluded from regalloc since we use it as FP + FP = R31, + + // FP regs + F0 = 32, // scratch, excluded from reg alloc + F1 = 33, // param, double return value + F2 = 34, // param + F3 = 35, // param + F4 = 36, // param + F5 = 37, // param + F6 = 38, // param + F7 = 39, // param + F8 = 40, // param + F9 = 41, // param + F10 = 42, // param + F11 = 43, // param + F12 = 44, // param + F13 = 45, // param + F14 = 46, // F14-31 saved + F15 = 47, + F16 = 48, + F17 = 49, + F18 = 50, + F19 = 51, + F20 = 52, + F21 = 53, + F22 = 54, + F23 = 55, + F24 = 56, + F25 = 57, + F26 = 58, + F27 = 59, + F28 = 60, + F29 = 61, + F30 = 62, + F31 = 63, + + // special purpose registers (SPR) + Rxer = 1, + Rlr = 8, + Rctr = 9, + + UnknownReg = 127, + FirstReg = R0, + LastReg = F31 + }; + + enum PpcOpcode { + // opcodes + PPC_add = 0x7C000214, // add + PPC_addo = 0x7C000614, // add & OE=1 (can set OV) + PPC_addi = 0x38000000, // add immediate + PPC_addis = 0x3C000000, // add immediate shifted + PPC_and = 0x7C000038, // and + PPC_andc = 0x7C000078, // and with compliment + PPC_andi = 0x70000000, // and immediate + PPC_andis = 0x74000000, // and immediate shifted + PPC_b = 0x48000000, // branch + PPC_bc = 0x40000000, // branch conditional + PPC_bcctr = 0x4C000420, // branch conditional to count register + PPC_cmp = 0x7C000000, // compare + PPC_cmpi = 0x2C000000, // compare immediate + PPC_cmpl = 0x7C000040, // compare logical + PPC_cmpli = 0x28000000, // compare logical immediate + PPC_cror = 0x4C000382, // condition register or + PPC_extsw = 0x7C0007B4, // extend sign word + PPC_fadd = 0xFC00002A, // floating add (double precision) + PPC_fcfid = 0xFC00069C, // floating convert from integer doubleword + PPC_fcmpu = 0xFC000000, // floating compare unordered + PPC_fdiv = 0xFC000024, // floating divide (double precision) + PPC_fmr = 0xFC000090, // floating move register (double precision) + PPC_fmul = 0xFC000032, // floating multiply (double precision) + PPC_fneg = 0xFC000050, // floating negate + PPC_fsub = 0xFC000028, // floating subtract (double precision) + PPC_lbz = 0x88000000, // load byte and zero + PPC_ld = 0xE8000000, // load doubleword + PPC_ldx = 0x7C00002A, // load doubleword indexed + PPC_lfd = 0xC8000000, // load floating point double + PPC_lfdx = 0x7C0004AE, // load floating-point double indexed + PPC_lwz = 0x80000000, // load word and zero + PPC_lwzx = 0x7C00002E, // load word and zero indexed + PPC_mfcr = 0x7C000026, // move from condition register + PPC_mfspr = 0x7C0002A6, // move from spr (special purpose register) + PPC_mtspr = 0x7C0003A6, // move to spr + PPC_mulli = 0x1C000000, // multiply low immediate + PPC_mullw = 0x7C0001D6, // multiply low word + PPC_neg = 0x7C0000D0, // negate + PPC_nor = 0x7C0000F8, // nor + PPC_or = 0x7C000378, // or + PPC_ori = 0x60000000, // or immediate + PPC_oris = 0x64000000, // or immediate shifted + PPC_rlwinm = 0x54000000, // rotate left word then and with mask + PPC_rldicl = 0x78000000, // rotate left doubleword immediate then clear left + PPC_rldicr = 0x78000004, // rotate left doubleword immediate then clear right + PPC_rldimi = 0x7800000C, // rotate left doubleword immediate then mask insert + PPC_sld = 0x7C000036, // shift left doubleword + PPC_slw = 0x7C000030, // shift left word + PPC_srad = 0x7C000634, // shift right algebraic doubleword (sign ext) + PPC_sradi = 0x7C000674, // shift right algebraic doubleword immediate + PPC_sraw = 0x7C000630, // shift right algebraic word (sign ext) + PPC_srawi = 0x7C000670, // shift right algebraic word immediate + PPC_srd = 0x7C000436, // shift right doubleword (zero ext) + PPC_srw = 0x7C000430, // shift right word (zero ext) + PPC_std = 0xF8000000, // store doubleword + PPC_stdu = 0xF8000001, // store doubleword with update + PPC_stdux = 0x7C00016A, // store doubleword with update indexed + PPC_stdx = 0x7C00012A, // store doubleword indexed + PPC_stfd = 0xD8000000, // store floating-point double + PPC_stfdx = 0x7C0005AE, // store floating-point double indexed + PPC_stw = 0x90000000, // store word + PPC_stwu = 0x94000000, // store word with update + PPC_stwux = 0x7C00016E, // store word with update indexed + PPC_stwx = 0x7C00012E, // store word indexed + PPC_subf = 0x7C000050, // subtract from + PPC_xor = 0x7C000278, // xor + PPC_xori = 0x68000000, // xor immediate + PPC_xoris = 0x6C000000, // xor immediate shifted + + // simplified mnemonics + PPC_mr = PPC_or, + PPC_not = PPC_nor, + PPC_nop = PPC_ori, + }; + + typedef uint64_t RegisterMask; + + static const RegisterMask GpRegs = 0xffffffff; + static const RegisterMask FpRegs = 0xffffffff00000000LL; + // R31 is a saved reg too, but we use it as our Frame ptr FP +#ifdef NANOJIT_64BIT + // R13 reserved for thread-specific storage on ppc64-darwin + static const RegisterMask SavedRegs = 0x7fffc000; // R14-R30 saved + static const int NumSavedRegs = 17; // R14-R30 +#else + static const RegisterMask SavedRegs = 0x7fffe000; // R13-R30 saved + static const int NumSavedRegs = 18; // R13-R30 +#endif + + static inline bool isValidDisplacement(LOpcode, int32_t) { + return true; + } + static inline bool IsFpReg(Register r) { + return r >= F0; + } + + verbose_only( extern const char* regNames[]; ) + + #define DECLARE_PLATFORM_STATS() + #define DECLARE_PLATFORM_REGALLOC() + +#ifdef NANOJIT_64BIT + #define DECL_PPC64()\ + void asm_qbinop(LIns*); +#else + #define DECL_PPC64() +#endif + + #define DECLARE_PLATFORM_ASSEMBLER() \ + const static Register argRegs[8], retRegs[2]; \ + void underrunProtect(int bytes); \ + void nativePageReset(); \ + void nativePageSetup(); \ + void br(NIns *addr, int link); \ + void br_far(NIns *addr, int link); \ + void asm_regarg(ArgSize, LIns*, Register); \ + void asm_li(Register r, int32_t imm); \ + void asm_li32(Register r, int32_t imm); \ + void asm_li64(Register r, uint64_t imm); \ + void asm_cmp(LOpcode op, LIns *a, LIns *b, ConditionRegister); \ + NIns* asm_branch_far(bool onfalse, LIns *cond, NIns * const targ); \ + NIns* asm_branch_near(bool onfalse, LIns *cond, NIns * const targ); \ + int max_param_size; /* bytes */ \ + DECL_PPC64() + + const int LARGEST_UNDERRUN_PROT = 9*4; // largest value passed to underrunProtect + + typedef uint32_t NIns; + + // Bytes of icache to flush after Assembler::patch + const size_t LARGEST_BRANCH_PATCH = 4 * sizeof(NIns); + + #define EMIT1(ins, fmt, ...) do {\ + underrunProtect(4);\ + *(--_nIns) = (NIns) (ins);\ + asm_output(fmt, ##__VA_ARGS__);\ + } while (0) /* no semi */ + + #define GPR(r) (r) + #define FPR(r) ((r)&31) + + #define Bx(li,aa,lk) EMIT1(PPC_b | ((li)&0xffffff)<<2 | (aa)<<1 | (lk),\ + "b%s%s %p", (lk)?"l":"", (aa)?"a":"", _nIns+(li)) + + #define B(li) Bx(li,0,0) + #define BA(li) Bx(li,1,0) + #define BL(li) Bx(li,0,1) + #define BLA(li) Bx(li,1,1) + + #define BCx(op,bo,bit,cr,bd,aa,lk) EMIT1(PPC_bc | (bo)<<21 | (4*(cr)+COND_##bit)<<16 |\ + ((bd)&0x3fff)<<2 | (aa)<<1 | (lk),\ + "%s%s%s cr%d,%p", #op, (lk)?"l":"", (aa)?"a":"", (cr), _nIns+(bd)) + + #define BLT(cr,bd) BCx(blt, BO_true, lt, cr, bd, 0, 0) + #define BGT(cr,bd) BCx(bgt, BO_true, gt, cr, bd, 0, 0) + #define BEQ(cr,bd) BCx(beq, BO_true, eq, cr, bd, 0, 0) + #define BGE(cr,bd) BCx(bge, BO_false, lt, cr, bd, 0, 0) + #define BLE(cr,bd) BCx(ble, BO_false, gt, cr, bd, 0, 0) + #define BNE(cr,bd) BCx(bne, BO_false, eq, cr, bd, 0, 0) + #define BNG(cr,bd) BCx(bng, BO_false, gt, cr, bd, 0, 0) + #define BNL(cr,bd) BCx(bnl, BO_false, lt, cr, bd, 0, 0) + + #define BCCTRx(op, bo, bit, cr, lk) EMIT1(PPC_bcctr | (bo)<<21 | (4*(cr)+COND_##bit)<<16 | (lk)&1,\ + "%sctr%s cr%d", #op, (lk)?"l":"", (cr)) + + #define BLTCTR(cr) BCCTRx(blt, BO_true, lt, cr, 0) + #define BGTCTR(cr) BCCTRx(bgt, BO_true, gt, cr, 0) + #define BEQCTR(cr) BCCTRx(beq, BO_true, eq, cr, 0) + #define BGECTR(cr) BCCTRx(bge, BO_false, lt, cr, 0) + #define BLECTR(cr) BCCTRx(ble, BO_false, gt, cr, 0) + #define BNECTR(cr) BCCTRx(bne, BO_false, eq, cr, 0) + #define BNGCTR(cr) BCCTRx(bng, BO_false, gt, cr, 0) + #define BNLCTR(cr) BCCTRx(bnl, BO_false, lt, cr, 0) + + #define Simple(asm,op) EMIT1(op, "%s", #asm) + + #define BCTR(link) EMIT1(0x4E800420 | (link), "bctr%s", (link) ? "l" : "") + #define BCTRL() BCTR(1) + + #define BLR() EMIT1(0x4E800020, "blr") + #define NOP() EMIT1(PPC_nop, "nop") /* ori 0,0,0 */ + + #define ALU2(op, rd, ra, rb, rc) EMIT1(PPC_##op | GPR(rd)<<21 | GPR(ra)<<16 | GPR(rb)<<11 | (rc),\ + "%s%s %s,%s,%s", #op, (rc)?".":"", gpn(rd), gpn(ra), gpn(rb)) + #define BITALU2(op, ra, rs, rb, rc) EMIT1(PPC_##op | GPR(rs)<<21 | GPR(ra)<<16 | GPR(rb)<<11 | (rc),\ + "%s%s %s,%s,%s", #op, (rc)?".":"", gpn(ra), gpn(rs), gpn(rb)) + #define FPUAB(op, d, a, b, rc) EMIT1(PPC_##op | FPR(d)<<21 | FPR(a)<<16 | FPR(b)<<11 | (rc),\ + "%s%s %s,%s,%s", #op, (rc)?".":"", gpn(d), gpn(a), gpn(b)) + #define FPUAC(op, d, a, c, rc) EMIT1(PPC_##op | FPR(d)<<21 | FPR(a)<<16 | FPR(c)<<6 | (rc),\ + "%s%s %s,%s,%s", #op, (rc)?".":"", gpn(d), gpn(a), gpn(c)) + + #define ADD(rd,ra,rb) ALU2(add, rd, ra, rb, 0) + #define ADD_(rd,ra,rb) ALU2(add, rd, ra, rb, 1) + #define ADDO(rd,ra,rb) ALU2(addo, rd, ra, rb, 0) + #define ADDO_(rd,ra,rb) ALU2(addo, rd, ra, rb, 1) + #define SUBF(rd,ra,rb) ALU2(subf, rd, ra, rb, 0) + #define SUBF_(rd,ra,rb) ALU2(subf, rd, ra, rb, 1) + + #define AND(rd,rs,rb) BITALU2(and, rd, rs, rb, 0) + #define AND_(rd,rs,rb) BITALU2(and, rd, rs, rb, 1) + #define OR(rd,rs,rb) BITALU2(or, rd, rs, rb, 0) + #define OR_(rd,rs,rb) BITALU2(or, rd, rs, rb, 1) + #define NOR(rd,rs,rb) BITALU2(nor, rd, rs, rb, 0) + #define NOR_(rd,rs,rb) BITALU2(nor, rd, rs, rb, 1) + #define SLW(rd,rs,rb) BITALU2(slw, rd, rs, rb, 0) + #define SLW_(rd,rs,rb) BITALU2(slw, rd, rs, rb, 1) + #define SRW(rd,rs,rb) BITALU2(srw, rd, rs, rb, 0) + #define SRW_(rd,rs,rb) BITALU2(srw, rd, rs, rb, 1) + #define SRAW(rd,rs,rb) BITALU2(sraw, rd, rs, rb, 0) + #define SRAW_(rd,rs,rb) BITALU2(sraw, rd, rs, rb, 1) + #define XOR(rd,rs,rb) BITALU2(xor, rd, rs, rb, 0) + #define XOR_(rd,rs,rb) BITALU2(xor, rd, rs, rb, 1) + + #define SLD(rd,rs,rb) BITALU2(sld, rd, rs, rb, 0) + #define SRD(rd,rs,rb) BITALU2(srd, rd, rs, rb, 0) + #define SRAD(rd,rs,rb) BITALU2(srad, rd, rs, rb, 0) + + #define FADD(rd,ra,rb) FPUAB(fadd, rd, ra, rb, 0) + #define FADD_(rd,ra,rb) FPUAB(fadd, rd, ra, rb, 1) + #define FDIV(rd,ra,rb) FPUAB(fdiv, rd, ra, rb, 0) + #define FDIV_(rd,ra,rb) FPUAB(fdiv, rd, ra, rb, 1) + #define FMUL(rd,ra,rb) FPUAC(fmul, rd, ra, rb, 0) + #define FMUL_(rd,ra,rb) FPUAC(fmul, rd, ra, rb, 1) + #define FSUB(rd,ra,rb) FPUAB(fsub, rd, ra, rb, 0) + #define FSUB_(rd,ra,rb) FPUAB(fsub, rd, ra, rb, 1) + + #define MULLI(rd,ra,simm) EMIT1(PPC_mulli | GPR(rd)<<21 | GPR(ra)<<16 | uint16_t(simm),\ + "mulli %s,%s,%d", gpn(rd), gpn(ra), int16_t(simm)) + #define MULLW(rd,ra,rb) EMIT1(PPC_mullw | GPR(rd)<<21 | GPR(ra)<<16 | GPR(rb)<<11,\ + "mullw %s,%s,%s", gpn(rd), gpn(ra), gpn(rb)) + + // same as ALU2 with rs=rb, for simplified mnemonics + #define ALU1(op, ra, rs, rc) EMIT1(PPC_##op | GPR(rs)<<21 | GPR(ra)<<16 | GPR(rs)<<11 | (rc),\ + "%s%s %s,%s", #op, (rc)?".":"", gpn(ra), gpn(rs)) + + #define MR(rd, rs) ALU1(mr, rd, rs, 0) // or rd,rs,rs + #define MR_(rd, rs) ALU1(mr, rd, rs, 1) // or. rd,rs,rs + #define NOT(rd, rs) ALU1(not, rd, rs, 0) // nor rd,rs,rs + #define NOT_(rd, rs) ALU1(not, rd, rs, 0) // nor. rd,rs,rs + + #define EXTSW(rd, rs) EMIT1(PPC_extsw | GPR(rs)<<21 | GPR(rd)<<16,\ + "extsw %s,%s", gpn(rd), gpn(rs)) + + #define NEG(rd, rs) EMIT1(PPC_neg | GPR(rd)<<21 | GPR(rs)<<16, "neg %s,%s", gpn(rd), gpn(rs)) + #define FNEG(rd,rs) EMIT1(PPC_fneg | FPR(rd)<<21 | FPR(rs)<<11, "fneg %s,%s", gpn(rd), gpn(rs)) + #define FMR(rd,rb) EMIT1(PPC_fmr | FPR(rd)<<21 | FPR(rb)<<11, "fmr %s,%s", gpn(rd), gpn(rb)) + #define FCFID(rd,rs) EMIT1(PPC_fcfid | FPR(rd)<<21 | FPR(rs)<<11, "fcfid %s,%s", gpn(rd), gpn(rs)) + + #define JMP(addr) br(addr, 0) + + #define SPR(spr) ((R##spr)>>5|(R##spr&31)<<5) + #define MTSPR(spr,rs) EMIT1(PPC_mtspr | GPR(rs)<<21 | SPR(spr)<<11,\ + "mt%s %s", #spr, gpn(rs)) + #define MFSPR(rd,spr) EMIT1(PPC_mfspr | GPR(rd)<<21 | SPR(spr)<<11,\ + "mf%s %s", #spr, gpn(rd)) + + #define MTXER(r) MTSPR(xer, r) + #define MTLR(r) MTSPR(lr, r) + #define MTCTR(r) MTSPR(ctr, r) + + #define MFXER(r) MFSPR(r, xer) + #define MFLR(r) MFSPR(r, lr) + #define MFCTR(r) MFSPR(r, ctr) + + #define MEMd(op, r, d, a) do {\ + NanoAssert(isS16(d) && (d&3)==0);\ + EMIT1(PPC_##op | GPR(r)<<21 | GPR(a)<<16 | uint16_t(d), "%s %s,%d(%s)", #op, gpn(r), int16_t(d), gpn(a));\ + } while(0) /* no addr */ + + #define FMEMd(op, r, d, b) do {\ + NanoAssert(isS16(d));\ + EMIT1(PPC_##op | FPR(r)<<21 | GPR(b)<<16 | uint16_t(d), "%s %s,%d(%s)", #op, gpn(r), int16_t(d), gpn(b));\ + } while(0) /* no addr */ + + #define MEMx(op, r, a, b) EMIT1(PPC_##op | GPR(r)<<21 | GPR(a)<<16 | GPR(b)<<11,\ + "%s %s,%s,%s", #op, gpn(r), gpn(a), gpn(b)) + #define FMEMx(op, r, a, b) EMIT1(PPC_##op | FPR(r)<<21 | GPR(a)<<16 | GPR(b)<<11,\ + "%s %s,%s,%s", #op, gpn(r), gpn(a), gpn(b)) + + #define MEMux(op, rs, ra, rb) EMIT1(PPC_##op | GPR(rs)<<21 | GPR(ra)<<16 | GPR(rb)<<11,\ + "%s %s,%s,%s", #op, gpn(rs), gpn(ra), gpn(rb)) + + #define LBZ(r, d, b) MEMd(lbz, r, d, b) + #define LWZ(r, d, b) MEMd(lwz, r, d, b) + #define LD(r, d, b) MEMd(ld, r, d, b) + #define LWZX(r, a, b) MEMx(lwzx, r, a, b) + #define LDX(r, a, b) MEMx(ldx, r, a, b) + + #define STW(r, d, b) MEMd(stw, r, d, b) + #define STWU(r, d, b) MEMd(stwu, r, d, b) + #define STWX(s, a, b) MEMx(stwx, s, a, b) + #define STWUX(s, a, b) MEMux(stwux, s, a, b) + + #define STD(r, d, b) MEMd(std, r, d, b) + #define STDU(r, d, b) MEMd(stdu, r, d, b) + #define STDX(s, a, b) MEMx(stdx, s, a, b) + #define STDUX(s, a, b) MEMux(stdux, s, a, b) + +#ifdef NANOJIT_64BIT + #define LP(r, d, b) LD(r, d, b) + #define STP(r, d, b) STD(r, d, b) + #define STPU(r, d, b) STDU(r, d, b) + #define STPX(s, a, b) STDX(s, a, b) + #define STPUX(s, a, b) STDUX(s, a, b) +#else + #define LP(r, d, b) LWZ(r, d, b) + #define STP(r, d, b) STW(r, d, b) + #define STPU(r, d, b) STWU(r, d, b) + #define STPX(s, a, b) STWX(s, a, b) + #define STPUX(s, a, b) STWUX(s, a, b) +#endif + + #define LFD(r, d, b) FMEMd(lfd, r, d, b) + #define LFDX(r, a, b) FMEMx(lfdx, r, a, b) + #define STFD(r, d, b) FMEMd(stfd, r, d, b) + #define STFDX(s, a, b) FMEMx(stfdx, s, a, b) + + #define ALUI(op,rd,ra,d) EMIT1(PPC_##op | GPR(rd)<<21 | GPR(ra)<<16 | uint16_t(d),\ + "%s %s,%s,%d (0x%x)", #op, gpn(rd), gpn(ra), int16_t(d), int16_t(d)) + + #define ADDI(rd,ra,d) ALUI(addi, rd, ra, d) + #define ADDIS(rd,ra,d) ALUI(addis, rd, ra, d) + + // bitwise operators have different src/dest registers + #define BITALUI(op,rd,ra,d) EMIT1(PPC_##op | GPR(ra)<<21 | GPR(rd)<<16 | uint16_t(d),\ + "%s %s,%s,%u (0x%x)", #op, gpn(rd), gpn(ra), uint16_t(d), uint16_t(d)) + + #define ANDI(rd,ra,d) BITALUI(andi, rd, ra, d) + #define ORI(rd,ra,d) BITALUI(ori, rd, ra, d) + #define ORIS(rd,ra,d) BITALUI(oris, rd, ra, d) + #define XORI(rd,ra,d) BITALUI(xori, rd, ra, d) + #define XORIS(rd,ra,d) BITALUI(xoris, rd, ra, d) + + #define SUBI(rd,ra,d) EMIT1(PPC_addi | GPR(rd)<<21 | GPR(ra)<<16 | uint16_t(-(d)),\ + "subi %s,%s,%d", gpn(rd), gpn(ra), (d)) + + #define LI(rd,v) EMIT1(PPC_addi | GPR(rd)<<21 | uint16_t(v),\ + "li %s,%d (0x%x)", gpn(rd), int16_t(v), int16_t(v)) /* addi rd,0,v */ + + #define LIS(rd,v) EMIT1(PPC_addis | GPR(rd)<<21 | uint16_t(v),\ + "lis %s,%d (0x%x)", gpn(rd), int16_t(v), int16_t(v)<<16) /* addis, rd,0,v */ + + #define MTCR(rs) /* mtcrf 0xff,rs */ + #define MFCR(rd) EMIT1(PPC_mfcr | GPR(rd)<<21, "mfcr %s", gpn(rd)) + + #define CMPx(op, crfd, ra, rb, l) EMIT1(PPC_##op | (crfd)<<23 | (l)<<21 | GPR(ra)<<16 | GPR(rb)<<11,\ + "%s%c cr%d,%s,%s", #op, (l)?'d':'w', (crfd), gpn(ra), gpn(rb)) + + #define CMPW(cr, ra, rb) CMPx(cmp, cr, ra, rb, 0) + #define CMPLW(cr, ra, rb) CMPx(cmpl, cr, ra, rb, 0) + #define CMPD(cr, ra, rb) CMPx(cmp, cr, ra, rb, 1) + #define CMPLD(cr, ra, rb) CMPx(cmpl, cr, ra, rb, 1) + + #define CMPxI(cr, ra, simm, l) EMIT1(PPC_cmpi | (cr)<<23 | (l)<<21 | GPR(ra)<<16 | uint16_t(simm),\ + "cmp%ci cr%d,%s,%d (0x%x)", (l)?'d':'w', (cr), gpn(ra), int16_t(simm), int16_t(simm)) + + #define CMPWI(cr, ra, simm) CMPxI(cr, ra, simm, 0) + #define CMPDI(cr, ra, simm) CMPxI(cr, ra, simm, 1) + + #define CMPLxI(cr, ra, uimm, l) EMIT1(PPC_cmpli | (cr)<<23 | (l)<<21 | GPR(ra)<<16 | uint16_t(uimm),\ + "cmp%ci cr%d,%s,%d (0x%x)", (l)?'d':'w', (cr), gpn(ra), uint16_t(uimm), uint16_t(uimm)) + + #define CMPLWI(cr, ra, uimm) CMPLxI(cr, ra, uimm, 0) + #define CMPLDI(cr, ra, uimm) CMPLxI(cr, ra, uimm, 1) + + #define FCMPx(op, crfd, ra, rb) EMIT1(PPC_##op | (crfd)<<23 | FPR(ra)<<16 | FPR(rb)<<11,\ + "%s cr%d,%s,%s", #op, (crfd), gpn(ra), gpn(rb)) + + #define FCMPU(cr, ra, rb) FCMPx(fcmpu, cr, ra, rb) + + #define CROR(cr,d,a,b) EMIT1(PPC_cror | (4*(cr)+COND_##d)<<21 | (4*(cr)+COND_##a)<<16 | (4*(cr)+COND_##b)<<11,\ + "cror %d,%d,%d", 4*(cr)+COND_##d, 4*(cr)+COND_##a, 4*(cr)+COND_##b) + + #define RLWINM(rd,rs,sh,mb,me) EMIT1(PPC_rlwinm | GPR(rs)<<21 | GPR(rd)<<16 | (sh)<<11 | (mb)<<6 | (me)<<1,\ + "rlwinm %s,%s,%d,%d,%d", gpn(rd), gpn(rs), (sh), (mb), (me)) + + #define LO5(sh) ((sh) & 31) + #define BIT6(sh) (((sh) >> 5) & 1) + #define SPLITMB(mb) (LO5(mb)<<1 | BIT6(mb)) + + #define RLDICL(rd,rs,sh,mb) \ + EMIT1(PPC_rldicl | GPR(rs)<<21 | GPR(rd)<<16 | LO5(sh)<<11 | SPLITMB(mb)<<5 | BIT6(sh)<<1,\ + "rldicl %s,%s,%d,%d", gpn(rd), gpn(rs), (sh), (mb)) + + // clrldi d,s,n => rldicl d,s,0,n + #define CLRLDI(rd,rs,n) \ + EMIT1(PPC_rldicl | GPR(rs)<<21 | GPR(rd)<<16 | SPLITMB(n)<<5,\ + "clrldi %s,%s,%d", gpn(rd), gpn(rs), (n)) + + #define RLDIMI(rd,rs,sh,mb) \ + EMIT1(PPC_rldimi | GPR(rs)<<21 | GPR(rd)<<16 | LO5(sh)<<11 | SPLITMB(mb)<<5 | BIT6(sh)<<1,\ + "rldimi %s,%s,%d,%d", gpn(rd), gpn(rs), (sh), (mb)) + + // insrdi rD,rS,n,b => rldimi rD,rS,64-(b+n),b: insert n bit value into rD starting at b + #define INSRDI(rd,rs,n,b) \ + EMIT1(PPC_rldimi | GPR(rs)<<21 | GPR(rd)<<16 | LO5(64-((b)+(n)))<<11 | SPLITMB(b)<<5 | BIT6(64-((b)+(n)))<<1,\ + "insrdi %s,%s,%d,%d", gpn(rd), gpn(rs), (n), (b)) + + #define EXTRWI(rd,rs,n,b) EMIT1(PPC_rlwinm | GPR(rs)<<21 | GPR(rd)<<16 | ((n)+(b))<<11 | (32-(n))<<6 | 31<<1,\ + "extrwi %s,%s,%d,%d", gpn(rd), gpn(rs), (n), (b)) + + // sldi rd,rs,n (n<64) => rldicr rd,rs,n,63-n + #define SLDI(rd,rs,n) EMIT1(PPC_rldicr | GPR(rs)<<21 | GPR(rd)<<16 | LO5(n)<<11 | SPLITMB(63-(n))<<5 | BIT6(n)<<1,\ + "sldi %s,%s,%d", gpn(rd), gpn(rs), (n)) + + #define SLWI(rd,rs,n) EMIT1(PPC_rlwinm | GPR(rs)<<21 | GPR(rd)<<16 | (n)<<11 | 0<<6 | (31-(n))<<1,\ + "slwi %s,%s,%d", gpn(rd), gpn(rs), (n)) + #define SRWI(rd,rs,n) EMIT1(PPC_rlwinm | GPR(rs)<<21 | GPR(rd)<<16 | (32-(n))<<11 | (n)<<6 | 31<<1,\ + "slwi %s,%s,%d", gpn(rd), gpn(rs), (n)) + #define SRAWI(rd,rs,n) EMIT1(PPC_srawi | GPR(rs)<<21 | GPR(rd)<<16 | (n)<<11,\ + "srawi %s,%s,%d", gpn(rd), gpn(rs), (n)) + +} // namespace nanojit + +#endif // __nanojit_NativePPC__ diff --git a/ape-server/deps/js/src/nanojit/NativeSparc.cpp b/ape-server/deps/js/src/nanojit/NativeSparc.cpp new file mode 100755 index 0000000..63768e4 --- /dev/null +++ b/ape-server/deps/js/src/nanojit/NativeSparc.cpp @@ -0,0 +1,1036 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is [Open Source Virtual Machine]. + * + * The Initial Developer of the Original Code is + * Adobe System Incorporated. + * Portions created by the Initial Developer are Copyright (C) 2004-2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adobe AS3 Team + * leon.sha@sun.com + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include +#include +#include "nanojit.h" + +namespace nanojit +{ +#ifdef FEATURE_NANOJIT + +#ifdef NJ_VERBOSE + const char *regNames[] = { + "%g0", "%g1", "%g2", "%g3", "%g4", "%g5", "%g6", "%g7", + "%o0", "%o1", "%o2", "%o3", "%o4", "%o5", "%sp", "%o7", + "%l0", "%l1", "%l2", "%l3", "%l4", "%l5", "%l6", "%l7", + "%i0", "%i1", "%i2", "%i3", "%i4", "%i5", "%fp", "%i7", + "%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7", + "%f8", "%f9", "%f10", "%f11", "%f12", "%f13", "%f14", "%f15", + "%f16", "%f17", "%f18", "%f19", "%f20", "%f21", "%f22", "%f23", + "%f24", "%f25", "%f26", "%f27", "%f28", "%f29", "%f30", "%f31" + }; +#endif + + const Register Assembler::argRegs[] = { I0, I1, I2, I3, I4, I5 }; + const Register Assembler::retRegs[] = { O0 }; + const Register Assembler::savedRegs[] = { L1 }; + + static const int kLinkageAreaSize = 68; + static const int kcalleeAreaSize = 80; // The max size. + +#define BIT_ROUND_UP(v,q) ( (((uintptr_t)v)+(q)-1) & ~((q)-1) ) +#define TODO(x) do{ verbose_only(outputf(#x);) NanoAssertMsgf(false, "%s", #x); } while(0) + + void Assembler::nInit(AvmCore* core) + { + has_cmov = true; + } + + void Assembler::nBeginAssembly() { + } + + NIns* Assembler::genPrologue() + { + /** + * Prologue + */ + underrunProtect(16); + uint32_t stackNeeded = STACK_GRANULARITY * _activation.tos; + uint32_t frameSize = stackNeeded + kcalleeAreaSize + kLinkageAreaSize; + frameSize = BIT_ROUND_UP(frameSize, 8); + + if (frameSize <= 4096) + SUBI(FP, frameSize, SP); + else { + SUB(FP, G1, SP); + ORI(G1, frameSize & 0x3FF, G1); + SETHI(frameSize, G1); + } + + verbose_only( + if (_logc->lcbits & LC_Assembly) { + outputf(" %p:",_nIns); + outputf(" patch entry:"); + }) + NIns *patchEntry = _nIns; + + // The frame size in SAVE is faked. We will still re-caculate SP later. + // We can use 0 here but it is not good for debuggers. + SAVEI(SP, -148, SP); + + // align the entry point + asm_align_code(); + + return patchEntry; + } + + void Assembler::asm_align_code() { + while(uintptr_t(_nIns) & 15) { + NOP(); + } + } + + void Assembler::nFragExit(LInsp guard) + { + SideExit* exit = guard->record()->exit; + Fragment *frag = exit->target; + GuardRecord *lr; + if (frag && frag->fragEntry) + { + JMP(frag->fragEntry); + lr = 0; + } + else + { + // Target doesn't exit yet. Emit jump to epilog, and set up to patch later. + if (!_epilogue) + _epilogue = genEpilogue(); + lr = guard->record(); + JMP_long((intptr_t)_epilogue); + lr->jmp = _nIns; + } + + // return value is GuardRecord* + SET32(int(lr), O0); + } + + NIns *Assembler::genEpilogue() + { + underrunProtect(12); + RESTORE(G0, G0, G0); //restore + JMPLI(I7, 8, G0); //ret + ORI(O0, 0, I0); + return _nIns; + } + + void Assembler::asm_call(LInsp ins) + { + const CallInfo* call = ins->callInfo(); + + underrunProtect(8); + NOP(); + + ArgSize sizes[MAXARGS]; + uint32_t argc = call->get_sizes(sizes); + + NanoAssert(ins->isop(LIR_pcall) || ins->isop(LIR_fcall)); + verbose_only(if (_logc->lcbits & LC_Assembly) + outputf(" %p:", _nIns); + ) + bool indirect = call->isIndirect(); + if (!indirect) { + CALL(call); + } + else { + argc--; + Register r = findSpecificRegFor(ins->arg(argc), I0); + JMPL(G0, I0, 15); + } + + uint32_t GPRIndex = O0; + uint32_t offset = kLinkageAreaSize; // start of parameters stack postion. + + for(int i=0; iarg(j), FpRegs); + GPRIndex += 2; + offset += 8; + + underrunProtect(48); + // We might be calling a varargs function. + // So, make sure the GPR's are also loaded with + // the value, or the stack contains it. + if (GPRIndex-2 <= O5) { + LDSW32(SP, offset-8, (Register)(GPRIndex-2)); + } + if (GPRIndex-1 <= O5) { + LDSW32(SP, offset-4, (Register)(GPRIndex-1)); + } + STDF32(r, offset-8, SP); + } else { + if (GPRIndex > O5) { + underrunProtect(12); + Register r = findRegFor(ins->arg(j), GpRegs); + STW32(r, offset, SP); + } else { + Register r = findSpecificRegFor(ins->arg(j), (Register)GPRIndex); + } + GPRIndex++; + offset += 4; + } + } + } + + Register Assembler::nRegisterAllocFromSet(RegisterMask set) + { + // need to implement faster way + int i=0; + while (!(set & rmask((Register)i))) + i ++; + _allocator.free &= ~rmask((Register)i); + return (Register) i; + } + + void Assembler::nRegisterResetAll(RegAlloc& a) + { + a.clear(); + a.free = GpRegs | FpRegs; + debug_only( a.managed = a.free; ) + } + + void Assembler::nPatchBranch(NIns* branch, NIns* location) + { + *(uint32_t*)&branch[0] &= 0xFFC00000; + *(uint32_t*)&branch[0] |= ((intptr_t)location >> 10) & 0x3FFFFF; + *(uint32_t*)&branch[1] &= 0xFFFFFC00; + *(uint32_t*)&branch[1] |= (intptr_t)location & 0x3FF; + } + + RegisterMask Assembler::hint(LIns* i, RegisterMask allow) + { + return allow; + } + + void Assembler::asm_qjoin(LIns *ins) + { + underrunProtect(40); + int d = findMemFor(ins); + AvmAssert(d); + LIns* lo = ins->oprnd1(); + LIns* hi = ins->oprnd2(); + + Register rr = ins->getReg(); + if (isKnownReg(rr) && (rmask(rr) & FpRegs)) + evict(rr, ins); + + if (hi->isconst()) { + STW32(L2, d+4, FP); + SET32(hi->imm32(), L2); + } else { + Register rh = findRegFor(hi, GpRegs); + STW32(rh, d+4, FP); + } + + if (lo->isconst()) { + STW32(L2, d, FP); + SET32(lo->imm32(), L2); + } else { + // okay if r gets recycled. + Register rl = findRegFor(lo, GpRegs); + STW32(rl, d, FP); + } + + freeRsrcOf(ins, false); // if we had a reg in use, emit a ST to flush it to mem + } + + + void Assembler::asm_restore(LInsp i, Register r) + { + underrunProtect(24); + if (i->isop(LIR_alloc)) { + ADD(FP, L2, r); + int32_t d = disp(i); + SET32(d, L2); + } + else if (i->isconst()) { + if (!i->getArIndex()) { + i->markAsClear(); + } + int v = i->imm32(); + SET32(v, r); + } else { + int d = findMemFor(i); + if (rmask(r) & FpRegs) { + LDDF32(FP, d, r); + } else { + LDSW32(FP, d, r); + } + } + } + + void Assembler::asm_store32(LIns *value, int dr, LIns *base) + { + underrunProtect(20); + if (value->isconst()) + { + Register rb = getBaseReg(LIR_sti, base, dr, GpRegs); + int c = value->imm32(); + STW32(L2, dr, rb); + SET32(c, L2); + } + else + { + // make sure what is in a register + Register ra, rb; + if (base->isop(LIR_alloc)) { + rb = FP; + dr += findMemFor(base); + ra = findRegFor(value, GpRegs); + } else if (base->isconst()) { + // absolute address + dr += base->imm32(); + ra = findRegFor(value, GpRegs); + rb = G0; + } else { + findRegFor2(GpRegs, value, ra, base, rb); + } + STW32(ra, dr, rb); + } + } + + void Assembler::asm_spill(Register rr, int d, bool pop, bool quad) + { + underrunProtect(24); + (void)quad; + if (d) { + if (rmask(rr) & FpRegs) { + STDF32(rr, d, FP); + } else { + STW32(rr, d, FP); + } + } + } + + void Assembler::asm_load64(LInsp ins) + { + underrunProtect(72); + LIns* base = ins->oprnd1(); + int db = ins->disp(); + Register rr = ins->getReg(); + + int dr = disp(ins); + Register rb; + if (base->isop(LIR_alloc)) { + rb = FP; + db += findMemFor(base); + } else { + rb = findRegFor(base, GpRegs); + } + ins->setReg(UnknownReg); + + // don't use an fpu reg to simply load & store the value. + if (dr) + asm_mmq(FP, dr, rb, db); + + freeRsrcOf(ins, false); + + if (rr != UnknownReg) + { + NanoAssert(rmask(rr)&FpRegs); + _allocator.retire(rr); + LDDF32(rb, db, rr); + } + } + + void Assembler::asm_store64(LInsp value, int dr, LInsp base) + { + underrunProtect(48); + if (value->isconstq()) + { + // if a constant 64-bit value just store it now rather than + // generating a pointless store/load/store sequence + Register rb = findRegFor(base, GpRegs); + STW32(L2, dr+4, rb); + SET32(value->imm64_0(), L2); + STW32(L2, dr, rb); + SET32(value->imm64_1(), L2); + return; + } + + if (value->isop(LIR_ldq) || value->isop(LIR_ldqc) || value->isop(LIR_qjoin)) + { + // value is 64bit struct or int64_t, or maybe a double. + // it may be live in an FPU reg. Either way, don't + // put it in an FPU reg just to load & store it. + + // a) if we know it's not a double, this is right. + // b) if we guarded that its a double, this store could be on + // the side exit, copying a non-double. + // c) maybe its a double just being stored. oh well. + + int da = findMemFor(value); + Register rb; + if (base->isop(LIR_alloc)) { + rb = FP; + dr += findMemFor(base); + } else { + rb = findRegFor(base, GpRegs); + } + asm_mmq(rb, dr, FP, da); + return; + } + + Register rb; + if (base->isop(LIR_alloc)) { + rb = FP; + dr += findMemFor(base); + } else { + rb = findRegFor(base, GpRegs); + } + + // if value already in a reg, use that, otherwise + // try to get it into XMM regs before FPU regs. + Register rv = ( value->isUnusedOrHasUnknownReg() + ? findRegFor(value, FpRegs) + : value->getReg() ); + + STDF32(rv, dr, rb); + } + + /** + * copy 64 bits: (rd+dd) <- (rs+ds) + */ + void Assembler::asm_mmq(Register rd, int dd, Register rs, int ds) + { + // value is either a 64bit struct or maybe a float + // that isn't live in an FPU reg. Either way, don't + // put it in an FPU reg just to load & store it. + Register t = registerAllocTmp(GpRegs & ~(rmask(rd)|rmask(rs))); + STW32(t, dd+4, rd); + LDSW32(rs, ds+4, t); + STW32(t, dd, rd); + LDSW32(rs, ds, t); + } + + NIns* Assembler::asm_branch(bool branchOnFalse, LInsp cond, NIns* targ) + { + NIns* at = 0; + LOpcode condop = cond->opcode(); + NanoAssert(cond->isCond()); + if (condop >= LIR_feq && condop <= LIR_fge) + { + return asm_fbranch(branchOnFalse, cond, targ); + } + + underrunProtect(32); + intptr_t tt = ((intptr_t)targ - (intptr_t)_nIns + 8) >> 2; + // !targ means that it needs patch. + if( !(isIMM22((int32_t)tt)) || !targ ) { + JMP_long_nocheck((intptr_t)targ); + at = _nIns; + NOP(); + BA(0, 5); + tt = 4; + } + NOP(); + + + // produce the branch + if (branchOnFalse) + { + if (condop == LIR_eq) + BNE(0, tt); + else if (condop == LIR_ov) + BVC(0, tt); + else if (condop == LIR_lt) + BGE(0, tt); + else if (condop == LIR_le) + BG(0, tt); + else if (condop == LIR_gt) + BLE(0, tt); + else if (condop == LIR_ge) + BL(0, tt); + else if (condop == LIR_ult) + BCC(0, tt); + else if (condop == LIR_ule) + BGU(0, tt); + else if (condop == LIR_ugt) + BLEU(0, tt); + else //if (condop == LIR_uge) + BCS(0, tt); + } + else // op == LIR_xt + { + if (condop == LIR_eq) + BE(0, tt); + else if (condop == LIR_ov) + BVS(0, tt); + else if (condop == LIR_lt) + BL(0, tt); + else if (condop == LIR_le) + BLE(0, tt); + else if (condop == LIR_gt) + BG(0, tt); + else if (condop == LIR_ge) + BGE(0, tt); + else if (condop == LIR_ult) + BCS(0, tt); + else if (condop == LIR_ule) + BLEU(0, tt); + else if (condop == LIR_ugt) + BGU(0, tt); + else //if (condop == LIR_uge) + BCC(0, tt); + } + asm_cmp(cond); + return at; + } + + void Assembler::asm_cmp(LIns *cond) + { + underrunProtect(12); + LOpcode condop = cond->opcode(); + + // LIR_ov recycles the flags set by arithmetic ops + if (condop == LIR_ov) + return; + + LInsp lhs = cond->oprnd1(); + LInsp rhs = cond->oprnd2(); + + NanoAssert((!lhs->isQuad() && !rhs->isQuad()) || (lhs->isQuad() && rhs->isQuad())); + + NanoAssert(!lhs->isQuad() && !rhs->isQuad()); + + // ready to issue the compare + if (rhs->isconst()) + { + int c = rhs->imm32(); + if (c == 0 && cond->isop(LIR_eq)) { + Register r = findRegFor(lhs, GpRegs); + ANDCC(r, r, G0); + } + else if (!rhs->isQuad()) { + Register r = getBaseReg(condop, lhs, c, GpRegs); + SUBCC(r, L2, G0); + SET32(c, L2); + } + } + else + { + Register ra, rb; + findRegFor2(GpRegs, lhs, ra, rhs, rb); + SUBCC(ra, rb, G0); + } + } + + void Assembler::asm_fcond(LInsp ins) + { + // only want certain regs + Register r = prepResultReg(ins, AllowableFlagRegs); + underrunProtect(8); + LOpcode condop = ins->opcode(); + NanoAssert(condop >= LIR_feq && condop <= LIR_fge); + if (condop == LIR_feq) + MOVFEI(1, 0, 0, 0, r); + else if (condop == LIR_fle) + MOVFLEI(1, 0, 0, 0, r); + else if (condop == LIR_flt) + MOVFLI(1, 0, 0, 0, r); + else if (condop == LIR_fge) + MOVFGEI(1, 0, 0, 0, r); + else // if (condop == LIR_fgt) + MOVFGI(1, 0, 0, 0, r); + ORI(G0, 0, r); + asm_fcmp(ins); + } + + void Assembler::asm_cond(LInsp ins) + { + underrunProtect(8); + // only want certain regs + LOpcode op = ins->opcode(); + Register r = prepResultReg(ins, AllowableFlagRegs); + + if (op == LIR_eq) + MOVEI(1, 1, 0, 0, r); + else if (op == LIR_ov) + MOVVSI(1, 1, 0, 0, r); + else if (op == LIR_lt) + MOVLI(1, 1, 0, 0, r); + else if (op == LIR_le) + MOVLEI(1, 1, 0, 0, r); + else if (op == LIR_gt) + MOVGI(1, 1, 0, 0, r); + else if (op == LIR_ge) + MOVGEI(1, 1, 0, 0, r); + else if (op == LIR_ult) + MOVEI(1, 1, 0, 0, r); + else if (op == LIR_ule) + MOVLEUI(1, 1, 0, 0, r); + else if (op == LIR_ugt) + MOVGUI(1, 1, 0, 0, r); + else // if (op == LIR_uge) + MOVCCI(1, 1, 0, 0, r); + ORI(G0, 0, r); + asm_cmp(ins); + } + + void Assembler::asm_arith(LInsp ins) + { + underrunProtect(28); + LOpcode op = ins->opcode(); + LInsp lhs = ins->oprnd1(); + LInsp rhs = ins->oprnd2(); + + Register rb = UnknownReg; + RegisterMask allow = GpRegs; + bool forceReg = (op == LIR_mul || !rhs->isconst()); + + if (lhs != rhs && forceReg) + { + if ((rb = asm_binop_rhs_reg(ins)) == UnknownReg) { + rb = findRegFor(rhs, allow); + } + allow &= ~rmask(rb); + } + else if ((op == LIR_add||op == LIR_iaddp) && lhs->isop(LIR_alloc) && rhs->isconst()) { + // add alloc+const, use lea + Register rr = prepResultReg(ins, allow); + int d = findMemFor(lhs) + rhs->imm32(); + ADD(FP, L2, rr); + SET32(d, L2); + } + + Register rr = prepResultReg(ins, allow); + // if this is last use of lhs in reg, we can re-use result reg + // else, lhs already has a register assigned. + Register ra = ( lhs->isUnusedOrHasUnknownReg() + ? findSpecificRegFor(lhs, rr) + : lhs->getReg() ); + + if (forceReg) + { + if (lhs == rhs) + rb = ra; + + if (op == LIR_add || op == LIR_iaddp) + ADDCC(rr, rb, rr); + else if (op == LIR_sub) + SUBCC(rr, rb, rr); + else if (op == LIR_mul) + MULX(rr, rb, rr); + else if (op == LIR_and) + AND(rr, rb, rr); + else if (op == LIR_or) + OR(rr, rb, rr); + else if (op == LIR_xor) + XOR(rr, rb, rr); + else if (op == LIR_lsh) + SLL(rr, rb, rr); + else if (op == LIR_rsh) + SRA(rr, rb, rr); + else if (op == LIR_ush) + SRL(rr, rb, rr); + else + NanoAssertMsg(0, "Unsupported"); + } + else + { + int c = rhs->imm32(); + if (op == LIR_add || op == LIR_iaddp) { + ADDCC(rr, L2, rr); + } else if (op == LIR_sub) { + SUBCC(rr, L2, rr); + } else if (op == LIR_and) + AND(rr, L2, rr); + else if (op == LIR_or) + OR(rr, L2, rr); + else if (op == LIR_xor) + XOR(rr, L2, rr); + else if (op == LIR_lsh) + SLL(rr, L2, rr); + else if (op == LIR_rsh) + SRA(rr, L2, rr); + else if (op == LIR_ush) + SRL(rr, L2, rr); + else + NanoAssertMsg(0, "Unsupported"); + SET32(c, L2); + } + + if ( rr != ra ) + ORI(ra, 0, rr); + } + + void Assembler::asm_neg_not(LInsp ins) + { + underrunProtect(8); + LOpcode op = ins->opcode(); + Register rr = prepResultReg(ins, GpRegs); + + LIns* lhs = ins->oprnd1(); + // if this is last use of lhs in reg, we can re-use result reg + // else, lhs already has a register assigned. + Register ra = ( lhs->isUnusedOrHasUnknownReg() + ? findSpecificRegFor(lhs, rr) + : lhs->getReg() ); + + if (op == LIR_not) + ORN(G0, rr, rr); + else + SUB(G0, rr, rr); + + if ( rr != ra ) + ORI(ra, 0, rr); + } + + void Assembler::asm_ld(LInsp ins) + { + underrunProtect(12); + LOpcode op = ins->opcode(); + LIns* base = ins->oprnd1(); + int d = ins->disp(); + Register rr = prepResultReg(ins, GpRegs); + Register ra = getBaseReg(ins->opcode(), base, d, GpRegs); + if (op == LIR_ldcb) { + LDUB32(ra, d, rr); + } else if (op == LIR_ldcs) { + LDUH32(ra, d, rr); + } else { + LDSW32(ra, d, rr); + } + } + + void Assembler::asm_cmov(LInsp ins) + { + underrunProtect(4); + LOpcode op = ins->opcode(); + LIns* condval = ins->oprnd1(); + LIns* iftrue = ins->oprnd2(); + LIns* iffalse = ins->oprnd3(); + + NanoAssert(condval->isCmp()); + NanoAssert(op == LIR_qcmov || (!iftrue->isQuad() && !iffalse->isQuad())); + + const Register rr = prepResultReg(ins, GpRegs); + + // this code assumes that neither LD nor MR nor MRcc set any of the condition flags. + // (This is true on Intel, is it true on all architectures?) + const Register iffalsereg = findRegFor(iffalse, GpRegs & ~rmask(rr)); + if (op == LIR_cmov) { + switch (condval->opcode()) { + // note that these are all opposites... + case LIR_eq: MOVNE (iffalsereg, 1, 0, 0, rr); break; + case LIR_ov: MOVVC (iffalsereg, 1, 0, 0, rr); break; + case LIR_lt: MOVGE (iffalsereg, 1, 0, 0, rr); break; + case LIR_le: MOVG (iffalsereg, 1, 0, 0, rr); break; + case LIR_gt: MOVLE (iffalsereg, 1, 0, 0, rr); break; + case LIR_ge: MOVL (iffalsereg, 1, 0, 0, rr); break; + case LIR_ult: MOVCC (iffalsereg, 1, 0, 0, rr); break; + case LIR_ule: MOVGU (iffalsereg, 1, 0, 0, rr); break; + case LIR_ugt: MOVLEU(iffalsereg, 1, 0, 0, rr); break; + case LIR_uge: MOVCS (iffalsereg, 1, 0, 0, rr); break; + debug_only( default: NanoAssert(0); break; ) + } + } else if (op == LIR_qcmov) { + NanoAssert(0); + } + /*const Register iftruereg =*/ findSpecificRegFor(iftrue, rr); + asm_cmp(condval); + } + + void Assembler::asm_qhi(LInsp ins) + { + underrunProtect(12); + Register rr = prepResultReg(ins, GpRegs); + LIns *q = ins->oprnd1(); + int d = findMemFor(q); + LDSW32(FP, d+4, rr); + } + + void Assembler::asm_param(LInsp ins) + { + uint32_t a = ins->paramArg(); + uint32_t kind = ins->paramKind(); + prepResultReg(ins, rmask(argRegs[a])); + } + + void Assembler::asm_int(LInsp ins) + { + underrunProtect(8); + Register rr = prepResultReg(ins, GpRegs); + int32_t val = ins->imm32(); + if (val == 0) + XOR(rr, rr, rr); + else + SET32(val, rr); + } + + void Assembler::asm_quad(LInsp ins) + { + underrunProtect(64); + Register rr = ins->getReg(); + if (rr != UnknownReg) + { + // @todo -- add special-cases for 0 and 1 + _allocator.retire(rr); + ins->setReg(UnknownReg); + NanoAssert((rmask(rr) & FpRegs) != 0); + findMemFor(ins); + int d = disp(ins); + LDDF32(FP, d, rr); + } + + // @todo, if we used xor, ldsd, fldz, etc above, we don't need mem here + int d = disp(ins); + freeRsrcOf(ins, false); + if (d) + { + STW32(L2, d+4, FP); + SET32(ins->imm64_0(), L2); + STW32(L2, d, FP); + SET32(ins->imm64_1(), L2); + } + } + + void Assembler::asm_qlo(LInsp ins) + { + } + + void Assembler::asm_fneg(LInsp ins) + { + underrunProtect(4); + Register rr = prepResultReg(ins, FpRegs); + LIns* lhs = ins->oprnd1(); + + // lhs into reg, prefer same reg as result + // if this is last use of lhs in reg, we can re-use result reg + // else, lhs already has a different reg assigned + Register ra = ( lhs->isUnusedOrHasUnknownReg() + ? findSpecificRegFor(lhs, rr) + : findRegFor(lhs, FpRegs) ); + + FNEGD(ra, rr); + } + + void Assembler::asm_fop(LInsp ins) + { + underrunProtect(4); + LOpcode op = ins->opcode(); + LIns *lhs = ins->oprnd1(); + LIns *rhs = ins->oprnd2(); + + RegisterMask allow = FpRegs; + Register ra = findRegFor(lhs, FpRegs); + Register rb = (rhs == lhs) ? ra : findRegFor(rhs, FpRegs); + + Register rr = prepResultReg(ins, allow); + + if (op == LIR_fadd) + FADDD(ra, rb, rr); + else if (op == LIR_fsub) + FSUBD(ra, rb, rr); + else if (op == LIR_fmul) + FMULD(ra, rb, rr); + else //if (op == LIR_fdiv) + FDIVD(ra, rb, rr); + + } + + void Assembler::asm_i2f(LInsp ins) + { + underrunProtect(32); + // where our result goes + Register rr = prepResultReg(ins, FpRegs); + int d = findMemFor(ins->oprnd1()); + FITOD(rr, rr); + LDDF32(FP, d, rr); + } + + Register Assembler::asm_prep_fcall(LInsp ins) + { + return prepResultReg(ins, rmask(F0)); + } + + void Assembler::asm_u2f(LInsp ins) + { + underrunProtect(72); + // where our result goes + Register rr = prepResultReg(ins, FpRegs); + Register rt = registerAllocTmp(FpRegs & ~(rmask(rr))); + Register gr = findRegFor(ins->oprnd1(), GpRegs); + int disp = -8; + + FABSS(rr, rr); + FSUBD(rt, rr, rr); + LDDF32(SP, disp, rr); + STWI(G0, disp+4, SP); + LDDF32(SP, disp, rt); + STWI(gr, disp+4, SP); + STWI(G1, disp, SP); + SETHI(0x43300000, G1); + } + + void Assembler::asm_nongp_copy(Register r, Register s) + { + underrunProtect(4); + NanoAssert((rmask(r) & FpRegs) && (rmask(s) & FpRegs)); + FMOVD(s, r); + } + + NIns * Assembler::asm_fbranch(bool branchOnFalse, LIns *cond, NIns *targ) + { + NIns *at = 0; + LOpcode condop = cond->opcode(); + NanoAssert(condop >= LIR_feq && condop <= LIR_fge); + underrunProtect(32); + intptr_t tt = ((intptr_t)targ - (intptr_t)_nIns + 8) >> 2; + // !targ means that it needs patch. + if( !(isIMM22((int32_t)tt)) || !targ ) { + JMP_long_nocheck((intptr_t)targ); + at = _nIns; + NOP(); + BA(0, 5); + tt = 4; + } + NOP(); + + // produce the branch + if (branchOnFalse) + { + if (condop == LIR_feq) + FBNE(0, tt); + else if (condop == LIR_fle) + FBUG(0, tt); + else if (condop == LIR_flt) + FBUGE(0, tt); + else if (condop == LIR_fge) + FBUL(0, tt); + else //if (condop == LIR_fgt) + FBULE(0, tt); + } + else // op == LIR_xt + { + if (condop == LIR_feq) + FBE(0, tt); + else if (condop == LIR_fle) + FBLE(0, tt); + else if (condop == LIR_flt) + FBL(0, tt); + else if (condop == LIR_fge) + FBGE(0, tt); + else //if (condop == LIR_fgt) + FBG(0, tt); + } + asm_fcmp(cond); + return at; + } + + void Assembler::asm_fcmp(LIns *cond) + { + underrunProtect(4); + LIns* lhs = cond->oprnd1(); + LIns* rhs = cond->oprnd2(); + + Register rLhs = findRegFor(lhs, FpRegs); + Register rRhs = findRegFor(rhs, FpRegs); + + FCMPD(rLhs, rRhs); + } + + void Assembler::nativePageReset() + { + } + + Register Assembler::asm_binop_rhs_reg(LInsp ins) + { + return UnknownReg; + } + + void Assembler::nativePageSetup() + { + NanoAssert(!_inExit); + if (!_nIns) + codeAlloc(codeStart, codeEnd, _nIns verbose_only(, codeBytes)); + if (!_nExitIns) + codeAlloc(exitStart, exitEnd, _nExitIns verbose_only(, exitBytes)); + } + + // Increment the 32-bit profiling counter at pCtr, without + // changing any registers. + verbose_only( + void Assembler::asm_inc_m32(uint32_t*) + { + // todo: implement this + } + ) + + void + Assembler::underrunProtect(int n) + { + NIns *eip = _nIns; + // This may be in a normal code chunk or an exit code chunk. + if (eip - n < codeStart) { + codeAlloc(codeStart, codeEnd, _nIns verbose_only(, codeBytes)); + JMP_long_nocheck((intptr_t)eip); + } + } + + void Assembler::asm_ret(LInsp ins) + { + genEpilogue(); + assignSavedRegs(); + LIns *val = ins->oprnd1(); + if (ins->isop(LIR_ret)) { + findSpecificRegFor(val, retRegs[0]); + } else { + findSpecificRegFor(val, F0); + } + } + + void Assembler::asm_promote(LIns *) { + // i2q or u2q + TODO(asm_promote); + } + + void Assembler::swapCodeChunks() { + SWAP(NIns*, _nIns, _nExitIns); + SWAP(NIns*, codeStart, exitStart); + SWAP(NIns*, codeEnd, exitEnd); + verbose_only( SWAP(size_t, codeBytes, exitBytes); ) + } + +#endif /* FEATURE_NANOJIT */ +} diff --git a/ape-server/deps/js/src/nanojit/NativeSparc.h b/ape-server/deps/js/src/nanojit/NativeSparc.h new file mode 100755 index 0000000..fe0c3d9 --- /dev/null +++ b/ape-server/deps/js/src/nanojit/NativeSparc.h @@ -0,0 +1,959 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is [Open Source Virtual Machine]. + * + * The Initial Developer of the Original Code is + * Adobe System Incorporated. + * Portions created by the Initial Developer are Copyright (C) 2004-2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adobe AS3 Team + * leon.sha@sun.com + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + + +#ifndef __nanojit_NativeSparc__ +#define __nanojit_NativeSparc__ + + +#define count_instr() +#define count_ret() +#define count_push() +#define count_pop() +#define count_st() +#define count_stq() +#define count_ld() +#define count_ldq() +#define count_call() +#define count_calli() +#define count_prolog() +#define count_alu() +#define count_mov() +#define count_fpu() +#define count_jmp() +#define count_jcc() +#define count_fpuld() +#define count_aluld() +#define count_alust() +#define count_pushld() +#define count_imt() + +namespace nanojit +{ + const int NJ_MAX_REGISTERS = 30; // L0 - L7, I0 - I5, F2 - F14 + + const int LARGEST_UNDERRUN_PROT = 32; // largest value passed to underrunProtect + +#define NJ_MAX_STACK_ENTRY 256 +#define NJ_MAX_PARAMETERS 1 + + const int NJ_ALIGN_STACK = 16; + + typedef uint32_t NIns; + + // Bytes of icache to flush after Assembler::patch + const size_t LARGEST_BRANCH_PATCH = 2 * sizeof(NIns); + + // These are used as register numbers in various parts of the code + typedef enum + { + G0 = 0, + G1 = 1, + G2 = 2, + G3 = 3, + G4 = 4, + G5 = 5, // Reserved for system + G6 = 6, // Reserved for system + G7 = 7, // Reserved for system + + O0 = 8, + O1 = 9, + O2 = 10, + O3 = 11, + O4 = 12, + O5 = 13, + O6 = 14, // SP + O7 = 15, // Not used. + + L0 = 16, + L1 = 17, + L2 = 18, + L3 = 19, + L4 = 20, + L5 = 21, + L6 = 22, + L7 = 23, + + I0 = 24, + I1 = 25, + I2 = 26, + I3 = 27, + I4 = 28, + I5 = 29, + I6 = 30, // FP + I7 = 31, // Not used + + SP = O6, + FP = I6, + + F0 = 0, + F1 = 1, + F2 = 2, + F3 = 3, + F4 = 4, + F5 = 5, + F6 = 6, + F7 = 7, + F8 = 8, + F9 = 9, + F10 = 10, + F11 = 11, + F12 = 12, + F13 = 13, + F14 = 14, + F15 = 15, + F16 = 16, + F17 = 17, + F18 = 18, + F19 = 19, + F20 = 20, + F21 = 21, + F22 = 22, + F23 = 23, + F24 = 24, + F25 = 25, + F26 = 26, + F27 = 27, + F28 = 28, + F29 = 29, + F30 = 30, + F31 = 31, + + // helpers + FRAME_PTR = G4, + + FirstReg = 0, + LastReg = 29, + UnknownReg = 30 + } + Register; + + // We only use 32 registers to be managed. + // So we choose some of them. + // And other unmanaged registers we can use them directly. + // The registers that can be used directly are G0-G4, L0, L2, L4, L6 + // SP, FP, F8-F12, F24-F28 + typedef int RegisterMask; +#define _rmask_(r) (1<<(r)) + + // Assembler::savedRegs[] is not needed for sparc because the + // registers are already saved automatically by "save" instruction. + // But NumSavedRegs is used as the length of savedRegs in LIR.h and Assembler.h. + // NumSavedRegs to be zero will cause a zero length array. + // So dummy L1 is added and NumSavedRegs is set to 1. + static const int NumSavedRegs = 1; + static const RegisterMask SavedRegs = 1<_address) - ((int)_nIns) + 4; \ + int i = 0x40000000 | ((offset >> 2) & 0x3FFFFFFF); \ + IMM32(i); \ + asm_output("call %s",(c->_name)); \ + } while (0) + +#define Format_2_1(rd, op2, imm22) do { \ + int i = rd << 25 | op2 << 22 | (imm22 & 0x3FFFFF); \ + IMM32(i); \ + } while (0) + +#define Format_2_2(a, cond, op2, disp22) \ + Format_2_1((a & 0x1) << 4 | (cond & 0xF), op2, disp22) + +#define Format_2_3(a, cond, op2, cc1, cc0, p, disp19) \ + Format_2_2(a, cond, op2, (cc1 & 0x1) << 21 | (cc0 & 0x1) << 20 | (p & 0x1) << 19 | (disp19 & 0x7FFFF)) + +#define Format_2_4(a, rcond, op2, d16hi, p, rs1, d16lo) \ + Format_2_2(a, (rcond & 0x7), op2, (d16hi & 0x3) << 20 | (p & 0x1) << 19 | rs1 << 14 | (d16lo & 0x3FFF)) + +#define Format_3(op1, rd, op3, bits19) do { \ + int i = op1 << 30 | rd << 25 | op3 << 19 | (bits19 & 0x7FFFF); \ + IMM32(i); \ + } while (0) + +#define Format_3_1(op1, rd, op3, rs1, bit8, rs2) \ + Format_3(op1, rd, op3, rs1 << 14 | (bit8 & 0xFF) << 5 | rs2) + +#define Format_3_1I(op1, rd, op3, rs1, simm13) \ + Format_3(op1, rd, op3, rs1 << 14 | 1 << 13 | (simm13 & 0x1FFF)) + +#define Format_3_2(op1, rd, op3, rs1, rcond, rs2) \ + Format_3(op1, rd, op3, rs1 << 14 | (rcond & 0x3) << 10 | rs2) + +#define Format_3_2I(op1, rd, op3, rs1, rcond, simm10) \ + Format_3(op1, rd, op3, rs1 << 14 | 1 << 13 | (rcond & 0x3) << 10 | (simm10 & 0x1FFF)) + +#define Format_3_3(op1, rd, op3, rs1, cmask, mmask) \ + Format_3(op1, rd, op3, rs1 << 14 | 1 << 13 | (cmask & 0x7) << 5 | (mmask & 0xF)) + +#define Format_3_4(op1, rd, op3, bits19) \ + Format_3(op1, rd, op3, bits19) + +#define Format_3_5(op1, rd, op3, rs1, x, rs2) \ + Format_3(op1, rd, op3, rs1 << 14 | (x & 0x1) << 12 | rs2) + +#define Format_3_6(op1, rd, op3, rs1, shcnt32) \ + Format_3(op1, rd, op3, rs1 << 14 | 1 << 13 | (shcnt32 & 0x3F)) + +#define Format_3_7(op1, rd, op3, rs1, shcnt64) \ + Format_3(op1, rd, op3, rs1 << 14 | 1 << 13 | 1 << 12 | (shcnt64 & 0x7F)) + +#define Format_3_8(op1, rd, op3, rs1, bits9, rs2) \ + Format_3(op1, rd, op3, rs1 << 14 | (bits9 & 0x1FF) << 5 | rs2) + +#define Format_3_9(op1, cc1, cc0, op3, rs1, bits9, rs2) \ + Format_3(op1, (cc1 & 0x1) << 1 | (cc0 & 0x1), op3, rs1 << 14 | (bits9 & 0x1FF) << 5 | rs2) + +#define Format_4_1(rd, op3, rs1, cc1, cc0, rs2) \ + Format_3(2, rd, op3, rs1 << 14 | (cc1 & 0x1) << 12 | (cc0 & 0x1) << 11 | rs2) + +#define Format_4_1I(rd, op3, rs1, cc1, cc0, simm11) \ + Format_3(2, rd, op3, rs1 << 14 | (cc1 & 0x1) << 12 | 1 << 13 |(cc0 & 0x1) << 11 | (simm11 & 0x7FF)) + +#define Format_4_2(rd, op3, cc2, cond, cc1, cc0, rs2) \ + Format_3(2, rd, op3, (cc2 & 0x1) << 18 | (cond & 0xF) << 14 | (cc1 & 0x1) << 12 | (cc0 & 0x1) << 11 | rs2) + +#define Format_4_2I(rd, op3, cc2, cond, cc1, cc0, simm11) \ + Format_3(2, rd, op3, (cc2 & 0x1) << 18 | (cond & 0xF) << 14 | 1 << 13 | (cc1 & 0x1) << 12 | (cc0 & 0x1) << 11 | (simm11 & 0x7FF)) + +#define Format_4_3(rd, op3, rs1, cc1, cc0, swap_trap) \ + Format_3(2, rd, op3, rs1 << 14 | 1 << 13 | (cc1 & 0x1) << 12 | (cc0 & 0x1) << 11 | (swap_trap & 0x7F)) + +#define Format_4_4(rd, op3, rs1, rcond, opf_low, rs2) \ + Format_3(2, rd, op3, rs1 << 14 | (rcond & 0x7) << 10 | (opf_low & 0x1F) << 5 | rs2) + +#define Format_4_5(rd, op3, cond, opf_cc, opf_low, rs2) \ + Format_3(2, rd, op3, (cond & 0xF) << 14 | (opf_cc & 0x7) << 11 | (opf_low & 0x3F) << 5 | rs2) + +#define ADDCC(rs1, rs2, rd) \ + do { \ + Format_3_1(2, rd, 0x10, rs1, 0, rs2); \ + asm_output("addcc %s, %s, %s", gpn(rs1), gpn(rs2), gpn(rd)); \ + } while (0) + +#define ADD(rs1, rs2, rd) \ + do { \ + Format_3_1(2, rd, 0, rs1, 0, rs2); \ + asm_output("add %s, %s, %s", gpn(rs1), gpn(rs2), gpn(rd)); \ + } while (0) + +#define AND(rs1, rs2, rd) \ + do { \ + Format_3_1(2, rd, 0x1, rs1, 0, rs2); \ + asm_output("and %s, %s, %s", gpn(rs1), gpn(rs2), gpn(rd)); \ + } while (0) + +#define BA(a, dsp22) \ + do { \ + Format_2_2(a, 0x8, 0x2, dsp22); \ + asm_output("ba %p", _nIns + dsp22 - 1); \ + } while (0) + +#define BE(a, dsp22) \ + do { \ + Format_2_2(a, 0x1, 0x2, dsp22); \ + asm_output("be %p", _nIns + dsp22 - 1); \ + } while (0) + +#define BG(a, dsp22) \ + do { \ + Format_2_2(a, 0xA, 0x2, dsp22); \ + asm_output("bg %p", _nIns + dsp22 - 1); \ + } while (0) + +#define BGU(a, dsp22) \ + do { \ + Format_2_2(a, 0xC, 0x2, dsp22); \ + asm_output("bgu %p", _nIns + dsp22 - 1); \ + } while (0) + +#define BGE(a, dsp22) \ + do { \ + Format_2_2(a, 0xB, 0x2, dsp22); \ + asm_output("bge %p", _nIns + dsp22 - 1); \ + } while (0) + +#define BL(a, dsp22) \ + do { \ + Format_2_2(a, 0x3, 0x2, dsp22); \ + asm_output("bl %p", _nIns + dsp22 - 1); \ + } while (0) + +#define BLE(a, dsp22) \ + do { \ + Format_2_2(a, 0x2, 0x2, dsp22); \ + asm_output("ble %p", _nIns + dsp22 - 1); \ + } while (0) + +#define BLEU(a, dsp22) \ + do { \ + Format_2_2(a, 0x4, 0x2, dsp22); \ + asm_output("bleu %p", _nIns + dsp22 - 1); \ + } while (0) + +#define BCC(a, dsp22) \ + do { \ + Format_2_2(a, 0xd, 0x2, dsp22); \ + asm_output("bcc %p", _nIns + dsp22 - 1); \ + } while (0) + +#define BCS(a, dsp22) \ + do { \ + Format_2_2(a, 0x5, 0x2, dsp22); \ + asm_output("bcs %p", _nIns + dsp22 - 1); \ + } while (0) + +#define BVC(a, dsp22) \ + do { \ + Format_2_2(a, 0xf, 0x2, dsp22); \ + asm_output("bvc %p", _nIns + dsp22 - 1); \ + } while (0) + +#define BVS(a, dsp22) \ + do { \ + Format_2_2(a, 0x7, 0x2, dsp22); \ + asm_output("bvc %p", _nIns + dsp22 - 1); \ + } while (0) + +#define BNE(a, dsp22) \ + do { \ + Format_2_2(a, 0x9, 0x2, dsp22); \ + asm_output("bne %p", _nIns + dsp22 - 1); \ + } while (0) + +#define FABSS(rs2, rd) \ + do { \ + Format_3_8(2, rd, 0x34, 0, 0x9, rs2); \ + asm_output("fabs %s, %s", gpn(rs2+32), gpn(rd+32)); \ + } while (0) + +#define FADDD(rs1, rs2, rd) \ + do { \ + Format_3_8(2, rd, 0x34, rs1, 0x42, rs2); \ + asm_output("faddd %s, %s, %s", gpn(rs1+32), gpn(rs2+32), gpn(rd+32)); \ + } while (0) + +#define FBE(a, dsp22) \ + do { \ + Format_2_2(a, 0x9, 0x6, dsp22); \ + asm_output("fbe %p", _nIns + dsp22 - 1); \ + } while(0) + +#define FBNE(a, dsp22) \ + do { \ + Format_2_2(a, 0x1, 0x6, dsp22); \ + asm_output("fbne %p", _nIns + dsp22 - 1); \ + } while(0) + +#define FBUE(a, dsp22) \ + do { \ + Format_2_2(a, 0xA, 0x6, dsp22); \ + asm_output("fbue %p", _nIns + dsp22 - 1); \ + } while(0) + +#define FBG(a, dsp22) \ + do { \ + Format_2_2(a, 0x6, 0x6, dsp22); \ + asm_output("fng %p", _nIns + dsp22 - 1); \ + } while(0) + +#define FBUG(a, dsp22) \ + do { \ + Format_2_2(a, 0x5, 0x6, dsp22); \ + asm_output("fbug %p", _nIns + dsp22 - 1); \ + } while(0) + +#define FBGE(a, dsp22) \ + do { \ + Format_2_2(a, 0xB, 0x6, dsp22); \ + asm_output("fbge %p", _nIns + dsp22 - 1); \ + } while(0) + +#define FBUGE(a, dsp22) \ + do { \ + Format_2_2(a, 0xC, 0x6, dsp22); \ + asm_output("fbuge %p", _nIns + dsp22 - 1); \ + } while(0) + +#define FBL(a, dsp22) \ + do { \ + Format_2_2(a, 0x4, 0x6, dsp22); \ + asm_output("fbl %p", _nIns + dsp22 - 1); \ + } while(0) + +#define FBUL(a, dsp22) \ + do { \ + Format_2_2(a, 0x3, 0x6, dsp22); \ + asm_output("fbl %p", _nIns + dsp22 - 1); \ + } while(0) + +#define FBLE(a, dsp22) \ + do { \ + Format_2_2(a, 0xD, 0x6, dsp22); \ + asm_output("fble %p", _nIns + dsp22 - 1); \ + } while(0) + +#define FBULE(a, dsp22) \ + do { \ + Format_2_2(a, 0xE, 0x6, dsp22); \ + asm_output("fbule %p", _nIns + dsp22 - 1); \ + } while(0) + +#define FCMPD(rs1, rs2) \ + do { \ + Format_3_9(2, 0, 0, 0x35, rs1, 0x52, rs2); \ + asm_output("fcmpd %s, %s", gpn(rs1+32), gpn(rs2+32)); \ + } while (0) + +#define FSUBD(rs1, rs2, rd) \ + do { \ + Format_3_8(2, rd, 0x34, rs1, 0x46, rs2); \ + asm_output("fsubd %s, %s, %s", gpn(rs1+32), gpn(rs2+32), gpn(rd+32)); \ + } while (0) + +#define FMULD(rs1, rs2, rd) \ + do { \ + Format_3_8(2, rd, 0x34, rs1, 0x4a, rs2); \ + asm_output("fmuld %s, %s, %s", gpn(rs1+32), gpn(rs2+32), gpn(rd+32)); \ + } while (0) + +#define FDIVD(rs1, rs2, rd) \ + do { \ + Format_3_8(2, rd, 0x34, rs1, 0x4e, rs2); \ + asm_output("fdivd %s, %s, %s", gpn(rs1+32), gpn(rs2+32), gpn(rd+32)); \ + } while (0) + +#define FMOVD(rs2, rd) \ + do { \ + Format_3_8(2, rd, 0x34, 0, 0x2, rs2); \ + asm_output("fmovd %s, %s", gpn(rs2+32), gpn(rd+32)); \ + } while (0) + +#define FNEGD(rs2, rd) \ + do { \ + Format_3_8(2, rd, 0x34, 0, 0x6, rs2); \ + asm_output("fnegd %s, %s", gpn(rs2+32), gpn(rd+32)); \ + } while (0) + +#define FITOD(rs2, rd) \ + do { \ + Format_3_8(2, rd, 0x34, 0, 0xc8, rs2); \ + asm_output("fitod %s, %s", gpn(rs2+32), gpn(rd+32)); \ + } while (0) + +#define JMPL(rs1, rs2, rd) \ + do { \ + Format_3_1(2, rd, 0x38, rs1, 0, rs2); \ + asm_output("jmpl [%s + %s]", gpn(rs1), gpn(rs2)); \ + } while (0) + +#define JMPLI(rs1, simm13, rd) \ + do { \ + Format_3_1I(2, rd, 0x38, rs1, simm13); \ + asm_output("jmpl [%s + %d]", gpn(rs1), simm13); \ + } while (0) + +#define LDF(rs1, rs2, rd) \ + do { \ + Format_3_1(3, rd, 0x20, rs1, 0, rs2); \ + asm_output("ld [%s + %s], %s", gpn(rs1), gpn(rs2), gpn(rd+32)); \ + } while (0) + +#define LDFI(rs1, simm13, rd) \ + do { \ + Format_3_1I(3, rd, 0x20, rs1, simm13); \ + asm_output("ld [%s + %d], %s", gpn(rs1), simm13, gpn(rd+32)); \ + } while (0) + +#define LDUB(rs1, rs2, rd) \ + do { \ + Format_3_1(3, rd, 0x1, rs1, 0, rs2); \ + asm_output("ld [%s + %s], %s", gpn(rs1), gpn(rs2), gpn(rd)); \ + } while (0) + +#define LDUBI(rs1, simm13, rd) \ + do { \ + Format_3_1I(3, rd, 0x1, rs1, simm13); \ + asm_output("ld [%s + %d], %s", gpn(rs1), simm13, gpn(rd)); \ + } while (0) + +#define LDUH(rs1, rs2, rd) \ + do { \ + Format_3_1(3, rd, 0x2, rs1, 0, rs2); \ + asm_output("ld [%s + %s], %s", gpn(rs1), gpn(rs2), gpn(rd)); \ + } while (0) + +#define LDUHI(rs1, simm13, rd) \ + do { \ + Format_3_1I(3, rd, 0x2, rs1, simm13); \ + asm_output("ld [%s + %d], %s", gpn(rs1), simm13, gpn(rd)); \ + } while (0) + +#define LDSW(rs1, rs2, rd) \ + do { \ + Format_3_1(3, rd, 0x8, rs1, 0, rs2); \ + asm_output("ld [%s + %s], %s", gpn(rs1), gpn(rs2), gpn(rd)); \ + } while (0) + +#define LDSWI(rs1, simm13, rd) \ + do { \ + Format_3_1I(3, rd, 0x8, rs1, simm13); \ + asm_output("ld [%s + %d], %s", gpn(rs1), simm13, gpn(rd)); \ + } while (0) + +#define MOVE(rs, cc2, cc1, cc0, rd) \ + do { \ + Format_4_2(rd, 0x2c, cc2, 1, cc1, cc0, rs); \ + asm_output("move %s, %s", gpn(rs), gpn(rd)); \ + } while (0) + +#define MOVNE(rs, cc2, cc1, cc0, rd) \ + do { \ + Format_4_2(rd, 0x2c, cc2, 9, cc1, cc0, rs); \ + asm_output("movne %s, %s", gpn(rs), gpn(rd)); \ + } while (0) + +#define MOVL(rs, cc2, cc1, cc0, rd) \ + do { \ + Format_4_2(rd, 0x2c, cc2, 3, cc1, cc0, rs); \ + asm_output("movl %s, %s", gpn(rs), gpn(rd)); \ + } while (0) + +#define MOVLE(rs, cc2, cc1, cc0, rd) \ + do { \ + Format_4_2(rd, 0x2c, cc2, 2, cc1, cc0, rs); \ + asm_output("movle %s, %s", gpn(rs), gpn(rd)); \ + } while (0) + +#define MOVG(rs, cc2, cc1, cc0, rd) \ + do { \ + Format_4_2(rd, 0x2c, cc2, 0xa, cc1, cc0, rs); \ + asm_output("movg %s, %s", gpn(rs), gpn(rd)); \ + } while (0) + +#define MOVGE(rs, cc2, cc1, cc0, rd) \ + do { \ + Format_4_2(rd, 0x2c, cc2, 0xb, cc1, cc0, rs); \ + asm_output("movge %s, %s", gpn(rs), gpn(rd)); \ + } while (0) + +#define MOVCS(rs, cc2, cc1, cc0, rd) \ + do { \ + Format_4_2(rd, 0x2c, cc2, 5, cc1, cc0, rs); \ + asm_output("movcs %s, %s", gpn(rs), gpn(rd)); \ + } while (0) + +#define MOVLEU(rs, cc2, cc1, cc0, rd) \ + do { \ + Format_4_2(rd, 0x2c, cc2, 4, cc1, cc0, rs); \ + asm_output("movleu %s, %s", gpn(rs), gpn(rd)); \ + } while (0) + +#define MOVGU(rs, cc2, cc1, cc0, rd) \ + do { \ + Format_4_2(rd, 0x2c, cc2, 0xc, cc1, cc0, rs); \ + asm_output("movgu %s, %s", gpn(rs), gpn(rd)); \ + } while (0) + +#define MOVCC(rs, cc2, cc1, cc0, rd) \ + do { \ + Format_4_2(rd, 0x2c, cc2, 0xd, cc1, cc0, rs); \ + asm_output("movcc %s, %s", gpn(rs), gpn(rd)); \ + } while (0) + +#define MOVVC(rs, cc2, cc1, cc0, rd) \ + do { \ + Format_4_2(rd, 0x2c, cc2, 0xf, cc1, cc0, rs); \ + asm_output("movvc %s, %s", gpn(rs), gpn(rd)); \ + } while (0) + +#define MOVEI(simm11, cc2, cc1, cc0, rd) \ + do { \ + Format_4_2I(rd, 0x2c, cc2, 1, cc1, cc0, simm11); \ + asm_output("move %d, %s", simm11, gpn(rd)); \ + } while (0) + +#define MOVFEI(simm11, cc2, cc1, cc0, rd) \ + do { \ + Format_4_2I(rd, 0x2c, cc2, 9, cc1, cc0, simm11); \ + asm_output("move %d, %s", simm11, gpn(rd)); \ + } while (0) + +#define MOVNEI(simm11, cc2, cc1, cc0, rd) \ + do { \ + Format_4_2I(rd, 0x2c, cc2, 9, cc1, cc0, simm11); \ + asm_output("move %d, %s", simm11, gpn(rd)); \ + } while (0) + +#define MOVLI(simm11, cc2, cc1, cc0, rd) \ + do { \ + Format_4_2I(rd, 0x2c, cc2, 3, cc1, cc0, simm11); \ + asm_output("move %d, %s", simm11, gpn(rd)); \ + } while (0) + +#define MOVFLI(simm11, cc2, cc1, cc0, rd) \ + do { \ + Format_4_2I(rd, 0x2c, cc2, 4, cc1, cc0, simm11); \ + asm_output("move %d, %s", simm11, gpn(rd)); \ + } while (0) + +#define MOVLEI(simm11, cc2, cc1, cc0, rd) \ + do { \ + Format_4_2I(rd, 0x2c, cc2, 2, cc1, cc0, simm11); \ + asm_output("movle %d, %s", simm11, gpn(rd)); \ + } while (0) + +#define MOVFLEI(simm11, cc2, cc1, cc0, rd) \ + do { \ + Format_4_2I(rd, 0x2c, cc2, 0xd, cc1, cc0, simm11); \ + asm_output("movle %d, %s", simm11, gpn(rd)); \ + } while (0) + +#define MOVGI(simm11, cc2, cc1, cc0, rd) \ + do { \ + Format_4_2I(rd, 0x2c, cc2, 0xa, cc1, cc0, simm11); \ + asm_output("movg %d, %s", simm11, gpn(rd)); \ + } while (0) + +#define MOVFGI(simm11, cc2, cc1, cc0, rd) \ + do { \ + Format_4_2I(rd, 0x2c, cc2, 6, cc1, cc0, simm11); \ + asm_output("movg %d, %s", simm11, gpn(rd)); \ + } while (0) + +#define MOVGEI(simm11, cc2, cc1, cc0, rd) \ + do { \ + Format_4_2I(rd, 0x2c, cc2, 0xb, cc1, cc0, simm11); \ + asm_output("movge %d, %s", simm11, gpn(rd)); \ + } while (0) + +#define MOVFGEI(simm11, cc2, cc1, cc0, rd) \ + do { \ + Format_4_2I(rd, 0x2c, cc2, 0xb, cc1, cc0, simm11); \ + asm_output("movge %d, %s", simm11, gpn(rd)); \ + } while (0) + +#define MOVLEUI(simm11, cc2, cc1, cc0, rd) \ + do { \ + Format_4_2I(rd, 0x2c, cc2, 4, cc1, cc0, simm11); \ + asm_output("movleu %d, %s", simm11, gpn(rd)); \ + } while (0) + +#define MOVGUI(simm11, cc2, cc1, cc0, rd) \ + do { \ + Format_4_2I(rd, 0x2c, cc2, 0xc, cc1, cc0, simm11); \ + asm_output("movgu %d, %s", simm11, gpn(rd)); \ + } while (0) + +#define MOVCCI(simm11, cc2, cc1, cc0, rd) \ + do { \ + Format_4_2I(rd, 0x2c, cc2, 0xd, cc1, cc0, simm11); \ + asm_output("movcc %d, %s", simm11, gpn(rd)); \ + } while (0) + +#define MOVVSI(simm11, cc2, cc1, cc0, rd) \ + do { \ + Format_4_2I(rd, 0x2c, cc2, 7, cc1, cc0, simm11); \ + asm_output("movvs %d, %s", simm11, gpn(rd)); \ + } while (0) + +#define MULX(rs1, rs2, rd) \ + do { \ + Format_3_1(2, rd, 0x9, rs1, 0, rs2); \ + asm_output("mul %s, %s, %s", gpn(rs1), gpn(rs2), gpn(rd)); \ + } while (0) + +#define NOP() \ + do { \ + Format_2_1(0, 0x4, 0); \ + asm_output("nop"); \ + } while (0) + +#define ORI(rs1, simm13, rd) \ + do { \ + Format_3_1I(2, rd, 0x2, rs1, simm13); \ + asm_output("or %s, %d, %s", gpn(rs1), simm13, gpn(rd)); \ + } while (0) + +#define OR(rs1, rs2, rd) \ + do { \ + Format_3_1(2, rd, 0x2, rs1, 0, rs2); \ + asm_output("or %s, %s, %s", gpn(rs1), gpn(rs2), gpn(rd)); \ + } while (0) + +#define ORN(rs1, rs2, rd) \ + do { \ + Format_3_1(2, rd, 0x6, rs1, 0, rs2); \ + asm_output("orn %s, %s, %s", gpn(rs1), gpn(rs2), gpn(rd)); \ + } while (0) + +#define ANDCC(rs1, rs2, rd) \ + do { \ + Format_3_1(2, rd, 0x11, rs1, 0, rs2); \ + asm_output("andcc %s, %s, %s", gpn(rs1), gpn(rs2), gpn(rd)); \ + } while (0) + +#define RESTORE(rs1, rs2, rd) \ + do { \ + Format_3_1(2, rd, 0x3D, rs1, 0, rs2); \ + asm_output("restore"); \ + } while (0) + +#define SAVEI(rs1, simm13, rd) \ + do { \ + Format_3_1I(2, rd, 0x3C, rs1, simm13); \ + asm_output("save %s, %d, %s", gpn(rs1), simm13, gpn(rd)); \ + } while (0) + +#define SAVE(rs1, rs2, rd) \ + do { \ + Format_3_1(2, rd, 0x3C, rs1, 0, rs2); \ + asm_output("save %s, %s, %s", gpn(rs1), gpn(rs2), gpn(rd)); \ + } while (0) + +#define SETHI(imm22, rd) \ + do { \ + Format_2_1(rd, 0x4, (imm22 >> 10)); \ + asm_output("sethi %p, %s", imm22, gpn(rd)); \ + } while (0) + +#define SLL(rs1, rs2, rd) \ + do { \ + Format_3_5(2, rd, 0x25, rs1, 0, rs2); \ + asm_output("sll %s, %s, %s", gpn(rs1), gpn(rs2), gpn(rd)); \ + } while (0) + +#define SRA(rs1, rs2, rd) \ + do { \ + Format_3_5(2, rd, 0x27, rs1, 0, rs2); \ + asm_output("sra %s, %s, %s", gpn(rs1), gpn(rs2), gpn(rd)); \ + } while (0) + +#define SRL(rs1, rs2, rd) \ + do { \ + Format_3_5(2, rd, 0x26, rs1, 0, rs2); \ + asm_output("srl %s, %s, %s", gpn(rs1), gpn(rs2), gpn(rd)); \ + } while (0) + +#define STF(rd, rs1, rs2) \ + do { \ + Format_3_1(3, rd, 0x24, rs1, 0, rs2); \ + asm_output("st %s, [%s + %s]", gpn(rd+32), gpn(rs1), gpn(rs2)); \ + } while (0) + +#define STFI(rd, simm13, rs1) \ + do { \ + Format_3_1I(3, rd, 0x24, rs1, simm13); \ + asm_output("st %s, [%s + %d]", gpn(rd+32), gpn(rs1), simm13); \ + } while (0) + +#define STW(rd, rs2, rs1) \ + do { \ + Format_3_1(3, rd, 0x4, rs1, 0, rs2); \ + asm_output("st %s, [%s + %s]", gpn(rd), gpn(rs1), gpn(rs2)); \ + } while (0) + +#define STWI(rd, simm13, rs1) \ + do { \ + Format_3_1I(3, rd, 0x4, rs1, simm13); \ + asm_output("st %s, [%s + %d]", gpn(rd), gpn(rs1), simm13); \ + } while (0) + +#define SUBCC(rs1, rs2, rd) \ + do { \ + Format_3_1(2, rd, 0x14, rs1, 0, rs2); \ + asm_output("subcc %s, %s, %s", gpn(rs1), gpn(rs2), gpn(rd)); \ + } while (0) + +#define SUB(rs1, rs2, rd) \ + do { \ + Format_3_1(2, rd, 0x4, rs1, 0, rs2); \ + asm_output("sub %s, %s, %s", gpn(rs1), gpn(rs2), gpn(rd)); \ + } while (0) + +#define SUBI(rs1, simm13, rd) \ + do { \ + Format_3_1I(2, rd, 0x4, rs1, simm13); \ + asm_output("sub %s, %d, %s", gpn(rs1), simm13, gpn(rd)); \ + } while (0) + +#define XOR(rs1, rs2, rd) \ + do { \ + Format_3_1(2, rd, 0x3, rs1, 0, rs2); \ + asm_output("xor %s, %s, %s", gpn(rs1), gpn(rs2), gpn(rd)); \ + } while (0) + + // Returns true if imm below 13-bit unsigned immediate) +#define isIMM13(imm) \ + (imm) <= 4095 && (imm) >= -4096 + +#define isIMM19(imm) \ + (imm) <= 0x3ffff && (imm) >= -0x40000 + +#define isIMM22(imm) \ + (imm) <= 0x1fffff && (imm) >= -0x200000 + +#define SET32(imm32, rd) \ + if(isIMM13(imm32)) { \ + ORI(G0, imm32, rd); \ + } else { \ + ORI(rd, imm32 & 0x3FF, rd); \ + SETHI(imm32, rd); \ + } + +#define STDF32(rd, imm32, rs1) \ + if(isIMM13(imm32+4)) { \ + STFI(rd+1, imm32+4, rs1); \ + STFI(rd, imm32, rs1); \ + } else { \ + STF(rd+1, L0, rs1); \ + SET32(imm32+4, L0); \ + STF(rd, L0, rs1); \ + SET32(imm32, L0); \ + } + +#define LDDF32(rs1, imm32, rd) \ + if(isIMM13(imm32+4)) { \ + LDFI(rs1, imm32+4, rd+1); \ + LDFI(rs1, imm32, rd); \ + } else { \ + LDF(rs1, L0, rd+1); \ + SET32(imm32+4, L0); \ + LDF(rs1, L0, rd); \ + SET32(imm32, L0); \ + } + +#define STW32(rd, imm32, rs1) \ + if(isIMM13(imm32)) { \ + STWI(rd, imm32, rs1); \ + } else { \ + STW(rd, L0, rs1); \ + SET32(imm32, L0); \ + } + +#define LDUB32(rs1, imm32, rd) \ + if(isIMM13(imm32)) { \ + LDUBI(rs1, imm32, rd); \ + } else { \ + LDUB(rs1, L0, rd); \ + SET32(imm32, L0); \ + } + +#define LDUH32(rs1, imm32, rd) \ + if(isIMM13(imm32)) { \ + LDUHI(rs1, imm32, rd); \ + } else { \ + LDUH(rs1, L0, rd); \ + SET32(imm32, L0); \ + } + +#define LDSW32(rs1, imm32, rd) \ + if(isIMM13(imm32)) { \ + LDSWI(rs1, imm32, rd); \ + } else { \ + LDSW(rs1, L0, rd); \ + SET32(imm32, L0); \ + } + + + // general Assemble +#define JMP_long_nocheck(t) do { NOP(); JMPL(G0, G2, G0); ORI(G2, t & 0x3FF, G2); SETHI(t, G2); } while(0) +#define JMP_long(t) do { underrunProtect(16); JMP_long_nocheck(t); } while(0) +#define JMP_long_placeholder() JMP_long(0) + +#define JCC(t, tt) \ + underrunProtect(32); \ + tt = ((intptr_t)t - (intptr_t)_nIns + 8) >> 2; \ + if( !(isIMM22((int32_t)tt)) ) { \ + NOP(); \ + JMPL(G0, G2, G0); \ + SET32((intptr_t)t, G2); \ + NOP(); \ + BA(0, 5); \ + tt = 4; \ + } \ + NOP() + +#define JMP(t) do { \ + if (!t) { \ + JMP_long_placeholder(); \ + } else { \ + intptr_t tt; \ + JCC(t, tt); \ + BA(0, tt); \ + } \ + } while(0) + +#define MR(d, s) do { underrunProtect(4); ORI(s, 0, d); } while(0) + + } +#endif // __nanojit_NativeSparc__ diff --git a/ape-server/deps/js/src/nanojit/NativeX64.cpp b/ape-server/deps/js/src/nanojit/NativeX64.cpp new file mode 100755 index 0000000..40c4765 --- /dev/null +++ b/ape-server/deps/js/src/nanojit/NativeX64.cpp @@ -0,0 +1,1775 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is [Open Source Virtual Machine]. + * + * The Initial Developer of the Original Code is + * Adobe System Incorporated. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adobe AS3 Team + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nanojit.h" + +// uncomment this to enable _vprof/_nvprof macros +//#define DOPROF +#include "../vprof/vprof.h" + +#if defined FEATURE_NANOJIT && defined NANOJIT_X64 + +/* +completion +- 64bit branch offsets +- finish cmov/qcmov with other conditions +- validate asm_cond with other conditions + +better code +- put R12 back in play as a base register +- no-disp addr modes (except RBP/R13) +- disp64 branch/call +- spill gp values to xmm registers? +- prefer xmm registers for copies since gprs are in higher demand? +- stack arg doubles +- stack based LIR_param + +tracing +- asm_qjoin +- asm_qhi +- nFragExit + +*/ + +namespace nanojit +{ + const Register Assembler::retRegs[] = { RAX }; +#ifdef _WIN64 + const Register Assembler::argRegs[] = { RCX, RDX, R8, R9 }; + const Register Assembler::savedRegs[] = { RBX, RSI, RDI, R12, R13, R14, R15 }; +#else + const Register Assembler::argRegs[] = { RDI, RSI, RDX, RCX, R8, R9 }; + const Register Assembler::savedRegs[] = { RBX, R12, R13, R14, R15 }; +#endif + + const char *regNames[] = { + "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", + "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15" + }; + + const char *gpRegNames32[] = { + "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", + "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d" + }; + + const char *gpRegNames8[] = { + "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil", + "r8l", "r9l", "r10l", "r11l", "r12l", "r13l", "r14l", "r15l" + }; + + const char *gpRegNames8hi[] = { + "ah", "ch", "dh", "bh" + }; + +#ifdef _DEBUG + #define TODO(x) todo(#x) + static void todo(const char *s) { + verbose_only( avmplus::AvmLog("%s",s); ) + NanoAssertMsgf(false, "%s", s); + } +#else + #define TODO(x) +#endif + + // MODRM and SIB restrictions: + // memory access modes != 11 require SIB if base&7 == 4 (RSP or R12) + // mode 00 with base == x101 means RIP+disp32 (RBP or R13), use mode 01 disp8=0 instead + // mode 01 or 11 with base = x101 means disp32 + EBP or R13, not RIP relative + // base == x100 means SIB byte is present, so using ESP|R12 as base requires SIB + // rex prefix required to use RSP-R15 as 8bit registers in mod/rm8 modes. + + // take R12 out of play as a base register because using ESP or R12 as base requires the SIB byte + const RegisterMask BaseRegs = GpRegs & ~rmask(R12); + + static inline int oplen(uint64_t op) { + return op & 255; + } + + // encode 2-register rex prefix. dropped if none of its bits are set. + static inline uint64_t rexrb(uint64_t op, Register r, Register b) { + int shift = 64 - 8*oplen(op); + uint64_t rex = ((op >> shift) & 255) | ((r&8)>>1) | ((b&8)>>3); + return rex != 0x40 ? op | rex << shift : op - 1; + } + + // encode 3-register rex prefix. dropped if none of its bits are set. + static inline uint64_t rexrxb(uint64_t op, Register r, Register x, Register b) { + int shift = 64 - 8*oplen(op); + uint64_t rex = ((op >> shift) & 255) | ((r&8)>>1) | ((x&8)>>2) | ((b&8)>>3); + return rex != 0x40 ? op | rex << shift : op - 1; + } + + // encode 2-register rex prefix. dropped if none of its bits are set, but + // keep REX if b >= rsp, to allow uniform use of all 16 8bit registers + static inline uint64_t rexrb8(uint64_t op, Register r, Register b) { + int shift = 64 - 8*oplen(op); + uint64_t rex = ((op >> shift) & 255) | ((r&8)>>1) | ((b&8)>>3); + return ((rex | (b & ~3)) != 0x40) ? (op | (rex << shift)) : op - 1; + } + + // encode 2-register rex prefix that follows a manditory prefix (66,F2,F3) + // [prefix][rex][opcode] + static inline uint64_t rexprb(uint64_t op, Register r, Register b) { + int shift = 64 - 8*oplen(op) + 8; + uint64_t rex = ((op >> shift) & 255) | ((r&8)>>1) | ((b&8)>>3); + // to drop rex, we replace rex with manditory prefix, and decrement length + return rex != 0x40 ? op | rex << shift : + ((op & ~(255LL<>(shift-8)&255) << shift) - 1; + } + + // [rex][opcode][mod-rr] + static inline uint64_t mod_rr(uint64_t op, Register r, Register b) { + return op | uint64_t((r&7)<<3 | (b&7))<<56; + } + + // [rex][opcode][modrm=r][sib=xb] + static inline uint64_t mod_rxb(uint64_t op, Register r, Register x, Register b) { + return op | /*modrm*/uint64_t((r&7)<<3)<<48 | /*sib*/uint64_t((x&7)<<3|(b&7))<<56; + } + + static inline uint64_t mod_disp32(uint64_t op, Register r, Register b, int32_t d) { + NanoAssert(IsGpReg(r) && IsGpReg(b)); + NanoAssert((b & 7) != 4); // using RSP or R12 as base requires SIB + if (isS8(d)) { + // op is: 0x[disp32=0][mod=2:r:b][op][rex][len] + NanoAssert((((op>>24)&255)>>6) == 2); // disp32 mode + int len = oplen(op); + op = (op & ~0xff000000LL) | (0x40 | (r&7)<<3 | (b&7))<<24; // replace mod + return op<<24 | int64_t(d)<<56 | (len-3); // shrink disp, add disp8 + } else { + // op is: 0x[disp32][mod][op][rex][len] + return op | int64_t(d)<<32 | uint64_t((r&7)<<3 | (b&7))<<24; + } + } + + // All the emit() functions should only be called from within codegen + // functions PUSHR(), SHR(), etc. + + void Assembler::emit(uint64_t op) { + int len = oplen(op); + // we will only move nIns by -len bytes, but we write 8 + // bytes. so need to protect 8 so we dont stomp the page + // header or the end of the preceding page (might segf) + underrunProtect(8); + ((int64_t*)_nIns)[-1] = op; + _nIns -= len; // move pointer by length encoded in opcode + _nvprof("x64-bytes", len); + } + + void Assembler::emit8(uint64_t op, int64_t v) { + NanoAssert(isS8(v)); + emit(op | uint64_t(v)<<56); + } + + void Assembler::emit_target8(size_t underrun, uint64_t op, NIns* target) { + underrunProtect(underrun); // must do this before calculating offset + // Nb: see emit_target32() for why we use _nIns here. + int64_t offset = target - _nIns; + NanoAssert(isS8(offset)); + emit(op | uint64_t(offset)<<56); + } + + void Assembler::emit_target32(size_t underrun, uint64_t op, NIns* target) { + underrunProtect(underrun); // must do this before calculating offset + // Nb: at this point in time, _nIns points to the most recently + // written instruction, ie. the jump's successor. So why do we use it + // to compute the offset, rather than the jump's address? Because in + // x86/x64-64 the offset in a relative jump is not from the jmp itself + // but from the following instruction. Eg. 'jmp $0' will jump to the + // next instruction. + int64_t offset = target ? target - _nIns : 0; + NanoAssert(isS32(offset)); + emit(op | uint64_t(uint32_t(offset))<<32); + } + + // 3-register modrm32+sib form + void Assembler::emitrxb(uint64_t op, Register r, Register x, Register b) { + emit(rexrxb(mod_rxb(op, r, x, b), r, x, b)); + } + + // 2-register modrm32 form + void Assembler::emitrr(uint64_t op, Register r, Register b) { + emit(rexrb(mod_rr(op, r, b), r, b)); + } + + // 2-register modrm8 form (8 bit operand size) + void Assembler::emitrr8(uint64_t op, Register r, Register b) { + emit(rexrb8(mod_rr(op, r, b), r, b)); + } + + // same as emitrr, but with a prefix byte + void Assembler::emitprr(uint64_t op, Register r, Register b) { + emit(rexprb(mod_rr(op, r, b), r, b)); + } + + // disp32 modrm form, when the disp fits in the instruction (opcode is 1-3 bytes) + void Assembler::emitrm(uint64_t op, Register r, int32_t d, Register b) { + emit(rexrb(mod_disp32(op, r, b, d), r, b)); + } + + // disp32 modrm form when the disp must be written separately (opcode is 4+ bytes) + uint64_t Assembler::emit_disp32(uint64_t op, int32_t d) { + if (isS8(d)) { + NanoAssert(((op>>56)&0xC0) == 0x80); // make sure mod bits == 2 == disp32 mode + underrunProtect(1+8); + *(--_nIns) = (NIns) d; + _nvprof("x64-bytes", 1); + op ^= 0xC000000000000000LL; // change mod bits to 1 == disp8 mode + } else { + underrunProtect(4+8); // room for displ plus fullsize op + *((int32_t*)(_nIns -= 4)) = d; + _nvprof("x64-bytes", 4); + } + return op; + } + + // disp32 modrm form when the disp must be written separately (opcode is 4+ bytes) + void Assembler::emitrm_wide(uint64_t op, Register r, int32_t d, Register b) { + op = emit_disp32(op, d); + emitrr(op, r, b); + } + + // disp32 modrm form when the disp must be written separately (opcode is 4+ bytes) + // p = prefix -- opcode must have a 66, F2, or F3 prefix + void Assembler::emitprm(uint64_t op, Register r, int32_t d, Register b) { + op = emit_disp32(op, d); + emitprr(op, r, b); + } + + void Assembler::emitrr_imm(uint64_t op, Register r, Register b, int32_t imm) { + NanoAssert(IsGpReg(r) && IsGpReg(b)); + underrunProtect(4+8); // room for imm plus fullsize op + *((int32_t*)(_nIns -= 4)) = imm; + _nvprof("x86-bytes", 4); + emitrr(op, r, b); + } + + void Assembler::emitr_imm64(uint64_t op, Register r, uint64_t imm64) { + underrunProtect(8+8); // imm64 + worst case instr len + *((uint64_t*)(_nIns -= 8)) = imm64; + _nvprof("x64-bytes", 8); + emitr(op, r); + } + + void Assembler::emitrxb_imm(uint64_t op, Register r, Register x, Register b, int32_t imm) { + NanoAssert(IsGpReg(r) && IsGpReg(x) && IsGpReg(b)); + underrunProtect(4+8); // room for imm plus fullsize op + *((int32_t*)(_nIns -= 4)) = imm; + _nvprof("x86-bytes", 4); + emitrxb(op, r, x, b); + } + + // op = [rex][opcode][modrm][imm8] + void Assembler::emitr_imm8(uint64_t op, Register b, int32_t imm8) { + NanoAssert(IsGpReg(b) && isS8(imm8)); + op |= uint64_t(imm8)<<56 | uint64_t(b&7)<<48; // modrm is 2nd to last byte + emit(rexrb(op, (Register)0, b)); + } + + void Assembler::emitxm_abs(uint64_t op, Register r, int32_t addr32) + { + underrunProtect(4+8); + *((int32_t*)(_nIns -= 4)) = addr32; + _nvprof("x64-bytes", 4); + op = op | uint64_t((r&7)<<3)<<48; // put rr[0:2] into mod/rm byte + op = rexrb(op, r, (Register)0); // put rr[3] into rex byte + emit(op); + } + + void Assembler::emitxm_rel(uint64_t op, Register r, NIns* addr64) + { + underrunProtect(4+8); + int32_t d = (int32_t)(addr64 - _nIns); + *((int32_t*)(_nIns -= 4)) = d; + _nvprof("x64-bytes", 4); + emitrr(op, r, (Register)0); + } + + // Succeeds if 'target' is within a signed 8-bit offset from the current + // instruction's address. + bool Assembler::isTargetWithinS8(NIns* target) + { + NanoAssert(target); + // First call underrunProtect(). Without it, we might compute the + // difference just before starting a new code chunk. + underrunProtect(8); + return isS8(target - _nIns); + } + + // Like isTargetWithinS8(), but for signed 32-bit offsets. + bool Assembler::isTargetWithinS32(NIns* target) + { + NanoAssert(target); + underrunProtect(8); + return isS32(target - _nIns); + } + +#define RB(r) gpRegNames8[(r)] +#define RBhi(r) gpRegNames8hi[(r)] +#define RL(r) gpRegNames32[(r)] +#define RQ(r) gpn(r) + +#define R Register +#define I int +#define I32 int32_t +#define U64 uint64_t +#define S size_t + + void Assembler::PUSHR(R r) { emitr(X64_pushr,r); asm_output("push %s", RQ(r)); } + void Assembler::POPR( R r) { emitr(X64_popr, r); asm_output("pop %s", RQ(r)); } + void Assembler::NOT( R r) { emitr(X64_not, r); asm_output("notl %s", RL(r)); } + void Assembler::NEG( R r) { emitr(X64_neg, r); asm_output("negl %s", RL(r)); } + void Assembler::IDIV( R r) { emitr(X64_idiv, r); asm_output("idivl edx:eax, %s",RL(r)); } + + void Assembler::SHR( R r) { emitr(X64_shr, r); asm_output("shrl %s, ecx", RL(r)); } + void Assembler::SAR( R r) { emitr(X64_sar, r); asm_output("sarl %s, ecx", RL(r)); } + void Assembler::SHL( R r) { emitr(X64_shl, r); asm_output("shll %s, ecx", RL(r)); } + void Assembler::SHRQ(R r) { emitr(X64_shrq, r); asm_output("shrq %s, ecx", RQ(r)); } + void Assembler::SARQ(R r) { emitr(X64_sarq, r); asm_output("sarq %s, ecx", RQ(r)); } + void Assembler::SHLQ(R r) { emitr(X64_shlq, r); asm_output("shlq %s, ecx", RQ(r)); } + + void Assembler::SHRI( R r, I i) { emit8(rexrb(X64_shri | U64(r&7)<<48, (R)0, r), i); asm_output("shrl %s, %d", RL(r), i); } + void Assembler::SARI( R r, I i) { emit8(rexrb(X64_sari | U64(r&7)<<48, (R)0, r), i); asm_output("sarl %s, %d", RL(r), i); } + void Assembler::SHLI( R r, I i) { emit8(rexrb(X64_shli | U64(r&7)<<48, (R)0, r), i); asm_output("shll %s, %d", RL(r), i); } + void Assembler::SHRQI(R r, I i) { emit8(rexrb(X64_shrqi | U64(r&7)<<48, (R)0, r), i); asm_output("shrq %s, %d", RQ(r), i); } + void Assembler::SARQI(R r, I i) { emit8(rexrb(X64_sarqi | U64(r&7)<<48, (R)0, r), i); asm_output("sarq %s, %d", RQ(r), i); } + void Assembler::SHLQI(R r, I i) { emit8(rexrb(X64_shlqi | U64(r&7)<<48, (R)0, r), i); asm_output("shlq %s, %d", RQ(r), i); } + + void Assembler::SETE( R r) { emitr8(X64_sete, r); asm_output("sete %s", RB(r)); } + void Assembler::SETL( R r) { emitr8(X64_setl, r); asm_output("setl %s", RB(r)); } + void Assembler::SETLE(R r) { emitr8(X64_setle,r); asm_output("setle %s",RB(r)); } + void Assembler::SETG( R r) { emitr8(X64_setg, r); asm_output("setg %s", RB(r)); } + void Assembler::SETGE(R r) { emitr8(X64_setge,r); asm_output("setge %s",RB(r)); } + void Assembler::SETB( R r) { emitr8(X64_setb, r); asm_output("setb %s", RB(r)); } + void Assembler::SETBE(R r) { emitr8(X64_setbe,r); asm_output("setbe %s",RB(r)); } + void Assembler::SETA( R r) { emitr8(X64_seta, r); asm_output("seta %s", RB(r)); } + void Assembler::SETAE(R r) { emitr8(X64_setae,r); asm_output("setae %s",RB(r)); } + void Assembler::SETO( R r) { emitr8(X64_seto, r); asm_output("seto %s", RB(r)); } + + void Assembler::ADDRR(R l, R r) { emitrr(X64_addrr,l,r); asm_output("addl %s, %s", RL(l),RL(r)); } + void Assembler::SUBRR(R l, R r) { emitrr(X64_subrr,l,r); asm_output("subl %s, %s", RL(l),RL(r)); } + void Assembler::ANDRR(R l, R r) { emitrr(X64_andrr,l,r); asm_output("andl %s, %s", RL(l),RL(r)); } + void Assembler::ORLRR(R l, R r) { emitrr(X64_orlrr,l,r); asm_output("orl %s, %s", RL(l),RL(r)); } + void Assembler::XORRR(R l, R r) { emitrr(X64_xorrr,l,r); asm_output("xorl %s, %s", RL(l),RL(r)); } + void Assembler::IMUL( R l, R r) { emitrr(X64_imul, l,r); asm_output("imull %s, %s",RL(l),RL(r)); } + void Assembler::CMPLR(R l, R r) { emitrr(X64_cmplr,l,r); asm_output("cmpl %s, %s", RL(l),RL(r)); } + void Assembler::MOVLR(R l, R r) { emitrr(X64_movlr,l,r); asm_output("movl %s, %s", RL(l),RL(r)); } + + void Assembler::ADDQRR( R l, R r) { emitrr(X64_addqrr, l,r); asm_output("addq %s, %s", RQ(l),RQ(r)); } + void Assembler::SUBQRR( R l, R r) { emitrr(X64_subqrr, l,r); asm_output("subq %s, %s", RQ(l),RQ(r)); } + void Assembler::ANDQRR( R l, R r) { emitrr(X64_andqrr, l,r); asm_output("andq %s, %s", RQ(l),RQ(r)); } + void Assembler::ORQRR( R l, R r) { emitrr(X64_orqrr, l,r); asm_output("orq %s, %s", RQ(l),RQ(r)); } + void Assembler::XORQRR( R l, R r) { emitrr(X64_xorqrr, l,r); asm_output("xorq %s, %s", RQ(l),RQ(r)); } + void Assembler::CMPQR( R l, R r) { emitrr(X64_cmpqr, l,r); asm_output("cmpq %s, %s", RQ(l),RQ(r)); } + void Assembler::MOVQR( R l, R r) { emitrr(X64_movqr, l,r); asm_output("movq %s, %s", RQ(l),RQ(r)); } + void Assembler::MOVAPSR(R l, R r) { emitrr(X64_movapsr,l,r); asm_output("movaps %s, %s",RQ(l),RQ(r)); } + + void Assembler::CMOVNO( R l, R r) { emitrr(X64_cmovno, l,r); asm_output("cmovlno %s, %s", RL(l),RL(r)); } + void Assembler::CMOVNE( R l, R r) { emitrr(X64_cmovne, l,r); asm_output("cmovlne %s, %s", RL(l),RL(r)); } + void Assembler::CMOVNL( R l, R r) { emitrr(X64_cmovnl, l,r); asm_output("cmovlnl %s, %s", RL(l),RL(r)); } + void Assembler::CMOVNLE(R l, R r) { emitrr(X64_cmovnle,l,r); asm_output("cmovlnle %s, %s", RL(l),RL(r)); } + void Assembler::CMOVNG( R l, R r) { emitrr(X64_cmovng, l,r); asm_output("cmovlng %s, %s", RL(l),RL(r)); } + void Assembler::CMOVNGE(R l, R r) { emitrr(X64_cmovnge,l,r); asm_output("cmovlnge %s, %s", RL(l),RL(r)); } + void Assembler::CMOVNB( R l, R r) { emitrr(X64_cmovnb, l,r); asm_output("cmovlnb %s, %s", RL(l),RL(r)); } + void Assembler::CMOVNBE(R l, R r) { emitrr(X64_cmovnbe,l,r); asm_output("cmovlnbe %s, %s", RL(l),RL(r)); } + void Assembler::CMOVNA( R l, R r) { emitrr(X64_cmovna, l,r); asm_output("cmovlna %s, %s", RL(l),RL(r)); } + void Assembler::CMOVNAE(R l, R r) { emitrr(X64_cmovnae,l,r); asm_output("cmovlnae %s, %s", RL(l),RL(r)); } + + void Assembler::CMOVQNO( R l, R r) { emitrr(X64_cmovqno, l,r); asm_output("cmovqno %s, %s", RQ(l),RQ(r)); } + void Assembler::CMOVQNE( R l, R r) { emitrr(X64_cmovqne, l,r); asm_output("cmovqne %s, %s", RQ(l),RQ(r)); } + void Assembler::CMOVQNL( R l, R r) { emitrr(X64_cmovqnl, l,r); asm_output("cmovqnl %s, %s", RQ(l),RQ(r)); } + void Assembler::CMOVQNLE(R l, R r) { emitrr(X64_cmovqnle,l,r); asm_output("cmovqnle %s, %s", RQ(l),RQ(r)); } + void Assembler::CMOVQNG( R l, R r) { emitrr(X64_cmovqng, l,r); asm_output("cmovqng %s, %s", RQ(l),RQ(r)); } + void Assembler::CMOVQNGE(R l, R r) { emitrr(X64_cmovqnge,l,r); asm_output("cmovqnge %s, %s", RQ(l),RQ(r)); } + void Assembler::CMOVQNB( R l, R r) { emitrr(X64_cmovqnb, l,r); asm_output("cmovqnb %s, %s", RQ(l),RQ(r)); } + void Assembler::CMOVQNBE(R l, R r) { emitrr(X64_cmovqnbe,l,r); asm_output("cmovqnbe %s, %s", RQ(l),RQ(r)); } + void Assembler::CMOVQNA( R l, R r) { emitrr(X64_cmovqna, l,r); asm_output("cmovqna %s, %s", RQ(l),RQ(r)); } + void Assembler::CMOVQNAE(R l, R r) { emitrr(X64_cmovqnae,l,r); asm_output("cmovqnae %s, %s", RQ(l),RQ(r)); } + + void Assembler::MOVSXDR(R l, R r) { emitrr(X64_movsxdr,l,r); asm_output("movsxd %s, %s",RQ(l),RL(r)); } + + void Assembler::MOVZX8(R l, R r) { emitrr8(X64_movzx8,l,r); asm_output("movzx %s, %s",RQ(l),RB(r)); } + +// XORPS is a 4x32f vector operation, we use it instead of the more obvious +// XORPD because it's one byte shorter. This is ok because it's only used for +// zeroing an XMM register; hence the single argument. + void Assembler::XORPS( R r) { emitprr(X64_xorps, r,r); asm_output("xorps %s, %s", RQ(r),RQ(r)); } + void Assembler::DIVSD( R l, R r) { emitprr(X64_divsd, l,r); asm_output("divsd %s, %s", RQ(l),RQ(r)); } + void Assembler::MULSD( R l, R r) { emitprr(X64_mulsd, l,r); asm_output("mulsd %s, %s", RQ(l),RQ(r)); } + void Assembler::ADDSD( R l, R r) { emitprr(X64_addsd, l,r); asm_output("addsd %s, %s", RQ(l),RQ(r)); } + void Assembler::SUBSD( R l, R r) { emitprr(X64_subsd, l,r); asm_output("subsd %s, %s", RQ(l),RQ(r)); } + void Assembler::CVTSQ2SD(R l, R r) { emitprr(X64_cvtsq2sd,l,r); asm_output("cvtsq2sd %s, %s",RQ(l),RQ(r)); } + void Assembler::CVTSI2SD(R l, R r) { emitprr(X64_cvtsi2sd,l,r); asm_output("cvtsi2sd %s, %s",RQ(l),RL(r)); } + void Assembler::UCOMISD( R l, R r) { emitprr(X64_ucomisd, l,r); asm_output("ucomisd %s, %s", RQ(l),RQ(r)); } + void Assembler::MOVQRX( R l, R r) { emitprr(X64_movqrx, r,l); asm_output("movq %s, %s", RQ(l),RQ(r)); } // Nb: r and l are deliberately reversed within the emitprr() call. + void Assembler::MOVQXR( R l, R r) { emitprr(X64_movqxr, l,r); asm_output("movq %s, %s", RQ(l),RQ(r)); } + +// MOVI must not affect condition codes! + void Assembler::MOVI( R r, I32 i32) { emitr_imm(X64_movi, r,i32); asm_output("movl %s, %d",RL(r),i32); } + void Assembler::ADDLRI(R r, I32 i32) { emitr_imm(X64_addlri,r,i32); asm_output("addl %s, %d",RL(r),i32); } + void Assembler::SUBLRI(R r, I32 i32) { emitr_imm(X64_sublri,r,i32); asm_output("subl %s, %d",RL(r),i32); } + void Assembler::ANDLRI(R r, I32 i32) { emitr_imm(X64_andlri,r,i32); asm_output("andl %s, %d",RL(r),i32); } + void Assembler::ORLRI( R r, I32 i32) { emitr_imm(X64_orlri, r,i32); asm_output("orl %s, %d", RL(r),i32); } + void Assembler::XORLRI(R r, I32 i32) { emitr_imm(X64_xorlri,r,i32); asm_output("xorl %s, %d",RL(r),i32); } + void Assembler::CMPLRI(R r, I32 i32) { emitr_imm(X64_cmplri,r,i32); asm_output("cmpl %s, %d",RL(r),i32); } + + void Assembler::ADDQRI( R r, I32 i32) { emitr_imm(X64_addqri, r,i32); asm_output("addq %s, %d", RQ(r),i32); } + void Assembler::SUBQRI( R r, I32 i32) { emitr_imm(X64_subqri, r,i32); asm_output("subq %s, %d", RQ(r),i32); } + void Assembler::ANDQRI( R r, I32 i32) { emitr_imm(X64_andqri, r,i32); asm_output("andq %s, %d", RQ(r),i32); } + void Assembler::ORQRI( R r, I32 i32) { emitr_imm(X64_orqri, r,i32); asm_output("orq %s, %d", RQ(r),i32); } + void Assembler::XORQRI( R r, I32 i32) { emitr_imm(X64_xorqri, r,i32); asm_output("xorq %s, %d", RQ(r),i32); } + void Assembler::CMPQRI( R r, I32 i32) { emitr_imm(X64_cmpqri, r,i32); asm_output("cmpq %s, %d", RQ(r),i32); } + void Assembler::MOVQI32(R r, I32 i32) { emitr_imm(X64_movqi32,r,i32); asm_output("movqi32 %s, %d",RQ(r),i32); } + + void Assembler::ADDLR8(R r, I32 i8) { emitr_imm8(X64_addlr8,r,i8); asm_output("addl %s, %d", RL(r),i8); } + void Assembler::SUBLR8(R r, I32 i8) { emitr_imm8(X64_sublr8,r,i8); asm_output("subl %s, %d", RL(r),i8); } + void Assembler::ANDLR8(R r, I32 i8) { emitr_imm8(X64_andlr8,r,i8); asm_output("andl %s, %d", RL(r),i8); } + void Assembler::ORLR8( R r, I32 i8) { emitr_imm8(X64_orlr8, r,i8); asm_output("orl %s, %d", RL(r),i8); } + void Assembler::XORLR8(R r, I32 i8) { emitr_imm8(X64_xorlr8,r,i8); asm_output("xorl %s, %d", RL(r),i8); } + void Assembler::CMPLR8(R r, I32 i8) { emitr_imm8(X64_cmplr8,r,i8); asm_output("cmpl %s, %d", RL(r),i8); } + + void Assembler::ADDQR8(R r, I32 i8) { emitr_imm8(X64_addqr8,r,i8); asm_output("addq %s, %d",RQ(r),i8); } + void Assembler::SUBQR8(R r, I32 i8) { emitr_imm8(X64_subqr8,r,i8); asm_output("subq %s, %d",RQ(r),i8); } + void Assembler::ANDQR8(R r, I32 i8) { emitr_imm8(X64_andqr8,r,i8); asm_output("andq %s, %d",RQ(r),i8); } + void Assembler::ORQR8( R r, I32 i8) { emitr_imm8(X64_orqr8, r,i8); asm_output("orq %s, %d", RQ(r),i8); } + void Assembler::XORQR8(R r, I32 i8) { emitr_imm8(X64_xorqr8,r,i8); asm_output("xorq %s, %d",RQ(r),i8); } + void Assembler::CMPQR8(R r, I32 i8) { emitr_imm8(X64_cmpqr8,r,i8); asm_output("cmpq %s, %d",RQ(r),i8); } + + void Assembler::IMULI(R l, R r, I32 i32) { emitrr_imm(X64_imuli,l,r,i32); asm_output("imuli %s, %s, %d",RL(l),RL(r),i32); } + + void Assembler::MOVQI(R r, U64 u64) { emitr_imm64(X64_movqi,r,u64); asm_output("movq %s, %p",RQ(r),(void*)u64); } + + void Assembler::LEARIP(R r, I32 d) { emitrm(X64_learip,r,d,(Register)0); asm_output("lea %s, %d(rip)",RQ(r),d); } + + void Assembler::LEAQRM(R r1, I d, R r2) { emitrm(X64_leaqrm,r1,d,r2); asm_output("leaq %s, %d(%s)",RQ(r1),d,RQ(r2)); } + void Assembler::MOVLRM(R r1, I d, R r2) { emitrm(X64_movlrm,r1,d,r2); asm_output("movl %s, %d(%s)",RL(r1),d,RQ(r2)); } + void Assembler::MOVQRM(R r1, I d, R r2) { emitrm(X64_movqrm,r1,d,r2); asm_output("movq %s, %d(%s)",RQ(r1),d,RQ(r2)); } + void Assembler::MOVLMR(R r1, I d, R r2) { emitrm(X64_movlmr,r1,d,r2); asm_output("movl %d(%s), %s",d,RQ(r1),RL(r2)); } + void Assembler::MOVQMR(R r1, I d, R r2) { emitrm(X64_movqmr,r1,d,r2); asm_output("movq %d(%s), %s",d,RQ(r1),RQ(r2)); } + + void Assembler::MOVZX8M( R r1, I d, R r2) { emitrm_wide(X64_movzx8m, r1,d,r2); asm_output("movzxb %s, %d(%s)",RQ(r1),d,RQ(r2)); } + void Assembler::MOVZX16M(R r1, I d, R r2) { emitrm_wide(X64_movzx16m,r1,d,r2); asm_output("movzxs %s, %d(%s)",RQ(r1),d,RQ(r2)); } + + void Assembler::MOVSDRM(R r1, I d, R r2) { emitprm(X64_movsdrm,r1,d,r2); asm_output("movsd %s, %d(%s)",RQ(r1),d,RQ(r2)); } + void Assembler::MOVSDMR(R r1, I d, R r2) { emitprm(X64_movsdmr,r1,d,r2); asm_output("movsd %d(%s), %s",d,RQ(r1),RQ(r2)); } + + void Assembler::JMP8( S n, NIns* t) { emit_target8(n, X64_jmp8,t); asm_output("jmp %p", t); } + + void Assembler::JMP32(S n, NIns* t) { emit_target32(n,X64_jmp, t); asm_output("jmp %p", t); } + + void Assembler::JMPX(R indexreg, NIns** table) { emitrxb_imm(X64_jmpx, (R)0, indexreg, (Register)5, (int32_t)(uintptr_t)table); asm_output("jmpq [%s*8 + %p]", RQ(indexreg), (void*)table); } + + void Assembler::JMPXB(R indexreg, R tablereg) { emitxb(X64_jmpxb, indexreg, tablereg); asm_output("jmp [%s*8 + %s]", RQ(indexreg), RQ(tablereg)); } + + void Assembler::JO( S n, NIns* t) { emit_target32(n,X64_jo, t); asm_output("jo %p", t); } + void Assembler::JE( S n, NIns* t) { emit_target32(n,X64_je, t); asm_output("je %p", t); } + void Assembler::JL( S n, NIns* t) { emit_target32(n,X64_jl, t); asm_output("jl %p", t); } + void Assembler::JLE(S n, NIns* t) { emit_target32(n,X64_jle, t); asm_output("jle %p",t); } + void Assembler::JG( S n, NIns* t) { emit_target32(n,X64_jg, t); asm_output("jg %p", t); } + void Assembler::JGE(S n, NIns* t) { emit_target32(n,X64_jge, t); asm_output("jge %p",t); } + void Assembler::JB( S n, NIns* t) { emit_target32(n,X64_jb, t); asm_output("jb %p", t); } + void Assembler::JBE(S n, NIns* t) { emit_target32(n,X64_jbe, t); asm_output("jbe %p",t); } + void Assembler::JA( S n, NIns* t) { emit_target32(n,X64_ja, t); asm_output("ja %p", t); } + void Assembler::JAE(S n, NIns* t) { emit_target32(n,X64_jae, t); asm_output("jae %p",t); } + void Assembler::JP( S n, NIns* t) { emit_target32(n,X64_jp, t); asm_output("jp %p",t); } + + void Assembler::JNO( S n, NIns* t) { emit_target32(n,X64_jo ^X64_jneg, t); asm_output("jno %p", t); } + void Assembler::JNE( S n, NIns* t) { emit_target32(n,X64_je ^X64_jneg, t); asm_output("jne %p", t); } + void Assembler::JNL( S n, NIns* t) { emit_target32(n,X64_jl ^X64_jneg, t); asm_output("jnl %p", t); } + void Assembler::JNLE(S n, NIns* t) { emit_target32(n,X64_jle^X64_jneg, t); asm_output("jnle %p",t); } + void Assembler::JNG( S n, NIns* t) { emit_target32(n,X64_jg ^X64_jneg, t); asm_output("jng %p", t); } + void Assembler::JNGE(S n, NIns* t) { emit_target32(n,X64_jge^X64_jneg, t); asm_output("jnge %p",t); } + void Assembler::JNB( S n, NIns* t) { emit_target32(n,X64_jb ^X64_jneg, t); asm_output("jnb %p", t); } + void Assembler::JNBE(S n, NIns* t) { emit_target32(n,X64_jbe^X64_jneg, t); asm_output("jnbe %p",t); } + void Assembler::JNA( S n, NIns* t) { emit_target32(n,X64_ja ^X64_jneg, t); asm_output("jna %p", t); } + void Assembler::JNAE(S n, NIns* t) { emit_target32(n,X64_jae^X64_jneg, t); asm_output("jnae %p",t); } + + void Assembler::JO8( S n, NIns* t) { emit_target8(n,X64_jo8, t); asm_output("jo %p", t); } + void Assembler::JE8( S n, NIns* t) { emit_target8(n,X64_je8, t); asm_output("je %p", t); } + void Assembler::JL8( S n, NIns* t) { emit_target8(n,X64_jl8, t); asm_output("jl %p", t); } + void Assembler::JLE8(S n, NIns* t) { emit_target8(n,X64_jle8, t); asm_output("jle %p",t); } + void Assembler::JG8( S n, NIns* t) { emit_target8(n,X64_jg8, t); asm_output("jg %p", t); } + void Assembler::JGE8(S n, NIns* t) { emit_target8(n,X64_jge8, t); asm_output("jge %p",t); } + void Assembler::JB8( S n, NIns* t) { emit_target8(n,X64_jb8, t); asm_output("jb %p", t); } + void Assembler::JBE8(S n, NIns* t) { emit_target8(n,X64_jbe8, t); asm_output("jbe %p",t); } + void Assembler::JA8( S n, NIns* t) { emit_target8(n,X64_ja8, t); asm_output("ja %p", t); } + void Assembler::JAE8(S n, NIns* t) { emit_target8(n,X64_jae8, t); asm_output("jae %p",t); } + void Assembler::JP8( S n, NIns* t) { emit_target8(n,X64_jp8, t); asm_output("jp %p",t); } + + void Assembler::JNO8( S n, NIns* t) { emit_target8(n,X64_jo8 ^X64_jneg8, t); asm_output("jno %p", t); } + void Assembler::JNE8( S n, NIns* t) { emit_target8(n,X64_je8 ^X64_jneg8, t); asm_output("jne %p", t); } + void Assembler::JNL8( S n, NIns* t) { emit_target8(n,X64_jl8 ^X64_jneg8, t); asm_output("jnl %p", t); } + void Assembler::JNLE8(S n, NIns* t) { emit_target8(n,X64_jle8^X64_jneg8, t); asm_output("jnle %p",t); } + void Assembler::JNG8( S n, NIns* t) { emit_target8(n,X64_jg8 ^X64_jneg8, t); asm_output("jng %p", t); } + void Assembler::JNGE8(S n, NIns* t) { emit_target8(n,X64_jge8^X64_jneg8, t); asm_output("jnge %p",t); } + void Assembler::JNB8( S n, NIns* t) { emit_target8(n,X64_jb8 ^X64_jneg8, t); asm_output("jnb %p", t); } + void Assembler::JNBE8(S n, NIns* t) { emit_target8(n,X64_jbe8^X64_jneg8, t); asm_output("jnbe %p",t); } + void Assembler::JNA8( S n, NIns* t) { emit_target8(n,X64_ja8 ^X64_jneg8, t); asm_output("jna %p", t); } + void Assembler::JNAE8(S n, NIns* t) { emit_target8(n,X64_jae8^X64_jneg8, t); asm_output("jnae %p",t); } + + void Assembler::CALL( S n, NIns* t) { emit_target32(n,X64_call,t); asm_output("call %p",t); } + + void Assembler::CALLRAX() { emit(X64_callrax); asm_output("call (rax)"); } + void Assembler::RET() { emit(X64_ret); asm_output("ret"); } + + void Assembler::MOVQSPR(I d, R r) { emit(X64_movqspr | U64(d) << 56 | U64((r&7)<<3) << 40 | U64((r&8)>>1) << 24); asm_output("movq %d(rsp), %s", d, RQ(r)); } // insert r into mod/rm and rex bytes + + void Assembler::XORPSA(R r, I32 i32) { emitxm_abs(X64_xorpsa, r, i32); asm_output("xorps %s, (0x%x)",RQ(r), i32); } + void Assembler::XORPSM(R r, NIns* a64) { emitxm_rel(X64_xorpsm, r, a64); asm_output("xorps %s, (%p)", RQ(r), a64); } + + void Assembler::X86_AND8R(R r) { emit(X86_and8r | U64(r<<3|(r|4))<<56); asm_output("andb %s, %s", RB(r), RBhi(r)); } + void Assembler::X86_SETNP(R r) { emit(X86_setnp | U64(r|4)<<56); asm_output("setnp %s", RBhi(r)); } + void Assembler::X86_SETE(R r) { emit(X86_sete | U64(r)<<56); asm_output("sete %s", RB(r)); } + +#undef R +#undef I +#undef I32 +#undef U64 +#undef S + + void Assembler::MR(Register d, Register s) { + NanoAssert(IsGpReg(d) && IsGpReg(s)); + MOVQR(d, s); + } + + // This is needed for guards; we must be able to patch the jmp later and + // we cannot do that if an 8-bit relative jump is used, so we can't use + // JMP(). + void Assembler::JMPl(NIns* target) { + JMP32(8, target); + } + + void Assembler::JMP(NIns *target) { + if (!target || isTargetWithinS32(target)) { + if (target && isTargetWithinS8(target)) { + JMP8(8, target); + } else { + JMP32(8, target); + } + } else { + TODO(jmp64); + } + } + + // register allocation for 2-address style ops of the form R = R (op) B + void Assembler::regalloc_binary(LIns *ins, RegisterMask allow, Register &rr, Register &ra, Register &rb) { +#ifdef _DEBUG + RegisterMask originalAllow = allow; +#endif + rb = UnknownReg; + LIns *a = ins->oprnd1(); + LIns *b = ins->oprnd2(); + if (a != b) { + rb = findRegFor(b, allow); + allow &= ~rmask(rb); + } + rr = prepResultReg(ins, allow); + // if this is last use of a in reg, we can re-use result reg + if (a->isUnusedOrHasUnknownReg()) { + ra = findSpecificRegForUnallocated(a, rr); + } else if (!(allow & rmask(a->getReg()))) { + // 'a' already has a register assigned, but it's not valid. + // To make sure floating point operations stay in FPU registers + // as much as possible, make sure that only a few opcodes are + // reserving GPRs. + NanoAssert(a->isop(LIR_quad) || a->isop(LIR_ldq) || a->isop(LIR_ldqc)|| a->isop(LIR_u2f) || a->isop(LIR_float)); + allow &= ~rmask(rr); + ra = findRegFor(a, allow); + } else { + ra = a->getReg(); + } + if (a == b) { + rb = ra; + } + NanoAssert(originalAllow & rmask(rr)); + NanoAssert(originalAllow & rmask(ra)); + NanoAssert(originalAllow & rmask(rb)); + } + + void Assembler::asm_qbinop(LIns *ins) { + asm_arith(ins); + } + + void Assembler::asm_shift(LIns *ins) { + // shift require rcx for shift count + LIns *b = ins->oprnd2(); + if (b->isconst()) { + asm_shift_imm(ins); + return; + } + Register rr, ra; + if (b != ins->oprnd1()) { + findSpecificRegFor(b, RCX); + regalloc_unary(ins, GpRegs & ~rmask(RCX), rr, ra); + } else { + // a == b means both must be in RCX + regalloc_unary(ins, rmask(RCX), rr, ra); + } + switch (ins->opcode()) { + default: + TODO(asm_shift); + case LIR_qursh: SHRQ(rr); break; + case LIR_qirsh: SARQ(rr); break; + case LIR_qilsh: SHLQ(rr); break; + case LIR_ush: SHR( rr); break; + case LIR_rsh: SAR( rr); break; + case LIR_lsh: SHL( rr); break; + } + if (rr != ra) + MR(rr, ra); + } + + void Assembler::asm_shift_imm(LIns *ins) { + Register rr, ra; + regalloc_unary(ins, GpRegs, rr, ra); + int shift = ins->oprnd2()->imm32() & 63; + switch (ins->opcode()) { + default: TODO(shiftimm); + case LIR_qursh: SHRQI(rr, shift); break; + case LIR_qirsh: SARQI(rr, shift); break; + case LIR_qilsh: SHLQI(rr, shift); break; + case LIR_ush: SHRI( rr, shift); break; + case LIR_rsh: SARI( rr, shift); break; + case LIR_lsh: SHLI( rr, shift); break; + } + if (rr != ra) + MR(rr, ra); + } + + static bool isImm32(LIns *ins) { + return ins->isconst() || (ins->isconstq() && isS32(ins->imm64())); + } + static int32_t getImm32(LIns *ins) { + return ins->isconst() ? ins->imm32() : int32_t(ins->imm64()); + } + + // binary op, integer regs, rhs is int32 const + void Assembler::asm_arith_imm(LIns *ins) { + LIns *b = ins->oprnd2(); + int32_t imm = getImm32(b); + LOpcode op = ins->opcode(); + Register rr, ra; + if (op == LIR_mul) { + // imul has true 3-addr form, it doesn't clobber ra + rr = prepResultReg(ins, GpRegs); + LIns *a = ins->oprnd1(); + ra = findRegFor(a, GpRegs); + IMULI(rr, ra, imm); + return; + } + regalloc_unary(ins, GpRegs, rr, ra); + if (isS8(imm)) { + switch (ins->opcode()) { + default: TODO(arith_imm8); + case LIR_iaddp: + case LIR_add: ADDLR8(rr, imm); break; + case LIR_and: ANDLR8(rr, imm); break; + case LIR_or: ORLR8( rr, imm); break; + case LIR_sub: SUBLR8(rr, imm); break; + case LIR_xor: XORLR8(rr, imm); break; + case LIR_qiadd: + case LIR_qaddp: ADDQR8(rr, imm); break; + case LIR_qiand: ANDQR8(rr, imm); break; + case LIR_qior: ORQR8( rr, imm); break; + case LIR_qxor: XORQR8(rr, imm); break; + } + } else { + switch (ins->opcode()) { + default: TODO(arith_imm); + case LIR_iaddp: + case LIR_add: ADDLRI(rr, imm); break; + case LIR_and: ANDLRI(rr, imm); break; + case LIR_or: ORLRI( rr, imm); break; + case LIR_sub: SUBLRI(rr, imm); break; + case LIR_xor: XORLRI(rr, imm); break; + case LIR_qiadd: + case LIR_qaddp: ADDQRI(rr, imm); break; + case LIR_qiand: ANDQRI(rr, imm); break; + case LIR_qior: ORQRI( rr, imm); break; + case LIR_qxor: XORQRI(rr, imm); break; + } + } + if (rr != ra) + MR(rr, ra); + } + + void Assembler::asm_div_mod(LIns *ins) { + LIns *div; + if (ins->opcode() == LIR_mod) { + // LIR_mod expects the LIR_div to be near + div = ins->oprnd1(); + prepResultReg(ins, rmask(RDX)); + } else { + div = ins; + evictIfActive(RDX); + } + + NanoAssert(div->isop(LIR_div)); + + LIns *lhs = div->oprnd1(); + LIns *rhs = div->oprnd2(); + + prepResultReg(div, rmask(RAX)); + + Register rhsReg = findRegFor(rhs, (GpRegs ^ (rmask(RAX)|rmask(RDX)))); + Register lhsReg = lhs->isUnusedOrHasUnknownReg() + ? findSpecificRegForUnallocated(lhs, RAX) + : lhs->getReg(); + IDIV(rhsReg); + SARI(RDX, 31); + MR(RDX, RAX); + if (RAX != lhsReg) + MR(RAX, lhsReg); + } + + // binary op with integer registers + void Assembler::asm_arith(LIns *ins) { + Register rr, ra, rb; + + switch (ins->opcode() & ~LIR64) { + case LIR_lsh: + case LIR_rsh: + case LIR_ush: + asm_shift(ins); + return; + case LIR_mod: + case LIR_div: + asm_div_mod(ins); + return; + default: + break; + } + + LIns *b = ins->oprnd2(); + if (isImm32(b)) { + asm_arith_imm(ins); + return; + } + regalloc_binary(ins, GpRegs, rr, ra, rb); + switch (ins->opcode()) { + default: TODO(asm_arith); + case LIR_or: ORLRR(rr, rb); break; + case LIR_sub: SUBRR(rr, rb); break; + case LIR_iaddp: + case LIR_add: ADDRR(rr, rb); break; + case LIR_and: ANDRR(rr, rb); break; + case LIR_xor: XORRR(rr, rb); break; + case LIR_mul: IMUL(rr, rb); break; + case LIR_qxor: XORQRR(rr, rb); break; + case LIR_qior: ORQRR(rr, rb); break; + case LIR_qiand: ANDQRR(rr, rb); break; + case LIR_qiadd: + case LIR_qaddp: ADDQRR(rr, rb); break; + } + if (rr != ra) + MR(rr,ra); + } + + // binary op with fp registers + void Assembler::asm_fop(LIns *ins) { + Register rr, ra, rb; + regalloc_binary(ins, FpRegs, rr, ra, rb); + switch (ins->opcode()) { + default: TODO(asm_fop); + case LIR_fdiv: DIVSD(rr, rb); break; + case LIR_fmul: MULSD(rr, rb); break; + case LIR_fadd: ADDSD(rr, rb); break; + case LIR_fsub: SUBSD(rr, rb); break; + } + if (rr != ra) { + asm_nongp_copy(rr, ra); + } + } + + void Assembler::asm_neg_not(LIns *ins) { + Register rr, ra; + regalloc_unary(ins, GpRegs, rr, ra); + NanoAssert(IsGpReg(ra)); + if (ins->isop(LIR_not)) + NOT(rr); + else + NEG(rr); + if (rr != ra) + MR(rr, ra); + } + + void Assembler::asm_call(LIns *ins) { + const CallInfo *call = ins->callInfo(); + ArgSize sizes[MAXARGS]; + int argc = call->get_sizes(sizes); + + bool indirect = call->isIndirect(); + if (!indirect) { + verbose_only(if (_logc->lcbits & LC_Assembly) + outputf(" %p:", _nIns); + ) + NIns *target = (NIns*)call->_address; + if (isTargetWithinS32(target)) { + CALL(8, target); + } else { + // can't reach target from here, load imm64 and do an indirect jump + CALLRAX(); + asm_quad(RAX, (uint64_t)target); + } + } else { + // Indirect call: we assign the address arg to RAX since it's not + // used for regular arguments, and is otherwise scratch since it's + // clobberred by the call. + asm_regarg(ARGSIZE_P, ins->arg(--argc), RAX); + CALLRAX(); + } + + #ifdef _WIN64 + int stk_used = 32; // always reserve 32byte shadow area + #else + int stk_used = 0; + Register fr = XMM0; + #endif + int arg_index = 0; + for (int i = 0; i < argc; i++) { + int j = argc - i - 1; + ArgSize sz = sizes[j]; + LIns* arg = ins->arg(j); + if ((sz & ARGSIZE_MASK_INT) && arg_index < NumArgRegs) { + // gp arg + asm_regarg(sz, arg, argRegs[arg_index]); + arg_index++; + } + #ifdef _WIN64 + else if (sz == ARGSIZE_F && arg_index < NumArgRegs) { + // double goes in XMM reg # based on overall arg_index + asm_regarg(sz, arg, Register(XMM0+arg_index)); + arg_index++; + } + #else + else if (sz == ARGSIZE_F && fr < XMM8) { + // double goes in next available XMM register + asm_regarg(sz, arg, fr); + fr = nextreg(fr); + } + #endif + else { + asm_stkarg(sz, arg, stk_used); + stk_used += sizeof(void*); + } + } + + if (stk_used > max_stk_used) + max_stk_used = stk_used; + } + + void Assembler::asm_regarg(ArgSize sz, LIns *p, Register r) { + if (sz == ARGSIZE_I) { + NanoAssert(!p->isQuad()); + if (p->isconst()) { + asm_quad(r, int64_t(p->imm32())); + return; + } + // sign extend int32 to int64 + MOVSXDR(r, r); + } else if (sz == ARGSIZE_U) { + NanoAssert(!p->isQuad()); + if (p->isconst()) { + asm_quad(r, uint64_t(uint32_t(p->imm32()))); + return; + } + // zero extend with 32bit mov, auto-zeros upper 32bits + MOVLR(r, r); + } + /* there is no point in folding an immediate here, because + * the argument register must be a scratch register and we're + * just before a call. Just reserving the register will cause + * the constant to be rematerialized nearby in asm_restore(), + * which is the same instruction we would otherwise emit right + * here, and moving it earlier in the stream provides more scheduling + * freedom to the cpu. */ + findSpecificRegFor(p, r); + } + + void Assembler::asm_stkarg(ArgSize sz, LIns *p, int stk_off) { + NanoAssert(isS8(stk_off)); + if (sz & ARGSIZE_MASK_INT) { + Register r = findRegFor(p, GpRegs); + MOVQSPR(stk_off, r); // movq [rsp+d8], r + if (sz == ARGSIZE_I) { + // extend int32 to int64 + NanoAssert(!p->isQuad()); + MOVSXDR(r, r); + } else if (sz == ARGSIZE_U) { + // extend uint32 to uint64 + NanoAssert(!p->isQuad()); + MOVLR(r, r); + } + } else { + TODO(asm_stkarg_non_int); + } + } + + void Assembler::asm_promote(LIns *ins) { + Register rr, ra; + regalloc_unary(ins, GpRegs, rr, ra); + NanoAssert(IsGpReg(ra)); + if (ins->isop(LIR_u2q)) { + MOVLR(rr, ra); // 32bit mov zeros the upper 32bits of the target + } else { + NanoAssert(ins->isop(LIR_i2q)); + MOVSXDR(rr, ra); // sign extend 32->64 + } + } + + // the CVTSI2SD instruction only writes to the low 64bits of the target + // XMM register, which hinders register renaming and makes dependence + // chains longer. So we precede with XORPS to clear the target register. + + void Assembler::asm_i2f(LIns *ins) { + Register r = prepResultReg(ins, FpRegs); + Register b = findRegFor(ins->oprnd1(), GpRegs); + CVTSI2SD(r, b); // cvtsi2sd xmmr, b only writes xmm:0:64 + XORPS(r); // xorps xmmr,xmmr to break dependency chains + } + + void Assembler::asm_u2f(LIns *ins) { + Register r = prepResultReg(ins, FpRegs); + Register b = findRegFor(ins->oprnd1(), GpRegs); + NanoAssert(!ins->oprnd1()->isQuad()); + // since oprnd1 value is 32bit, its okay to zero-extend the value without worrying about clobbering. + CVTSQ2SD(r, b); // convert int64 to double + XORPS(r); // xorps xmmr,xmmr to break dependency chains + MOVLR(b, b); // zero extend u32 to int64 + } + + void Assembler::asm_cmov(LIns *ins) { + LIns* cond = ins->oprnd1(); + LIns* iftrue = ins->oprnd2(); + LIns* iffalse = ins->oprnd3(); + NanoAssert(cond->isCmp()); + NanoAssert((ins->isop(LIR_qcmov) && iftrue->isQuad() && iffalse->isQuad()) || + (ins->isop(LIR_cmov) && !iftrue->isQuad() && !iffalse->isQuad())); + + // this code assumes that neither LD nor MR nor MRcc set any of the condition flags. + // (This is true on Intel, is it true on all architectures?) + const Register rr = prepResultReg(ins, GpRegs); + const Register rf = findRegFor(iffalse, GpRegs & ~rmask(rr)); + + LOpcode condop = cond->opcode(); + if (ins->opcode() == LIR_cmov) { + switch (condop & ~LIR64) { + case LIR_ov: CMOVNO( rr, rf); break; + case LIR_eq: CMOVNE( rr, rf); break; + case LIR_lt: CMOVNL( rr, rf); break; + case LIR_gt: CMOVNG( rr, rf); break; + case LIR_le: CMOVNLE(rr, rf); break; + case LIR_ge: CMOVNGE(rr, rf); break; + case LIR_ult: CMOVNB( rr, rf); break; + case LIR_ugt: CMOVNA( rr, rf); break; + case LIR_ule: CMOVNBE(rr, rf); break; + case LIR_uge: CMOVNAE(rr, rf); break; + default: NanoAssert(0); break; + } + } else { + switch (condop & ~LIR64) { + case LIR_ov: CMOVQNO( rr, rf); break; + case LIR_eq: CMOVQNE( rr, rf); break; + case LIR_lt: CMOVQNL( rr, rf); break; + case LIR_gt: CMOVQNG( rr, rf); break; + case LIR_le: CMOVQNLE(rr, rf); break; + case LIR_ge: CMOVQNGE(rr, rf); break; + case LIR_ult: CMOVQNB( rr, rf); break; + case LIR_ugt: CMOVQNA( rr, rf); break; + case LIR_ule: CMOVQNBE(rr, rf); break; + case LIR_uge: CMOVQNAE(rr, rf); break; + default: NanoAssert(0); break; + } + } + /*const Register rt =*/ findSpecificRegFor(iftrue, rr); + asm_cmp(cond); + } + + NIns* Assembler::asm_branch(bool onFalse, LIns *cond, NIns *target) { + LOpcode condop = cond->opcode(); + if (condop >= LIR_feq && condop <= LIR_fge) + return asm_fbranch(onFalse, cond, target); + + // we must ensure there's room for the instr before calculating + // the offset. and the offset, determines the opcode (8bit or 32bit) + NanoAssert((condop & ~LIR64) >= LIR_ov); + NanoAssert((condop & ~LIR64) <= LIR_uge); + if (target && isTargetWithinS8(target)) { + if (onFalse) { + switch (condop & ~LIR64) { + case LIR_ov: JNO8( 8, target); break; + case LIR_eq: JNE8( 8, target); break; + case LIR_lt: JNL8( 8, target); break; + case LIR_gt: JNG8( 8, target); break; + case LIR_le: JNLE8(8, target); break; + case LIR_ge: JNGE8(8, target); break; + case LIR_ult: JNB8( 8, target); break; + case LIR_ugt: JNA8( 8, target); break; + case LIR_ule: JNBE8(8, target); break; + case LIR_uge: JNAE8(8, target); break; + default: NanoAssert(0); break; + } + } else { + switch (condop & ~LIR64) { + case LIR_ov: JO8( 8, target); break; + case LIR_eq: JE8( 8, target); break; + case LIR_lt: JL8( 8, target); break; + case LIR_gt: JG8( 8, target); break; + case LIR_le: JLE8(8, target); break; + case LIR_ge: JGE8(8, target); break; + case LIR_ult: JB8( 8, target); break; + case LIR_ugt: JA8( 8, target); break; + case LIR_ule: JBE8(8, target); break; + case LIR_uge: JAE8(8, target); break; + default: NanoAssert(0); break; + } + } + } else { + if (onFalse) { + switch (condop & ~LIR64) { + case LIR_ov: JNO( 8, target); break; + case LIR_eq: JNE( 8, target); break; + case LIR_lt: JNL( 8, target); break; + case LIR_gt: JNG( 8, target); break; + case LIR_le: JNLE(8, target); break; + case LIR_ge: JNGE(8, target); break; + case LIR_ult: JNB( 8, target); break; + case LIR_ugt: JNA( 8, target); break; + case LIR_ule: JNBE(8, target); break; + case LIR_uge: JNAE(8, target); break; + default: NanoAssert(0); break; + } + } else { + switch (condop & ~LIR64) { + case LIR_ov: JO( 8, target); break; + case LIR_eq: JE( 8, target); break; + case LIR_lt: JL( 8, target); break; + case LIR_gt: JG( 8, target); break; + case LIR_le: JLE(8, target); break; + case LIR_ge: JGE(8, target); break; + case LIR_ult: JB( 8, target); break; + case LIR_ugt: JA( 8, target); break; + case LIR_ule: JBE(8, target); break; + case LIR_uge: JAE(8, target); break; + default: NanoAssert(0); break; + } + } + } + NIns *patch = _nIns; // addr of instr to patch + asm_cmp(cond); + return patch; + } + + void Assembler::asm_cmp(LIns *cond) { + // LIR_ov recycles the flags set by arithmetic ops + if (cond->opcode() == LIR_ov) + return; + LIns *b = cond->oprnd2(); + if (isImm32(b)) { + asm_cmp_imm(cond); + return; + } + LIns *a = cond->oprnd1(); + Register ra, rb; + if (a != b) { + findRegFor2(GpRegs, a, ra, b, rb); + } else { + // optimize-me: this will produce a const result! + ra = rb = findRegFor(a, GpRegs); + } + + LOpcode condop = cond->opcode(); + if (condop & LIR64) + CMPQR(ra, rb); + else + CMPLR(ra, rb); + } + + void Assembler::asm_cmp_imm(LIns *cond) { + LIns *a = cond->oprnd1(); + LIns *b = cond->oprnd2(); + Register ra = findRegFor(a, GpRegs); + int32_t imm = getImm32(b); + if (isS8(imm)) { + if (cond->opcode() & LIR64) + CMPQR8(ra, imm); + else + CMPLR8(ra, imm); + } else { + if (cond->opcode() & LIR64) + CMPQRI(ra, imm); + else + CMPLRI(ra, imm); + } + } + + // compiling floating point branches + // discussion in https://bugzilla.mozilla.org/show_bug.cgi?id=443886 + // + // fucom/p/pp: c3 c2 c0 jae ja jbe jb je jne + // ucomisd: Z P C !C !C&!Z C|Z C Z !Z + // -- -- -- -- ----- --- -- -- -- + // unordered 1 1 1 T T T + // greater > 0 0 0 T T T + // less < 0 0 1 T T T + // equal = 1 0 0 T T T + // + // here's the cases, using conditionals: + // + // branch >= > <= < = + // ------ --- --- --- --- --- + // LIR_jt jae ja swap+jae swap+ja jp over je + // LIR_jf jb jbe swap+jb swap+jbe jne+jp + + NIns* Assembler::asm_fbranch(bool onFalse, LIns *cond, NIns *target) { + LOpcode condop = cond->opcode(); + NIns *patch; + LIns *a = cond->oprnd1(); + LIns *b = cond->oprnd2(); + if (condop == LIR_feq) { + if (onFalse) { + // branch if unordered or != + JP(16, target); // underrun of 12 needed, round up for overhang --> 16 + JNE(0, target); // no underrun needed, previous was enough + patch = _nIns; + } else { + // jp skip (2byte) + // jeq target + // skip: ... + underrunProtect(16); // underrun of 7 needed but we write 2 instr --> 16 + NIns *skip = _nIns; + JE(0, target); // no underrun needed, previous was enough + patch = _nIns; + JP8(0, skip); // ditto + } + } + else { + if (condop == LIR_flt) { + condop = LIR_fgt; + LIns *t = a; a = b; b = t; + } else if (condop == LIR_fle) { + condop = LIR_fge; + LIns *t = a; a = b; b = t; + } + if (condop == LIR_fgt) { + if (onFalse) + JBE(8, target); + else + JA(8, target); + } else { // LIR_fge + if (onFalse) + JB(8, target); + else + JAE(8, target); + } + patch = _nIns; + } + fcmp(a, b); + return patch; + } + + void Assembler::asm_fcond(LIns *ins) { + LOpcode op = ins->opcode(); + LIns *a = ins->oprnd1(); + LIns *b = ins->oprnd2(); + if (op == LIR_feq) { + // result = ZF & !PF, must do logic on flags + // r = al|bl|cl|dl, can only use rh without rex prefix + Register r = prepResultReg(ins, 1<isop(LIR_alloc)) { + int d = disp(ins); + LEAQRM(r, d, FP); + } + else if (ins->isconst()) { + if (!ins->getArIndex()) { + ins->markAsClear(); + } + // unsafe to use xor r,r for zero because it changes cc's + MOVI(r, ins->imm32()); + } + else if (ins->isconstq() && IsGpReg(r)) { + if (!ins->getArIndex()) { + ins->markAsClear(); + } + // unsafe to use xor r,r for zero because it changes cc's + asm_quad(r, ins->imm64()); + } + else { + int d = findMemFor(ins); + if (IsFpReg(r)) { + NanoAssert(ins->isQuad()); + // load 64bits into XMM. don't know if double or int64, assume double. + MOVSDRM(r, d, FP); + } else if (ins->isQuad()) { + MOVQRM(r, d, FP); + } else { + MOVLRM(r, d, FP); + } + } + } + + void Assembler::asm_cond(LIns *ins) { + LOpcode op = ins->opcode(); + // unlike x86-32, with a rex prefix we can use any GP register as an 8bit target + Register r = prepResultReg(ins, GpRegs); + // SETcc only sets low 8 bits, so extend + MOVZX8(r, r); + switch (op) { + default: + TODO(cond); + case LIR_qeq: + case LIR_eq: SETE(r); break; + case LIR_qlt: + case LIR_lt: SETL(r); break; + case LIR_qle: + case LIR_le: SETLE(r); break; + case LIR_qgt: + case LIR_gt: SETG(r); break; + case LIR_qge: + case LIR_ge: SETGE(r); break; + case LIR_qult: + case LIR_ult: SETB(r); break; + case LIR_qule: + case LIR_ule: SETBE(r); break; + case LIR_qugt: + case LIR_ugt: SETA(r); break; + case LIR_quge: + case LIR_uge: SETAE(r); break; + case LIR_ov: SETO(r); break; + } + asm_cmp(ins); + } + + void Assembler::asm_ret(LIns *ins) { + genEpilogue(); + + // Restore RSP from RBP, undoing SUB(RSP,amt) in the prologue + MR(RSP,FP); + + assignSavedRegs(); + LIns *value = ins->oprnd1(); + Register r = ins->isop(LIR_ret) ? RAX : XMM0; + findSpecificRegFor(value, r); + } + + void Assembler::asm_nongp_copy(Register d, Register s) { + if (!IsFpReg(d) && IsFpReg(s)) { + // gpr <- xmm: use movq r/m64, xmm (66 REX.W 0F 7E /r) + MOVQRX(d, s); + } else if (IsFpReg(d) && IsFpReg(s)) { + // xmm <- xmm: use movaps. movsd r,r causes partial register stall + MOVAPSR(d, s); + } else { + // xmm <- gpr: use movq xmm, r/m64 (66 REX.W 0F 6E /r) + MOVQXR(d, s); + } + } + + void Assembler::regalloc_load(LIns *ins, Register &rr, int32_t &dr, Register &rb) { + dr = ins->disp(); + LIns *base = ins->oprnd1(); + rb = getBaseReg(ins->opcode(), base, dr, BaseRegs); + if (ins->isUnusedOrHasUnknownReg()) { + // use a gpr in case we're copying a non-double + rr = prepResultReg(ins, GpRegs & ~rmask(rb)); + } else { + // keep already assigned register + rr = ins->getReg(); + freeRsrcOf(ins, false); + } + } + + void Assembler::asm_load64(LIns *ins) { + Register rr, rb; + int32_t dr; + regalloc_load(ins, rr, dr, rb); + if (IsGpReg(rr)) { + // general 64bit load, 32bit const displacement + MOVQRM(rr, dr, rb); + } else { + // load 64bits into XMM. don't know if double or int64, assume double. + MOVSDRM(rr, dr, rb); + } + } + + void Assembler::asm_ld(LIns *ins) { + NanoAssert(!ins->isQuad()); + Register r, b; + int32_t d; + regalloc_load(ins, r, d, b); + LOpcode op = ins->opcode(); + switch (op) { + case LIR_ldcb: MOVZX8M( r, d, b); break; + case LIR_ldcs: MOVZX16M(r, d, b); break; + default: MOVLRM( r, d, b); break; + } + } + + void Assembler::asm_store64(LIns *value, int d, LIns *base) { + NanoAssert(value->isQuad()); + Register b = getBaseReg(LIR_stqi, base, d, BaseRegs); + + // if we have to choose a register, use a GPR, but not the base reg + Register r; + if (value->isUnusedOrHasUnknownReg()) { + RegisterMask allow; + // XXX: isFloat doesn't cover float/fmod! see bug 520208. + if (value->isFloat() || value->isop(LIR_float) || value->isop(LIR_fmod)) { + allow = FpRegs; + } else { + allow = GpRegs; + } + r = findRegFor(value, allow & ~rmask(b)); + } else { + r = value->getReg(); + } + + if (IsGpReg(r)) { + // gpr store + MOVQMR(r, d, b); + } + else { + // xmm store + MOVSDMR(r, d, b); + } + } + + void Assembler::asm_store32(LIns *value, int d, LIns *base) { + NanoAssert(!value->isQuad()); + Register b = getBaseReg(LIR_sti, base, d, BaseRegs); + Register r = findRegFor(value, GpRegs & ~rmask(b)); + + // store 32bits to 64bit addr. use rex so we can use all 16 regs + MOVLMR(r, d, b); + } + + // generate a 64bit constant, must not affect condition codes! + void Assembler::asm_quad(Register r, uint64_t v) { + NanoAssert(IsGpReg(r)); + if (isU32(v)) { + MOVI(r, int32_t(v)); + } else if (isS32(v)) { + // safe for sign-extension 32->64 + MOVQI32(r, int32_t(v)); + } else if (isTargetWithinS32((NIns*)v)) { + // value is with +/- 2GB from RIP, can use LEA with RIP-relative disp32 + int32_t d = int32_t(int64_t(v)-int64_t(_nIns)); + LEARIP(r, d); + } else { + MOVQI(r, v); + } + } + + void Assembler::asm_int(LIns *ins) { + Register r = prepResultReg(ins, GpRegs); + int32_t v = ins->imm32(); + if (v == 0) { + // special case for zero + XORRR(r, r); + } else { + MOVI(r, v); + } + } + + void Assembler::asm_quad(LIns *ins) { + uint64_t v = ins->imm64(); + RegisterMask allow = v == 0 ? GpRegs|FpRegs : GpRegs; + Register r = prepResultReg(ins, allow); + if (v == 0) { + if (IsGpReg(r)) { + // special case for zero + XORRR(r, r); + } else { + // xorps for xmm + XORPS(r); + } + } else { + asm_quad(r, v); + } + } + + void Assembler::asm_qjoin(LIns*) { + TODO(asm_qjoin); + } + + Register Assembler::asm_prep_fcall(LIns *ins) { + return prepResultReg(ins, rmask(XMM0)); + } + + void Assembler::asm_param(LIns *ins) { + uint32_t a = ins->paramArg(); + uint32_t kind = ins->paramKind(); + if (kind == 0) { + // ordinary param + // first four or six args always in registers for x86_64 ABI + if (a < (uint32_t)NumArgRegs) { + // incoming arg in register + prepResultReg(ins, rmask(argRegs[a])); + } else { + // todo: support stack based args, arg 0 is at [FP+off] where off + // is the # of regs to be pushed in genProlog() + TODO(asm_param_stk); + } + } + else { + // saved param + prepResultReg(ins, rmask(savedRegs[a])); + } + } + + // register allocation for 2-address style unary ops of the form R = (op) R + void Assembler::regalloc_unary(LIns *ins, RegisterMask allow, Register &rr, Register &ra) { + LIns *a = ins->oprnd1(); + rr = prepResultReg(ins, allow); + // if this is last use of a in reg, we can re-use result reg + if (a->isUnusedOrHasUnknownReg()) { + ra = findSpecificRegForUnallocated(a, rr); + } else { + // 'a' already has a register assigned. Caller must emit a copy + // to rr once instr code is generated. (ie mov rr,ra ; op rr) + ra = a->getReg(); + } + NanoAssert(allow & rmask(rr)); + } + + static const AVMPLUS_ALIGN16(int64_t) negateMask[] = {0x8000000000000000LL,0}; + + void Assembler::asm_fneg(LIns *ins) { + Register rr, ra; + if (isS32((uintptr_t)negateMask) || isTargetWithinS32((NIns*)negateMask)) { + regalloc_unary(ins, FpRegs, rr, ra); + if (isS32((uintptr_t)negateMask)) { + // builtin code is in bottom or top 2GB addr space, use absolute addressing + XORPSA(rr, (int32_t)(uintptr_t)negateMask); + } else { + // jit code is within +/-2GB of builtin code, use rip-relative + XORPSM(rr, (NIns*)negateMask); + } + if (ra != rr) + asm_nongp_copy(rr,ra); + } else { + // this is just hideous - can't use RIP-relative load, can't use + // absolute-address load, and cant move imm64 const to XMM. + // so do it all in a GPR. hrmph. + rr = prepResultReg(ins, GpRegs); + ra = findRegFor(ins->oprnd1(), GpRegs & ~rmask(rr)); + XORQRR(rr, ra); // xor rr, ra + asm_quad(rr, negateMask[0]); // mov rr, 0x8000000000000000 + } + } + + void Assembler::asm_qhi(LIns*) { + TODO(asm_qhi); + } + + void Assembler::asm_qlo(LIns *ins) { + Register rr, ra; + regalloc_unary(ins, GpRegs, rr, ra); + NanoAssert(IsGpReg(ra)); + MOVLR(rr, ra); // 32bit mov zeros the upper 32bits of the target + } + + void Assembler::asm_spill(Register rr, int d, bool /*pop*/, bool quad) { + if (d) { + if (!IsFpReg(rr)) { + if (quad) + MOVQMR(rr, d, FP); + else + MOVLMR(rr, d, FP); + } else { + // store 64bits from XMM to memory + NanoAssert(quad); + MOVSDMR(rr, d, FP); + } + } + } + + NIns* Assembler::genPrologue() { + // activation frame is 4 bytes per entry even on 64bit machines + uint32_t stackNeeded = max_stk_used + _activation.tos * 4; + + uint32_t stackPushed = + sizeof(void*) + // returnaddr + sizeof(void*); // ebp + uint32_t aligned = alignUp(stackNeeded + stackPushed, NJ_ALIGN_STACK); + uint32_t amt = aligned - stackPushed; + + // Reserve stackNeeded bytes, padded + // to preserve NJ_ALIGN_STACK-byte alignment. + if (amt) { + if (isS8(amt)) + SUBQR8(RSP, amt); + else + SUBQRI(RSP, amt); + } + + verbose_only( outputAddr=true; asm_output("[patch entry]"); ) + NIns *patchEntry = _nIns; + MR(FP, RSP); // Establish our own FP. + PUSHR(FP); // Save caller's FP. + + return patchEntry; + } + + NIns* Assembler::genEpilogue() { + // pop rbp + // ret + RET(); + POPR(RBP); + return _nIns; + } + + void Assembler::nRegisterResetAll(RegAlloc &a) { + // add scratch registers to our free list for the allocator + a.clear(); +#ifdef _WIN64 + a.free = 0x001fffcf; // rax-rbx, rsi, rdi, r8-r15, xmm0-xmm5 +#else + a.free = 0xffffffff & ~(1<,jp, for LIR_jf(feq) + // we just patched the jne, now patch the jp. + next += 6; + NanoAssert(((int32_t*)next)[-1] == 0); + NanoAssert(isS32(target - next)); + ((int32_t*)next)[-1] = int32_t(target - next); + } + } + + Register Assembler::nRegisterAllocFromSet(RegisterMask set) { + #if defined _MSC_VER + DWORD tr; + _BitScanForward(&tr, set); + _allocator.free &= ~rmask((Register)tr); + return (Register) tr; + #else + // gcc asm syntax + Register r; + asm("bsf %1, %%eax\n\t" + "btr %%eax, %2\n\t" + "movl %%eax, %0\n\t" + : "=m"(r) : "m"(set), "m"(_allocator.free) : "%eax", "memory"); + (void)set; + return r; + #endif + } + + void Assembler::nFragExit(LIns *guard) { + SideExit *exit = guard->record()->exit; + Fragment *frag = exit->target; + GuardRecord *lr = 0; + bool destKnown = (frag && frag->fragEntry); + // Generate jump to epilog and initialize lr. + // If the guard is LIR_xtbl, use a jump table with epilog in every entry + if (guard->isop(LIR_xtbl)) { + NanoAssert(!guard->isop(LIR_xtbl)); + } else { + // If the guard already exists, use a simple jump. + if (destKnown) { + JMP(frag->fragEntry); + lr = 0; + } else { // target doesn't exist. Use 0 jump offset and patch later + if (!_epilogue) + _epilogue = genEpilogue(); + lr = guard->record(); + JMPl(_epilogue); + lr->jmp = _nIns; + } + } + + MR(RSP, RBP); + + // return value is GuardRecord* + asm_quad(RAX, uintptr_t(lr)); + } + + void Assembler::nInit(AvmCore*) { + } + + void Assembler::nBeginAssembly() { + max_stk_used = 0; + } + + // This should only be called from within emit() et al. + void Assembler::underrunProtect(ptrdiff_t bytes) { + NanoAssertMsg(bytes<=LARGEST_UNDERRUN_PROT, "constant LARGEST_UNDERRUN_PROT is too small"); + NIns *pc = _nIns; + NIns *top = codeStart; // this may be in a normal code chunk or an exit code chunk + + #if PEDANTIC + // pedanticTop is based on the last call to underrunProtect; any time we call + // underrunProtect and would use more than what's already protected, then insert + // a page break jump. Sometimes, this will be to a new page, usually it's just + // the next instruction + + NanoAssert(pedanticTop >= top); + if (pc - bytes < pedanticTop) { + // no page break required, but insert a far branch anyway just to be difficult + const int br_size = 8; // opcode + 32bit addr + if (pc - bytes - br_size < top) { + // really do need a page break + verbose_only(if (_logc->lcbits & LC_Assembly) outputf("newpage %p:", pc);) + // This may be in a normal code chunk or an exit code chunk. + codeAlloc(codeStart, codeEnd, _nIns verbose_only(, codeBytes)); + } + // now emit the jump, but make sure we won't need another page break. + // we're pedantic, but not *that* pedantic. + pedanticTop = _nIns - br_size; + JMP(pc); + pedanticTop = _nIns - bytes; + } + #else + if (pc - bytes < top) { + verbose_only(if (_logc->lcbits & LC_Assembly) outputf("newpage %p:", pc);) + // This may be in a normal code chunk or an exit code chunk. + codeAlloc(codeStart, codeEnd, _nIns verbose_only(, codeBytes)); + // This jump will call underrunProtect again, but since we're on a new + // page, nothing will happen. + JMP(pc); + } + #endif + } + + RegisterMask Assembler::hint(LIns *, RegisterMask allow) { + return allow; + } + + void Assembler::nativePageSetup() { + NanoAssert(!_inExit); + if (!_nIns) { + codeAlloc(codeStart, codeEnd, _nIns verbose_only(, codeBytes)); + IF_PEDANTIC( pedanticTop = _nIns; ) + } + if (!_nExitIns) { + codeAlloc(exitStart, exitEnd, _nExitIns verbose_only(, exitBytes)); + } + } + + void Assembler::nativePageReset() + {} + + // Increment the 32-bit profiling counter at pCtr, without + // changing any registers. + verbose_only( + void Assembler::asm_inc_m32(uint32_t* /*pCtr*/) + { + // todo: implement this + } + ) + + void Assembler::asm_jtbl(LIns* ins, NIns** table) + { + // exclude R12 because ESP and R12 cannot be used as an index + // (index=100 in SIB means "none") + Register indexreg = findRegFor(ins->oprnd1(), GpRegs & ~rmask(R12)); + if (isS32((intptr_t)table)) { + // table is in low 2GB or high 2GB, can use absolute addressing + // jmpq [indexreg*8 + table] + JMPX(indexreg, table); + } else { + // don't use R13 for base because we want to use mod=00, i.e. [index*8+base + 0] + Register tablereg = registerAllocTmp(GpRegs & ~(rmask(indexreg)|rmask(R13))); + // jmp [indexreg*8 + tablereg] + JMPXB(indexreg, tablereg); + // tablereg <- #table + asm_quad(tablereg, (uint64_t)table); + } + } + + void Assembler::swapCodeChunks() { + SWAP(NIns*, _nIns, _nExitIns); + SWAP(NIns*, codeStart, exitStart); + SWAP(NIns*, codeEnd, exitEnd); + verbose_only( SWAP(size_t, codeBytes, exitBytes); ) + } + +} // namespace nanojit + +#endif // FEATURE_NANOJIT && NANOJIT_X64 diff --git a/ape-server/deps/js/src/nanojit/NativeX64.h b/ape-server/deps/js/src/nanojit/NativeX64.h new file mode 100755 index 0000000..66e488e --- /dev/null +++ b/ape-server/deps/js/src/nanojit/NativeX64.h @@ -0,0 +1,568 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is [Open Source Virtual Machine]. + * + * The Initial Developer of the Original Code is + * Adobe System Incorporated. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adobe AS3 Team + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef __nanojit_NativeX64__ +#define __nanojit_NativeX64__ + +#ifndef NANOJIT_64BIT +#error "NANOJIT_64BIT must be defined for X64 backend" +#endif + +#ifdef PERFM +#define DOPROF +#include "../vprof/vprof.h" +#define count_instr() _nvprof("x64",1) +#define count_prolog() _nvprof("x64-prolog",1); count_instr(); +#define count_imt() _nvprof("x64-imt",1) count_instr() +#else +#define count_instr() +#define count_prolog() +#define count_imt() +#endif + +namespace nanojit +{ +#define NJ_MAX_STACK_ENTRY 256 +#define NJ_ALIGN_STACK 16 +#define NJ_JTBL_SUPPORTED 1 + + enum Register { + RAX = 0, // 1st int return, # of sse varargs + RCX = 1, // 4th int arg + RDX = 2, // 3rd int arg 2nd return + RBX = 3, // saved + RSP = 4, // stack ptr + RBP = 5, // frame ptr, saved, sib reqd + RSI = 6, // 2nd int arg + RDI = 7, // 1st int arg + R8 = 8, // 5th int arg + R9 = 9, // 6th int arg + R10 = 10, // scratch + R11 = 11, // scratch + R12 = 12, // saved + R13 = 13, // saved, sib reqd like rbp + R14 = 14, // saved + R15 = 15, // saved + + XMM0 = 16, // 1st double arg, return + XMM1 = 17, // 2nd double arg, return + XMM2 = 18, // 3rd double arg + XMM3 = 19, // 4th double arg + XMM4 = 20, // 5th double arg + XMM5 = 21, // 6th double arg + XMM6 = 22, // 7th double arg + XMM7 = 23, // 8th double arg + XMM8 = 24, // scratch + XMM9 = 25, // scratch + XMM10 = 26, // scratch + XMM11 = 27, // scratch + XMM12 = 28, // scratch + XMM13 = 29, // scratch + XMM14 = 30, // scratch + XMM15 = 31, // scratch + + FP = RBP, + UnknownReg = 32, + FirstReg = RAX, + LastReg = XMM15 + }; + +/* + * Micro-templating variable-length opcodes, idea first + * describe by Mike Pall of Luajit. + * + * X86-64 opcode encodings: LSB encodes the length of the + * opcode in bytes, remaining bytes are encoded as 1-7 bytes + * in a single uint64_t value. The value is written as a single + * store into the code stream, and the code pointer is decremented + * by the length. each successive instruction partially overlaps + * the previous one. + * + * emit methods below are able to encode mod/rm, sib, rex, and + * register and small immediate values into these opcode values + * without much branchy code. + * + * these opcodes encapsulate all the const parts of the instruction. + * for example, the alu-immediate opcodes (add, sub, etc) encode + * part of their opcode in the R field of the mod/rm byte; this + * hardcoded value is in the constant below, and the R argument + * to emitrr() is 0. In a few cases, a whole instruction is encoded + * this way (eg callrax). + * + * when a disp32, imm32, or imm64 suffix can't fit in an 8-byte + * opcode, then it is written into the code separately and not counted + * in the opcode length. + */ + + enum X64Opcode +#if defined(_MSC_VER) && _MSC_VER >= 1400 +#pragma warning(disable:4480) // nonstandard extension used: specifying underlying type for enum + : uint64_t +#endif + { + // 64bit opcode constants + // msb lsb len + X64_addqrr = 0xC003480000000003LL, // 64bit add r += b + X64_addqri = 0xC081480000000003LL, // 64bit add r += int64(imm32) + X64_addqr8 = 0x00C0834800000004LL, // 64bit add r += int64(imm8) + X64_andqri = 0xE081480000000003LL, // 64bit and r &= int64(imm32) + X64_andqr8 = 0x00E0834800000004LL, // 64bit and r &= int64(imm8) + X64_orqri = 0xC881480000000003LL, // 64bit or r |= int64(imm32) + X64_orqr8 = 0x00C8834800000004LL, // 64bit or r |= int64(imm8) + X64_xorqri = 0xF081480000000003LL, // 64bit xor r ^= int64(imm32) + X64_xorqr8 = 0x00F0834800000004LL, // 64bit xor r ^= int64(imm8) + X64_addlri = 0xC081400000000003LL, // 32bit add r += imm32 + X64_addlr8 = 0x00C0834000000004LL, // 32bit add r += imm8 + X64_andlri = 0xE081400000000003LL, // 32bit and r &= imm32 + X64_andlr8 = 0x00E0834000000004LL, // 32bit and r &= imm8 + X64_orlri = 0xC881400000000003LL, // 32bit or r |= imm32 + X64_orlr8 = 0x00C8834000000004LL, // 32bit or r |= imm8 + X64_sublri = 0xE881400000000003LL, // 32bit sub r -= imm32 + X64_sublr8 = 0x00E8834000000004LL, // 32bit sub r -= imm8 + X64_xorlri = 0xF081400000000003LL, // 32bit xor r ^= imm32 + X64_xorlr8 = 0x00F0834000000004LL, // 32bit xor r ^= imm8 + X64_addrr = 0xC003400000000003LL, // 32bit add r += b + X64_andqrr = 0xC023480000000003LL, // 64bit and r &= b + X64_andrr = 0xC023400000000003LL, // 32bit and r &= b + X64_call = 0x00000000E8000005LL, // near call + X64_callrax = 0xD0FF000000000002LL, // indirect call to addr in rax (no REX) + X64_cmovqno = 0xC0410F4800000004LL, // 64bit conditional mov if (no overflow) r = b + X64_cmovqnae= 0xC0420F4800000004LL, // 64bit conditional mov if (uint <) r = b + X64_cmovqnb = 0xC0430F4800000004LL, // 64bit conditional mov if (uint >=) r = b + X64_cmovqne = 0xC0450F4800000004LL, // 64bit conditional mov if (c) r = b + X64_cmovqna = 0xC0460F4800000004LL, // 64bit conditional mov if (uint <=) r = b + X64_cmovqnbe= 0xC0470F4800000004LL, // 64bit conditional mov if (uint >) r = b + X64_cmovqnge= 0xC04C0F4800000004LL, // 64bit conditional mov if (int <) r = b + X64_cmovqnl = 0xC04D0F4800000004LL, // 64bit conditional mov if (int >=) r = b + X64_cmovqng = 0xC04E0F4800000004LL, // 64bit conditional mov if (int <=) r = b + X64_cmovqnle= 0xC04F0F4800000004LL, // 64bit conditional mov if (int >) r = b + X64_cmovno = 0xC0410F4000000004LL, // 32bit conditional mov if (no overflow) r = b + X64_cmovnae = 0xC0420F4000000004LL, // 32bit conditional mov if (uint <) r = b + X64_cmovnb = 0xC0430F4000000004LL, // 32bit conditional mov if (uint >=) r = b + X64_cmovne = 0xC0450F4000000004LL, // 32bit conditional mov if (c) r = b + X64_cmovna = 0xC0460F4000000004LL, // 32bit conditional mov if (uint <=) r = b + X64_cmovnbe = 0xC0470F4000000004LL, // 32bit conditional mov if (uint >) r = b + X64_cmovnge = 0xC04C0F4000000004LL, // 32bit conditional mov if (int <) r = b + X64_cmovnl = 0xC04D0F4000000004LL, // 32bit conditional mov if (int >=) r = b + X64_cmovng = 0xC04E0F4000000004LL, // 32bit conditional mov if (int <=) r = b + X64_cmovnle = 0xC04F0F4000000004LL, // 32bit conditional mov if (int >) r = b + X64_cmplr = 0xC03B400000000003LL, // 32bit compare r,b + X64_cmpqr = 0xC03B480000000003LL, // 64bit compare r,b + X64_cmplri = 0xF881400000000003LL, // 32bit compare r,imm32 + X64_cmpqri = 0xF881480000000003LL, // 64bit compare r,int64(imm32) + X64_cmplr8 = 0x00F8834000000004LL, // 32bit compare r,imm8 + X64_cmpqr8 = 0x00F8834800000004LL, // 64bit compare r,int64(imm8) + X64_cvtsi2sd= 0xC02A0F40F2000005LL, // convert int32 to double r = (double) b + X64_cvtsq2sd= 0xC02A0F48F2000005LL, // convert int64 to double r = (double) b + X64_divsd = 0xC05E0F40F2000005LL, // divide scalar double r /= b + X64_mulsd = 0xC0590F40F2000005LL, // multiply scalar double r *= b + X64_addsd = 0xC0580F40F2000005LL, // add scalar double r += b + X64_idiv = 0xF8F7400000000003LL, // 32bit signed div (rax = rdx:rax/r, rdx=rdx:rax%r) + X64_imul = 0xC0AF0F4000000004LL, // 32bit signed mul r *= b + X64_imuli = 0xC069400000000003LL, // 32bit signed mul r = b * imm32 + X64_imul8 = 0x00C06B4000000004LL, // 32bit signed mul r = b * imm8 + X64_jmp = 0x00000000E9000005LL, // jump near rel32 + X64_jmp8 = 0x00EB000000000002LL, // jump near rel8 + X64_jo = 0x00000000800F0006LL, // jump near if overflow + X64_jb = 0x00000000820F0006LL, // jump near if below (uint <) + X64_jae = 0x00000000830F0006LL, // jump near if above or equal (uint >=) + X64_ja = 0x00000000870F0006LL, // jump near if above (uint >) + X64_jbe = 0x00000000860F0006LL, // jump near if below or equal (uint <=) + X64_je = 0x00000000840F0006LL, // near jump if equal + X64_jl = 0x000000008C0F0006LL, // jump near if less (int <) + X64_jge = 0x000000008D0F0006LL, // jump near if greater or equal (int >=) + X64_jg = 0x000000008F0F0006LL, // jump near if greater (int >) + X64_jle = 0x000000008E0F0006LL, // jump near if less or equal (int <=) + X64_jp = 0x000000008A0F0006LL, // jump near if parity (PF == 1) + X64_jneg = 0x0000000001000000LL, // xor with this mask to negate the condition + X64_jo8 = 0x0070000000000002LL, // jump near if overflow + X64_jb8 = 0x0072000000000002LL, // jump near if below (uint <) + X64_jae8 = 0x0073000000000002LL, // jump near if above or equal (uint >=) + X64_ja8 = 0x0077000000000002LL, // jump near if above (uint >) + X64_jbe8 = 0x0076000000000002LL, // jump near if below or equal (uint <=) + X64_je8 = 0x0074000000000002LL, // near jump if equal + X64_jne8 = 0x0075000000000002LL, // jump near if not equal + X64_jl8 = 0x007C000000000002LL, // jump near if less (int <) + X64_jge8 = 0x007D000000000002LL, // jump near if greater or equal (int >=) + X64_jg8 = 0x007F000000000002LL, // jump near if greater (int >) + X64_jle8 = 0x007E000000000002LL, // jump near if less or equal (int <=) + X64_jp8 = 0x007A000000000002LL, // jump near if parity (PF == 1) + X64_jnp8 = 0x007B000000000002LL, // jump near if not parity (PF == 0) + X64_jneg8 = 0x0001000000000000LL, // xor with this mask to negate the condition + X64_leaqrm = 0x00000000808D4807LL, // 64bit load effective addr reg <- disp32+base + X64_learm = 0x00000000808D4007LL, // 32bit load effective addr reg <- disp32+base + X64_learip = 0x00000000058D4807LL, // 64bit RIP-relative lea. reg <- disp32+rip (modrm = 00rrr101 = 05) + X64_movlr = 0xC08B400000000003LL, // 32bit mov r <- b + X64_movlmr = 0x0000000080894007LL, // 32bit store r -> [b+d32] + X64_movlrm = 0x00000000808B4007LL, // 32bit load r <- [b+d32] + X64_movqmr = 0x0000000080894807LL, // 64bit store gpr -> [b+d32] + X64_movqspr = 0x0024448948000005LL, // 64bit store gpr -> [rsp+d32] (sib required) + X64_movqr = 0xC08B480000000003LL, // 64bit mov r <- b + X64_movqi = 0xB848000000000002LL, // 64bit mov r <- imm64 + X64_movi = 0xB840000000000002LL, // 32bit mov r <- imm32 + X64_movqi32 = 0xC0C7480000000003LL, // 64bit mov r <- int64(imm32) + X64_movapsr = 0xC0280F4000000004LL, // 128bit mov xmm <- xmm + X64_movqrx = 0xC07E0F4866000005LL, // 64bit mov b <- xmm-r (reverses the usual r/b order) + X64_movqxr = 0xC06E0F4866000005LL, // 64bit mov b -> xmm-r + X64_movqrm = 0x00000000808B4807LL, // 64bit load r <- [b+d32] + X64_movsdrr = 0xC0100F40F2000005LL, // 64bit mov xmm-r <- xmm-b (upper 64bits unchanged) + X64_movsdrm = 0x80100F40F2000005LL, // 64bit load xmm-r <- [b+d32] (upper 64 cleared) + X64_movsdmr = 0x80110F40F2000005LL, // 64bit store xmm-r -> [b+d32] + X64_movsxdr = 0xC063480000000003LL, // sign extend i32 to i64 r = (int64)(int32) b + X64_movzx8 = 0xC0B60F4000000004LL, // zero extend i8 to i64 r = (uint64)(uint8) b + X64_movzx8m = 0x80B60F4000000004LL, // zero extend i8 load to i32 r <- [b+d32] + X64_movzx16m= 0x80B70F4000000004LL, // zero extend i16 load to i32 r <- [b+d32] + X64_neg = 0xD8F7400000000003LL, // 32bit two's compliment b = -b + X64_nop1 = 0x9000000000000001LL, // one byte NOP + X64_nop2 = 0x9066000000000002LL, // two byte NOP + X64_nop3 = 0x001F0F0000000003LL, // three byte NOP + X64_nop4 = 0x00401F0F00000004LL, // four byte NOP + X64_nop5 = 0x0000441F0F000005LL, // five byte NOP + X64_nop6 = 0x0000441F0F660006LL, // six byte NOP + X64_nop7 = 0x00000000801F0F07LL, // seven byte NOP + X64_not = 0xD0F7400000000003LL, // 32bit ones compliment b = ~b + X64_orlrr = 0xC00B400000000003LL, // 32bit or r |= b + X64_orqrr = 0xC00B480000000003LL, // 64bit or r |= b + X64_popr = 0x5840000000000002LL, // 64bit pop r <- [rsp++] + X64_pushr = 0x5040000000000002LL, // 64bit push r -> [--rsp] + X64_pxor = 0xC0EF0F4066000005LL, // 128bit xor xmm-r ^= xmm-b + X64_ret = 0xC300000000000001LL, // near return from called procedure + X64_sete = 0xC0940F4000000004LL, // set byte if equal (ZF == 1) + X64_seto = 0xC0900F4000000004LL, // set byte if overflow (OF == 1) + X64_setc = 0xC0920F4000000004LL, // set byte if carry (CF == 1) + X64_setl = 0xC09C0F4000000004LL, // set byte if less (int <) (SF != OF) + X64_setle = 0xC09E0F4000000004LL, // set byte if less or equal (int <=) (ZF == 1 || SF != OF) + X64_setg = 0xC09F0F4000000004LL, // set byte if greater (int >) (ZF == 0 && SF == OF) + X64_setge = 0xC09D0F4000000004LL, // set byte if greater or equal (int >=) (SF == OF) + X64_seta = 0xC0970F4000000004LL, // set byte if above (uint >) (CF == 0 && ZF == 0) + X64_setae = 0xC0930F4000000004LL, // set byte if above or equal (uint >=) (CF == 0) + X64_setb = 0xC0920F4000000004LL, // set byte if below (uint <) (CF == 1) + X64_setbe = 0xC0960F4000000004LL, // set byte if below or equal (uint <=) (ZF == 1 || CF == 1) + X64_subsd = 0xC05C0F40F2000005LL, // subtract scalar double r -= b + X64_shl = 0xE0D3400000000003LL, // 32bit left shift r <<= rcx + X64_shlq = 0xE0D3480000000003LL, // 64bit left shift r <<= rcx + X64_shr = 0xE8D3400000000003LL, // 32bit uint right shift r >>= rcx + X64_shrq = 0xE8D3480000000003LL, // 64bit uint right shift r >>= rcx + X64_sar = 0xF8D3400000000003LL, // 32bit int right shift r >>= rcx + X64_sarq = 0xF8D3480000000003LL, // 64bit int right shift r >>= rcx + X64_shli = 0x00E0C14000000004LL, // 32bit left shift r <<= imm8 + X64_shlqi = 0x00E0C14800000004LL, // 64bit left shift r <<= imm8 + X64_sari = 0x00F8C14000000004LL, // 32bit int right shift r >>= imm8 + X64_sarqi = 0x00F8C14800000004LL, // 64bit int right shift r >>= imm8 + X64_shri = 0x00E8C14000000004LL, // 32bit uint right shift r >>= imm8 + X64_shrqi = 0x00E8C14800000004LL, // 64bit uint right shift r >>= imm8 + X64_subqrr = 0xC02B480000000003LL, // 64bit sub r -= b + X64_subrr = 0xC02B400000000003LL, // 32bit sub r -= b + X64_subqri = 0xE881480000000003LL, // 64bit sub r -= int64(imm32) + X64_subqr8 = 0x00E8834800000004LL, // 64bit sub r -= int64(imm8) + X64_ucomisd = 0xC02E0F4066000005LL, // unordered compare scalar double + X64_xorqrr = 0xC033480000000003LL, // 64bit xor r &= b + X64_xorrr = 0xC033400000000003LL, // 32bit xor r &= b + X64_xorpd = 0xC0570F4066000005LL, // 128bit xor xmm (two packed doubles) + X64_xorps = 0xC0570F4000000004LL, // 128bit xor xmm (four packed singles), one byte shorter + X64_xorpsm = 0x05570F4000000004LL, // 128bit xor xmm, [rip+disp32] + X64_xorpsa = 0x2504570F40000005LL, // 128bit xor xmm, [disp32] + X64_jmpx = 0xC524ff4000000004LL, // jmp [d32+x*8] + X64_jmpxb = 0xC024ff4000000004LL, // jmp [b+x*8] + + X86_and8r = 0xC022000000000002LL, // and rl,rh + X86_sete = 0xC0940F0000000003LL, // no-rex version of X64_sete + X86_setnp = 0xC09B0F0000000003LL // no-rex set byte if odd parity (ordered fcmp result) (PF == 0) + }; + + typedef uint32_t RegisterMask; + + static const RegisterMask GpRegs = 0xffff; + static const RegisterMask FpRegs = 0xffff0000; +#ifdef _MSC_VER + static const RegisterMask SavedRegs = 1< + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +#include "nanojit.h" + +#ifdef _MSC_VER + // disable some specific warnings which are normally useful, but pervasive in the code-gen macros + #pragma warning(disable:4310) // cast truncates constant value +#endif + +namespace nanojit +{ + #if defined FEATURE_NANOJIT && defined NANOJIT_IA32 + + #ifdef NJ_VERBOSE + const char *regNames[] = { + "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", + "xmm0","xmm1","xmm2","xmm3","xmm4","xmm5","xmm6","xmm7", + "f0" + }; + #endif + + #define TODO(x) do{ verbose_only(outputf(#x);) NanoAssertMsgf(false, "%s", #x); } while(0) + + const Register Assembler::argRegs[] = { ECX, EDX }; + const Register Assembler::retRegs[] = { EAX, EDX }; + const Register Assembler::savedRegs[] = { EBX, ESI, EDI }; + + const static uint8_t max_abi_regs[] = { + 2, /* ABI_FASTCALL */ + 1, /* ABI_THISCALL */ + 0, /* ABI_STDCALL */ + 0 /* ABI_CDECL */ + }; + + + void Assembler::nInit(AvmCore* core) + { + (void) core; + VMPI_getDate(); + } + + void Assembler::nBeginAssembly() { + max_stk_args = 0; + } + + NIns* Assembler::genPrologue() + { + /** + * Prologue + */ + uint32_t stackNeeded = max_stk_args + STACK_GRANULARITY * _activation.tos; + + uint32_t stackPushed = + STACK_GRANULARITY + // returnaddr + STACK_GRANULARITY; // ebp + + uint32_t aligned = alignUp(stackNeeded + stackPushed, NJ_ALIGN_STACK); + uint32_t amt = aligned - stackPushed; + + // Reserve stackNeeded bytes, padded + // to preserve NJ_ALIGN_STACK-byte alignment. + if (amt) + { + SUBi(SP, amt); + } + + verbose_only( outputAddr=true; asm_output("[frag entry]"); ) + NIns *fragEntry = _nIns; + MR(FP, SP); // Establish our own FP. + PUSHr(FP); // Save caller's FP. + + return fragEntry; + } + + void Assembler::nFragExit(LInsp guard) + { + SideExit *exit = guard->record()->exit; + bool trees = config.tree_opt; + Fragment *frag = exit->target; + GuardRecord *lr = 0; + bool destKnown = (frag && frag->fragEntry); + + // Generate jump to epilog and initialize lr. + // If the guard is LIR_xtbl, use a jump table with epilog in every entry + if (guard->isop(LIR_xtbl)) { + lr = guard->record(); + Register r = EDX; + SwitchInfo* si = guard->record()->exit->switchInfo; + if (!_epilogue) + _epilogue = genEpilogue(); + emitJumpTable(si, _epilogue); + JMP_indirect(r); + LEAmi4(r, si->table, r); + } else { + // If the guard already exists, use a simple jump. + if (destKnown && !trees) { + JMP(frag->fragEntry); + lr = 0; + } else { // Target doesn't exist. Jump to an epilogue for now. This can be patched later. + if (!_epilogue) + _epilogue = genEpilogue(); + lr = guard->record(); + JMP_long(_epilogue); + lr->jmp = _nIns; + } + } + + // profiling for the exit + verbose_only( + if (_logc->lcbits & LC_FragProfile) { + INCLi( &guard->record()->profCount ); + } + ) + + // Restore ESP from EBP, undoing SUBi(SP,amt) in the prologue + MR(SP,FP); + + // return value is GuardRecord* + LDi(EAX, int(lr)); + } + + NIns *Assembler::genEpilogue() + { + RET(); + POPr(FP); // Restore caller's FP. + + return _nIns; + } + + void Assembler::asm_call(LInsp ins) + { + const CallInfo* call = ins->callInfo(); + // must be signed, not unsigned + uint32_t iargs = call->count_iargs(); + int32_t fargs = call->count_args() - iargs; + + bool indirect = call->isIndirect(); + if (indirect) { + // target arg isn't pushed, its consumed in the call + iargs --; + } + + AbiKind abi = call->_abi; + uint32_t max_regs = max_abi_regs[abi]; + if (max_regs > iargs) + max_regs = iargs; + + int32_t istack = iargs-max_regs; // first 2 4B args are in registers + int32_t extra = 0; + const int32_t pushsize = 4*istack + 8*fargs; // actual stack space used + +#if _MSC_VER + // msc only provides 4-byte alignment, anything more than 4 on windows + // x86-32 requires dynamic ESP alignment in prolog/epilog and static + // esp-alignment here. + uint32_t align = 4;//NJ_ALIGN_STACK; +#else + uint32_t align = NJ_ALIGN_STACK; +#endif + + if (pushsize) { + if (config.fixed_esp) { + // In case of fastcall, stdcall and thiscall the callee cleans up the stack, + // and since we reserve max_stk_args words in the prolog to call functions + // and don't adjust the stack pointer individually for each call we have + // to undo here any changes the callee just did to the stack. + if (abi != ABI_CDECL) + SUBi(SP, pushsize); + } else { + // stack re-alignment + // only pop our adjustment amount since callee pops args in FASTCALL mode + extra = alignUp(pushsize, align) - pushsize; + if (call->_abi == ABI_CDECL) { + // with CDECL only, caller pops args + ADDi(SP, extra+pushsize); + } else if (extra > 0) { + ADDi(SP, extra); + } + } + } + + NanoAssert(ins->isop(LIR_pcall) || ins->isop(LIR_fcall)); + if (!indirect) { + CALL(call); + } + else { + // indirect call. x86 Calling conventions don't use EAX as an + // argument, and do use EAX as a return value. We need a register + // for the address to call, so we use EAX since it will always be + // available + CALLr(call, EAX); + } + + // make sure fpu stack is empty before call (restoreCallerSaved) + NanoAssert(_allocator.isFree(FST0)); + // note: this code requires that ref arguments (ARGSIZE_Q) + // be one of the first two arguments + // pre-assign registers to the first N 4B args based on the calling convention + uint32_t n = 0; + + ArgSize sizes[MAXARGS]; + uint32_t argc = call->get_sizes(sizes); + int32_t stkd = 0; + + if (indirect) { + argc--; + asm_arg(ARGSIZE_P, ins->arg(argc), EAX, stkd); + if (!config.fixed_esp) + stkd = 0; + } + + for(uint32_t i=0; i < argc; i++) + { + uint32_t j = argc-i-1; + ArgSize sz = sizes[j]; + Register r = UnknownReg; + if (n < max_regs && sz != ARGSIZE_F) { + r = argRegs[n++]; // tell asm_arg what reg to use + } + asm_arg(sz, ins->arg(j), r, stkd); + if (!config.fixed_esp) + stkd = 0; + } + + if (config.fixed_esp) { + if (pushsize > max_stk_args) + max_stk_args = pushsize; + } else if (extra > 0) { + SUBi(SP, extra); + } + } + + Register Assembler::nRegisterAllocFromSet(RegisterMask set) + { + Register r; + RegAlloc ®s = _allocator; + #ifdef WIN32 + _asm + { + mov ecx, regs + bsf eax, set // i = first bit set + btr RegAlloc::free[ecx], eax // free &= ~rmask(i) + mov r, eax + } + #else + asm( + "bsf %1, %%eax\n\t" + "btr %%eax, %2\n\t" + "movl %%eax, %0\n\t" + : "=m"(r) : "m"(set), "m"(regs.free) : "%eax", "memory" ); + #endif /* WIN32 */ + return r; + } + + void Assembler::nRegisterResetAll(RegAlloc& a) + { + // add scratch registers to our free list for the allocator + a.clear(); + a.free = SavedRegs | ScratchRegs; + if (!config.sse2) + a.free &= ~XmmRegs; + debug_only( a.managed = a.free; ) + } + + void Assembler::nPatchBranch(NIns* branch, NIns* targ) + { + intptr_t offset = intptr_t(targ) - intptr_t(branch); + if (branch[0] == JMP32) { + *(int32_t*)&branch[1] = offset - 5; + } else if (branch[0] == JCC32) { + *(int32_t*)&branch[2] = offset - 6; + } else + NanoAssertMsg(0, "Unknown branch type in nPatchBranch"); + } + + RegisterMask Assembler::hint(LIns* i, RegisterMask allow) + { + uint32_t op = i->opcode(); + int prefer = allow; + if (op == LIR_icall) { + prefer &= rmask(retRegs[0]); + } + else if (op == LIR_fcall) { + prefer &= rmask(FST0); + } + else if (op == LIR_param) { + if (i->paramKind() == 0) { + uint32_t max_regs = max_abi_regs[_thisfrag->lirbuf->abi]; + if (i->paramArg() < max_regs) + prefer &= rmask(argRegs[i->paramArg()]); + } else { + if (i->paramArg() < NumSavedRegs) + prefer &= rmask(savedRegs[i->paramArg()]); + } + } + else if (op == LIR_callh || (op == LIR_rsh && i->oprnd1()->opcode()==LIR_callh)) { + prefer &= rmask(retRegs[1]); + } + else if (i->isCmp()) { + prefer &= AllowableFlagRegs; + } + else if (i->isconst()) { + prefer &= ScratchRegs; + } + return (_allocator.free & prefer) ? prefer : allow; + } + + void Assembler::asm_qjoin(LIns *ins) + { + int d = findMemFor(ins); + AvmAssert(d); + LIns* lo = ins->oprnd1(); + LIns* hi = ins->oprnd2(); + + Register rr = ins->getReg(); + if (isKnownReg(rr) && (rmask(rr) & FpRegs)) + evict(rr, ins); + + if (hi->isconst()) + { + STi(FP, d+4, hi->imm32()); + } + else + { + Register r = findRegFor(hi, GpRegs); + ST(FP, d+4, r); + } + + if (lo->isconst()) + { + STi(FP, d, lo->imm32()); + } + else + { + // okay if r gets recycled. + Register r = findRegFor(lo, GpRegs); + ST(FP, d, r); + } + + freeRsrcOf(ins, false); // if we had a reg in use, emit a ST to flush it to mem + } + + void Assembler::asm_load(int d, Register r) + { + if (rmask(r) & FpRegs) + { + if (rmask(r) & XmmRegs) { + SSE_LDQ(r, d, FP); + } else { + FLDQ(d, FP); + } + } + else + { + LD(r, d, FP); + } + } + + // WARNING: the code generated by this function must not affect the + // condition codes. See asm_cmp(). + void Assembler::asm_restore(LInsp i, Register r) + { + uint32_t arg; + uint32_t abi_regcount; + if (i->isop(LIR_alloc)) { + LEA(r, disp(i), FP); + } + else if (i->isconst()) { + if (!i->getArIndex()) { + i->markAsClear(); + } + LDi(r, i->imm32()); + } + else if (i->isop(LIR_param) && i->paramKind() == 0 && + (arg = i->paramArg()) >= (abi_regcount = max_abi_regs[_thisfrag->lirbuf->abi])) { + // incoming arg is on stack, can restore it from there instead of spilling + if (!i->getArIndex()) { + i->markAsClear(); + } + // compute position of argument relative to ebp. higher argument + // numbers are at higher positive offsets. the first abi_regcount + // arguments are in registers, rest on stack. +8 accomodates the + // return address and saved ebp value. assuming abi_regcount == 0: + // low-addr ebp + // [frame...][saved-ebp][return-addr][arg0][arg1]... + int d = (arg - abi_regcount) * sizeof(intptr_t) + 8; + LD(r, d, FP); + } + else { + int d = findMemFor(i); + asm_load(d,r); + } + } + + void Assembler::asm_store32(LIns *value, int dr, LIns *base) + { + if (value->isconst()) + { + Register rb = getBaseReg(LIR_sti, base, dr, GpRegs); + int c = value->imm32(); + STi(rb, dr, c); + } + else + { + // make sure what is in a register + Register ra, rb; + if (base->isop(LIR_alloc)) { + rb = FP; + dr += findMemFor(base); + ra = findRegFor(value, GpRegs); + } else if (base->isconst()) { + // absolute address + dr += base->imm32(); + ra = findRegFor(value, GpRegs); + rb = UnknownReg; + } else { + findRegFor2(GpRegs, value, ra, base, rb); + } + ST(rb, dr, ra); + } + } + + void Assembler::asm_spill(Register rr, int d, bool pop, bool quad) + { + (void)quad; + if (d) + { + // save to spill location + if (rmask(rr) & FpRegs) + { + if (rmask(rr) & XmmRegs) { + SSE_STQ(d, FP, rr); + } else { + FSTQ((pop?1:0), d, FP); + } + } + else + { + ST(FP, d, rr); + } + } + else if (pop && (rmask(rr) & x87Regs)) + { + // pop the fpu result since it isn't used + FSTP(FST0); + } + } + + void Assembler::asm_load64(LInsp ins) + { + LIns* base = ins->oprnd1(); + int db = ins->disp(); + Register rr = ins->getReg(); + + if (isKnownReg(rr) && rmask(rr) & XmmRegs) + { + freeRsrcOf(ins, false); + Register rb = getBaseReg(ins->opcode(), base, db, GpRegs); + SSE_LDQ(rr, db, rb); + } + else + { + int dr = disp(ins); + Register rb; + if (base->isop(LIR_alloc)) { + rb = FP; + db += findMemFor(base); + } else { + rb = findRegFor(base, GpRegs); + } + ins->setReg(UnknownReg); + + // don't use an fpu reg to simply load & store the value. + if (dr) + asm_mmq(FP, dr, rb, db); + + freeRsrcOf(ins, false); + + if (isKnownReg(rr)) + { + NanoAssert(rmask(rr)&FpRegs); + _allocator.retire(rr); + FLDQ(db, rb); + } + } + } + + void Assembler::asm_store64(LInsp value, int dr, LInsp base) + { + if (value->isconstq()) + { + // if a constant 64-bit value just store it now rather than + // generating a pointless store/load/store sequence + Register rb; + if (base->isop(LIR_alloc)) { + rb = FP; + dr += findMemFor(base); + } else { + rb = findRegFor(base, GpRegs); + } + STi(rb, dr+4, value->imm64_1()); + STi(rb, dr, value->imm64_0()); + return; + } + + if (value->isop(LIR_ldq) || value->isop(LIR_ldqc) || value->isop(LIR_qjoin)) + { + // value is 64bit struct or int64_t, or maybe a double. + // it may be live in an FPU reg. Either way, don't + // put it in an FPU reg just to load & store it. + + // a) if we know it's not a double, this is right. + // b) if we guarded that its a double, this store could be on + // the side exit, copying a non-double. + // c) maybe its a double just being stored. oh well. + + if (config.sse2) { + Register rv = findRegFor(value, XmmRegs); + Register rb; + if (base->isop(LIR_alloc)) { + rb = FP; + dr += findMemFor(base); + } else { + rb = findRegFor(base, GpRegs); + } + SSE_STQ(dr, rb, rv); + return; + } + + int da = findMemFor(value); + Register rb; + if (base->isop(LIR_alloc)) { + rb = FP; + dr += findMemFor(base); + } else { + rb = findRegFor(base, GpRegs); + } + asm_mmq(rb, dr, FP, da); + return; + } + + Register rb; + if (base->isop(LIR_alloc)) { + rb = FP; + dr += findMemFor(base); + } else { + rb = findRegFor(base, GpRegs); + } + + // if value already in a reg, use that, otherwise + // try to get it into XMM regs before FPU regs. + bool pop = value->isUnusedOrHasUnknownReg(); + Register rv = ( pop + ? findRegFor(value, config.sse2 ? XmmRegs : FpRegs) + : value->getReg() ); + + if (rmask(rv) & XmmRegs) { + SSE_STQ(dr, rb, rv); + } else { + FSTQ(pop?1:0, dr, rb); + } + } + + /** + * copy 64 bits: (rd+dd) <- (rs+ds) + */ + void Assembler::asm_mmq(Register rd, int dd, Register rs, int ds) + { + // value is either a 64bit struct or maybe a float + // that isn't live in an FPU reg. Either way, don't + // put it in an FPU reg just to load & store it. + if (config.sse2) + { + // use SSE to load+store 64bits + Register t = registerAllocTmp(XmmRegs); + SSE_STQ(dd, rd, t); + SSE_LDQ(t, ds, rs); + } + else + { + // get a scratch reg + Register t = registerAllocTmp(GpRegs & ~(rmask(rd)|rmask(rs))); + ST(rd, dd+4, t); + LD(t, ds+4, rs); + ST(rd, dd, t); + LD(t, ds, rs); + } + } + + NIns* Assembler::asm_branch(bool branchOnFalse, LInsp cond, NIns* targ) + { + LOpcode condop = cond->opcode(); + NanoAssert(cond->isCond()); + + // Handle float conditions separately. + if (condop >= LIR_feq && condop <= LIR_fge) { + return asm_fbranch(branchOnFalse, cond, targ); + } + + if (branchOnFalse) { + // op == LIR_xf + switch (condop) { + case LIR_ov: JNO(targ); break; + case LIR_eq: JNE(targ); break; + case LIR_lt: JNL(targ); break; + case LIR_le: JNLE(targ); break; + case LIR_gt: JNG(targ); break; + case LIR_ge: JNGE(targ); break; + case LIR_ult: JNB(targ); break; + case LIR_ule: JNBE(targ); break; + case LIR_ugt: JNA(targ); break; + case LIR_uge: JNAE(targ); break; + default: NanoAssert(0); break; + } + } else { + // op == LIR_xt + switch (condop) { + case LIR_ov: JO(targ); break; + case LIR_eq: JE(targ); break; + case LIR_lt: JL(targ); break; + case LIR_le: JLE(targ); break; + case LIR_gt: JG(targ); break; + case LIR_ge: JGE(targ); break; + case LIR_ult: JB(targ); break; + case LIR_ule: JBE(targ); break; + case LIR_ugt: JA(targ); break; + case LIR_uge: JAE(targ); break; + default: NanoAssert(0); break; + } + } + NIns* at = _nIns; + asm_cmp(cond); + return at; + } + + void Assembler::asm_switch(LIns* ins, NIns* exit) + { + LIns* diff = ins->oprnd1(); + findSpecificRegFor(diff, EDX); + JMP(exit); + } + + void Assembler::asm_jtbl(LIns* ins, NIns** table) + { + Register indexreg = findRegFor(ins->oprnd1(), GpRegs); + JMP_indexed(indexreg, 2, table); + } + + // This generates a 'test' or 'cmp' instruction for a condition, which + // causes the condition codes to be set appropriately. It's used with + // conditional branches, conditional moves, and when generating + // conditional values. For example: + // + // LIR: eq1 = eq a, 0 + // LIR: xf1: xf eq1 -> ... + // asm: test edx, edx # generated by this function + // asm: je ... + // + // If this is the only use of eq1, then on entry 'cond' is *not* marked as + // used, and we do not allocate a register for it. That's because its + // result ends up in the condition codes rather than a normal register. + // This doesn't get recorded in the regstate and so the asm code that + // consumes the result (eg. a conditional branch like 'je') must follow + // shortly after. + // + // If eq1 is instead used again later, we will also generate code + // (eg. in asm_cond()) to compute it into a normal register, something + // like this: + // + // LIR: eq1 = eq a, 0 + // LIR: test edx, edx + // asm: sete ebx + // asm: movzx ebx, ebx + // + // In this case we end up computing the condition twice, but that's ok, as + // it's just as short as testing eq1's value in the code generated for the + // guard. + // + // WARNING: Because the condition code update is not recorded in the + // regstate, this function cannot generate any code that will affect the + // condition codes prior to the generation of the test/cmp, because any + // such code will be run after the test/cmp but before the instruction + // that consumes the condition code. And because this function calls + // findRegFor() before the test/cmp is generated, and findRegFor() calls + // asm_restore(), that means that asm_restore() cannot generate code which + // affects the condition codes. + // + void Assembler::asm_cmp(LIns *cond) + { + LOpcode condop = cond->opcode(); + + // LIR_ov recycles the flags set by arithmetic ops + if (condop == LIR_ov) + return; + + LInsp lhs = cond->oprnd1(); + LInsp rhs = cond->oprnd2(); + + NanoAssert((!lhs->isQuad() && !rhs->isQuad()) || (lhs->isQuad() && rhs->isQuad())); + + // Not supported yet. + NanoAssert(!lhs->isQuad() && !rhs->isQuad()); + + // ready to issue the compare + if (rhs->isconst()) + { + int c = rhs->imm32(); + if (c == 0 && cond->isop(LIR_eq)) { + Register r = findRegFor(lhs, GpRegs); + TEST(r,r); + } else if (!rhs->isQuad()) { + Register r = getBaseReg(condop, lhs, c, GpRegs); + CMPi(r, c); + } + } + else + { + Register ra, rb; + findRegFor2(GpRegs, lhs, ra, rhs, rb); + CMP(ra, rb); + } + } + + void Assembler::asm_fcond(LInsp ins) + { + LOpcode opcode = ins->opcode(); + + // only want certain regs + Register r = prepResultReg(ins, AllowableFlagRegs); + + // SETcc only sets low 8 bits, so extend + MOVZX8(r,r); + + if (config.sse2) { + // LIR_flt and LIR_fgt are handled by the same case because + // asm_fcmp() converts LIR_flt(a,b) to LIR_fgt(b,a). Likewise + // for LIR_fle/LIR_fge. + switch (opcode) { + case LIR_feq: SETNP(r); break; + case LIR_flt: + case LIR_fgt: SETA(r); break; + case LIR_fle: + case LIR_fge: SETAE(r); break; + default: NanoAssert(0); break; + } + } else { + SETNP(r); + } + asm_fcmp(ins); + } + + void Assembler::asm_cond(LInsp ins) + { + // only want certain regs + LOpcode op = ins->opcode(); + Register r = prepResultReg(ins, AllowableFlagRegs); + // SETcc only sets low 8 bits, so extend + MOVZX8(r,r); + switch (op) { + case LIR_ov: SETO(r); break; + case LIR_eq: SETE(r); break; + case LIR_lt: SETL(r); break; + case LIR_le: SETLE(r); break; + case LIR_gt: SETG(r); break; + case LIR_ge: SETGE(r); break; + case LIR_ult: SETB(r); break; + case LIR_ule: SETBE(r); break; + case LIR_ugt: SETA(r); break; + case LIR_uge: SETAE(r); break; + default: NanoAssert(0); break; + } + asm_cmp(ins); + } + + void Assembler::asm_arith(LInsp ins) + { + LOpcode op = ins->opcode(); + LInsp lhs = ins->oprnd1(); + + if (op == LIR_mod) { + asm_div_mod(ins); + return; + } + + LInsp rhs = ins->oprnd2(); + + bool forceReg; + RegisterMask allow = GpRegs; + Register rb = UnknownReg; + + switch (op) { + case LIR_div: + // Nb: if the div feeds into a mod it will be handled by + // asm_div_mod() rather than here. + forceReg = true; + rb = findRegFor(rhs, (GpRegs ^ (rmask(EAX)|rmask(EDX)))); + allow = rmask(EAX); + evictIfActive(EDX); + break; + case LIR_mul: + forceReg = true; + break; + case LIR_lsh: + case LIR_rsh: + case LIR_ush: + forceReg = !rhs->isconst(); + if (forceReg) { + rb = findSpecificRegFor(rhs, ECX); + allow &= ~rmask(rb); + } + break; + case LIR_add: + case LIR_addp: + if (lhs->isop(LIR_alloc) && rhs->isconst()) { + // add alloc+const, use lea + Register rr = prepResultReg(ins, allow); + int d = findMemFor(lhs) + rhs->imm32(); + LEA(rr, d, FP); + return; + } + /* fall through */ + default: + forceReg = !rhs->isconst(); + break; + } + + // if we need a register for the rhs and don't have one yet, get it + if (forceReg && lhs != rhs && !isKnownReg(rb)) { + rb = findRegFor(rhs, allow); + allow &= ~rmask(rb); + } + + Register rr = prepResultReg(ins, allow); + // if this is last use of lhs in reg, we can re-use result reg + // else, lhs already has a register assigned. + Register ra = ( lhs->isUnusedOrHasUnknownReg() + ? findSpecificRegForUnallocated(lhs, rr) + : lhs->getReg() ); + + if (forceReg) + { + if (lhs == rhs) + rb = ra; + + switch (op) { + case LIR_add: + case LIR_addp: + ADD(rr, rb); + break; + case LIR_sub: + SUB(rr, rb); + break; + case LIR_mul: + MUL(rr, rb); + break; + case LIR_and: + AND(rr, rb); + break; + case LIR_or: + OR(rr, rb); + break; + case LIR_xor: + XOR(rr, rb); + break; + case LIR_lsh: + SHL(rr, rb); + break; + case LIR_rsh: + SAR(rr, rb); + break; + case LIR_ush: + SHR(rr, rb); + break; + case LIR_div: + DIV(rb); + CDQ(); + break; + default: + NanoAssertMsg(0, "Unsupported"); + } + } + else + { + int c = rhs->imm32(); + switch (op) { + case LIR_addp: + // this doesn't set cc's, only use it when cc's not required. + LEA(rr, c, ra); + ra = rr; // suppress mov + break; + case LIR_add: + ADDi(rr, c); + break; + case LIR_sub: + SUBi(rr, c); + break; + case LIR_and: + ANDi(rr, c); + break; + case LIR_or: + ORi(rr, c); + break; + case LIR_xor: + XORi(rr, c); + break; + case LIR_lsh: + SHLi(rr, c); + break; + case LIR_rsh: + SARi(rr, c); + break; + case LIR_ush: + SHRi(rr, c); + break; + default: + NanoAssertMsg(0, "Unsupported"); + break; + } + } + + if ( rr != ra ) + MR(rr,ra); + } + + // This is called when we have a mod(div(divLhs, divRhs)) sequence. + void Assembler::asm_div_mod(LInsp mod) + { + LInsp div = mod->oprnd1(); + + // LIR_mod expects the LIR_div to be near (no interference from the register allocator) + + NanoAssert(mod->isop(LIR_mod)); + NanoAssert(div->isop(LIR_div)); + + LInsp divLhs = div->oprnd1(); + LInsp divRhs = div->oprnd2(); + + prepResultReg(mod, rmask(EDX)); + prepResultReg(div, rmask(EAX)); + + Register rDivRhs = findRegFor(divRhs, (GpRegs ^ (rmask(EAX)|rmask(EDX)))); + + Register rDivLhs = ( divLhs->isUnusedOrHasUnknownReg() + ? findSpecificRegFor(divLhs, EAX) + : divLhs->getReg() ); + + DIV(rDivRhs); + CDQ(); // sign-extend EAX into EDX:EAX + + if ( EAX != rDivLhs ) + MR(EAX, rDivLhs); + } + + void Assembler::asm_neg_not(LInsp ins) + { + LOpcode op = ins->opcode(); + Register rr = prepResultReg(ins, GpRegs); + + LIns* lhs = ins->oprnd1(); + // if this is last use of lhs in reg, we can re-use result reg + // else, lhs already has a register assigned. + Register ra = ( lhs->isUnusedOrHasUnknownReg() + ? findSpecificRegForUnallocated(lhs, rr) + : lhs->getReg() ); + + if (op == LIR_not) + NOT(rr); + else + NEG(rr); + + if ( rr != ra ) + MR(rr,ra); + } + + void Assembler::asm_ld(LInsp ins) + { + LOpcode op = ins->opcode(); + LIns* base = ins->oprnd1(); + int32_t d = ins->disp(); + Register rr = prepResultReg(ins, GpRegs); + + if (base->isconst()) { + intptr_t addr = base->imm32(); + addr += d; + if (op == LIR_ldcb) + LD8Zdm(rr, addr); + else if (op == LIR_ldcs) + LD16Zdm(rr, addr); + else + LDdm(rr, addr); + return; + } + + /* Search for add(X,Y) */ + if (base->opcode() == LIR_piadd) { + int scale = 0; + LIns *lhs = base->oprnd1(); + LIns *rhs = base->oprnd2(); + + /* See if we can bypass any SHLs, by searching for + * add(X, shl(Y,Z)) -> mov r, [X+Y*Z] + */ + if (rhs->opcode() == LIR_pilsh && rhs->oprnd2()->isconst()) { + scale = rhs->oprnd2()->imm32(); + if (scale >= 1 && scale <= 3) + rhs = rhs->oprnd1(); + else + scale = 0; + } + + /* Does LHS have a register yet? If not, re-use the result reg. + * @todo -- If LHS is const, we could eliminate a register use. + */ + Register rleft = ( lhs->isUnusedOrHasUnknownReg() + ? findSpecificRegForUnallocated(lhs, rr) + : lhs->getReg() ); + + /* Does RHS have a register yet? If not, try to re-use the result reg. */ + Register rright = ( rr != rleft && rhs->isUnusedOrHasUnknownReg() + ? findSpecificRegForUnallocated(rhs, rr) + : findRegFor(rhs, GpRegs & ~(rmask(rleft))) ); + + if (op == LIR_ldcb) + LD8Zsib(rr, d, rleft, rright, scale); + else if (op == LIR_ldcs) + LD16Zsib(rr, d, rleft, rright, scale); + else + LDsib(rr, d, rleft, rright, scale); + + return; + } + + Register ra = getBaseReg(op, base, d, GpRegs); + if (op == LIR_ldcb) + LD8Z(rr, d, ra); + else if (op == LIR_ldcs) + LD16Z(rr, d, ra); + else + LD(rr, d, ra); + } + + void Assembler::asm_cmov(LInsp ins) + { + LOpcode op = ins->opcode(); + LIns* condval = ins->oprnd1(); + LIns* iftrue = ins->oprnd2(); + LIns* iffalse = ins->oprnd3(); + + NanoAssert(condval->isCmp()); + NanoAssert(op == LIR_qcmov || (!iftrue->isQuad() && !iffalse->isQuad())); + + const Register rr = prepResultReg(ins, GpRegs); + + // this code assumes that neither LD nor MR nor MRcc set any of the condition flags. + // (This is true on Intel, is it true on all architectures?) + const Register iffalsereg = findRegFor(iffalse, GpRegs & ~rmask(rr)); + if (op == LIR_cmov) { + switch (condval->opcode()) + { + // note that these are all opposites... + case LIR_eq: MRNE(rr, iffalsereg); break; + case LIR_ov: MRNO(rr, iffalsereg); break; + case LIR_lt: MRGE(rr, iffalsereg); break; + case LIR_le: MRG(rr, iffalsereg); break; + case LIR_gt: MRLE(rr, iffalsereg); break; + case LIR_ge: MRL(rr, iffalsereg); break; + case LIR_ult: MRAE(rr, iffalsereg); break; + case LIR_ule: MRA(rr, iffalsereg); break; + case LIR_ugt: MRBE(rr, iffalsereg); break; + case LIR_uge: MRB(rr, iffalsereg); break; + default: NanoAssert(0); break; + } + } else if (op == LIR_qcmov) { + NanoAssert(0); + } + /*const Register iftruereg =*/ findSpecificRegFor(iftrue, rr); + asm_cmp(condval); + } + + void Assembler::asm_qhi(LInsp ins) + { + Register rr = prepResultReg(ins, GpRegs); + LIns *q = ins->oprnd1(); + int d = findMemFor(q); + LD(rr, d+4, FP); + } + + void Assembler::asm_param(LInsp ins) + { + uint32_t a = ins->paramArg(); + uint32_t kind = ins->paramKind(); + if (kind == 0) { + // ordinary param + AbiKind abi = _thisfrag->lirbuf->abi; + uint32_t abi_regcount = max_abi_regs[abi]; + if (a < abi_regcount) { + // incoming arg in register + prepResultReg(ins, rmask(argRegs[a])); + } else { + // incoming arg is on stack, and EBP points nearby (see genPrologue) + Register r = prepResultReg(ins, GpRegs); + int d = (a - abi_regcount) * sizeof(intptr_t) + 8; + LD(r, d, FP); + } + } + else { + // saved param + prepResultReg(ins, rmask(savedRegs[a])); + } + } + + void Assembler::asm_int(LInsp ins) + { + Register rr = prepResultReg(ins, GpRegs); + int32_t val = ins->imm32(); + if (val == 0) + XOR(rr,rr); + else + LDi(rr, val); + } + + void Assembler::asm_quad(LInsp ins) + { + Register rr = ins->getReg(); + if (isKnownReg(rr)) + { + // @todo -- add special-cases for 0 and 1 + _allocator.retire(rr); + ins->setReg(UnknownReg); + NanoAssert((rmask(rr) & FpRegs) != 0); + + const double d = ins->imm64f(); + const uint64_t q = ins->imm64(); + if (rmask(rr) & XmmRegs) { + if (q == 0.0) { + // test (int64)0 since -0.0 == 0.0 + SSE_XORPDr(rr, rr); + } else if (d == 1.0) { + // 1.0 is extremely frequent and worth special-casing! + static const double k_ONE = 1.0; + LDSDm(rr, &k_ONE); + } else if (d && d == (int)d) { + // can fit in 32bits? then use cvt which is faster + Register gr = registerAllocTmp(GpRegs); + SSE_CVTSI2SD(rr, gr); + SSE_XORPDr(rr,rr); // zero rr to ensure no dependency stalls + LDi(gr, (int)d); + } else { + findMemFor(ins); + const int d = disp(ins); + SSE_LDQ(rr, d, FP); + } + } else { + if (q == 0.0) { + // test (int64)0 since -0.0 == 0.0 + FLDZ(); + } else if (d == 1.0) { + FLD1(); + } else { + findMemFor(ins); + int d = disp(ins); + FLDQ(d,FP); + } + } + } + + // @todo, if we used xor, ldsd, fldz, etc above, we don't need mem here + int d = disp(ins); + freeRsrcOf(ins, false); + if (d) + { + STi(FP,d+4,ins->imm64_1()); + STi(FP,d, ins->imm64_0()); + } + } + + void Assembler::asm_qlo(LInsp ins) + { + LIns *q = ins->oprnd1(); + + if (!config.sse2) + { + Register rr = prepResultReg(ins, GpRegs); + int d = findMemFor(q); + LD(rr, d, FP); + } + else + { + Register rr = ins->getReg(); + if (!isKnownReg(rr)) { + // store quad in spill loc + int d = disp(ins); + freeRsrcOf(ins, false); + Register qr = findRegFor(q, XmmRegs); + SSE_MOVDm(d, FP, qr); + } else { + freeRsrcOf(ins, false); + Register qr = findRegFor(q, XmmRegs); + SSE_MOVD(rr,qr); + } + } + } + + // negateMask is used by asm_fneg. +#if defined __SUNPRO_CC + // From Sun Studio C++ Readme: #pragma align inside namespace requires mangled names. + // Initialize here to avoid multithreading contention issues during initialization. + static uint32_t negateMask_temp[] = {0, 0, 0, 0, 0, 0, 0}; + + static uint32_t* negateMaskInit() + { + uint32_t* negateMask = (uint32_t*)alignUp(negateMask_temp, 16); + negateMask[1] = 0x80000000; + return negateMask; + } + + static uint32_t *negateMask = negateMaskInit(); +#else + static const AVMPLUS_ALIGN16(uint32_t) negateMask[] = {0,0x80000000,0,0}; +#endif + + void Assembler::asm_fneg(LInsp ins) + { + if (config.sse2) + { + LIns *lhs = ins->oprnd1(); + + Register rr = prepResultReg(ins, XmmRegs); + Register ra; + + // if this is last use of lhs in reg, we can re-use result reg + // else, lhs already has a register assigned. + if (lhs->isUnusedOrHasUnknownReg()) { + ra = findSpecificRegForUnallocated(lhs, rr); + } else { + ra = lhs->getReg(); + if ((rmask(ra) & XmmRegs) == 0) { + /* We need this case on AMD64, because it's possible that + * an earlier instruction has done a quadword load and reserved a + * GPR. If so, ask for a new register. + */ + ra = findRegFor(lhs, XmmRegs); + } + } + + SSE_XORPD(rr, negateMask); + + if (rr != ra) + SSE_MOVSD(rr, ra); + } + else + { + Register rr = prepResultReg(ins, FpRegs); + + LIns* lhs = ins->oprnd1(); + + // lhs into reg, prefer same reg as result + + // if this is last use of lhs in reg, we can re-use result reg + // else, lhs already has a different reg assigned + if (lhs->isUnusedOrHasUnknownReg()) + findSpecificRegForUnallocated(lhs, rr); + + NanoAssert(lhs->getReg()==FST0); + // assume that the lhs is in ST(0) and rhs is on stack + FCHS(); + + // if we had more than one fpu reg, this is where + // we would move ra into rr if rr != ra. + } + } + + void Assembler::asm_arg(ArgSize sz, LInsp p, Register r, int32_t& stkd) + { + if (sz == ARGSIZE_Q) + { + // ref arg - use lea + if (isKnownReg(r)) + { + // arg in specific reg + int da = findMemFor(p); + LEA(r, da, FP); + } + else + { + NanoAssert(0); // not supported + } + } + else if (sz == ARGSIZE_I || sz == ARGSIZE_U) + { + if (isKnownReg(r)) { + // arg goes in specific register + if (p->isconst()) { + LDi(r, p->imm32()); + } else { + if (p->isUsed()) { + if (!p->hasKnownReg()) { + // load it into the arg reg + int d = findMemFor(p); + if (p->isop(LIR_alloc)) { + LEA(r, d, FP); + } else { + LD(r, d, FP); + } + } else { + // it must be in a saved reg + MR(r, p->getReg()); + } + } + else { + // this is the last use, so fine to assign it + // to the scratch reg, it's dead after this point. + findSpecificRegFor(p, r); + } + } + } + else { + if (config.fixed_esp) + asm_stkarg(p, stkd); + else + asm_pusharg(p); + } + } + else + { + NanoAssert(sz == ARGSIZE_F); + asm_farg(p, stkd); + } + } + + void Assembler::asm_pusharg(LInsp p) + { + // arg goes on stack + if (!p->isUsed() && p->isconst()) + { + // small const we push directly + PUSHi(p->imm32()); + } + else if (!p->isUsed() || p->isop(LIR_alloc)) + { + Register ra = findRegFor(p, GpRegs); + PUSHr(ra); + } + else if (!p->hasKnownReg()) + { + PUSHm(disp(p), FP); + } + else + { + PUSHr(p->getReg()); + } + } + + void Assembler::asm_stkarg(LInsp p, int32_t& stkd) + { + // arg goes on stack + if (!p->isUsed() && p->isconst()) + { + // small const we push directly + STi(SP, stkd, p->imm32()); + } + else { + Register ra; + if (!p->isUsed() || p->getReg() == UnknownReg || p->isop(LIR_alloc)) + ra = findRegFor(p, GpRegs & (~SavedRegs)); + else + ra = p->getReg(); + ST(SP, stkd, ra); + } + + stkd += sizeof(int32_t); + } + + void Assembler::asm_farg(LInsp p, int32_t& stkd) + { + NanoAssert(p->isQuad()); + Register r = findRegFor(p, FpRegs); + if (rmask(r) & XmmRegs) { + SSE_STQ(stkd, SP, r); + } else { + FSTPQ(stkd, SP); + + // + // 22Jul09 rickr - Enabling the evict causes a 10% slowdown on primes + // + // evict() triggers a very expensive fstpq/fldq pair around the store. + // We need to resolve the bug some other way. + // + // see https://bugzilla.mozilla.org/show_bug.cgi?id=491084 + + /* It's possible that the same LIns* with r=FST0 will appear in the argument list more + * than once. In this case FST0 will not have been evicted and the multiple pop + * actions will unbalance the FPU stack. A quick fix is to always evict FST0 manually. + */ + evictIfActive(FST0); + } + if (!config.fixed_esp) + SUBi(ESP,8); + + stkd += sizeof(double); + } + + void Assembler::asm_fop(LInsp ins) + { + LOpcode op = ins->opcode(); + if (config.sse2) + { + LIns *lhs = ins->oprnd1(); + LIns *rhs = ins->oprnd2(); + + RegisterMask allow = XmmRegs; + Register rb = UnknownReg; + if (lhs != rhs) { + rb = findRegFor(rhs,allow); + allow &= ~rmask(rb); + } + + Register rr = prepResultReg(ins, allow); + Register ra; + + // if this is last use of lhs in reg, we can re-use result reg + if (lhs->isUnusedOrHasUnknownReg()) { + ra = findSpecificRegForUnallocated(lhs, rr); + } else if ((rmask(lhs->getReg()) & XmmRegs) == 0) { + // We need this case on AMD64, because it's possible that + // an earlier instruction has done a quadword load and reserved a + // GPR. If so, ask for a new register. + ra = findRegFor(lhs, XmmRegs); + } else { + // lhs already has a register assigned but maybe not from the allow set + ra = findRegFor(lhs, allow); + } + + if (lhs == rhs) + rb = ra; + + if (op == LIR_fadd) + SSE_ADDSD(rr, rb); + else if (op == LIR_fsub) + SSE_SUBSD(rr, rb); + else if (op == LIR_fmul) + SSE_MULSD(rr, rb); + else //if (op == LIR_fdiv) + SSE_DIVSD(rr, rb); + + if (rr != ra) + SSE_MOVSD(rr, ra); + } + else + { + // we swap lhs/rhs on purpose here, works out better + // if you only have one fpu reg. use divr/subr. + LIns* rhs = ins->oprnd1(); + LIns* lhs = ins->oprnd2(); + Register rr = prepResultReg(ins, rmask(FST0)); + + // make sure rhs is in memory + int db = findMemFor(rhs); + + // lhs into reg, prefer same reg as result + + // last use of lhs in reg, can reuse rr + // else, lhs already has a different reg assigned + if (lhs->isUnusedOrHasUnknownReg()) + findSpecificRegForUnallocated(lhs, rr); + + NanoAssert(lhs->getReg()==FST0); + // assume that the lhs is in ST(0) and rhs is on stack + if (op == LIR_fadd) + { FADD(db, FP); } + else if (op == LIR_fsub) + { FSUBR(db, FP); } + else if (op == LIR_fmul) + { FMUL(db, FP); } + else if (op == LIR_fdiv) + { FDIVR(db, FP); } + } + } + + void Assembler::asm_i2f(LInsp ins) + { + // where our result goes + Register rr = prepResultReg(ins, FpRegs); + if (rmask(rr) & XmmRegs) + { + // todo support int value in memory + Register gr = findRegFor(ins->oprnd1(), GpRegs); + SSE_CVTSI2SD(rr, gr); + SSE_XORPDr(rr,rr); // zero rr to ensure no dependency stalls + } + else + { + int d = findMemFor(ins->oprnd1()); + FILD(d, FP); + } + } + + Register Assembler::asm_prep_fcall(LInsp ins) + { + return prepResultReg(ins, rmask(FST0)); + } + + void Assembler::asm_u2f(LInsp ins) + { + // where our result goes + Register rr = prepResultReg(ins, FpRegs); + if (rmask(rr) & XmmRegs) + { + // don't call findRegFor, we want a reg we can stomp on for a very short time, + // not a reg that will continue to be associated with the LIns + Register gr = registerAllocTmp(GpRegs); + + // technique inspired by gcc disassembly + // Edwin explains it: + // + // gr is 0..2^32-1 + // + // sub gr,0x80000000 + // + // now gr is -2^31..2^31-1, i.e. the range of int, but not the same value + // as before + // + // cvtsi2sd rr,gr + // + // rr is now a double with the int value range + // + // addsd rr, 2147483648.0 + // + // adding back double(0x80000000) makes the range 0..2^32-1. + + static const double k_NEGONE = 2147483648.0; + SSE_ADDSDm(rr, &k_NEGONE); + + SSE_CVTSI2SD(rr, gr); + SSE_XORPDr(rr,rr); // zero rr to ensure no dependency stalls + + LIns* op1 = ins->oprnd1(); + Register xr; + if (op1->isUsed() && (xr = op1->getReg(), isKnownReg(xr)) && (rmask(xr) & GpRegs)) + { + LEA(gr, 0x80000000, xr); + } + else + { + const int d = findMemFor(ins->oprnd1()); + SUBi(gr, 0x80000000); + LD(gr, d, FP); + } + } + else + { + const int disp = -8; + const Register base = SP; + Register gr = findRegFor(ins->oprnd1(), GpRegs); + NanoAssert(rr == FST0); + FILDQ(disp, base); + STi(base, disp+4, 0); // high 32 bits = 0 + ST(base, disp, gr); // low 32 bits = unsigned value + } + } + + void Assembler::asm_nongp_copy(Register r, Register s) + { + if ((rmask(r) & XmmRegs) && (rmask(s) & XmmRegs)) { + SSE_MOVSD(r, s); + } else if ((rmask(r) & GpRegs) && (rmask(s) & XmmRegs)) { + SSE_MOVD(r, s); + } else { + if (rmask(r) & XmmRegs) { + // x87 -> xmm + NanoAssertMsg(false, "Should not move data from GPR to XMM"); + } else { + // xmm -> x87 + NanoAssertMsg(false, "Should not move data from GPR/XMM to x87 FPU"); + } + } + } + + NIns* Assembler::asm_fbranch(bool branchOnFalse, LIns *cond, NIns *targ) + { + NIns* at; + LOpcode opcode = cond->opcode(); + + if (config.sse2) { + // LIR_flt and LIR_fgt are handled by the same case because + // asm_fcmp() converts LIR_flt(a,b) to LIR_fgt(b,a). Likewise + // for LIR_fle/LIR_fge. + if (branchOnFalse) { + // op == LIR_xf + switch (opcode) { + case LIR_feq: JP(targ); break; + case LIR_flt: + case LIR_fgt: JNA(targ); break; + case LIR_fle: + case LIR_fge: JNAE(targ); break; + default: NanoAssert(0); break; + } + } else { + // op == LIR_xt + switch (opcode) { + case LIR_feq: JNP(targ); break; + case LIR_flt: + case LIR_fgt: JA(targ); break; + case LIR_fle: + case LIR_fge: JAE(targ); break; + default: NanoAssert(0); break; + } + } + } else { + if (branchOnFalse) + JP(targ); + else + JNP(targ); + } + + at = _nIns; + asm_fcmp(cond); + + return at; + } + + // WARNING: This function cannot generate any code that will affect the + // condition codes prior to the generation of the + // ucomisd/fcompp/fcmop/fcom. See asm_cmp() for more details. + void Assembler::asm_fcmp(LIns *cond) + { + LOpcode condop = cond->opcode(); + NanoAssert(condop >= LIR_feq && condop <= LIR_fge); + LIns* lhs = cond->oprnd1(); + LIns* rhs = cond->oprnd2(); + NanoAssert(lhs->isQuad() && rhs->isQuad()); + + if (config.sse2) { + // First, we convert (a < b) into (b > a), and (a <= b) into (b >= a). + if (condop == LIR_flt) { + condop = LIR_fgt; + LIns* t = lhs; lhs = rhs; rhs = t; + } else if (condop == LIR_fle) { + condop = LIR_fge; + LIns* t = lhs; lhs = rhs; rhs = t; + } + + if (condop == LIR_feq) { + if (lhs == rhs) { + // We can generate better code for LIR_feq when lhs==rhs (NaN test). + + // ucomisd ZPC outcome (SETNP/JNP succeeds if P==0) + // ------- --- ------- + // UNORDERED 111 SETNP/JNP fails + // EQUAL 100 SETNP/JNP succeeds + + Register r = findRegFor(lhs, XmmRegs); + SSE_UCOMISD(r, r); + } else { + // LAHF puts the flags into AH like so: SF:ZF:0:AF:0:PF:1:CF (aka. SZ0A_0P1C). + // We then mask out the bits as follows. + // - LIR_feq: mask == 0x44 == 0100_0100b, which extracts 0Z00_0P00 from AH. + int mask = 0x44; + + // ucomisd ZPC lahf/test(0x44) SZP outcome + // ------- --- --------- --- ------- + // UNORDERED 111 0100_0100 001 SETNP/JNP fails + // EQUAL 100 0100_0000 000 SETNP/JNP succeeds + // GREATER_THAN 000 0000_0000 011 SETNP/JNP fails + // LESS_THAN 001 0000_0000 011 SETNP/JNP fails + + evictIfActive(EAX); + Register ra, rb; + findRegFor2(XmmRegs, lhs, ra, rhs, rb); + + TEST_AH(mask); + LAHF(); + SSE_UCOMISD(ra, rb); + } + } else { + // LIR_fgt: + // ucomisd ZPC outcome (SETA/JA succeeds if CZ==00) + // ------- --- ------- + // UNORDERED 111 SETA/JA fails + // EQUAL 100 SETA/JA fails + // GREATER_THAN 000 SETA/JA succeeds + // LESS_THAN 001 SETA/JA fails + // + // LIR_fge: + // ucomisd ZPC outcome (SETAE/JAE succeeds if C==0) + // ------- --- ------- + // UNORDERED 111 SETAE/JAE fails + // EQUAL 100 SETAE/JAE succeeds + // GREATER_THAN 000 SETAE/JAE succeeds + // LESS_THAN 001 SETAE/JAE fails + + Register ra, rb; + findRegFor2(XmmRegs, lhs, ra, rhs, rb); + SSE_UCOMISD(ra, rb); + } + + } else { + // First, we convert (a > b) into (b < a), and (a >= b) into (b <= a). + // Note that this is the opposite of the sse2 conversion above. + if (condop == LIR_fgt) { + condop = LIR_flt; + LIns* t = lhs; lhs = rhs; rhs = t; + } else if (condop == LIR_fge) { + condop = LIR_fle; + LIns* t = lhs; lhs = rhs; rhs = t; + } + + // FNSTSW_AX puts the flags into AH like so: B:C3:TOP3:TOP2:TOP1:C2:C1:C0. + // Furthermore, fcom/fcomp/fcompp sets C3:C2:C0 the same values + // that Z:P:C are set by ucomisd, and the relative positions in AH + // line up. (Someone at Intel has a sense of humour.) Therefore + // we can use the same lahf/test(mask) technique as used in the + // sse2 case above. We could use fcomi/fcomip/fcomipp which set + // ZPC directly and then use LAHF instead of FNSTSW_AX and make + // this code generally more like the sse2 code, but we don't + // because fcomi/fcomip/fcomipp/lahf aren't available on earlier + // x86 machines. + // + // The masks are as follows: + // - LIR_feq: mask == 0x44 == 0100_0100b, which extracts 0Z00_0P00 from AH. + // - LIR_flt: mask == 0x05 == 0000_0101b, which extracts 0000_0P0C from AH. + // - LIR_fle: mask == 0x41 == 0100_0001b, which extracts 0Z00_000C from AH. + // + // LIR_feq (very similar to the sse2 case above): + // ucomisd C3:C2:C0 lahf/test(0x44) SZP outcome + // ------- -------- --------- --- ------- + // UNORDERED 111 0100_0100 001 SETNP fails + // EQUAL 100 0100_0000 000 SETNP succeeds + // GREATER_THAN 000 0000_0000 011 SETNP fails + // LESS_THAN 001 0000_0000 011 SETNP fails + // + // LIR_flt: + // fcom C3:C2:C0 lahf/test(0x05) SZP outcome + // ------- -------- --------- --- ------- + // UNORDERED 111 0000_0101 001 SETNP fails + // EQUAL 100 0000_0000 011 SETNP fails + // GREATER_THAN 000 0000_0000 011 SETNP fails + // LESS_THAN 001 0000_0001 000 SETNP succeeds + // + // LIR_fle: + // fcom C3:C2:C0 lahf/test(0x41) SZP outcome + // ------- --- --------- --- ------- + // UNORDERED 111 0100_0001 001 SETNP fails + // EQUAL 100 0100_0000 000 SETNP succeeds + // GREATER_THAN 000 0000_0000 011 SETNP fails + // LESS_THAN 001 0000_0001 010 SETNP succeeds + + int mask = 0; // init to avoid MSVC compile warnings + switch (condop) { + case LIR_feq: mask = 0x44; break; + case LIR_flt: mask = 0x05; break; + case LIR_fle: mask = 0x41; break; + default: NanoAssert(0); break; + } + + evictIfActive(EAX); + int pop = lhs->isUnusedOrHasUnknownReg(); + findSpecificRegFor(lhs, FST0); + + if (lhs == rhs) { + // NaN test. + // value in ST(0) + TEST_AH(mask); + FNSTSW_AX(); + if (pop) + FCOMPP(); + else + FCOMP(); + FLDr(FST0); // DUP + } else { + int d = findMemFor(rhs); + // lhs is in ST(0) and rhs is on stack + TEST_AH(mask); + FNSTSW_AX(); + FCOM((pop?1:0), d, FP); + } + } + } + + // Increment the 32-bit profiling counter at pCtr, without + // changing any registers. + verbose_only( + void Assembler::asm_inc_m32(uint32_t* pCtr) + { + INCLi(pCtr); + } + ) + + void Assembler::nativePageReset() + {} + + void Assembler::nativePageSetup() + { + NanoAssert(!_inExit); + if (!_nIns) + codeAlloc(codeStart, codeEnd, _nIns verbose_only(, codeBytes)); + if (!_nExitIns) + codeAlloc(exitStart, exitEnd, _nExitIns verbose_only(, exitBytes)); + } + + // enough room for n bytes + void Assembler::underrunProtect(int n) + { + NIns *eip = _nIns; + NanoAssertMsg(n<=LARGEST_UNDERRUN_PROT, "constant LARGEST_UNDERRUN_PROT is too small"); + // This may be in a normal code chunk or an exit code chunk. + if (eip - n < codeStart) { + codeAlloc(codeStart, codeEnd, _nIns verbose_only(, codeBytes)); + JMP(eip); + } + } + + void Assembler::asm_ret(LInsp ins) + { + genEpilogue(); + + // Restore ESP from EBP, undoing SUBi(SP,amt) in the prologue + MR(SP,FP); + + assignSavedRegs(); + LIns *val = ins->oprnd1(); + if (ins->isop(LIR_ret)) { + findSpecificRegFor(val, retRegs[0]); + } else { + findSpecificRegFor(val, FST0); + fpu_pop(); + } + } + + void Assembler::asm_promote(LIns *) { + // i2q or u2q + TODO(asm_promote); + } + + void Assembler::swapCodeChunks() { + SWAP(NIns*, _nIns, _nExitIns); + SWAP(NIns*, codeStart, exitStart); + SWAP(NIns*, codeEnd, exitEnd); + verbose_only( SWAP(size_t, codeBytes, exitBytes); ) + } + + #endif /* FEATURE_NANOJIT */ +} diff --git a/ape-server/deps/js/src/nanojit/Nativei386.h b/ape-server/deps/js/src/nanojit/Nativei386.h new file mode 100755 index 0000000..10c255b --- /dev/null +++ b/ape-server/deps/js/src/nanojit/Nativei386.h @@ -0,0 +1,872 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is [Open Source Virtual Machine]. + * + * The Initial Developer of the Original Code is + * Adobe System Incorporated. + * Portions created by the Initial Developer are Copyright (C) 2004-2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adobe AS3 Team + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + + +#ifndef __nanojit_Nativei386__ +#define __nanojit_Nativei386__ + +#ifdef PERFM +#define DOPROF +#include "../vprof/vprof.h" +#define count_instr() _nvprof("x86",1) +#define count_ret() _nvprof("x86-ret",1); count_instr(); +#define count_push() _nvprof("x86-push",1); count_instr(); +#define count_pop() _nvprof("x86-pop",1); count_instr(); +#define count_st() _nvprof("x86-st",1); count_instr(); +#define count_stq() _nvprof("x86-stq",1); count_instr(); +#define count_ld() _nvprof("x86-ld",1); count_instr(); +#define count_ldq() _nvprof("x86-ldq",1); count_instr(); +#define count_call() _nvprof("x86-call",1); count_instr(); +#define count_calli() _nvprof("x86-calli",1); count_instr(); +#define count_prolog() _nvprof("x86-prolog",1); count_instr(); +#define count_alu() _nvprof("x86-alu",1); count_instr(); +#define count_mov() _nvprof("x86-mov",1); count_instr(); +#define count_fpu() _nvprof("x86-fpu",1); count_instr(); +#define count_jmp() _nvprof("x86-jmp",1); count_instr(); +#define count_jcc() _nvprof("x86-jcc",1); count_instr(); +#define count_fpuld() _nvprof("x86-ldq",1); _nvprof("x86-fpu",1); count_instr() +#define count_aluld() _nvprof("x86-ld",1); _nvprof("x86-alu",1); count_instr() +#define count_alust() _nvprof("x86-ld",1); _nvprof("x86-alu",1); _nvprof("x86-st",1); count_instr() +#define count_pushld() _nvprof("x86-ld",1); _nvprof("x86-push",1); count_instr() +#define count_imt() _nvprof("x86-imt",1) count_instr() +#else +#define count_instr() +#define count_ret() +#define count_push() +#define count_pop() +#define count_st() +#define count_stq() +#define count_ld() +#define count_ldq() +#define count_call() +#define count_calli() +#define count_prolog() +#define count_alu() +#define count_mov() +#define count_fpu() +#define count_jmp() +#define count_jcc() +#define count_fpuld() +#define count_aluld() +#define count_alust() +#define count_pushld() +#define count_imt() +#endif + +namespace nanojit +{ + const int NJ_MAX_REGISTERS = 24; // gpregs, x87 regs, xmm regs + + #define NJ_MAX_STACK_ENTRY 256 + #define NJ_MAX_PARAMETERS 1 + #define NJ_JTBL_SUPPORTED 1 + + // Preserve a 16-byte stack alignment, to support the use of + // SSE instructions like MOVDQA (if not by Tamarin itself, + // then by the C functions it calls). + const int NJ_ALIGN_STACK = 16; + + const int32_t LARGEST_UNDERRUN_PROT = 32; // largest value passed to underrunProtect + + typedef uint8_t NIns; + + // Bytes of icache to flush after Assembler::patch + const size_t LARGEST_BRANCH_PATCH = 16 * sizeof(NIns); + + // These are used as register numbers in various parts of the code + typedef enum + { + // general purpose 32bit regs + EAX = 0, // return value, scratch + ECX = 1, // this/arg0, scratch + EDX = 2, // arg1, return-msw, scratch + EBX = 3, + ESP = 4, // stack pointer + EBP = 5, // frame pointer + ESI = 6, + EDI = 7, + + SP = ESP, // alias SP to ESP for convenience + FP = EBP, // alias FP to EBP for convenience + + // SSE regs come before X87 so we prefer them + XMM0 = 8, + XMM1 = 9, + XMM2 = 10, + XMM3 = 11, + XMM4 = 12, + XMM5 = 13, + XMM6 = 14, + XMM7 = 15, + + // X87 regs + FST0 = 16, + + FirstReg = 0, + LastReg = 16, + UnknownReg = 17 + } + Register; + + typedef int RegisterMask; + + static const int NumSavedRegs = 3; + static const RegisterMask SavedRegs = 1<>8) + +#define ALU2m(c,r,d,b) \ + underrunProtect(9); \ + MODRMm(r,d,b); \ + *(--_nIns) = (uint8_t) (c);\ + *(--_nIns) = (uint8_t) ((c)>>8) + +#define ALU2sib(c,r,base,index,scale,disp) \ + underrunProtect(8); \ + MODRMSIB(r,base,index,scale,disp); \ + *(--_nIns) = (uint8_t) (c); \ + *(--_nIns) = (uint8_t) ((c)>>8) + +#define ALU(c,d,s) \ + underrunProtect(2);\ + MODRM(d,s); \ + *(--_nIns) = (uint8_t) (c) + +#define ALUi(c,r,i) \ + underrunProtect(6); \ + NanoAssert(unsigned(r)<8);\ + if (isS8(i)) { \ + *(--_nIns) = uint8_t(i); \ + MODRM((c>>3),(r)); \ + *(--_nIns) = uint8_t(0x83); \ + } else { \ + IMM32(i); \ + if ( (r) == EAX) { \ + *(--_nIns) = (uint8_t) (c); \ + } else { \ + MODRM((c>>3),(r)); \ + *(--_nIns) = uint8_t(0x81); \ + } \ + } + +#define ALUmi(c,d,b,i) \ + underrunProtect(10); \ + NanoAssert(((unsigned)b)<8); \ + if (isS8(i)) { \ + *(--_nIns) = uint8_t(i); \ + MODRMm((c>>3),(d),(b)); \ + *(--_nIns) = uint8_t(0x83); \ + } else { \ + IMM32(i); \ + MODRMm((c>>3),(d),(b)); \ + *(--_nIns) = uint8_t(0x81); \ + } + +#define ALU2(c,d,s) \ + underrunProtect(3); \ + MODRM((d),(s)); \ + _nIns -= 2; \ + _nIns[0] = (uint8_t) ( ((c)>>8) ); \ + _nIns[1] = (uint8_t) ( (c) ) + +#define LAHF() do { count_alu(); ALU0(0x9F); asm_output("lahf"); } while(0) +#define SAHF() do { count_alu(); ALU0(0x9E); asm_output("sahf"); } while(0) +#define OR(l,r) do { count_alu(); ALU(0x0b, (l),(r)); asm_output("or %s,%s",gpn(l),gpn(r)); } while(0) +#define AND(l,r) do { count_alu(); ALU(0x23, (l),(r)); asm_output("and %s,%s",gpn(l),gpn(r)); } while(0) +#define XOR(l,r) do { count_alu(); ALU(0x33, (l),(r)); asm_output("xor %s,%s",gpn(l),gpn(r)); } while(0) +#define ADD(l,r) do { count_alu(); ALU(0x03, (l),(r)); asm_output("add %s,%s",gpn(l),gpn(r)); } while(0) +#define SUB(l,r) do { count_alu(); ALU(0x2b, (l),(r)); asm_output("sub %s,%s",gpn(l),gpn(r)); } while(0) +#define MUL(l,r) do { count_alu(); ALU2(0x0faf,(l),(r)); asm_output("mul %s,%s",gpn(l),gpn(r)); } while(0) +#define DIV(r) do { count_alu(); ALU(0xf7, (Register)7,(r)); asm_output("idiv edx:eax, %s",gpn(r)); } while(0) +#define NOT(r) do { count_alu(); ALU(0xf7, (Register)2,(r)); asm_output("not %s",gpn(r)); } while(0) +#define NEG(r) do { count_alu(); ALU(0xf7, (Register)3,(r)); asm_output("neg %s",gpn(r)); } while(0) +#define SHR(r,s) do { count_alu(); ALU(0xd3, (Register)5,(r)); asm_output("shr %s,%s",gpn(r),gpn(s)); } while(0) +#define SAR(r,s) do { count_alu(); ALU(0xd3, (Register)7,(r)); asm_output("sar %s,%s",gpn(r),gpn(s)); } while(0) +#define SHL(r,s) do { count_alu(); ALU(0xd3, (Register)4,(r)); asm_output("shl %s,%s",gpn(r),gpn(s)); } while(0) + +#define SHIFT(c,r,i) \ + underrunProtect(3);\ + *--_nIns = (uint8_t)(i);\ + MODRM((Register)c,r);\ + *--_nIns = 0xc1; + +#define SHLi(r,i) do { count_alu(); SHIFT(4,r,i); asm_output("shl %s,%d", gpn(r),i); } while(0) +#define SHRi(r,i) do { count_alu(); SHIFT(5,r,i); asm_output("shr %s,%d", gpn(r),i); } while(0) +#define SARi(r,i) do { count_alu(); SHIFT(7,r,i); asm_output("sar %s,%d", gpn(r),i); } while(0) + +#define MOVZX8(d,s) do { count_alu(); ALU2(0x0fb6,d,s); asm_output("movzx %s,%s", gpn(d),gpn(s)); } while(0) + +#define SUBi(r,i) do { count_alu(); ALUi(0x2d,r,i); asm_output("sub %s,%d",gpn(r),i); } while(0) +#define ADDi(r,i) do { count_alu(); ALUi(0x05,r,i); asm_output("add %s,%d",gpn(r),i); } while(0) +#define ANDi(r,i) do { count_alu(); ALUi(0x25,r,i); asm_output("and %s,%d",gpn(r),i); } while(0) +#define ORi(r,i) do { count_alu(); ALUi(0x0d,r,i); asm_output("or %s,%d",gpn(r),i); } while(0) +#define XORi(r,i) do { count_alu(); ALUi(0x35,r,i); asm_output("xor %s,%d",gpn(r),i); } while(0) + +#define ADDmi(d,b,i) do { count_alust(); ALUmi(0x05, d, b, i); asm_output("add %d(%s), %d", d, gpn(b), i); } while(0) + +#define TEST(d,s) do { count_alu(); ALU(0x85,d,s); asm_output("test %s,%s",gpn(d),gpn(s)); } while(0) +#define CMP(l,r) do { count_alu(); ALU(0x3b, (l),(r)); asm_output("cmp %s,%s",gpn(l),gpn(r)); } while(0) +#define CMPi(r,i) do { count_alu(); ALUi(0x3d,r,i); asm_output("cmp %s,%d",gpn(r),i); } while(0) + +#define MR(d,s) do { count_mov(); ALU(0x8b,d,s); asm_output("mov %s,%s",gpn(d),gpn(s)); } while(0) +#define LEA(r,d,b) do { count_alu(); ALUm(0x8d, r,d,b); asm_output("lea %s,%d(%s)",gpn(r),d,gpn(b)); } while(0) +// lea %r, d(%i*4) +// This addressing mode is not supported by the MODRMSIB macro. +#define LEAmi4(r,d,i) do { count_alu(); IMM32(d); *(--_nIns) = (2<<6)|((uint8_t)i<<3)|5; *(--_nIns) = (0<<6)|((uint8_t)r<<3)|4; *(--_nIns) = 0x8d; asm_output("lea %s, %p(%s*4)", gpn(r), (void*)d, gpn(i)); } while(0) + +#define CDQ() do { SARi(EDX, 31); MR(EDX, EAX); } while(0) + +#define INCLi(p) do { count_alu(); \ + underrunProtect(6); \ + IMM32((uint32_t)(ptrdiff_t)p); *(--_nIns) = 0x05; *(--_nIns) = 0xFF; \ + asm_output("incl (%p)", (void*)p); } while (0) + +#define SETE(r) do { count_alu(); ALU2(0x0f94,(r),(r)); asm_output("sete %s",gpn(r)); } while(0) +#define SETNP(r) do { count_alu(); ALU2(0x0f9B,(r),(r)); asm_output("setnp %s",gpn(r)); } while(0) +#define SETL(r) do { count_alu(); ALU2(0x0f9C,(r),(r)); asm_output("setl %s",gpn(r)); } while(0) +#define SETLE(r) do { count_alu(); ALU2(0x0f9E,(r),(r)); asm_output("setle %s",gpn(r)); } while(0) +#define SETG(r) do { count_alu(); ALU2(0x0f9F,(r),(r)); asm_output("setg %s",gpn(r)); } while(0) +#define SETGE(r) do { count_alu(); ALU2(0x0f9D,(r),(r)); asm_output("setge %s",gpn(r)); } while(0) +#define SETB(r) do { count_alu(); ALU2(0x0f92,(r),(r)); asm_output("setb %s",gpn(r)); } while(0) +#define SETBE(r) do { count_alu(); ALU2(0x0f96,(r),(r)); asm_output("setbe %s",gpn(r)); } while(0) +#define SETA(r) do { count_alu(); ALU2(0x0f97,(r),(r)); asm_output("seta %s",gpn(r)); } while(0) +#define SETAE(r) do { count_alu(); ALU2(0x0f93,(r),(r)); asm_output("setae %s",gpn(r)); } while(0) +#define SETO(r) do { count_alu(); ALU2(0x0f92,(r),(r)); asm_output("seto %s",gpn(r)); } while(0) + +#define MREQ(dr,sr) do { count_alu(); ALU2(0x0f44,dr,sr); asm_output("cmove %s,%s", gpn(dr),gpn(sr)); } while(0) +#define MRNE(dr,sr) do { count_alu(); ALU2(0x0f45,dr,sr); asm_output("cmovne %s,%s", gpn(dr),gpn(sr)); } while(0) +#define MRL(dr,sr) do { count_alu(); ALU2(0x0f4C,dr,sr); asm_output("cmovl %s,%s", gpn(dr),gpn(sr)); } while(0) +#define MRLE(dr,sr) do { count_alu(); ALU2(0x0f4E,dr,sr); asm_output("cmovle %s,%s", gpn(dr),gpn(sr)); } while(0) +#define MRG(dr,sr) do { count_alu(); ALU2(0x0f4F,dr,sr); asm_output("cmovg %s,%s", gpn(dr),gpn(sr)); } while(0) +#define MRGE(dr,sr) do { count_alu(); ALU2(0x0f4D,dr,sr); asm_output("cmovge %s,%s", gpn(dr),gpn(sr)); } while(0) +#define MRB(dr,sr) do { count_alu(); ALU2(0x0f42,dr,sr); asm_output("cmovb %s,%s", gpn(dr),gpn(sr)); } while(0) +#define MRBE(dr,sr) do { count_alu(); ALU2(0x0f46,dr,sr); asm_output("cmovbe %s,%s", gpn(dr),gpn(sr)); } while(0) +#define MRA(dr,sr) do { count_alu(); ALU2(0x0f47,dr,sr); asm_output("cmova %s,%s", gpn(dr),gpn(sr)); } while(0) +#define MRAE(dr,sr) do { count_alu(); ALU2(0x0f43,dr,sr); asm_output("cmovae %s,%s", gpn(dr),gpn(sr)); } while(0) +#define MRNO(dr,sr) do { count_alu(); ALU2(0x0f41,dr,sr); asm_output("cmovno %s,%s", gpn(dr),gpn(sr)); } while(0) + +// these aren't currently used but left in for reference +//#define LDEQ(r,d,b) do { ALU2m(0x0f44,r,d,b); asm_output("cmove %s,%d(%s)", gpn(r),d,gpn(b)); } while(0) +//#define LDNEQ(r,d,b) do { ALU2m(0x0f45,r,d,b); asm_output("cmovne %s,%d(%s)", gpn(r),d,gpn(b)); } while(0) + +#define LD(reg,disp,base) do { \ + count_ld();\ + ALUm(0x8b,reg,disp,base); \ + asm_output("mov %s,%d(%s)",gpn(reg),disp,gpn(base)); } while(0) + +#define LDdm(reg,addr) do { \ + count_ld(); \ + ALUdm(0x8b,reg,addr); \ + asm_output("mov %s,0(%lx)",gpn(reg),(unsigned long)addr); \ + } while (0) + + +#define SIBIDX(n) "1248"[n] + +#define LDsib(reg,disp,base,index,scale) do { \ + count_ld(); \ + ALUsib(0x8b,reg,base,index,scale,disp); \ + asm_output("mov %s,%d(%s+%s*%c)",gpn(reg),disp,gpn(base),gpn(index),SIBIDX(scale)); \ + } while (0) + +// load 16-bit, sign extend +#define LD16S(r,d,b) do { count_ld(); ALU2m(0x0fbf,r,d,b); asm_output("movsx %s,%d(%s)", gpn(r),d,gpn(b)); } while(0) + +// load 16-bit, zero extend +#define LD16Z(r,d,b) do { count_ld(); ALU2m(0x0fb7,r,d,b); asm_output("movsz %s,%d(%s)", gpn(r),d,gpn(b)); } while(0) + +#define LD16Zdm(r,addr) do { count_ld(); ALU2dm(0x0fb7,r,addr); asm_output("movsz %s,0(%lx)", gpn(r),(unsigned long)addr); } while (0) + +#define LD16Zsib(r,disp,base,index,scale) do { \ + count_ld(); \ + ALU2sib(0x0fb7,r,base,index,scale,disp); \ + asm_output("movsz %s,%d(%s+%s*%c)",gpn(r),disp,gpn(base),gpn(index),SIBIDX(scale)); \ + } while (0) + +// load 8-bit, zero extend +#define LD8Z(r,d,b) do { count_ld(); ALU2m(0x0fb6,r,d,b); asm_output("movzx %s,%d(%s)", gpn(r),d,gpn(b)); } while(0) + +#define LD8Zdm(r,addr) do { \ + count_ld(); \ + NanoAssert((d)>=0&&(d)<=31); \ + ALU2dm(0x0fb6,r,addr); \ + asm_output("movzx %s,0(%lx)", gpn(r),(long unsigned)addr); \ + } while(0) + +#define LD8Zsib(r,disp,base,index,scale) do { \ + count_ld(); \ + NanoAssert((d)>=0&&(d)<=31); \ + ALU2sib(0x0fb6,r,base,index,scale,disp); \ + asm_output("movzx %s,%d(%s+%s*%c)",gpn(r),disp,gpn(base),gpn(index),SIBIDX(scale)); \ + } while(0) + + +#define LDi(r,i) do { \ + count_ld();\ + underrunProtect(5); \ + IMM32(i); \ + NanoAssert(((unsigned)r)<8); \ + *(--_nIns) = (uint8_t) (0xb8 | (r) ); \ + asm_output("mov %s,%d",gpn(r),i); } while(0) + +#define ST(base,disp,reg) do { \ + count_st();\ + ALUm(0x89,reg,disp,base); \ + asm_output("mov %d(%s),%s",disp,base==UnknownReg?"0":gpn(base),gpn(reg)); } while(0) + +#define STi(base,disp,imm) do { \ + count_st();\ + underrunProtect(12); \ + IMM32(imm); \ + MODRMm(0, disp, base); \ + *(--_nIns) = 0xc7; \ + asm_output("mov %d(%s),%d",disp,gpn(base),imm); } while(0) + +#define RET() do { count_ret(); ALU0(0xc3); asm_output("ret"); } while(0) +#define NOP() do { count_alu(); ALU0(0x90); asm_output("nop"); } while(0) +#define INT3() do { ALU0(0xcc); asm_output("int3"); } while(0) + +#define PUSHi(i) do { \ + count_push();\ + if (isS8(i)) { \ + underrunProtect(2); \ + _nIns-=2; _nIns[0] = 0x6a; _nIns[1] = (uint8_t)(i); \ + asm_output("push %d",i); \ + } else \ + { PUSHi32(i); } } while(0) + +#define PUSHi32(i) do { \ + count_push();\ + underrunProtect(5); \ + IMM32(i); \ + *(--_nIns) = 0x68; \ + asm_output("push %d",i); } while(0) + +#define PUSHr(r) do { \ + count_push();\ + underrunProtect(1); \ + NanoAssert(((unsigned)r)<8); \ + *(--_nIns) = (uint8_t) ( 0x50 | (r) ); \ + asm_output("push %s",gpn(r)); } while(0) + +#define PUSHm(d,b) do { \ + count_pushld();\ + ALUm(0xff, 6, d, b); \ + asm_output("push %d(%s)",d,gpn(b)); } while(0) + +#define POPr(r) do { \ + count_pop();\ + underrunProtect(1); \ + NanoAssert(((unsigned)r)<8); \ + *(--_nIns) = (uint8_t) ( 0x58 | (r) ); \ + asm_output("pop %s",gpn(r)); } while(0) + +#define JCC32 0x0f +#define JMP8 0xeb +#define JMP32 0xe9 + +#define JCC(o,t,n) do { \ + count_jcc();\ + underrunProtect(6); \ + intptr_t tt = (intptr_t)t - (intptr_t)_nIns; \ + if (isS8(tt)) { \ + verbose_only( NIns* next = _nIns; (void)next; ) \ + _nIns -= 2; \ + _nIns[0] = (uint8_t) ( 0x70 | (o) ); \ + _nIns[1] = (uint8_t) (tt); \ + asm_output("%-5s %p",(n),(next+tt)); \ + } else { \ + verbose_only( NIns* next = _nIns; ) \ + IMM32(tt); \ + _nIns -= 2; \ + _nIns[0] = JCC32; \ + _nIns[1] = (uint8_t) ( 0x80 | (o) ); \ + asm_output("%-5s %p",(n),(next+tt)); \ + } } while(0) + +#define JMP_long(t) do { \ + count_jmp();\ + underrunProtect(5); \ + intptr_t tt = (intptr_t)t - (intptr_t)_nIns; \ + JMP_long_nochk_offset(tt); \ + verbose_only( verbose_outputf("%010lx:", (unsigned long)_nIns); ) \ + } while(0) + +#define JMP(t) do { \ + count_jmp();\ + underrunProtect(5); \ + intptr_t tt = (intptr_t)t - (intptr_t)_nIns; \ + if (isS8(tt)) { \ + verbose_only( NIns* next = _nIns; (void)next; ) \ + _nIns -= 2; \ + _nIns[0] = JMP8; \ + _nIns[1] = (uint8_t) ( (tt)&0xff ); \ + asm_output("jmp %p",(next+tt)); \ + } else { \ + JMP_long_nochk_offset(tt); \ + } } while(0) + +// this should only be used when you can guarantee there is enough room on the page +#define JMP_long_nochk_offset(o) do {\ + verbose_only( NIns* next = _nIns; (void)next; ) \ + IMM32((o)); \ + *(--_nIns) = JMP32; \ + asm_output("jmp %p",(next+(o))); } while(0) + +#define JMP_indirect(r) do { \ + underrunProtect(2); \ + MODRMm(4, 0, r); \ + *(--_nIns) = 0xff; \ + asm_output("jmp *(%s)", gpn(r)); } while (0) + +#define JMP_indexed(x, ss, addr) do { \ + underrunProtect(7); \ + IMM32(addr); \ + _nIns -= 3;\ + _nIns[0] = (NIns) 0xff; /* jmp */ \ + _nIns[1] = (NIns) (0<<6 | 4<<3 | 4); /* modrm: base=sib + disp32 */ \ + _nIns[2] = (NIns) ((ss)<<6 | (x)<<3 | 5); /* sib: x<>16)&0xff); \ + _nIns[1] = (uint8_t)(((c)>>8)&0xff); \ + _nIns[2] = (uint8_t)((c)&0xff) + +#define SSEm(c,r,d,b) \ + underrunProtect(9); \ + MODRMm((r),(d),(b)); \ + _nIns -= 3; \ + _nIns[0] = (uint8_t)(((c)>>16)&0xff); \ + _nIns[1] = (uint8_t)(((c)>>8)&0xff); \ + _nIns[2] = (uint8_t)((c)&0xff) + +#define LDSD(r,d,b)do { \ + count_ldq();\ + SSEm(0xf20f10, (r)&7, (d), (b)); \ + asm_output("movsd %s,%d(%s)",gpn(r),(d),gpn(b)); \ + } while(0) + +#define LDSDm(r,addr)do { \ + count_ldq();\ + underrunProtect(8); \ + const double* daddr = addr; \ + IMM32(int32_t(daddr));\ + *(--_nIns) = uint8_t(((r)&7)<<3|5); \ + *(--_nIns) = 0x10;\ + *(--_nIns) = 0x0f;\ + *(--_nIns) = 0xf2;\ + asm_output("movsd %s,(#%p) // =%f",gpn(r),(void*)daddr,*daddr); \ + } while(0) + +#define STSD(d,b,r)do { \ + count_stq();\ + SSEm(0xf20f11, (r)&7, (d), (b)); \ + asm_output("movsd %d(%s),%s",(d),gpn(b),gpn(r)); \ + } while(0) + +#define SSE_LDQ(r,d,b)do { \ + count_ldq();\ + SSEm(0xf30f7e, (r)&7, (d), (b)); \ + asm_output("movq %s,%d(%s)",gpn(r),d,gpn(b)); \ + } while(0) + +#define SSE_STQ(d,b,r)do { \ + count_stq();\ + SSEm(0x660fd6, (r)&7, (d), (b)); \ + asm_output("movq %d(%s),%s",(d),gpn(b),gpn(r)); \ + } while(0) + +#define SSE_CVTSI2SD(xr,gr) do{ \ + count_fpu();\ + SSE(0xf20f2a, (xr)&7, (gr)&7); \ + asm_output("cvtsi2sd %s,%s",gpn(xr),gpn(gr)); \ + } while(0) + +#define CVTDQ2PD(dstr,srcr) do{ \ + count_fpu();\ + SSE(0xf30fe6, (dstr)&7, (srcr)&7); \ + asm_output("cvtdq2pd %s,%s",gpn(dstr),gpn(srcr)); \ + } while(0) + +// move and zero-extend gpreg to xmm reg +#define SSE_MOVD(d,s) do{ \ + count_mov();\ + if (_is_xmm_reg_(s)) { \ + NanoAssert(_is_gp_reg_(d)); \ + SSE(0x660f7e, (s)&7, (d)&7); \ + } else { \ + NanoAssert(_is_gp_reg_(s)); \ + NanoAssert(_is_xmm_reg_(d)); \ + SSE(0x660f6e, (d)&7, (s)&7); \ + } \ + asm_output("movd %s,%s",gpn(d),gpn(s)); \ + } while(0) + +#define SSE_MOVSD(rd,rs) do{ \ + count_mov();\ + NanoAssert(_is_xmm_reg_(rd) && _is_xmm_reg_(rs));\ + SSE(0xf20f10, (rd)&7, (rs)&7); \ + asm_output("movsd %s,%s",gpn(rd),gpn(rs)); \ + } while(0) + +#define SSE_MOVDm(d,b,xrs) do {\ + count_st();\ + NanoAssert(_is_xmm_reg_(xrs) && (_is_gp_reg_(b) || b==FP));\ + SSEm(0x660f7e, (xrs)&7, d, b);\ + asm_output("movd %d(%s),%s", d, gpn(b), gpn(xrs));\ + } while(0) + +#define SSE_ADDSD(rd,rs) do{ \ + count_fpu();\ + NanoAssert(_is_xmm_reg_(rd) && _is_xmm_reg_(rs));\ + SSE(0xf20f58, (rd)&7, (rs)&7); \ + asm_output("addsd %s,%s",gpn(rd),gpn(rs)); \ + } while(0) + +#define SSE_ADDSDm(r,addr)do { \ + count_fpuld();\ + underrunProtect(8); \ + NanoAssert(_is_xmm_reg_(r));\ + const double* daddr = addr; \ + IMM32(int32_t(daddr));\ + *(--_nIns) = uint8_t(((r)&7)<<3|5); \ + *(--_nIns) = 0x58;\ + *(--_nIns) = 0x0f;\ + *(--_nIns) = 0xf2;\ + asm_output("addsd %s,%p // =%f",gpn(r),(void*)daddr,*daddr); \ + } while(0) + +#define SSE_SUBSD(rd,rs) do{ \ + count_fpu();\ + NanoAssert(_is_xmm_reg_(rd) && _is_xmm_reg_(rs));\ + SSE(0xf20f5c, (rd)&7, (rs)&7); \ + asm_output("subsd %s,%s",gpn(rd),gpn(rs)); \ + } while(0) +#define SSE_MULSD(rd,rs) do{ \ + count_fpu();\ + NanoAssert(_is_xmm_reg_(rd) && _is_xmm_reg_(rs));\ + SSE(0xf20f59, (rd)&7, (rs)&7); \ + asm_output("mulsd %s,%s",gpn(rd),gpn(rs)); \ + } while(0) +#define SSE_DIVSD(rd,rs) do{ \ + count_fpu();\ + NanoAssert(_is_xmm_reg_(rd) && _is_xmm_reg_(rs));\ + SSE(0xf20f5e, (rd)&7, (rs)&7); \ + asm_output("divsd %s,%s",gpn(rd),gpn(rs)); \ + } while(0) +#define SSE_UCOMISD(rl,rr) do{ \ + count_fpu();\ + NanoAssert(_is_xmm_reg_(rl) && _is_xmm_reg_(rr));\ + SSE(0x660f2e, (rl)&7, (rr)&7); \ + asm_output("ucomisd %s,%s",gpn(rl),gpn(rr)); \ + } while(0) + +#define CVTSI2SDm(xr,d,b) do{ \ + count_fpu();\ + NanoAssert(_is_xmm_reg_(xr) && _is_gp_reg_(b));\ + SSEm(0xf20f2a, (xr)&7, (d), (b)); \ + asm_output("cvtsi2sd %s,%d(%s)",gpn(xr),(d),gpn(b)); \ + } while(0) + +#define SSE_XORPD(r, maskaddr) do {\ + count_fpuld();\ + underrunProtect(8); \ + IMM32(maskaddr);\ + *(--_nIns) = uint8_t(((r)&7)<<3|5); \ + *(--_nIns) = 0x57;\ + *(--_nIns) = 0x0f;\ + *(--_nIns) = 0x66;\ + asm_output("xorpd %s,[0x%p]",gpn(r),(void*)(maskaddr));\ + } while(0) + +#define SSE_XORPDr(rd,rs) do{ \ + count_fpu();\ + SSE(0x660f57, (rd)&7, (rs)&7); \ + asm_output("xorpd %s,%s",gpn(rd),gpn(rs)); \ + } while(0) + +// floating point unit +#define FPUc(o) \ + underrunProtect(2); \ + *(--_nIns) = ((uint8_t)(o)&0xff); \ + *(--_nIns) = (uint8_t)(((o)>>8)&0xff) + +#define FPU(o,r) \ + underrunProtect(2); \ + *(--_nIns) = uint8_t(((uint8_t)(o)&0xff) | (r&7));\ + *(--_nIns) = (uint8_t)(((o)>>8)&0xff) + +#define FPUm(o,d,b) \ + underrunProtect(7); \ + MODRMm((uint8_t)(o), d, b); \ + *(--_nIns) = (uint8_t)((o)>>8) + +#define TEST_AH(i) do { \ + count_alu();\ + underrunProtect(3); \ + *(--_nIns) = ((uint8_t)(i)); \ + *(--_nIns) = 0xc4; \ + *(--_nIns) = 0xf6; \ + asm_output("test ah, %d",i); } while(0) + +#define TEST_AX(i) do { \ + count_fpu();\ + underrunProtect(5); \ + *(--_nIns) = (0); \ + *(--_nIns) = ((uint8_t)(i)); \ + *(--_nIns) = ((uint8_t)((i)>>8)); \ + *(--_nIns) = (0); \ + *(--_nIns) = 0xa9; \ + asm_output("test ax, %d",i); } while(0) + +#define FNSTSW_AX() do { count_fpu(); FPUc(0xdfe0); asm_output("fnstsw_ax"); } while(0) +#define FCHS() do { count_fpu(); FPUc(0xd9e0); asm_output("fchs"); } while(0) +#define FLD1() do { count_fpu(); FPUc(0xd9e8); asm_output("fld1"); fpu_push(); } while(0) +#define FLDZ() do { count_fpu(); FPUc(0xd9ee); asm_output("fldz"); fpu_push(); } while(0) +#define FFREE(r) do { count_fpu(); FPU(0xddc0, r); asm_output("ffree %s",fpn(r)); } while(0) +#define FSTQ(p,d,b) do { count_stq(); FPUm(0xdd02|(p), d, b); asm_output("fst%sq %d(%s)",((p)?"p":""),d,gpn(b)); if (p) fpu_pop(); } while(0) +#define FSTPQ(d,b) FSTQ(1,d,b) +#define FCOM(p,d,b) do { count_fpuld(); FPUm(0xdc02|(p), d, b); asm_output("fcom%s %d(%s)",((p)?"p":""),d,gpn(b)); if (p) fpu_pop(); } while(0) +#define FLDQ(d,b) do { count_ldq(); FPUm(0xdd00, d, b); asm_output("fldq %d(%s)",d,gpn(b)); fpu_push();} while(0) +#define FILDQ(d,b) do { count_fpuld(); FPUm(0xdf05, d, b); asm_output("fildq %d(%s)",d,gpn(b)); fpu_push(); } while(0) +#define FILD(d,b) do { count_fpuld(); FPUm(0xdb00, d, b); asm_output("fild %d(%s)",d,gpn(b)); fpu_push(); } while(0) +#define FADD(d,b) do { count_fpu(); FPUm(0xdc00, d, b); asm_output("fadd %d(%s)",d,gpn(b)); } while(0) +#define FSUB(d,b) do { count_fpu(); FPUm(0xdc04, d, b); asm_output("fsub %d(%s)",d,gpn(b)); } while(0) +#define FSUBR(d,b) do { count_fpu(); FPUm(0xdc05, d, b); asm_output("fsubr %d(%s)",d,gpn(b)); } while(0) +#define FMUL(d,b) do { count_fpu(); FPUm(0xdc01, d, b); asm_output("fmul %d(%s)",d,gpn(b)); } while(0) +#define FDIV(d,b) do { count_fpu(); FPUm(0xdc06, d, b); asm_output("fdiv %d(%s)",d,gpn(b)); } while(0) +#define FDIVR(d,b) do { count_fpu(); FPUm(0xdc07, d, b); asm_output("fdivr %d(%s)",d,gpn(b)); } while(0) +#define FINCSTP() do { count_fpu(); FPUc(0xd9f7); asm_output("fincstp"); } while(0) +#define FSTP(r) do { count_fpu(); FPU(0xddd8, r&7); asm_output("fstp %s",fpn(r)); fpu_pop();} while(0) +#define FCOMP() do { count_fpu(); FPUc(0xD8D9); asm_output("fcomp"); fpu_pop();} while(0) +#define FCOMPP() do { count_fpu(); FPUc(0xDED9); asm_output("fcompp"); fpu_pop();fpu_pop();} while(0) +#define FLDr(r) do { count_ldq(); FPU(0xd9c0,r); asm_output("fld %s",fpn(r)); fpu_push(); } while(0) +#define EMMS() do { count_fpu(); FPUc(0x0f77); asm_output("emms"); } while (0) + +// standard direct call +#define CALL(c) do { \ + count_call();\ + underrunProtect(5); \ + int offset = (c->_address) - ((int)_nIns); \ + IMM32( (uint32_t)offset ); \ + *(--_nIns) = 0xE8; \ + verbose_only(asm_output("call %s",(c->_name));) \ + debug_only(if ((c->_argtypes & ARGSIZE_MASK_ANY)==ARGSIZE_F) fpu_push();)\ +} while (0) + +// indirect call thru register +#define CALLr(c,r) do { \ + count_calli();\ + underrunProtect(2);\ + ALU(0xff, 2, (r));\ + verbose_only(asm_output("call %s",gpn(r));) \ + debug_only(if ((c->_argtypes & ARGSIZE_MASK_ANY)==ARGSIZE_F) fpu_push();)\ +} while (0) + + +} +#endif // __nanojit_Nativei386__ diff --git a/ape-server/deps/js/src/nanojit/RegAlloc.cpp b/ape-server/deps/js/src/nanojit/RegAlloc.cpp new file mode 100755 index 0000000..b60eb3b --- /dev/null +++ b/ape-server/deps/js/src/nanojit/RegAlloc.cpp @@ -0,0 +1,65 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is [Open Source Virtual Machine]. + * + * The Initial Developer of the Original Code is + * Adobe System Incorporated. + * Portions created by the Initial Developer are Copyright (C) 2004-2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adobe AS3 Team + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nanojit.h" + +namespace nanojit +{ + #ifdef FEATURE_NANOJIT + + #ifdef _DEBUG + + uint32_t RegAlloc::countActive() + { + int cnt = 0; + for(Register i=FirstReg; i <= LastReg; i = nextreg(i)) + cnt += active[i] ? 1 : 0; + return cnt; + } + + bool RegAlloc::isConsistent(Register r, LIns* i) + { + NanoAssert(r != UnknownReg); + return (isFree(r) && !getActive(r) && !i) || + (!isFree(r) && getActive(r)== i && i ); + } + + #endif /*DEBUG*/ + #endif /* FEATURE_NANOJIT */ +} diff --git a/ape-server/deps/js/src/nanojit/RegAlloc.h b/ape-server/deps/js/src/nanojit/RegAlloc.h new file mode 100755 index 0000000..90285e4 --- /dev/null +++ b/ape-server/deps/js/src/nanojit/RegAlloc.h @@ -0,0 +1,185 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is [Open Source Virtual Machine]. + * + * The Initial Developer of the Original Code is + * Adobe System Incorporated. + * Portions created by the Initial Developer are Copyright (C) 2004-2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adobe AS3 Team + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + + +#ifndef __nanojit_RegAlloc__ +#define __nanojit_RegAlloc__ + + +namespace nanojit +{ + inline RegisterMask rmask(Register r) + { + return RegisterMask(1) << r; + } + + class RegAlloc + { + public: + RegAlloc() + { + clear(); + } + + void clear() + { + VMPI_memset(this, 0, sizeof(*this)); + } + + bool isFree(Register r) + { + NanoAssert(r != UnknownReg); + return (free & rmask(r)) != 0; + } + + void addFree(Register r) + { + NanoAssert(!isFree(r)); + free |= rmask(r); + } + + void removeFree(Register r) + { + NanoAssert(isFree(r)); + free &= ~rmask(r); + } + + void addActive(Register r, LIns* v) + { + // Count++; + NanoAssert(v); + NanoAssert(r != UnknownReg); + NanoAssert(active[r] == NULL); + active[r] = v; + useActive(r); + } + + void useActive(Register r) + { + NanoAssert(r != UnknownReg); + NanoAssert(active[r] != NULL); + usepri[r] = priority++; + } + + void removeActive(Register r) + { + //registerReleaseCount++; + NanoAssert(r != UnknownReg); + NanoAssert(active[r] != NULL); + + // remove the given register from the active list + active[r] = NULL; + } + + void retire(Register r) + { + NanoAssert(r != UnknownReg); + NanoAssert(active[r] != NULL); + active[r] = NULL; + free |= rmask(r); + } + + int32_t getPriority(Register r) { + NanoAssert(r != UnknownReg && active[r]); + return usepri[r]; + } + + LIns* getActive(Register r) { + NanoAssert(r != UnknownReg); + return active[r]; + } + + debug_only( uint32_t countActive(); ) + debug_only( bool isConsistent(Register r, LIns* v); ) + debug_only( RegisterMask managed; ) // the registers managed by the register allocator + + // Some basics: + // + // - 'active' indicates which registers are active at a particular + // point, and for each active register, which instruction + // defines the value it holds. At the start of register + // allocation no registers are active. + // + // - 'free' indicates which registers are free at a particular point + // and thus available for use. At the start of register + // allocation most registers are free; those that are not + // aren't available for general use, e.g. the stack pointer and + // frame pointer registers. + // + // - 'managed' is exactly this list of initially free registers, + // ie. the registers managed by the register allocator. + // + // - Each LIns has a "reservation" which includes a register value, + // 'reg'. Combined with 'active', this provides a two-way + // mapping between registers and LIR instructions. + // + // - Invariant 1: each register must be in exactly one of the + // following states at all times: unmanaged, free, or active. + // In terms of the relevant fields: + // + // * A register in 'managed' must be in 'active' or 'free' but + // not both. + // + // * A register not in 'managed' must be in neither 'active' nor + // 'free'. + // + // - Invariant 2: the two-way mapping between active registers and + // their defining instructions must always hold in both + // directions and be unambiguous. More specifically: + // + // * An LIns can appear at most once in 'active'. + // + // * An LIns named by 'active[R]' must have an in-use + // reservation that names R. + // + // * And vice versa: an LIns with an in-use reservation that + // names R must be named by 'active[R]'. + // + // * If an LIns's reservation names 'UnknownReg' then LIns + // should not be in 'active'. + // + LIns* active[LastReg + 1]; // active[r] = LIns that defines r + int32_t usepri[LastReg + 1]; // used priority. lower = more likely to spill. + RegisterMask free; + int32_t priority; + + DECLARE_PLATFORM_REGALLOC() + }; +} +#endif // __nanojit_RegAlloc__ diff --git a/ape-server/deps/js/src/nanojit/VMPI.cpp b/ape-server/deps/js/src/nanojit/VMPI.cpp new file mode 100755 index 0000000..79993e7 --- /dev/null +++ b/ape-server/deps/js/src/nanojit/VMPI.cpp @@ -0,0 +1,152 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version 1.1 (the + * "License"); you may not use this file except in compliance with the License. You may obtain + * a copy of the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT + * WARRANTY OF ANY KIND, either express or implied. See the License for the specific + * language governing rights and limitations under the License. + * + * The Original Code is [Open Source Virtual Machine.] + * + * The Initial Developer of the Original Code is Adobe System Incorporated. Portions created + * by the Initial Developer are Copyright (C)[ 2004-2006 ] Adobe Systems Incorporated. All Rights + * Reserved. + * + * Contributor(s): Adobe AS3 Team + * Andreas Gal + * + * Alternatively, the contents of this file may be used under the terms of either the GNU + * General Public License Version 2 or later (the "GPL"), or the GNU Lesser General Public + * License Version 2.1 or later (the "LGPL"), in which case the provisions of the GPL or the + * LGPL are applicable instead of those above. If you wish to allow use of your version of this + * file only under the terms of either the GPL or the LGPL, and not to allow others to use your + * version of this file under the terms of the MPL, indicate your decision by deleting provisions + * above and replace them with the notice and other provisions required by the GPL or the + * LGPL. If you do not delete the provisions above, a recipient may use your version of this file + * under the terms of any one of the MPL, the GPL or the LGPL. + * + ***** END LICENSE BLOCK ***** */ + +#include "nanojit.h" + +#ifdef SOLARIS + #include + #include + #include + #include + extern "C" caddr_t _getfp(void); + typedef caddr_t maddr_ptr; +#else + typedef void *maddr_ptr; +#endif + +using namespace avmplus; + +#ifdef WIN32 +void +VMPI_setPageProtection(void *address, + size_t size, + bool executableFlag, + bool writeableFlag) +{ + DWORD oldProtectFlags = 0; + DWORD newProtectFlags = 0; + if ( executableFlag && writeableFlag ) { + newProtectFlags = PAGE_EXECUTE_READWRITE; + } else if ( executableFlag ) { + newProtectFlags = PAGE_EXECUTE_READ; + } else if ( writeableFlag ) { + newProtectFlags = PAGE_READWRITE; + } else { + newProtectFlags = PAGE_READONLY; + } + + BOOL retval; + MEMORY_BASIC_INFORMATION mbi; + do { + VirtualQuery(address, &mbi, sizeof(MEMORY_BASIC_INFORMATION)); + size_t markSize = size > mbi.RegionSize ? mbi.RegionSize : size; + + retval = VirtualProtect(address, markSize, newProtectFlags, &oldProtectFlags); + NanoAssert(retval); + + address = (char*) address + markSize; + size -= markSize; + } while(size > 0 && retval); + + // We should not be clobbering PAGE_GUARD protections + NanoAssert((oldProtectFlags & PAGE_GUARD) == 0); +} + +#elif defined(AVMPLUS_OS2) + +void +VMPI_setPageProtection(void *address, + size_t size, + bool executableFlag, + bool writeableFlag) +{ + ULONG flags = PAG_READ; + if (executableFlag) { + flags |= PAG_EXECUTE; + } + if (writeableFlag) { + flags |= PAG_WRITE; + } + address = (void*)((size_t)address & ~(0xfff)); + size = (size + 0xfff) & ~(0xfff); + + ULONG attribFlags = PAG_FREE; + while (size) { + ULONG attrib; + ULONG range = size; + ULONG retval = DosQueryMem(address, &range, &attrib); + AvmAssert(retval == 0); + + // exit if this is the start of the next memory object + if (attrib & attribFlags) { + break; + } + attribFlags |= PAG_BASE; + + range = size > range ? range : size; + retval = DosSetMem(address, range, flags); + AvmAssert(retval == 0); + + address = (char*)address + range; + size -= range; + } +} + +#else // !WIN32 && !AVMPLUS_OS2 + +void VMPI_setPageProtection(void *address, + size_t size, + bool executableFlag, + bool writeableFlag) +{ + int bitmask = sysconf(_SC_PAGESIZE) - 1; + // mprotect requires that the addresses be aligned on page boundaries + void *endAddress = (void*) ((char*)address + size); + void *beginPage = (void*) ((size_t)address & ~bitmask); + void *endPage = (void*) (((size_t)endAddress + bitmask) & ~bitmask); + size_t sizePaged = (size_t)endPage - (size_t)beginPage; + + int flags = PROT_READ; + if (executableFlag) { + flags |= PROT_EXEC; + } + if (writeableFlag) { + flags |= PROT_WRITE; + } + int retval = mprotect((maddr_ptr)beginPage, (unsigned int)sizePaged, flags); + AvmAssert(retval == 0); + (void)retval; +} + +#endif // WIN32 diff --git a/ape-server/deps/js/src/nanojit/VMPI.h b/ape-server/deps/js/src/nanojit/VMPI.h new file mode 100755 index 0000000..0549079 --- /dev/null +++ b/ape-server/deps/js/src/nanojit/VMPI.h @@ -0,0 +1,132 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is [Open Source Virtual Machine]. + * + * The Initial Developer of the Original Code is + * Adobe System Incorporated. + * Portions created by the Initial Developer are Copyright (C) 2004-2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adobe AS3 Team + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Stub VMPI implementation to support standalone nanojit repository. + * + * Really only works if you *don't* have a busted-up C library. + */ + +#ifndef __VMPI_h__ +#define __VMPI_h__ + +#if defined(HAVE_CONFIG_H) && defined(NANOJIT_CENTRAL) +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#if defined(AVMPLUS_UNIX) || defined(AVMPLUS_OS2) +#include +#include +#endif + +#ifdef AVMPLUS_WIN32 +#if ! defined(_STDINT_H) +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed int int32_t; +typedef signed __int64 int64_t; +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned __int64 uint64_t; +#endif +#else +#include +#include +#endif + +#define VMPI_strlen strlen +#define VMPI_strcat strcat +#define VMPI_strcmp strcmp +#define VMPI_strncat strncat +#define VMPI_strcpy strcpy +#define VMPI_sprintf sprintf +#define VMPI_vfprintf vfprintf +#define VMPI_memset memset +#define VMPI_isdigit isdigit +#define VMPI_getDate() + +extern void VMPI_setPageProtection(void *address, + size_t size, + bool executableFlag, + bool writeableFlag); + +// Keep this warning-set relatively in sync with platform/win32/win32-platform.h in tamarin. + +#ifdef _MSC_VER + #pragma warning(disable:4201) // nonstandard extension used : nameless struct/union + #pragma warning(disable:4512) // assignment operator could not be generated + #pragma warning(disable:4511) // can't generate copy ctor + #pragma warning(disable:4127) // conditional expression is constant - appears to be compiler noise primarily + #pragma warning(disable:4611) // interaction between _setjmp and destruct + #pragma warning(disable:4725) // instruction may be inaccurate on some Pentiums + #pragma warning(disable:4611) // interaction between '_setjmp' and C++ object destruction is non-portable + #pragma warning(disable:4251) // X needs to have dll-interface to be used by clients of class Y + + // enable some that are off even in /W4 mode, but are still handy + #pragma warning(default:4265) // 'class' : class has virtual functions, but destructor is not virtual + #pragma warning(default:4905) // wide string literal cast to 'LPSTR' + #pragma warning(default:4906) // string literal cast to 'LPWSTR' + #pragma warning(default:4263) // 'function' : member function does not override any base class virtual member function + #pragma warning(default:4264) // 'virtual_function' : no override available for virtual member function from base 'class'; function is hidden + #pragma warning(default:4266) // 'function' : no override available for virtual member function from base 'type'; function is hidden + #pragma warning(default:4242) // 'identifier' : conversion from 'type1' to 'type2', possible loss of data + #pragma warning(default:4263) // member function does not override any base class virtual member function + #pragma warning(default:4296) // expression is always true (false) (Generally, an unsigned variable was used in a comparison operation with zero.) +#endif + +// This part defined in avmshell.h but similarly required for a warning-free nanojit experience. +#ifdef _MSC_VER +#pragma warning(disable:4996) // 'scanf' was declared deprecated +#endif + +// This part is inhibited manually by the CFLAGS in the tamarin configury. +#ifdef _MSC_VER +#pragma warning(disable:4291) // presence of a 'new' operator in nanojit/Allocator.h without matching 'delete' +#endif + +#endif diff --git a/ape-server/deps/js/src/nanojit/avmplus.cpp b/ape-server/deps/js/src/nanojit/avmplus.cpp new file mode 100755 index 0000000..94bbc60 --- /dev/null +++ b/ape-server/deps/js/src/nanojit/avmplus.cpp @@ -0,0 +1,172 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version 1.1 (the + * "License"); you may not use this file except in compliance with the License. You may obtain + * a copy of the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT + * WARRANTY OF ANY KIND, either express or implied. See the License for the specific + * language governing rights and limitations under the License. + * + * The Original Code is [Open Source Virtual Machine.] + * + * The Initial Developer of the Original Code is Adobe System Incorporated. Portions created + * by the Initial Developer are Copyright (C)[ 2004-2006 ] Adobe Systems Incorporated. All Rights + * Reserved. + * + * Contributor(s): Adobe AS3 Team + * Andreas Gal + * + * Alternatively, the contents of this file may be used under the terms of either the GNU + * General Public License Version 2 or later (the "GPL"), or the GNU Lesser General Public + * License Version 2.1 or later (the "LGPL"), in which case the provisions of the GPL or the + * LGPL are applicable instead of those above. If you wish to allow use of your version of this + * file only under the terms of either the GPL or the LGPL, and not to allow others to use your + * version of this file under the terms of the MPL, indicate your decision by deleting provisions + * above and replace them with the notice and other provisions required by the GPL or the + * LGPL. If you do not delete the provisions above, a recipient may use your version of this file + * under the terms of any one of the MPL, the GPL or the LGPL. + * + ***** END LICENSE BLOCK ***** */ + +#include "nanojit.h" + +#ifdef SOLARIS + #include + #include + #include + #include + extern "C" caddr_t _getfp(void); + typedef caddr_t maddr_ptr; +#else + typedef void *maddr_ptr; +#endif + +#if defined(AVMPLUS_ARM) && defined(UNDER_CE) +extern "C" bool +blx_lr_broken() { + return false; +} +#endif + +using namespace avmplus; + +Config AvmCore::config; + +void +avmplus::AvmLog(char const *msg, ...) { + va_list ap; + va_start(ap, msg); + VMPI_vfprintf(stderr, msg, ap); + va_end(ap); +} + +#ifdef _DEBUG +void NanoAssertFail() +{ + #if defined(WIN32) + DebugBreak(); + exit(3); + #elif defined(XP_OS2) || (defined(__GNUC__) && defined(__i386)) + asm("int $3"); + abort(); + #else + abort(); + #endif +} +#endif + +#ifdef WINCE + +// Due to the per-process heap slots on Windows Mobile, we can often run in to OOM +// situations. jemalloc has worked around this problem, and so we use it here. +// Using posix_memalign (or other malloc)functions) here only works because the OS +// and hardware doesn't check for the execute bit being set. + +#ifndef MOZ_MEMORY +#error MOZ_MEMORY required for building on WINCE +#endif + +void* +nanojit::CodeAlloc::allocCodeChunk(size_t nbytes) { + void * buffer; + posix_memalign(&buffer, 4096, nbytes); + return buffer; +} + +void +nanojit::CodeAlloc::freeCodeChunk(void *p, size_t nbytes) { + ::free(p); +} + +#elif defined(WIN32) + +void* +nanojit::CodeAlloc::allocCodeChunk(size_t nbytes) { + return VirtualAlloc(NULL, + nbytes, + MEM_COMMIT | MEM_RESERVE, + PAGE_EXECUTE_READWRITE); +} + +void +nanojit::CodeAlloc::freeCodeChunk(void *p, size_t) { + VirtualFree(p, 0, MEM_RELEASE); +} + +#elif defined(AVMPLUS_OS2) + +void* +nanojit::CodeAlloc::allocCodeChunk(size_t nbytes) { + + // alloc from high memory, fallback to low memory if that fails + void * addr; + if (DosAllocMem(&addr, nbytes, OBJ_ANY | + PAG_COMMIT | PAG_READ | PAG_WRITE | PAG_EXECUTE)) { + if (DosAllocMem(&addr, nbytes, + PAG_COMMIT | PAG_READ | PAG_WRITE | PAG_EXECUTE)) { + return 0; + } + } + return addr; +} + +void +nanojit::CodeAlloc::freeCodeChunk(void *p, size_t nbytes) { + DosFreeMem(p); +} + +#elif defined(AVMPLUS_UNIX) + +void* +nanojit::CodeAlloc::allocCodeChunk(size_t nbytes) { + return mmap(NULL, + nbytes, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANON, + -1, + 0); +} + +void +nanojit::CodeAlloc::freeCodeChunk(void *p, size_t nbytes) { + munmap((maddr_ptr)p, nbytes); +} + +#else // !WIN32 && !AVMPLUS_OS2 && !AVMPLUS_UNIX + +void* +nanojit::CodeAlloc::allocCodeChunk(size_t nbytes) { + return valloc(nbytes); +} + +void +nanojit::CodeAlloc::freeCodeChunk(void *p, size_t nbytes) { + ::free(p); +} + +#endif // WIN32 + diff --git a/ape-server/deps/js/src/nanojit/avmplus.h b/ape-server/deps/js/src/nanojit/avmplus.h new file mode 100755 index 0000000..0f5fc9d --- /dev/null +++ b/ape-server/deps/js/src/nanojit/avmplus.h @@ -0,0 +1,458 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version 1.1 (the + * "License"); you may not use this file except in compliance with the License. You may obtain + * a copy of the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT + * WARRANTY OF ANY KIND, either express or implied. See the License for the specific + * language governing rights and limitations under the License. + * + * The Original Code is [Open Source Virtual Machine.] + * + * The Initial Developer of the Original Code is Adobe System Incorporated. Portions created + * by the Initial Developer are Copyright (C)[ 2004-2006 ] Adobe Systems Incorporated. All Rights + * Reserved. + * + * Contributor(s): Adobe AS3 Team + * Andreas Gal + * Asko Tontti + * + * Alternatively, the contents of this file may be used under the terms of either the GNU + * General Public License Version 2 or later (the "GPL"), or the GNU Lesser General Public + * License Version 2.1 or later (the "LGPL"), in which case the provisions of the GPL or the + * LGPL are applicable instead of those above. If you wish to allow use of your version of this + * file only under the terms of either the GPL or the LGPL, and not to allow others to use your + * version of this file under the terms of the MPL, indicate your decision by deleting provisions + * above and replace them with the notice and other provisions required by the GPL or the + * LGPL. If you do not delete the provisions above, a recipient may use your version of this file + * under the terms of any one of the MPL, the GPL or the LGPL. + * + ***** END LICENSE BLOCK ***** */ + +#ifndef avm_h___ +#define avm_h___ + +#include "VMPI.h" + +#ifdef AVMPLUS_ARM +#define ARM_ARCH AvmCore::config.arch +#define ARM_VFP AvmCore::config.vfp +#define ARM_THUMB2 AvmCore::config.thumb2 +#else +#define ARM_VFP 1 +#define ARM_THUMB2 1 +#endif + +#if !defined(AVMPLUS_LITTLE_ENDIAN) && !defined(AVMPLUS_BIG_ENDIAN) +#ifdef IS_BIG_ENDIAN +#define AVMPLUS_BIG_ENDIAN +#else +#define AVMPLUS_LITTLE_ENDIAN +#endif +#endif + +#if defined(_MSC_VER) && defined(_M_IX86) +#define FASTCALL __fastcall +#elif defined(__GNUC__) && defined(__i386__) && \ + ((__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) +#define FASTCALL __attribute__((fastcall)) +#else +#define FASTCALL +#define NO_FASTCALL +#endif + +#if defined(NO_FASTCALL) +#if defined(AVMPLUS_IA32) +#define SIMULATE_FASTCALL(lr, state_ptr, frag_ptr, func_addr) \ + asm volatile( \ + "call *%%esi" \ + : "=a" (lr) \ + : "c" (state_ptr), "d" (frag_ptr), "S" (func_addr) \ + : "memory", "cc" \ + ); +#endif /* defined(AVMPLUS_IA32) */ +#endif /* defined(NO_FASTCALL) */ + +#ifdef WIN32 +#include +#elif defined(AVMPLUS_OS2) +#define INCL_DOSMEMMGR +#include +#endif + +#if defined(DEBUG) || defined(NJ_NO_VARIADIC_MACROS) +#if !defined _DEBUG +#define _DEBUG +#endif +#define NJ_VERBOSE 1 +#define NJ_PROFILE 1 +#include +#endif + +#ifdef _DEBUG +void NanoAssertFail(); +#endif + +#define AvmAssert(x) assert(x) +#define AvmAssertMsg(x, y) +#define AvmDebugLog(x) printf x + +#if defined(AVMPLUS_IA32) +#if defined(_MSC_VER) +__declspec(naked) static inline __int64 rdtsc() +{ + __asm + { + rdtsc; + ret; + } +} +#elif defined(SOLARIS) +static inline unsigned long long rdtsc(void) +{ + unsigned long long int x; + asm volatile (".byte 0x0f, 0x31" : "=A" (x)); + return x; +} +#elif defined(__i386__) +static __inline__ unsigned long long rdtsc(void) +{ + unsigned long long int x; + __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x)); + return x; +} +#endif /* compilers */ + +#elif defined(__x86_64__) + +static __inline__ uint64_t rdtsc(void) +{ + unsigned hi, lo; + __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi)); + return ( (uint64_t)lo)|( ((uint64_t)hi)<<32 ); +} + +#elif defined(_MSC_VER) && defined(_M_AMD64) + +#include +#pragma intrinsic(__rdtsc) + +static inline unsigned __int64 rdtsc(void) +{ + return __rdtsc(); +} + +#elif defined(__powerpc__) + +typedef unsigned long long int unsigned long long; + +static __inline__ unsigned long long rdtsc(void) +{ + unsigned long long int result=0; + unsigned long int upper, lower,tmp; + __asm__ volatile( + "0: \n" + "\tmftbu %0 \n" + "\tmftb %1 \n" + "\tmftbu %2 \n" + "\tcmpw %2,%0 \n" + "\tbne 0b \n" + : "=r"(upper),"=r"(lower),"=r"(tmp) + ); + result = upper; + result = result<<32; + result = result|lower; + + return(result); +} + +#endif /* architecture */ + +struct JSContext; + +#ifdef PERFM +# define PERFM_NVPROF(n,v) _nvprof(n,v) +# define PERFM_NTPROF(n) _ntprof(n) +# define PERFM_TPROF_END() _tprof_end() +#else +# define PERFM_NVPROF(n,v) +# define PERFM_NTPROF(n) +# define PERFM_TPROF_END() +#endif + +namespace avmplus { + + typedef int FunctionID; + + extern void AvmLog(char const *msg, ...); + + class Config + { + public: + Config() { + memset(this, 0, sizeof(Config)); +#ifdef DEBUG + verbose = false; + verbose_addrs = 1; + verbose_exits = 1; + verbose_live = 1; + show_stats = 1; +#endif + } + + uint32_t tree_opt:1; + uint32_t quiet_opt:1; + uint32_t verbose:1; + uint32_t verbose_addrs:1; + uint32_t verbose_live:1; + uint32_t verbose_exits:1; + uint32_t show_stats:1; + +#if defined (AVMPLUS_IA32) + // Whether or not we can use SSE2 instructions and conditional moves. + bool sse2; + bool use_cmov; + // Whether to use a virtual stack pointer + bool fixed_esp; +#endif + +#if defined (AVMPLUS_ARM) + // Whether or not to generate VFP instructions. +# if defined (NJ_FORCE_SOFTFLOAT) + static const bool vfp = false; +# else + bool vfp; +# endif + + // The ARM architecture version. +# if defined (NJ_FORCE_ARM_ARCH_VERSION) + static const unsigned int arch = NJ_FORCE_ARM_ARCH_VERSION; +# else + unsigned int arch; +# endif + + // Support for Thumb, even if it isn't used by nanojit. This is used to + // determine whether or not to generate interworking branches. +# if defined (NJ_FORCE_NO_ARM_THUMB) + static const bool thumb = false; +# else + bool thumb; +# endif + + // Support for Thumb2, even if it isn't used by nanojit. This is used to + // determine whether or not to use some of the ARMv6T2 instructions. +# if defined (NJ_FORCE_NO_ARM_THUMB2) + static const bool thumb2 = false; +# else + bool thumb2; +# endif + +#endif + +#if defined (NJ_FORCE_SOFTFLOAT) + static const bool soft_float = true; +#else + bool soft_float; +#endif + }; + + static const int kstrconst_emptyString = 0; + + class AvmInterpreter + { + class Labels { + public: + const char* format(const void* ip) + { + static char buf[33]; + sprintf(buf, "%p", ip); + return buf; + } + }; + + Labels _labels; + public: + Labels* labels; + + AvmInterpreter() + { + labels = &_labels; + } + + }; + + class AvmConsole + { + public: + AvmConsole& operator<<(const char* s) + { + fprintf(stdout, "%s", s); + return *this; + } + }; + + class AvmCore + { + public: + AvmInterpreter interp; + AvmConsole console; + + static Config config; + +#ifdef AVMPLUS_IA32 + static inline bool + use_sse2() + { + return config.sse2; + } +#endif + + static inline bool + use_cmov() + { +#ifdef AVMPLUS_IA32 + return config.use_cmov; +#else + return true; +#endif + } + + static inline bool + quiet_opt() + { + return config.quiet_opt; + } + + static inline bool + verbose() + { + return config.verbose; + } + + }; + + /** + * Bit vectors are an efficent method of keeping True/False information + * on a set of items or conditions. Class BitSet provides functions + * to manipulate individual bits in the vector. + * + * Since most vectors are rather small an array of longs is used by + * default to house the value of the bits. If more bits are needed + * then an array is allocated dynamically outside of this object. + * + * This object is not optimized for a fixed sized bit vector + * it instead allows for dynamically growing the bit vector. + */ + class BitSet + { + public: + enum { kUnit = 8*sizeof(long), + kDefaultCapacity = 4 }; + + BitSet() + { + capacity = kDefaultCapacity; + reset(); + } + + ~BitSet() + { + if (capacity > kDefaultCapacity) + free(bits.ptr); + } + + void reset() + { + if (capacity > kDefaultCapacity) + for(int i=0; i= capacity) + grow(index+1); + + if (capacity > kDefaultCapacity) + bits.ptr[index] |= (1< kDefaultCapacity) + bits.ptr[index] &= ~(1< kDefaultCapacity) + value = ( bits.ptr[index] & (1< kDefaultCapacity) + for(int i=0; i kDefaultCapacity) + free(bits.ptr); + + bits.ptr = newBits; + capacity = newCapacity; + } + + // by default we use the array, but if the vector + // size grows beyond kDefaultCapacity we allocate + // space dynamically. + int capacity; + union + { + long ar[kDefaultCapacity]; + long* ptr; + } + bits; + }; +} + +#endif diff --git a/ape-server/deps/js/src/nanojit/manifest.mk b/ape-server/deps/js/src/nanojit/manifest.mk new file mode 100755 index 0000000..5c0c6f1 --- /dev/null +++ b/ape-server/deps/js/src/nanojit/manifest.mk @@ -0,0 +1,79 @@ + +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is [Open Source Virtual Machine]. +# +# The Initial Developer of the Original Code is +# Adobe System Incorporated. +# Portions created by the Initial Developer are Copyright (C) 2005-2006 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +nanojit_cpu_cxxsrc = $(error Unrecognized target CPU.) + +ifeq (i686,$(TARGET_CPU)) +nanojit_cpu_cxxsrc := Nativei386.cpp +endif + +ifeq (x86_64,$(TARGET_CPU)) +nanojit_cpu_cxxsrc := NativeX64.cpp +endif + +ifeq (powerpc,$(TARGET_CPU)) +nanojit_cpu_cxxsrc := NativePPC.cpp +endif + +ifeq (ppc64,$(TARGET_CPU)) +nanojit_cpu_cxxsrc := NativePPC.cpp +endif + +ifeq (arm,$(TARGET_CPU)) +nanojit_cpu_cxxsrc := NativeARM.cpp +endif + +ifeq (sparc,$(TARGET_CPU)) +nanojit_cpu_cxxsrc := NativeSparc.cpp +endif + +avmplus_CXXSRCS := $(avmplus_CXXSRCS) \ + $(curdir)/Allocator.cpp \ + $(curdir)/Assembler.cpp \ + $(curdir)/CodeAlloc.cpp \ + $(curdir)/Containers.cpp \ + $(curdir)/Fragmento.cpp \ + $(curdir)/LIR.cpp \ + $(curdir)/RegAlloc.cpp \ + $(curdir)/$(nanojit_cpu_cxxsrc) \ + $(NULL) + +ifeq ($(COMPILER),VS) +# Disable the 'cast truncates constant value' warning, incurred by +# macros encoding instruction operands in machine code fields. +$(curdir)/Assembler.obj $(curdir)/Nativei386.obj: avmplus_CXXFLAGS += -wd4310 +endif diff --git a/ape-server/deps/js/src/nanojit/nanojit.h b/ape-server/deps/js/src/nanojit/nanojit.h new file mode 100755 index 0000000..1a259a9 --- /dev/null +++ b/ape-server/deps/js/src/nanojit/nanojit.h @@ -0,0 +1,290 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is [Open Source Virtual Machine]. + * + * The Initial Developer of the Original Code is + * Adobe System Incorporated. + * Portions created by the Initial Developer are Copyright (C) 2004-2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Adobe AS3 Team + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef __nanojit_h__ +#define __nanojit_h__ + +#include "avmplus.h" + +#ifdef FEATURE_NANOJIT + +#if defined AVMPLUS_IA32 + #define NANOJIT_IA32 +#elif defined AVMPLUS_ARM + #define NANOJIT_ARM +#elif defined AVMPLUS_PPC + #define NANOJIT_PPC +#elif defined AVMPLUS_SPARC + #define NANOJIT_SPARC +#elif defined AVMPLUS_AMD64 + #define NANOJIT_X64 +#else + #error "unknown nanojit architecture" +#endif + +#ifdef AVMPLUS_64BIT +#define NANOJIT_64BIT +#endif + +#if defined NANOJIT_64BIT + #define IF_64BIT(...) __VA_ARGS__ + #define UNLESS_64BIT(...) +#else + #define IF_64BIT(...) + #define UNLESS_64BIT(...) __VA_ARGS__ +#endif + +// set ARM_VFP constant if not already set +#if !defined(ARM_VFP) + #ifdef AVMPLUS_ARM + #if defined(NJ_ARM_VFP) + #define ARM_VFP 1 + #else + #define ARM_VFP 0 + #endif + #else + // some LIR features should test VFP on ARM, + // but can be set to "always on" on non-ARM + #define ARM_VFP 1 + #endif +#endif + +// Embed no-op macros that let Valgrind work with the JIT. +#ifdef MOZ_VALGRIND +# define JS_VALGRIND +#endif +#ifdef JS_VALGRIND +# include +#else +# define VALGRIND_DISCARD_TRANSLATIONS(addr, szB) +#endif + +namespace nanojit +{ + /** + * ------------------------------------------- + * START AVM bridging definitions + * ------------------------------------------- + */ + typedef avmplus::AvmCore AvmCore; + + const uint32_t MAXARGS = 8; + + #ifdef NJ_NO_VARIADIC_MACROS + inline void NanoAssertMsgf(bool a,const char *f,...) {} + inline void NanoAssertMsg(bool a,const char *m) {} + inline void NanoAssert(bool a) {} + #elif defined(_DEBUG) + + #define __NanoAssertMsgf(a, file_, line_, f, ...) \ + if (!(a)) { \ + avmplus::AvmLog("Assertion failed: " f "%s (%s:%d)\n", __VA_ARGS__, #a, file_, line_); \ + NanoAssertFail(); \ + } + + #define _NanoAssertMsgf(a, file_, line_, f, ...) __NanoAssertMsgf(a, file_, line_, f, __VA_ARGS__) + + #define NanoAssertMsgf(a,f,...) do { __NanoAssertMsgf(a, __FILE__, __LINE__, f ": ", __VA_ARGS__); } while (0) + #define NanoAssertMsg(a,m) do { __NanoAssertMsgf(a, __FILE__, __LINE__, "\"%s\": ", m); } while (0) + #define NanoAssert(a) do { __NanoAssertMsgf(a, __FILE__, __LINE__, "%s", ""); } while (0) + #else + #define NanoAssertMsgf(a,f,...) do { } while (0) /* no semi */ + #define NanoAssertMsg(a,m) do { } while (0) /* no semi */ + #define NanoAssert(a) do { } while (0) /* no semi */ + #endif + + /* + * Sun Studio C++ compiler has a bug + * "sizeof expression not accepted as size of array parameter" + * The bug number is 6688515. It is not public yet. + * Turn off this assert for Sun Studio until this bug is fixed. + */ + #ifdef __SUNPRO_CC + #define NanoStaticAssert(condition) + #else + #define NanoStaticAssert(condition) \ + extern void nano_static_assert(int arg[(condition) ? 1 : -1]) + #endif + + + /** + * ------------------------------------------- + * END AVM bridging definitions + * ------------------------------------------- + */ +} + +#ifdef AVMPLUS_VERBOSE +#ifndef NJ_VERBOSE_DISABLED + #define NJ_VERBOSE 1 +#endif +#ifndef NJ_PROFILE_DISABLED + #define NJ_PROFILE 1 +#endif +#endif + +#ifdef NJ_NO_VARIADIC_MACROS + #include + #define verbose_outputf if (_logc->lcbits & LC_Assembly) \ + Assembler::outputf + #define verbose_only(x) x +#elif defined(NJ_VERBOSE) + #include + #define verbose_outputf if (_logc->lcbits & LC_Assembly) \ + Assembler::outputf + #define verbose_only(...) __VA_ARGS__ +#else + #define verbose_outputf + #define verbose_only(...) +#endif /*NJ_VERBOSE*/ + +#ifdef _DEBUG + #define debug_only(x) x +#else + #define debug_only(x) +#endif /* DEBUG */ + +#ifdef NJ_PROFILE + #define counter_struct_begin() struct { + #define counter_struct_end() } _stats; + #define counter_define(x) int32_t x + #define counter_value(x) _stats.x + #define counter_set(x,v) (counter_value(x)=(v)) + #define counter_adjust(x,i) (counter_value(x)+=(int32_t)(i)) + #define counter_reset(x) counter_set(x,0) + #define counter_increment(x) counter_adjust(x,1) + #define counter_decrement(x) counter_adjust(x,-1) + #define profile_only(x) x +#else + #define counter_struct_begin() + #define counter_struct_end() + #define counter_define(x) + #define counter_value(x) + #define counter_set(x,v) + #define counter_adjust(x,i) + #define counter_reset(x) + #define counter_increment(x) + #define counter_decrement(x) + #define profile_only(x) +#endif /* NJ_PROFILE */ + +#define isS8(i) ( int32_t(i) == int8_t(i) ) +#define isU8(i) ( int32_t(i) == uint8_t(i) ) +#define isS16(i) ( int32_t(i) == int16_t(i) ) +#define isU16(i) ( int32_t(i) == uint16_t(i) ) +#define isS24(i) ( (int32_t((i)<<8)>>8) == (i) ) + +static inline bool isS32(intptr_t i) { + return int32_t(i) == i; +} + +static inline bool isU32(uintptr_t i) { + return uint32_t(i) == i; +} + +#define alignTo(x,s) ((((uintptr_t)(x)))&~(((uintptr_t)s)-1)) +#define alignUp(x,s) ((((uintptr_t)(x))+(((uintptr_t)s)-1))&~(((uintptr_t)s)-1)) + +// ------------------------------------------------------------------- +// START debug-logging definitions +// ------------------------------------------------------------------- + +/* Debug printing stuff. All Nanojit and jstracer debug printing + should be routed through LogControl::printf. Don't use + ad-hoc calls to printf, fprintf(stderr, ...) etc. + + Similarly, don't use ad-hoc getenvs etc to decide whether or not to + print debug output. Instead consult the relevant control bit in + LogControl::lcbits in the LogControl object you are supplied with. +*/ + +# if defined(__GNUC__) +# define PRINTF_CHECK(x, y) __attribute__((format(__printf__, x, y))) +# else +# define PRINTF_CHECK(x, y) +# endif + +namespace nanojit { + + // LogControl, a class for controlling and routing debug output + + enum LC_Bits { + /* Output control bits for Nanojit code. Only use bits 15 + and below, so that callers can use bits 16 and above for + themselves. */ + // TODO: add entries for the writer pipeline + LC_FragProfile = 1<<7, // collect per-frag usage counts + LC_Activation = 1<<6, // enable printActivationState + LC_Liveness = 1<<5, // (show LIR liveness analysis) + LC_ReadLIR = 1<<4, // As read from LirBuffer + LC_AfterSF = 1<<3, // After StackFilter + LC_RegAlloc = 1<<2, // stuff to do with reg alloc + LC_Assembly = 1<<1, // final assembly + LC_NoCodeAddrs = 1<<0 // (don't show code addresses on asm output) + }; + + class LogControl + { + public: + // All Nanojit and jstracer printing should be routed through + // this function. + void printf( const char* format, ... ) PRINTF_CHECK(2,3); + + // An OR of LC_Bits values, indicating what should be output + uint32_t lcbits; + }; + +} + +// ------------------------------------------------------------------- +// END debug-logging definitions +// ------------------------------------------------------------------- + + +#include "Allocator.h" +#include "Containers.h" +#include "Native.h" +#include "CodeAlloc.h" +#include "LIR.h" +#include "RegAlloc.h" +#include "Fragmento.h" +#include "Assembler.h" + +#endif // FEATURE_NANOJIT +#endif // __nanojit_h__ diff --git a/ape-server/deps/js/src/perfect.js b/ape-server/deps/js/src/perfect.js new file mode 100755 index 0000000..aeca121 --- /dev/null +++ b/ape-server/deps/js/src/perfect.js @@ -0,0 +1,39 @@ +// Some simple testing of new, eval and some string stuff. + +// constructor -- expression array initialization +function ExprArray(n,v) +{ + // Initializes n values to v coerced to a string. + for (var i = 0; i < n; i++) { + this[i] = "" + v; + } +} + + +// Print the perfect numbers up to n and the sum expression for n's divisors. +function perfect(n) +{ + print("The perfect numbers up to " + n + " are:"); + + // We build sumOfDivisors[i] to hold a string expression for + // the sum of the divisors of i, excluding i itself. + var sumOfDivisors = new ExprArray(n+1,1); + for (var divisor = 2; divisor <= n; divisor++) { + for (var j = divisor + divisor; j <= n; j += divisor) { + sumOfDivisors[j] += " + " + divisor; + } + // At this point everything up to 'divisor' has its sumOfDivisors + // expression calculated, so we can determine whether it's perfect + // already by evaluating. + if (eval(sumOfDivisors[divisor]) == divisor) { + print("" + divisor + " = " + sumOfDivisors[divisor]); + } + } + print("That's all."); +} + + +print("\nA number is 'perfect' if it is equal to the sum of its") +print("divisors (excluding itself).\n"); +perfect(500); + diff --git a/ape-server/deps/js/src/plify_jsdhash.sed b/ape-server/deps/js/src/plify_jsdhash.sed new file mode 100755 index 0000000..90b3692 --- /dev/null +++ b/ape-server/deps/js/src/plify_jsdhash.sed @@ -0,0 +1,39 @@ +/ * Double hashing implementation./a\ +\ * GENERATED BY js/src/plify_jsdhash.sed -- DO NOT EDIT!!! +/ * Double hashing, a la Knuth 6./a\ +\ * GENERATED BY js/src/plify_jsdhash.sed -- DO NOT EDIT!!! +s/jsdhash_h___/pldhash_h___/ +s/jsdhash\.bigdump/pldhash.bigdump/ +s/jstypes\.h/nscore.h/ +s/jsbit\.h/prbit.h/ +s/jsdhash\.h/pldhash.h/ +s/jsdhash\.c/pldhash.c/ +s/jsdhash:/pldhash:/ +s/jsutil\.h/nsDebug.h/ +s/JS_DHASH/PL_DHASH/g +s/JS_DHash/PL_DHash/g +s/JSDHash/PLDHash/g +s/JSHash/PLHash/g +s/uint8 /PRUint8/g +s/uint16 /PRUint16/g +s/uint32 /PRUint32/g +s/\([^U]\)int8 /\1PRInt8/g +s/\([^U]\)int16 /\1PRInt16/g +s/\([^U]\)int32 /\1PRInt32/g +s/uint8/PRUint8/g +s/uint16/PRUint16/g +s/uint32/PRUint32/g +s/\([^U]\)int8/\1PRInt8/g +s/\([^U]\)int16/\1PRInt16/g +s/\([^U]\)int32/\1PRInt32/g +s/JSBool/PRBool/g +s/extern JS_PUBLIC_API(\([^()]*\))/NS_COM_GLUE \1/ +s/JS_PUBLIC_API(\([^()]*\))/\1/ +s/JS_NewDHashTable/PL_NewDHashTable/ +s/JS_ASSERT(0)/NS_NOTREACHED("0")/ +s/\( *\)JS_ASSERT(\(.*\));/\1NS_ASSERTION(\2,\n\1 "\2");/ +s/JSDHASH_ONELINE_ASSERT(\(.*\));/NS_ASSERTION(\1, "\1");/ +s/JS_UNLIKELY/NS_UNLIKELY/g +s/JS_LIKELY/NS_LIKELY/g +s/JS_/PR_/g +s/fprintf(stderr,/printf_stderr(/ diff --git a/ape-server/deps/js/src/prmjtime.cpp b/ape-server/deps/js/src/prmjtime.cpp new file mode 100755 index 0000000..986f480 --- /dev/null +++ b/ape-server/deps/js/src/prmjtime.cpp @@ -0,0 +1,911 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * PR time code. + */ +#ifdef SOLARIS +#define _REENTRANT 1 +#endif +#include +#include +#include "jstypes.h" +#include "jsstdint.h" +#include "jsutil.h" + +#include "jsprf.h" +#include "jslock.h" +#include "prmjtime.h" + +#define PRMJ_DO_MILLISECONDS 1 + +#ifdef XP_OS2 +#include +#endif +#ifdef XP_WIN +#include +#include +#include /* for fabs */ +#include /* for timeBegin/EndPeriod */ +/* VC++ 8.0 or later, and not WINCE */ +#if _MSC_VER >= 1400 && !defined(WINCE) +#define NS_HAVE_INVALID_PARAMETER_HANDLER 1 +#endif +#ifdef NS_HAVE_INVALID_PARAMETER_HANDLER +#include /* for _set_invalid_parameter_handler */ +#include /* for _CrtSetReportMode */ +#endif + +#ifdef JS_THREADSAFE +#include +#endif + +#endif + +#if defined(XP_UNIX) || defined(XP_BEOS) + +#ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris */ +extern int gettimeofday(struct timeval *tv); +#endif + +#include + +#endif /* XP_UNIX */ + +#define PRMJ_YEAR_DAYS 365L +#define PRMJ_FOUR_YEARS_DAYS (4 * PRMJ_YEAR_DAYS + 1) +#define PRMJ_CENTURY_DAYS (25 * PRMJ_FOUR_YEARS_DAYS - 1) +#define PRMJ_FOUR_CENTURIES_DAYS (4 * PRMJ_CENTURY_DAYS + 1) +#define PRMJ_HOUR_SECONDS 3600L +#define PRMJ_DAY_SECONDS (24L * PRMJ_HOUR_SECONDS) +#define PRMJ_YEAR_SECONDS (PRMJ_DAY_SECONDS * PRMJ_YEAR_DAYS) +#define PRMJ_MAX_UNIX_TIMET 2145859200L /*time_t value equiv. to 12/31/2037 */ + +/* function prototypes */ +static void PRMJ_basetime(JSInt64 tsecs, PRMJTime *prtm); +/* + * get the difference in seconds between this time zone and UTC (GMT) + */ +JSInt32 +PRMJ_LocalGMTDifference() +{ + struct tm ltime; + +#if defined(XP_WIN) && !defined(WINCE) + /* Windows does not follow POSIX. Updates to the + * TZ environment variable are not reflected + * immediately on that platform as they are + * on UNIX systems without this call. + */ + _tzset(); +#endif + /* get the difference between this time zone and GMT */ + memset((char *)<ime,0,sizeof(ltime)); + ltime.tm_mday = 2; + ltime.tm_year = 70; + return (JSInt32)mktime(<ime) - (24L * 3600L); +} + +/* Constants for GMT offset from 1970 */ +#define G1970GMTMICROHI 0x00dcdcad /* micro secs to 1970 hi */ +#define G1970GMTMICROLOW 0x8b3fa000 /* micro secs to 1970 low */ + +#define G2037GMTMICROHI 0x00e45fab /* micro secs to 2037 high */ +#define G2037GMTMICROLOW 0x7a238000 /* micro secs to 2037 low */ + +/* Convert from base time to extended time */ +static JSInt64 +PRMJ_ToExtendedTime(JSInt32 base_time) +{ + JSInt64 exttime; + JSInt64 g1970GMTMicroSeconds; + JSInt64 low; + JSInt32 diff; + JSInt64 tmp; + JSInt64 tmp1; + + diff = PRMJ_LocalGMTDifference(); + JSLL_UI2L(tmp, PRMJ_USEC_PER_SEC); + JSLL_I2L(tmp1,diff); + JSLL_MUL(tmp,tmp,tmp1); + + JSLL_UI2L(g1970GMTMicroSeconds,G1970GMTMICROHI); + JSLL_UI2L(low,G1970GMTMICROLOW); + JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,16); + JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,16); + JSLL_ADD(g1970GMTMicroSeconds,g1970GMTMicroSeconds,low); + + JSLL_I2L(exttime,base_time); + JSLL_ADD(exttime,exttime,g1970GMTMicroSeconds); + JSLL_SUB(exttime,exttime,tmp); + return exttime; +} + +#ifdef HAVE_SYSTEMTIMETOFILETIME + +static const JSInt64 win2un = JSLL_INIT(0x19DB1DE, 0xD53E8000); + +#define FILETIME2INT64(ft) (((JSInt64)ft.dwHighDateTime) << 32LL | (JSInt64)ft.dwLowDateTime) + +#endif + +#if defined(HAVE_GETSYSTEMTIMEASFILETIME) || defined(HAVE_SYSTEMTIMETOFILETIME) + +#if defined(HAVE_GETSYSTEMTIMEASFILETIME) +inline void +LowResTime(LPFILETIME lpft) +{ + GetSystemTimeAsFileTime(lpft); +} +#elif defined(HAVE_SYSTEMTIMETOFILETIME) +inline void +LowResTime(LPFILETIME lpft) +{ + GetCurrentFT(lpft); +} +#else +#error "No implementation of PRMJ_Now was selected." +#endif + +typedef struct CalibrationData { + long double freq; /* The performance counter frequency */ + long double offset; /* The low res 'epoch' */ + long double timer_offset; /* The high res 'epoch' */ + + /* The last high res time that we returned since recalibrating */ + JSInt64 last; + + JSBool calibrated; + +#ifdef JS_THREADSAFE + CRITICAL_SECTION data_lock; + CRITICAL_SECTION calibration_lock; +#endif +#ifdef WINCE + JSInt64 granularity; +#endif +} CalibrationData; + +static CalibrationData calibration = { 0 }; + +static void +NowCalibrate() +{ + FILETIME ft, ftStart; + LARGE_INTEGER liFreq, now; + + if (calibration.freq == 0.0) { + if(!QueryPerformanceFrequency(&liFreq)) { + /* High-performance timer is unavailable */ + calibration.freq = -1.0; + } else { + calibration.freq = (long double) liFreq.QuadPart; + } + } + if (calibration.freq > 0.0) { + JSInt64 calibrationDelta = 0; + + /* By wrapping a timeBegin/EndPeriod pair of calls around this loop, + the loop seems to take much less time (1 ms vs 15ms) on Vista. */ + timeBeginPeriod(1); + LowResTime(&ftStart); + do { + LowResTime(&ft); + } while (memcmp(&ftStart,&ft, sizeof(ft)) == 0); + timeEndPeriod(1); + +#ifdef WINCE + calibration.granularity = (FILETIME2INT64(ft) - + FILETIME2INT64(ftStart))/10; +#endif + /* + calibrationDelta = (FILETIME2INT64(ft) - FILETIME2INT64(ftStart))/10; + fprintf(stderr, "Calibration delta was %I64d us\n", calibrationDelta); + */ + + QueryPerformanceCounter(&now); + + calibration.offset = (long double) FILETIME2INT64(ft); + calibration.timer_offset = (long double) now.QuadPart; + + /* The windows epoch is around 1600. The unix epoch is around + 1970. win2un is the difference (in windows time units which + are 10 times more highres than the JS time unit) */ + calibration.offset -= win2un; + calibration.offset *= 0.1; + calibration.last = 0; + + calibration.calibrated = JS_TRUE; + } +} + +#define CALIBRATIONLOCK_SPINCOUNT 0 +#define DATALOCK_SPINCOUNT 4096 +#define LASTLOCK_SPINCOUNT 4096 + +#ifdef JS_THREADSAFE +static PRStatus +NowInit(void) +{ + memset(&calibration, 0, sizeof(calibration)); + NowCalibrate(); +#ifdef WINCE + InitializeCriticalSection(&calibration.calibration_lock); + InitializeCriticalSection(&calibration.data_lock); +#else + InitializeCriticalSectionAndSpinCount(&calibration.calibration_lock, CALIBRATIONLOCK_SPINCOUNT); + InitializeCriticalSectionAndSpinCount(&calibration.data_lock, DATALOCK_SPINCOUNT); +#endif + return PR_SUCCESS; +} + +void +PRMJ_NowShutdown() +{ + DeleteCriticalSection(&calibration.calibration_lock); + DeleteCriticalSection(&calibration.data_lock); +} + +#define MUTEX_LOCK(m) EnterCriticalSection(m) +#define MUTEX_TRYLOCK(m) TryEnterCriticalSection(m) +#define MUTEX_UNLOCK(m) LeaveCriticalSection(m) +#ifdef WINCE +#define MUTEX_SETSPINCOUNT(m, c) +#else +#define MUTEX_SETSPINCOUNT(m, c) SetCriticalSectionSpinCount((m),(c)) +#endif + +static PRCallOnceType calibrationOnce = { 0 }; + +#else + +#define MUTEX_LOCK(m) +#define MUTEX_TRYLOCK(m) 1 +#define MUTEX_UNLOCK(m) +#define MUTEX_SETSPINCOUNT(m, c) + +#endif + +#endif /* HAVE_GETSYSTEMTIMEASFILETIME */ + + +#if defined(XP_OS2) +JSInt64 +PRMJ_Now(void) +{ + JSInt64 s, us, ms2us, s2us; + struct timeb b; + + ftime(&b); + JSLL_UI2L(ms2us, PRMJ_USEC_PER_MSEC); + JSLL_UI2L(s2us, PRMJ_USEC_PER_SEC); + JSLL_UI2L(s, b.time); + JSLL_UI2L(us, b.millitm); + JSLL_MUL(us, us, ms2us); + JSLL_MUL(s, s, s2us); + JSLL_ADD(s, s, us); + return s; +} + +#elif defined(XP_UNIX) || defined(XP_BEOS) +JSInt64 +PRMJ_Now(void) +{ + struct timeval tv; + JSInt64 s, us, s2us; + +#ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris */ + gettimeofday(&tv); +#else + gettimeofday(&tv, 0); +#endif /* _SVID_GETTOD */ + JSLL_UI2L(s2us, PRMJ_USEC_PER_SEC); + JSLL_UI2L(s, tv.tv_sec); + JSLL_UI2L(us, tv.tv_usec); + JSLL_MUL(s, s, s2us); + JSLL_ADD(s, s, us); + return s; +} + +#else +/* + +Win32 python-esque pseudo code +Please see bug 363258 for why the win32 timing code is so complex. + +calibration mutex : Win32CriticalSection(spincount=0) +data mutex : Win32CriticalSection(spincount=4096) + +def NowInit(): + init mutexes + PRMJ_NowCalibration() + +def NowCalibration(): + expensive up-to-15ms call + +def PRMJ_Now(): + returnedTime = 0 + needCalibration = False + cachedOffset = 0.0 + calibrated = False + PR_CallOnce(PRMJ_NowInit) + do + if not global.calibrated or needCalibration: + acquire calibration mutex + acquire data mutex + + // Only recalibrate if someone didn't already + if cachedOffset == calibration.offset: + // Have all waiting threads immediately wait + set data mutex spin count = 0 + PRMJ_NowCalibrate() + calibrated = 1 + + set data mutex spin count = default + release data mutex + release calibration mutex + + calculate lowres time + + if highres timer available: + acquire data mutex + calculate highres time + cachedOffset = calibration.offset + highres time = calibration.last = max(highres time, calibration.last) + release data mutex + + get kernel tick interval + + if abs(highres - lowres) < kernel tick: + returnedTime = highres time + needCalibration = False + else: + if calibrated: + returnedTime = lowres + needCalibration = False + else: + needCalibration = True + else: + returnedTime = lowres + while needCalibration + +*/ + +JSInt64 +PRMJ_Now(void) +{ + static int nCalls = 0; + long double lowresTime, highresTimerValue; + FILETIME ft; + LARGE_INTEGER now; + JSBool calibrated = JS_FALSE; + JSBool needsCalibration = JS_FALSE; + JSInt64 returnedTime; + long double cachedOffset = 0.0; + + /* To avoid regressing startup time (where high resolution is likely + not needed), give the old behavior for the first few calls. + This does not appear to be needed on Vista as the timeBegin/timeEndPeriod + calls seem to immediately take effect. */ + int thiscall = JS_ATOMIC_INCREMENT(&nCalls); + /* 10 seems to be the number of calls to load with a blank homepage */ + if (thiscall <= 10) { + LowResTime(&ft); + return (FILETIME2INT64(ft)-win2un)/10L; + } + + /* For non threadsafe platforms, NowInit is not necessary */ +#ifdef JS_THREADSAFE + PR_CallOnce(&calibrationOnce, NowInit); +#endif + do { + if (!calibration.calibrated || needsCalibration) { + MUTEX_LOCK(&calibration.calibration_lock); + MUTEX_LOCK(&calibration.data_lock); + + /* Recalibrate only if no one else did before us */ + if(calibration.offset == cachedOffset) { + /* Since calibration can take a while, make any other + threads immediately wait */ + MUTEX_SETSPINCOUNT(&calibration.data_lock, 0); + + NowCalibrate(); + + calibrated = JS_TRUE; + + /* Restore spin count */ + MUTEX_SETSPINCOUNT(&calibration.data_lock, DATALOCK_SPINCOUNT); + } + MUTEX_UNLOCK(&calibration.data_lock); + MUTEX_UNLOCK(&calibration.calibration_lock); + } + + + /* Calculate a low resolution time */ + LowResTime(&ft); + lowresTime = 0.1*(long double)(FILETIME2INT64(ft) - win2un); + + if (calibration.freq > 0.0) { + long double highresTime, diff; + + DWORD timeAdjustment, timeIncrement; + BOOL timeAdjustmentDisabled; + + /* Default to 15.625 ms if the syscall fails */ + long double skewThreshold = 15625.25; + /* Grab high resolution time */ + QueryPerformanceCounter(&now); + highresTimerValue = (long double)now.QuadPart; + + MUTEX_LOCK(&calibration.data_lock); + highresTime = calibration.offset + PRMJ_USEC_PER_SEC* + (highresTimerValue-calibration.timer_offset)/calibration.freq; + cachedOffset = calibration.offset; + + /* On some dual processor/core systems, we might get an earlier time + so we cache the last time that we returned */ + calibration.last = JS_MAX(calibration.last,(JSInt64)highresTime); + returnedTime = calibration.last; + MUTEX_UNLOCK(&calibration.data_lock); + +#ifdef WINCE + /* Get an estimate of clock ticks per second from our own test */ + skewThreshold = calibration.granularity; +#else + /* Rather than assume the NT kernel ticks every 15.6ms, ask it */ + if (GetSystemTimeAdjustment(&timeAdjustment, + &timeIncrement, + &timeAdjustmentDisabled)) { + if (timeAdjustmentDisabled) { + /* timeAdjustment is in units of 100ns */ + skewThreshold = timeAdjustment/10.0; + } else { + /* timeIncrement is in units of 100ns */ + skewThreshold = timeIncrement/10.0; + } + } +#endif + /* Check for clock skew */ + diff = lowresTime - highresTime; + + /* For some reason that I have not determined, the skew can be + up to twice a kernel tick. This does not seem to happen by + itself, but I have only seen it triggered by another program + doing some kind of file I/O. The symptoms are a negative diff + followed by an equally large positive diff. */ + if (fabs(diff) > 2*skewThreshold) { + /*fprintf(stderr,"Clock skew detected (diff = %f)!\n", diff);*/ + + if (calibrated) { + /* If we already calibrated once this instance, and the + clock is still skewed, then either the processor(s) are + wildly changing clockspeed or the system is so busy that + we get switched out for long periods of time. In either + case, it would be infeasible to make use of high + resolution results for anything, so let's resort to old + behavior for this call. It's possible that in the + future, the user will want the high resolution timer, so + we don't disable it entirely. */ + returnedTime = (JSInt64)lowresTime; + needsCalibration = JS_FALSE; + } else { + /* It is possible that when we recalibrate, we will return a + value less than what we have returned before; this is + unavoidable. We cannot tell the different between a + faulty QueryPerformanceCounter implementation and user + changes to the operating system time. Since we must + respect user changes to the operating system time, we + cannot maintain the invariant that Date.now() never + decreases; the old implementation has this behavior as + well. */ + needsCalibration = JS_TRUE; + } + } else { + /* No detectable clock skew */ + returnedTime = (JSInt64)highresTime; + needsCalibration = JS_FALSE; + } + } else { + /* No high resolution timer is available, so fall back */ + returnedTime = (JSInt64)lowresTime; + } + } while (needsCalibration); + + return returnedTime; +} +#endif + +/* Get the DST timezone offset for the time passed in */ +JSInt64 +PRMJ_DSTOffset(JSInt64 local_time) +{ + JSInt64 us2s; + time_t local; + JSInt32 diff; + JSInt64 maxtimet; + struct tm tm; + PRMJTime prtm; +#ifndef HAVE_LOCALTIME_R + struct tm *ptm; +#endif + + + JSLL_UI2L(us2s, PRMJ_USEC_PER_SEC); + JSLL_DIV(local_time, local_time, us2s); + + /* get the maximum of time_t value */ + JSLL_UI2L(maxtimet,PRMJ_MAX_UNIX_TIMET); + + if(JSLL_CMP(local_time,>,maxtimet)){ + JSLL_UI2L(local_time,PRMJ_MAX_UNIX_TIMET); + } else if(!JSLL_GE_ZERO(local_time)){ + /*go ahead a day to make localtime work (does not work with 0) */ + JSLL_UI2L(local_time,PRMJ_DAY_SECONDS); + } + +#if defined(XP_WIN) && !defined(WINCE) + /* Windows does not follow POSIX. Updates to the + * TZ environment variable are not reflected + * immediately on that platform as they are + * on UNIX systems without this call. + */ + _tzset(); +#endif + + JSLL_L2UI(local,local_time); + PRMJ_basetime(local_time,&prtm); +#ifndef HAVE_LOCALTIME_R + ptm = localtime(&local); + if(!ptm){ + return 0; + } + tm = *ptm; +#else + localtime_r(&local,&tm); /* get dst information */ +#endif + + diff = ((tm.tm_hour - prtm.tm_hour) * PRMJ_HOUR_SECONDS) + + ((tm.tm_min - prtm.tm_min) * 60); + + if (diff < 0) + diff += PRMJ_DAY_SECONDS; + + JSLL_UI2L(local_time,diff); + + JSLL_MUL(local_time,local_time,us2s); + + return(local_time); +} + +#ifdef NS_HAVE_INVALID_PARAMETER_HANDLER +static void +PRMJ_InvalidParameterHandler(const wchar_t *expression, + const wchar_t *function, + const wchar_t *file, + unsigned int line, + uintptr_t pReserved) +{ + /* empty */ +} +#endif + +/* Format a time value into a buffer. Same semantics as strftime() */ +size_t +PRMJ_FormatTime(char *buf, int buflen, const char *fmt, PRMJTime *prtm) +{ + size_t result = 0; +#if defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS) + struct tm a; + int fake_tm_year = 0; +#ifdef NS_HAVE_INVALID_PARAMETER_HANDLER + _invalid_parameter_handler oldHandler; + int oldReportMode; +#endif + + /* Zero out the tm struct. Linux, SunOS 4 struct tm has extra members int + * tm_gmtoff, char *tm_zone; when tm_zone is garbage, strftime gets + * confused and dumps core. NSPR20 prtime.c attempts to fill these in by + * calling mktime on the partially filled struct, but this doesn't seem to + * work as well; the result string has "can't get timezone" for ECMA-valid + * years. Might still make sense to use this, but find the range of years + * for which valid tz information exists, and map (per ECMA hint) from the + * given year into that range. + + * N.B. This hasn't been tested with anything that actually _uses_ + * tm_gmtoff; zero might be the wrong thing to set it to if you really need + * to format a time. This fix is for jsdate.c, which only uses + * JS_FormatTime to get a string representing the time zone. */ + memset(&a, 0, sizeof(struct tm)); + + a.tm_sec = prtm->tm_sec; + a.tm_min = prtm->tm_min; + a.tm_hour = prtm->tm_hour; + a.tm_mday = prtm->tm_mday; + a.tm_mon = prtm->tm_mon; + a.tm_wday = prtm->tm_wday; + +#if defined(HAVE_LOCALTIME_R) && defined(HAVE_TM_ZONE_TM_GMTOFF) + { + struct tm td; + time_t bogus = 0; + localtime_r(&bogus, &td); + a.tm_gmtoff = td.tm_gmtoff; + a.tm_zone = td.tm_zone; + } +#endif + + /* + * Years before 1900 and after 9999 cause strftime() to abort on Windows. + * To avoid that we replace it with FAKE_YEAR_BASE + year % 100 and then + * replace matching substrings in the strftime() result with the real year. + * Note that FAKE_YEAR_BASE should be a multiple of 100 to make 2-digit + * year formats (%y) work correctly (since we won't find the fake year + * in that case). + * e.g. new Date(1873, 0).toLocaleFormat('%Y %y') => "1873 73" + * See bug 327869. + */ +#define FAKE_YEAR_BASE 9900 + if (prtm->tm_year < 1900 || prtm->tm_year > 9999) { + fake_tm_year = FAKE_YEAR_BASE + prtm->tm_year % 100; + a.tm_year = fake_tm_year - 1900; + } + else { + a.tm_year = prtm->tm_year - 1900; + } + a.tm_yday = prtm->tm_yday; + a.tm_isdst = prtm->tm_isdst; + + /* + * Even with the above, SunOS 4 seems to detonate if tm_zone and tm_gmtoff + * are null. This doesn't quite work, though - the timezone is off by + * tzoff + dst. (And mktime seems to return -1 for the exact dst + * changeover time.) + */ + +#ifdef NS_HAVE_INVALID_PARAMETER_HANDLER + oldHandler = _set_invalid_parameter_handler(PRMJ_InvalidParameterHandler); + oldReportMode = _CrtSetReportMode(_CRT_ASSERT, 0); +#endif + + result = strftime(buf, buflen, fmt, &a); + +#ifdef NS_HAVE_INVALID_PARAMETER_HANDLER + _set_invalid_parameter_handler(oldHandler); + _CrtSetReportMode(_CRT_ASSERT, oldReportMode); +#endif + + if (fake_tm_year && result) { + char real_year[16]; + char fake_year[16]; + size_t real_year_len; + size_t fake_year_len; + char* p; + + sprintf(real_year, "%d", prtm->tm_year); + real_year_len = strlen(real_year); + sprintf(fake_year, "%d", fake_tm_year); + fake_year_len = strlen(fake_year); + + /* Replace the fake year in the result with the real year. */ + for (p = buf; (p = strstr(p, fake_year)); p += real_year_len) { + size_t new_result = result + real_year_len - fake_year_len; + if ((int)new_result >= buflen) { + return 0; + } + memmove(p + real_year_len, p + fake_year_len, strlen(p + fake_year_len)); + memcpy(p, real_year, real_year_len); + result = new_result; + *(buf + result) = '\0'; + } + } +#endif + return result; +} + +/* table for number of days in a month */ +static int mtab[] = { + /* jan, feb,mar,apr,may,jun */ + 31,28,31,30,31,30, + /* july,aug,sep,oct,nov,dec */ + 31,31,30,31,30,31 +}; + +/* + * basic time calculation functionality for localtime and gmtime + * setups up prtm argument with correct values based upon input number + * of seconds. + */ +static void +PRMJ_basetime(JSInt64 tsecs, PRMJTime *prtm) +{ + /* convert tsecs back to year,month,day,hour,secs */ + JSInt32 year = 0; + JSInt32 month = 0; + JSInt32 yday = 0; + JSInt32 mday = 0; + JSInt32 wday = 6; /* start on a Sunday */ + JSInt32 days = 0; + JSInt32 seconds = 0; + JSInt32 minutes = 0; + JSInt32 hours = 0; + JSInt32 isleap = 0; + + /* Temporaries used for various computations */ + JSInt64 result; + JSInt64 result1; + JSInt64 result2; + + JSInt64 base; + + /* Some variables for intermediate result storage to make computing isleap + easier/faster */ + JSInt32 fourCenturyBlocks; + JSInt32 centuriesLeft; + JSInt32 fourYearBlocksLeft; + JSInt32 yearsLeft; + + /* Since leap years work by 400/100/4 year intervals, precompute the length + of those in seconds if they start at the beginning of year 1. */ + JSInt64 fourYears; + JSInt64 century; + JSInt64 fourCenturies; + + JSLL_UI2L(result, PRMJ_DAY_SECONDS); + + JSLL_I2L(fourYears, PRMJ_FOUR_YEARS_DAYS); + JSLL_MUL(fourYears, fourYears, result); + + JSLL_I2L(century, PRMJ_CENTURY_DAYS); + JSLL_MUL(century, century, result); + + JSLL_I2L(fourCenturies, PRMJ_FOUR_CENTURIES_DAYS); + JSLL_MUL(fourCenturies, fourCenturies, result); + + /* get the base time via UTC */ + base = PRMJ_ToExtendedTime(0); + JSLL_UI2L(result, PRMJ_USEC_PER_SEC); + JSLL_DIV(base,base,result); + JSLL_ADD(tsecs,tsecs,base); + + /* Compute our |year|, |isleap|, and part of |days|. When this part is + done, |year| should hold the year our date falls in (number of whole + years elapsed before our date), isleap should hold 1 if the year the + date falls in is a leap year and 0 otherwise. */ + + /* First do year 0; it's special and nonleap. */ + JSLL_UI2L(result, PRMJ_YEAR_SECONDS); + if (!JSLL_CMP(tsecs,<,result)) { + days = PRMJ_YEAR_DAYS; + year = 1; + JSLL_SUB(tsecs, tsecs, result); + } + + /* Now use those constants we computed above */ + JSLL_UDIVMOD(&result1, &result2, tsecs, fourCenturies); + JSLL_L2I(fourCenturyBlocks, result1); + year += fourCenturyBlocks * 400; + days += fourCenturyBlocks * PRMJ_FOUR_CENTURIES_DAYS; + tsecs = result2; + + JSLL_UDIVMOD(&result1, &result2, tsecs, century); + JSLL_L2I(centuriesLeft, result1); + year += centuriesLeft * 100; + days += centuriesLeft * PRMJ_CENTURY_DAYS; + tsecs = result2; + + JSLL_UDIVMOD(&result1, &result2, tsecs, fourYears); + JSLL_L2I(fourYearBlocksLeft, result1); + year += fourYearBlocksLeft * 4; + days += fourYearBlocksLeft * PRMJ_FOUR_YEARS_DAYS; + tsecs = result2; + + /* Recall that |result| holds PRMJ_YEAR_SECONDS */ + JSLL_UDIVMOD(&result1, &result2, tsecs, result); + JSLL_L2I(yearsLeft, result1); + year += yearsLeft; + days += yearsLeft * PRMJ_YEAR_DAYS; + tsecs = result2; + + /* now compute isleap. Note that we don't have to use %, since we've + already computed those remainders. Also note that they're all offset by + 1 because of the 1 for year 0. */ + isleap = + (yearsLeft == 3) && (fourYearBlocksLeft != 24 || centuriesLeft == 3); + JS_ASSERT(isleap == + ((year % 4 == 0) && (year % 100 != 0 || year % 400 == 0))); + + JSLL_UI2L(result1,PRMJ_DAY_SECONDS); + + JSLL_DIV(result,tsecs,result1); + JSLL_L2I(mday,result); + + /* let's find the month */ + while(((month == 1 && isleap) ? + (mday >= mtab[month] + 1) : + (mday >= mtab[month]))){ + yday += mtab[month]; + days += mtab[month]; + + mday -= mtab[month]; + + /* it's a Feb, check if this is a leap year */ + if(month == 1 && isleap != 0){ + yday++; + days++; + mday--; + } + month++; + } + + /* now adjust tsecs */ + JSLL_MUL(result,result,result1); + JSLL_SUB(tsecs,tsecs,result); + + mday++; /* day of month always start with 1 */ + days += mday; + wday = (days + wday) % 7; + + yday += mday; + + /* get the hours */ + JSLL_UI2L(result1,PRMJ_HOUR_SECONDS); + JSLL_DIV(result,tsecs,result1); + JSLL_L2I(hours,result); + JSLL_MUL(result,result,result1); + JSLL_SUB(tsecs,tsecs,result); + + /* get minutes */ + JSLL_UI2L(result1,60); + JSLL_DIV(result,tsecs,result1); + JSLL_L2I(minutes,result); + JSLL_MUL(result,result,result1); + JSLL_SUB(tsecs,tsecs,result); + + JSLL_L2I(seconds,tsecs); + + prtm->tm_usec = 0L; + prtm->tm_sec = (JSInt8)seconds; + prtm->tm_min = (JSInt8)minutes; + prtm->tm_hour = (JSInt8)hours; + prtm->tm_mday = (JSInt8)mday; + prtm->tm_mon = (JSInt8)month; + prtm->tm_wday = (JSInt8)wday; + prtm->tm_year = (JSInt16)year; + prtm->tm_yday = (JSInt16)yday; +} diff --git a/ape-server/deps/js/src/prmjtime.h b/ape-server/deps/js/src/prmjtime.h new file mode 100755 index 0000000..2ff07eb --- /dev/null +++ b/ape-server/deps/js/src/prmjtime.h @@ -0,0 +1,103 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef prmjtime_h___ +#define prmjtime_h___ +/* + * PR date stuff for mocha and java. Placed here temporarily not to break + * Navigator and localize changes to mocha. + */ +#include +#include "jslong.h" +#ifdef MOZILLA_CLIENT +#include "jscompat.h" +#endif + +JS_BEGIN_EXTERN_C + +typedef struct PRMJTime PRMJTime; + +/* + * Broken down form of 64 bit time value. + */ +struct PRMJTime { + JSInt32 tm_usec; /* microseconds of second (0-999999) */ + JSInt8 tm_sec; /* seconds of minute (0-59) */ + JSInt8 tm_min; /* minutes of hour (0-59) */ + JSInt8 tm_hour; /* hour of day (0-23) */ + JSInt8 tm_mday; /* day of month (1-31) */ + JSInt8 tm_mon; /* month of year (0-11) */ + JSInt8 tm_wday; /* 0=sunday, 1=monday, ... */ + JSInt32 tm_year; /* absolute year, AD */ + JSInt16 tm_yday; /* day of year (0 to 365) */ + JSInt8 tm_isdst; /* non-zero if DST in effect */ +}; + +/* Some handy constants */ +#define PRMJ_USEC_PER_SEC 1000000L +#define PRMJ_USEC_PER_MSEC 1000L + +/* Return the current local time in micro-seconds */ +extern JSInt64 +PRMJ_Now(void); + +/* Release the resources associated with PRMJ_Now; don't call PRMJ_Now again */ +#if defined(JS_THREADSAFE) && (defined(HAVE_GETSYSTEMTIMEASFILETIME) || defined(HAVE_SYSTEMTIMETOFILETIME)) +extern void +PRMJ_NowShutdown(void); +#else +#define PRMJ_NowShutdown() +#endif + +/* get the difference between this time zone and gmt timezone in seconds */ +extern JSInt32 +PRMJ_LocalGMTDifference(void); + +/* Format a time value into a buffer. Same semantics as strftime() */ +extern size_t +PRMJ_FormatTime(char *buf, int buflen, const char *fmt, PRMJTime *tm); + +/* Get the DST offset for the local time passed in */ +extern JSInt64 +PRMJ_DSTOffset(JSInt64 local_time); + +JS_END_EXTERN_C + +#endif /* prmjtime_h___ */ + diff --git a/ape-server/deps/js/src/ref-config/AIX4.1.mk b/ape-server/deps/js/src/ref-config/AIX4.1.mk new file mode 100755 index 0000000..09c7cb9 --- /dev/null +++ b/ape-server/deps/js/src/ref-config/AIX4.1.mk @@ -0,0 +1,65 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config stuff for AIX +# + +CC = xlC_r +CCC = xlC_r + +RANLIB = ranlib + +#.c.o: +# $(CC) -c -MD $*.d $(CFLAGS) $< +ARCH := aix +CPU_ARCH = rs6000 +GFX_ARCH = x +INLINES = js_compare_and_swap:js_fast_lock1:js_fast_unlock1:js_lock_get_slot:js_lock_set_slot:js_lock_scope1 + +OS_CFLAGS = -qarch=com -qinline+$(INLINES) -DXP_UNIX -DAIX -DAIXV3 -DSYSV -DHAVE_LOCALTIME_R +OS_LIBS = -lbsd -lsvld -lm +#-lpthreads -lc_r + +MKSHLIB = $(LD) -bM:SRE -bh:4 -bnoentry -berok +XLDFLAGS += -lc + +ifdef JS_THREADSAFE +XLDFLAGS += -lsvld +endif diff --git a/ape-server/deps/js/src/ref-config/AIX4.2.mk b/ape-server/deps/js/src/ref-config/AIX4.2.mk new file mode 100755 index 0000000..1e3f1f1 --- /dev/null +++ b/ape-server/deps/js/src/ref-config/AIX4.2.mk @@ -0,0 +1,64 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config stuff for AIX +# + +CC = xlC_r +CCC = xlC_r +CFLAGS += -qarch=com -qnoansialias -qinline+$(INLINES) -DXP_UNIX -DAIX -DAIXV3 -DSYSV -DHAVE_LOCALTIME_R + +RANLIB = ranlib + +#.c.o: +# $(CC) -c -MD $*.d $(CFLAGS) $< +ARCH := aix +CPU_ARCH = rs6000 +GFX_ARCH = x +INLINES = js_compare_and_swap:js_fast_lock1:js_fast_unlock1:js_lock_get_slot:js_lock_set_slot:js_lock_scope1 + +#-lpthreads -lc_r + +MKSHLIB = /usr/lpp/xlC/bin/makeC++SharedLib_r -p 0 -G -berok + +ifdef JS_THREADSAFE +XLDFLAGS += -ldl +endif + diff --git a/ape-server/deps/js/src/ref-config/AIX4.3.mk b/ape-server/deps/js/src/ref-config/AIX4.3.mk new file mode 100755 index 0000000..df05d8c --- /dev/null +++ b/ape-server/deps/js/src/ref-config/AIX4.3.mk @@ -0,0 +1,65 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config stuff for AIX +# + +CC = xlC_r +CCC = xlC_r +CFLAGS += -qarch=com -qnoansialias -qinline+$(INLINES) -DXP_UNIX -DAIX -DAIXV3 -DSYSV -DAIX4_3 -DHAVE_LOCALTIME_R + +RANLIB = ranlib + +#.c.o: +# $(CC) -c -MD $*.d $(CFLAGS) $< +ARCH := aix +CPU_ARCH = rs6000 +GFX_ARCH = x +INLINES = js_compare_and_swap:js_fast_lock1:js_fast_unlock1:js_lock_get_slot:js_lock_set_slot:js_lock_scope1 + +#-lpthreads -lc_r + +MKSHLIB_BIN = /usr/ibmcxx/bin/makeC++SharedLib_r +MKSHLIB = $(MKSHLIB_BIN) -p 0 -G -berok -bM:UR + +ifdef JS_THREADSAFE +XLDFLAGS += -ldl +endif + diff --git a/ape-server/deps/js/src/ref-config/Darwin.mk b/ape-server/deps/js/src/ref-config/Darwin.mk new file mode 100755 index 0000000..86c4b9d --- /dev/null +++ b/ape-server/deps/js/src/ref-config/Darwin.mk @@ -0,0 +1,85 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Steve Zellers (zellers@apple.com) +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config for Mac OS X as of PR3 +# Just ripped from Linux config +# + +CC = gcc +CCC = g++ +CFLAGS += -Wall -Wno-format -MMD +OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -D_BSD_SOURCE -DPOSIX_SOURCE -DDARWIN + +RANLIB = ranlib +MKSHLIB = $(CCC) -dynamiclib $(XMKSHLIBOPTS) -framework System + +SO_SUFFIX = dylib + +#.c.o: +# $(CC) -c -MD $*.d $(CFLAGS) $< + +CPU_ARCH = $(shell uname -m) +ifeq (86,$(findstring 86,$(CPU_ARCH))) +CPU_ARCH = x86 +OS_CFLAGS+= -DX86_LINUX +OS_CFLAGS += -DAVMPLUS_IA32 -DAVMPLUS_UNIX +NANOJIT_ARCH = i386 +endif +GFX_ARCH = x + +OS_LIBS = -lc -framework System + +ASFLAGS += -x assembler-with-cpp + +ifeq ($(CPU_ARCH),alpha) + +# Ask the C compiler on alpha linux to let us work with denormalized +# double values, which are required by the ECMA spec. + +OS_CFLAGS += -mieee +endif + +# Use the editline library to provide line-editing support. +JS_EDITLINE = 1 + +# Don't allow Makefile.ref to use libmath +NO_LIBM = 1 + diff --git a/ape-server/deps/js/src/ref-config/Darwin1.3.mk b/ape-server/deps/js/src/ref-config/Darwin1.3.mk new file mode 100755 index 0000000..05d3767 --- /dev/null +++ b/ape-server/deps/js/src/ref-config/Darwin1.3.mk @@ -0,0 +1,81 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Steve Zellers (zellers@apple.com) +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config for Mac OS X as of PR3 +# Just ripped from Linux config +# + +CC = cc +CCC = g++ +CFLAGS += -Wall -Wno-format +OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -D_BSD_SOURCE -DPOSIX_SOURCE -DRHAPSODY + +RANLIB = ranlib +MKSHLIB = libtool $(XMKSHLIBOPTS) -framework System + +#.c.o: +# $(CC) -c -MD $*.d $(CFLAGS) $< + +CPU_ARCH = $(shell uname -m) +ifeq (86,$(findstring 86,$(CPU_ARCH))) +CPU_ARCH = x86 +OS_CFLAGS+= -DX86_LINUX +endif +GFX_ARCH = x + +OS_LIBS = -lc -framework System + +ASFLAGS += -x assembler-with-cpp + +ifeq ($(CPU_ARCH),alpha) + +# Ask the C compiler on alpha linux to let us work with denormalized +# double values, which are required by the ECMA spec. + +OS_CFLAGS += -mieee +endif + +# Use the editline library to provide line-editing support. +JS_EDITLINE = 1 + +# Don't allow Makefile.ref to use libmath +NO_LIBM = 1 + diff --git a/ape-server/deps/js/src/ref-config/Darwin1.4.mk b/ape-server/deps/js/src/ref-config/Darwin1.4.mk new file mode 100755 index 0000000..f7b6af8 --- /dev/null +++ b/ape-server/deps/js/src/ref-config/Darwin1.4.mk @@ -0,0 +1,41 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mike McCabe +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +include $(DEPTH)/config/Darwin1.3.mk diff --git a/ape-server/deps/js/src/ref-config/Darwin5.2.mk b/ape-server/deps/js/src/ref-config/Darwin5.2.mk new file mode 100755 index 0000000..9b9b6ff --- /dev/null +++ b/ape-server/deps/js/src/ref-config/Darwin5.2.mk @@ -0,0 +1,81 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Steve Zellers (zellers@apple.com) +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config for Mac OS X as of PR3 +# Just ripped from Linux config +# + +CC = cc +CCC = g++ +CFLAGS += -Wall -Wno-format +OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -D_BSD_SOURCE -DPOSIX_SOURCE -DDARWIN + +RANLIB = ranlib +MKSHLIB = libtool $(XMKSHLIBOPTS) -framework System + +#.c.o: +# $(CC) -c -MD $*.d $(CFLAGS) $< + +CPU_ARCH = $(shell uname -m) +ifeq (86,$(findstring 86,$(CPU_ARCH))) +CPU_ARCH = x86 +OS_CFLAGS+= -DX86_LINUX +endif +GFX_ARCH = x + +OS_LIBS = -lc -framework System + +ASFLAGS += -x assembler-with-cpp + +ifeq ($(CPU_ARCH),alpha) + +# Ask the C compiler on alpha linux to let us work with denormalized +# double values, which are required by the ECMA spec. + +OS_CFLAGS += -mieee +endif + +# Use the editline library to provide line-editing support. +JS_EDITLINE = 1 + +# Don't allow Makefile.ref to use libmath +NO_LIBM = 1 + diff --git a/ape-server/deps/js/src/ref-config/Darwin5.3.mk b/ape-server/deps/js/src/ref-config/Darwin5.3.mk new file mode 100755 index 0000000..9b9b6ff --- /dev/null +++ b/ape-server/deps/js/src/ref-config/Darwin5.3.mk @@ -0,0 +1,81 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Steve Zellers (zellers@apple.com) +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config for Mac OS X as of PR3 +# Just ripped from Linux config +# + +CC = cc +CCC = g++ +CFLAGS += -Wall -Wno-format +OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -D_BSD_SOURCE -DPOSIX_SOURCE -DDARWIN + +RANLIB = ranlib +MKSHLIB = libtool $(XMKSHLIBOPTS) -framework System + +#.c.o: +# $(CC) -c -MD $*.d $(CFLAGS) $< + +CPU_ARCH = $(shell uname -m) +ifeq (86,$(findstring 86,$(CPU_ARCH))) +CPU_ARCH = x86 +OS_CFLAGS+= -DX86_LINUX +endif +GFX_ARCH = x + +OS_LIBS = -lc -framework System + +ASFLAGS += -x assembler-with-cpp + +ifeq ($(CPU_ARCH),alpha) + +# Ask the C compiler on alpha linux to let us work with denormalized +# double values, which are required by the ECMA spec. + +OS_CFLAGS += -mieee +endif + +# Use the editline library to provide line-editing support. +JS_EDITLINE = 1 + +# Don't allow Makefile.ref to use libmath +NO_LIBM = 1 + diff --git a/ape-server/deps/js/src/ref-config/Darwin64.mk b/ape-server/deps/js/src/ref-config/Darwin64.mk new file mode 100755 index 0000000..db195b5 --- /dev/null +++ b/ape-server/deps/js/src/ref-config/Darwin64.mk @@ -0,0 +1,72 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Steve Zellers (zellers@apple.com) +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config for Mac OS X as of PR3 +# Just ripped from Linux config +# + +CC = cc +CCC = g++ +CFLAGS += -Wall -Wno-format -MMD +OS_LDFLAGS += -m64 +OS_CFLAGS = -m64 -DXP_UNIX -DSVR4 -DSYSV -D_BSD_SOURCE -DPOSIX_SOURCE -DDARWIN + +RANLIB = ranlib +MKSHLIB = $(CCC) -dynamiclib $(XMKSHLIBOPTS) -framework System + +SO_SUFFIX = dylib + +#.c.o: +# $(CC) -c -MD $*.d $(CFLAGS) $< + +CPU_ARCH = x86_64 +GFX_ARCH = x + +OS_LIBS = -lc -framework System + +ASFLAGS += -x assembler-with-cpp + +# Use the editline library to provide line-editing support. +JS_EDITLINE = 1 + +# Don't allow Makefile.ref to use libmath +NO_LIBM = 1 + diff --git a/ape-server/deps/js/src/ref-config/HP-UXB.10.10.mk b/ape-server/deps/js/src/ref-config/HP-UXB.10.10.mk new file mode 100755 index 0000000..8cd9d20 --- /dev/null +++ b/ape-server/deps/js/src/ref-config/HP-UXB.10.10.mk @@ -0,0 +1,77 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config stuff for HPUX +# + +# CC = gcc +# CCC = g++ +# CFLAGS += -Wall -Wno-format -fPIC + +CC = cc -Ae +Z +CCC = CC -Ae +a1 +eh +Z + +RANLIB = echo +MKSHLIB = $(LD) -b + +SO_SUFFIX = sl + +#.c.o: +# $(CC) -c -MD $*.d $(CFLAGS) $< + +CPU_ARCH = hppa +GFX_ARCH = x + +OS_CFLAGS = -DXP_UNIX -DHPUX -DSYSV -DHAVE_LOCALTIME_R +OS_LIBS = -ldld + +ifeq ($(OS_RELEASE),B.10) +PLATFORM_FLAGS += -DHPUX10 -Dhpux10 +PORT_FLAGS += -DRW_NO_OVERLOAD_SCHAR -DHAVE_MODEL_H +ifeq ($(OS_VERSION),.10) +PLATFORM_FLAGS += -DHPUX10_10 +endif +ifeq ($(OS_VERSION),.20) +PLATFORM_FLAGS += -DHPUX10_20 +endif +ifeq ($(OS_VERSION),.30) +PLATFORM_FLAGS += -DHPUX10_30 +endif +endif diff --git a/ape-server/deps/js/src/ref-config/HP-UXB.10.20.mk b/ape-server/deps/js/src/ref-config/HP-UXB.10.20.mk new file mode 100755 index 0000000..8cd9d20 --- /dev/null +++ b/ape-server/deps/js/src/ref-config/HP-UXB.10.20.mk @@ -0,0 +1,77 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config stuff for HPUX +# + +# CC = gcc +# CCC = g++ +# CFLAGS += -Wall -Wno-format -fPIC + +CC = cc -Ae +Z +CCC = CC -Ae +a1 +eh +Z + +RANLIB = echo +MKSHLIB = $(LD) -b + +SO_SUFFIX = sl + +#.c.o: +# $(CC) -c -MD $*.d $(CFLAGS) $< + +CPU_ARCH = hppa +GFX_ARCH = x + +OS_CFLAGS = -DXP_UNIX -DHPUX -DSYSV -DHAVE_LOCALTIME_R +OS_LIBS = -ldld + +ifeq ($(OS_RELEASE),B.10) +PLATFORM_FLAGS += -DHPUX10 -Dhpux10 +PORT_FLAGS += -DRW_NO_OVERLOAD_SCHAR -DHAVE_MODEL_H +ifeq ($(OS_VERSION),.10) +PLATFORM_FLAGS += -DHPUX10_10 +endif +ifeq ($(OS_VERSION),.20) +PLATFORM_FLAGS += -DHPUX10_20 +endif +ifeq ($(OS_VERSION),.30) +PLATFORM_FLAGS += -DHPUX10_30 +endif +endif diff --git a/ape-server/deps/js/src/ref-config/HP-UXB.11.00.mk b/ape-server/deps/js/src/ref-config/HP-UXB.11.00.mk new file mode 100755 index 0000000..239188d --- /dev/null +++ b/ape-server/deps/js/src/ref-config/HP-UXB.11.00.mk @@ -0,0 +1,80 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config stuff for HPUX +# + +ifdef NS_USE_NATIVE + CC = cc +Z +DAportable +DS2.0 +u4 +# LD = aCC +Z -b -Wl,+s -Wl,-B,symbolic +else + CC = gcc -Wall -Wno-format -fPIC + CCC = g++ -Wall -Wno-format -fPIC +endif + +RANLIB = echo +MKSHLIB = $(LD) -b + +SO_SUFFIX = sl + +#.c.o: +# $(CC) -c -MD $*.d $(CFLAGS) $< + +CPU_ARCH = hppa +GFX_ARCH = x + +OS_CFLAGS = -DXP_UNIX -DHPUX -DSYSV -D_HPUX -DNATIVE -D_POSIX_C_SOURCE=199506L -DHAVE_LOCALTIME_R +OS_LIBS = -ldld + +XLDFLAGS = -lpthread + +ifeq ($(OS_RELEASE),B.10) +PLATFORM_FLAGS += -DHPUX10 -Dhpux10 +PORT_FLAGS += -DRW_NO_OVERLOAD_SCHAR -DHAVE_MODEL_H +ifeq ($(OS_VERSION),.10) +PLATFORM_FLAGS += -DHPUX10_10 +endif +ifeq ($(OS_VERSION),.20) +PLATFORM_FLAGS += -DHPUX10_20 +endif +ifeq ($(OS_VERSION),.30) +PLATFORM_FLAGS += -DHPUX10_30 +endif +endif diff --git a/ape-server/deps/js/src/ref-config/HP-UXB.11.31.mk b/ape-server/deps/js/src/ref-config/HP-UXB.11.31.mk new file mode 100755 index 0000000..8c1cc40 --- /dev/null +++ b/ape-server/deps/js/src/ref-config/HP-UXB.11.31.mk @@ -0,0 +1,65 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config stuff for HPUX +# +ifdef NS_USE_NATIVE + CC = cc +Z +u4 +else + CC = gcc -Wall -Wno-format -fPIC + CCC = g++ -Wall -Wno-format -fPIC +endif + +RANLIB = echo +MKSHLIB = $(LD) -b + +SO_SUFFIX = sl + +#.c.o: +# $(CC) -c -MD $*.d $(CFLAGS) $< + +CPU_ARCH = ia64 +GFX_ARCH = x + +OS_CFLAGS = -DXP_UNIX -DHPUX -DSYSV -D_HPUX \ + -DNATIVE -D_POSIX_C_SOURCE=199506L -DHAVE_LOCALTIME_R +OS_LIBS = -ldld + +XLDFLAGS = -lpthread diff --git a/ape-server/deps/js/src/ref-config/IRIX.mk b/ape-server/deps/js/src/ref-config/IRIX.mk new file mode 100755 index 0000000..88b162f --- /dev/null +++ b/ape-server/deps/js/src/ref-config/IRIX.mk @@ -0,0 +1,87 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config stuff for IRIX +# + +CPU_ARCH = mips +GFX_ARCH = x + +RANLIB = /bin/true + +#NS_USE_GCC = 1 + +ifndef NS_USE_NATIVE +CC = gcc +CCC = g++ +AS = $(CC) -x assembler-with-cpp +ODD_CFLAGS = -Wall -Wno-format +ifdef BUILD_OPT +OPTIMIZER = -O6 +endif +else +ifeq ($(OS_RELEASE),6.2) +CC = cc -n32 -DIRIX6_2 +endif +ifeq ($(OS_RELEASE),6.3) +CC = cc -n32 -DIRIX6_3 +endif +ifeq ($(OS_RELEASE),6.5) +CC = cc -n32 -DIRIX6_5 +endif +CCC = CC +# LD = CC +ODD_CFLAGS = -fullwarn -xansi +ifdef BUILD_OPT +OPTIMIZER += -Olimit 4000 +endif +endif + +# For purify +HAVE_PURIFY = 1 +PURE_OS_CFLAGS = $(ODD_CFLAGS) -DXP_UNIX -DSVR4 -DSW_THREADS -DIRIX -DHAVE_LOCALTIME_R + +OS_CFLAGS = $(PURE_OS_CFLAGS) -MDupdate $(DEPENDENCIES) + +BSDECHO = echo +MKSHLIB = $(LD) -n32 -shared + +# Use the editline library to provide line-editing support. +JS_EDITLINE = 1 diff --git a/ape-server/deps/js/src/ref-config/IRIX5.3.mk b/ape-server/deps/js/src/ref-config/IRIX5.3.mk new file mode 100755 index 0000000..f38cc94 --- /dev/null +++ b/ape-server/deps/js/src/ref-config/IRIX5.3.mk @@ -0,0 +1,44 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config stuff for IRIX5.3 +# + +include $(DEPTH)/config/IRIX.mk diff --git a/ape-server/deps/js/src/ref-config/IRIX6.1.mk b/ape-server/deps/js/src/ref-config/IRIX6.1.mk new file mode 100755 index 0000000..354f1d1 --- /dev/null +++ b/ape-server/deps/js/src/ref-config/IRIX6.1.mk @@ -0,0 +1,44 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config stuff for IRIX6.3 +# + +include $(DEPTH)/config/IRIX.mk diff --git a/ape-server/deps/js/src/ref-config/IRIX6.2.mk b/ape-server/deps/js/src/ref-config/IRIX6.2.mk new file mode 100755 index 0000000..354f1d1 --- /dev/null +++ b/ape-server/deps/js/src/ref-config/IRIX6.2.mk @@ -0,0 +1,44 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config stuff for IRIX6.3 +# + +include $(DEPTH)/config/IRIX.mk diff --git a/ape-server/deps/js/src/ref-config/IRIX6.3.mk b/ape-server/deps/js/src/ref-config/IRIX6.3.mk new file mode 100755 index 0000000..354f1d1 --- /dev/null +++ b/ape-server/deps/js/src/ref-config/IRIX6.3.mk @@ -0,0 +1,44 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config stuff for IRIX6.3 +# + +include $(DEPTH)/config/IRIX.mk diff --git a/ape-server/deps/js/src/ref-config/IRIX6.5.mk b/ape-server/deps/js/src/ref-config/IRIX6.5.mk new file mode 100755 index 0000000..354f1d1 --- /dev/null +++ b/ape-server/deps/js/src/ref-config/IRIX6.5.mk @@ -0,0 +1,44 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config stuff for IRIX6.3 +# + +include $(DEPTH)/config/IRIX.mk diff --git a/ape-server/deps/js/src/ref-config/Linux_All.mk b/ape-server/deps/js/src/ref-config/Linux_All.mk new file mode 100755 index 0000000..6c289b4 --- /dev/null +++ b/ape-server/deps/js/src/ref-config/Linux_All.mk @@ -0,0 +1,105 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config for all versions of Linux +# + +CC = gcc +CCC = g++ +LD = g++ +CFLAGS += -Wall -Wno-format -MMD +OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -D_BSD_SOURCE -DPOSIX_SOURCE -DHAVE_LOCALTIME_R -DLINUX + +RANLIB = echo +MKSHLIB = $(LD) -shared $(XMKSHLIBOPTS) + +#.c.o: +# $(CC) -c -MD $*.d $(CFLAGS) $< + +CPU_ARCH = $(shell uname -m) +# don't filter in x86-64 architecture +ifneq (x86_64,$(CPU_ARCH)) +ifeq (86,$(findstring 86,$(CPU_ARCH))) +CPU_ARCH = x86 +OS_CFLAGS += -DX86_LINUX -DAVMPLUS_IA32 -DAVMPLUS_UNIX -DAVMPLUS_LINUX +NANOJIT_ARCH = i386 +endif # 86 +endif # !x86_64 + +#JIT disabled until x64 port is cleaned up +#ifeq ($(CPU_ARCH),x86_64) +#OS_CFLAGS += -DAVMPLUS_AMD64 -DAVMPLUS_64BIT -DAVMPLUS_UNIX -DAVMPLUS_LINUX +#NANOJIT_ARCH = i386 +#endif + +ifeq ($(CPU_ARCH),arm) +OS_CFLAGS += -DAVMPLUS_ARM -DAVMPLUS_UNIX -DAVMPLUS_LINUX +NANOJIT_ARCH = ARM +endif + +GFX_ARCH = x + +OS_LIBS = -lm -lc + +ASFLAGS += -x assembler-with-cpp + + +ifeq ($(CPU_ARCH),alpha) + +# Ask the C compiler on alpha linux to let us work with denormalized +# double values, which are required by the ECMA spec. + +OS_CFLAGS += -mieee +endif + +# Use the editline library to provide line-editing support. +JS_EDITLINE = 1 + +ifeq ($(CPU_ARCH),x86_64) +# Use VA_COPY() standard macro on x86-64 +# FIXME: better use it everywhere +OS_CFLAGS += -DHAVE_VA_COPY -DVA_COPY=va_copy +endif + +ifeq ($(CPU_ARCH),x86_64) +# We need PIC code for shared libraries +# FIXME: better patch rules.mk & fdlibm/Makefile* +OS_CFLAGS += -DPIC -fPIC +endif diff --git a/ape-server/deps/js/src/ref-config/Mac_OS10.0.mk b/ape-server/deps/js/src/ref-config/Mac_OS10.0.mk new file mode 100755 index 0000000..74ba151 --- /dev/null +++ b/ape-server/deps/js/src/ref-config/Mac_OS10.0.mk @@ -0,0 +1,82 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Steve Zellers (zellers@apple.com) +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config for Mac OS X as of PR3 +# Just ripped from Linux config +# + +CC = cc +CCC = g++ +CFLAGS += -Wall -Wno-format +OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -D_BSD_SOURCE -DPOSIX_SOURCE +-DRHAPSODY + +RANLIB = ranlib +MKSHLIB = libtool -dynamic $(XMKSHLIBOPTS) -framework System + +#.c.o: +# $(CC) -c -MD $*.d $(CFLAGS) $< + +CPU_ARCH = $(shell uname -m) +ifeq (86,$(findstring 86,$(CPU_ARCH))) +CPU_ARCH = x86 +OS_CFLAGS+= -DX86_LINUX +endif +GFX_ARCH = x + +OS_LIBS = -lc -framework System + +ASFLAGS += -x assembler-with-cpp + +ifeq ($(CPU_ARCH),alpha) + +# Ask the C compiler on alpha linux to let us work with denormalized +# double values, which are required by the ECMA spec. + +OS_CFLAGS += -mieee +endif + +# Use the editline library to provide line-editing support. +JS_EDITLINE = 1 + +# Don't allow Makefile.ref to use libmath +NO_LIBM = 1 + diff --git a/ape-server/deps/js/src/ref-config/OSF1V4.0.mk b/ape-server/deps/js/src/ref-config/OSF1V4.0.mk new file mode 100755 index 0000000..337ca74 --- /dev/null +++ b/ape-server/deps/js/src/ref-config/OSF1V4.0.mk @@ -0,0 +1,72 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config stuff for Data General DG/UX +# + +# +# Initial DG/UX port by Marc Fraioli (fraioli@dg-rtp.dg.com) +# + +ifndef NS_USE_NATIVE +CC = gcc +CCC = g++ +CFLAGS += -mieee -Wall -Wno-format +else +CC = cc +CCC = cxx +CFLAGS += -ieee -std +# LD = cxx +endif + +RANLIB = echo +MKSHLIB = $(LD) -shared -taso -all -expect_unresolved "*" + +# +# _DGUX_SOURCE is needed to turn on a lot of stuff in the headers if +# you're not using DG's compiler. It shouldn't hurt if you are. +# +# _POSIX4A_DRAFT10_SOURCE is needed to pick up localtime_r, used in +# prtime.c +# +OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -DDGUX -D_DGUX_SOURCE -D_POSIX4A_DRAFT10_SOURCE -DOSF1 -DHAVE_LOCALTIME_R +OS_LIBS = -lsocket -lnsl + +NOSUCHFILE = /no-such-file diff --git a/ape-server/deps/js/src/ref-config/OSF1V5.0.mk b/ape-server/deps/js/src/ref-config/OSF1V5.0.mk new file mode 100755 index 0000000..b65738c --- /dev/null +++ b/ape-server/deps/js/src/ref-config/OSF1V5.0.mk @@ -0,0 +1,69 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config stuff for Tru64 Unix 5.0 +# + +# +# Initial DG/UX port by Marc Fraioli (fraioli@dg-rtp.dg.com) +# + +ifndef NS_USE_NATIVE +CC = gcc +CCC = g++ +CFLAGS += -mieee -Wall -Wno-format +else +CC = cc +CCC = cxx +CFLAGS += -ieee -std -pthread +# LD = cxx +endif + +RANLIB = echo +MKSHLIB = $(LD) -shared -all -expect_unresolved "*" + +# +# _POSIX4A_DRAFT10_SOURCE is needed to pick up localtime_r, used in +# prtime.c +# +OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -D_POSIX4A_DRAFT10_SOURCE -DOSF1 -DHAVE_LOCALTIME_R +OS_LIBS = -lsocket -lnsl + +NOSUCHFILE = /no-such-file diff --git a/ape-server/deps/js/src/ref-config/SunOS4.1.4.mk b/ape-server/deps/js/src/ref-config/SunOS4.1.4.mk new file mode 100755 index 0000000..62f4815 --- /dev/null +++ b/ape-server/deps/js/src/ref-config/SunOS4.1.4.mk @@ -0,0 +1,101 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config stuff for SunOS4.1 +# + +CC = gcc +CCC = g++ +RANLIB = ranlib + +#.c.o: +# $(CC) -c -MD $*.d $(CFLAGS) $< + +CPU_ARCH = sparc +GFX_ARCH = x + +# A pile of -D's to build xfe on sunos +MOZ_CFLAGS = -DSTRINGS_ALIGNED -DNO_REGEX -DNO_ISDIR -DUSE_RE_COMP \ + -DNO_REGCOMP -DUSE_GETWD -DNO_MEMMOVE -DNO_ALLOCA \ + -DBOGUS_MB_MAX -DNO_CONST + +# Purify doesn't like -MDupdate +NOMD_OS_CFLAGS = -DXP_UNIX -Wall -Wno-format -DSW_THREADS -DSUNOS4 -DNEED_SYSCALL \ + $(MOZ_CFLAGS) + +OS_CFLAGS = $(NOMD_OS_CFLAGS) -MDupdate $(DEPENDENCIES) +OS_LIBS = -ldl -lm + +MKSHLIB = $(LD) -L$(MOTIF)/lib + +HAVE_PURIFY = 1 +MOTIF = /home/motif/usr +MOTIFLIB = -L$(MOTIF)/lib -lXm +INCLUDES += -I/usr/X11R5/include -I$(MOTIF)/include + +NOSUCHFILE = /solaris-rm-f-sucks + +LOCALE_MAP = $(DEPTH)/cmd/xfe/intl/sunos.lm + +EN_LOCALE = en_US +DE_LOCALE = de +FR_LOCALE = fr +JP_LOCALE = ja +SJIS_LOCALE = ja_JP.SJIS +KR_LOCALE = ko +CN_LOCALE = zh +TW_LOCALE = zh_TW +I2_LOCALE = i2 +IT_LOCALE = it +SV_LOCALE = sv +ES_LOCALE = es +NL_LOCALE = nl +PT_LOCALE = pt + +LOC_LIB_DIR = /usr/openwin/lib/locale + +BSDECHO = echo + +# +# These defines are for building unix plugins +# +BUILD_UNIX_PLUGINS = 1 +DSO_LDOPTS = +DSO_LDFLAGS = diff --git a/ape-server/deps/js/src/ref-config/SunOS5.10.mk b/ape-server/deps/js/src/ref-config/SunOS5.10.mk new file mode 100755 index 0000000..dc0b0a0 --- /dev/null +++ b/ape-server/deps/js/src/ref-config/SunOS5.10.mk @@ -0,0 +1,50 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1999 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config stuff for SunOS5.10, using vendor gcc and NSPR +# + +include $(DEPTH)/config/SunOS5.5.mk + +INCLUDES += -I/usr/sfw/include/mozilla/nspr +OTHER_LIBS += -L/usr/sfw/lib/mozilla -R/usr/sfw/lib/mozilla + +CC=gcc + diff --git a/ape-server/deps/js/src/ref-config/SunOS5.3.mk b/ape-server/deps/js/src/ref-config/SunOS5.3.mk new file mode 100755 index 0000000..bd615de --- /dev/null +++ b/ape-server/deps/js/src/ref-config/SunOS5.3.mk @@ -0,0 +1,91 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config stuff for SunOS5.3 +# + +CC = gcc +CCC = g++ +CFLAGS += -Wall -Wno-format + +#CC = /opt/SUNWspro/SC3.0.1/bin/cc +RANLIB = echo + +#.c.o: +# $(CC) -c -MD $*.d $(CFLAGS) $< + +CPU_ARCH = sparc +GFX_ARCH = x + +OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -DSOLARIS -DHAVE_LOCALTIME_R +OS_LIBS = -lsocket -lnsl -ldl + +ASFLAGS += -P -L -K PIC -D_ASM -D__STDC__=0 + +HAVE_PURIFY = 1 + +NOSUCHFILE = /solaris-rm-f-sucks + +ifndef JS_NO_ULTRA +ULTRA_OPTIONS := -xarch=v8plus +ULTRA_OPTIONSD := -DULTRA_SPARC +else +ULTRA_OPTIONS := -xarch=v8 +ULTRA_OPTIONSD := +endif + +ifeq ($(OS_CPUARCH),sun4u) +DEFINES += $(ULTRA_OPTIONSD) +ifeq ($(findstring gcc,$(CC)),gcc) +DEFINES += -Wa,$(ULTRA_OPTIONS),$(ULTRA_OPTIONSD) +else +ASFLAGS += $(ULTRA_OPTIONS) $(ULTRA_OPTIONSD) +endif +endif + +ifeq ($(OS_CPUARCH),sun4m) +ifeq ($(findstring gcc,$(CC)),gcc) +DEFINES += -Wa,-xarch=v8 +else +ASFLAGS += -xarch=v8 +endif +endif + +MKSHLIB = $(LD) -G diff --git a/ape-server/deps/js/src/ref-config/SunOS5.4.mk b/ape-server/deps/js/src/ref-config/SunOS5.4.mk new file mode 100755 index 0000000..de01924 --- /dev/null +++ b/ape-server/deps/js/src/ref-config/SunOS5.4.mk @@ -0,0 +1,92 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config stuff for SunOS5.4 +# + +ifdef NS_USE_NATIVE +CC = cc +CCC = CC +else +CC = gcc +CCC = g++ +CFLAGS += -Wall -Wno-format +endif + +RANLIB = echo + +CPU_ARCH = sparc +GFX_ARCH = x + +OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -D__svr4 -DSOLARIS -DHAVE_LOCALTIME_R +OS_LIBS = -lsocket -lnsl -ldl + +ASFLAGS += -P -L -K PIC -D_ASM -D__STDC__=0 + +HAVE_PURIFY = 1 + +NOSUCHFILE = /solaris-rm-f-sucks + +ifndef JS_NO_ULTRA +ULTRA_OPTIONS := -xarch=v8plus +ULTRA_OPTIONSD := -DULTRA_SPARC +else +ULTRA_OPTIONS := -xarch=v8 +ULTRA_OPTIONSD := +endif + +ifeq ($(OS_CPUARCH),sun4u) +DEFINES += $(ULTRA_OPTIONSD) +ifeq ($(findstring gcc,$(CC)),gcc) +DEFINES += -Wa,$(ULTRA_OPTIONS),$(ULTRA_OPTIONSD) +else +ASFLAGS += $(ULTRA_OPTIONS) $(ULTRA_OPTIONSD) +endif +endif + +ifeq ($(OS_CPUARCH),sun4m) +ifeq ($(findstring gcc,$(CC)),gcc) +DEFINES += -Wa,-xarch=v8 +else +ASFLAGS += -xarch=v8 +endif +endif + +MKSHLIB = $(LD) -G diff --git a/ape-server/deps/js/src/ref-config/SunOS5.5.1.mk b/ape-server/deps/js/src/ref-config/SunOS5.5.1.mk new file mode 100755 index 0000000..648f72f --- /dev/null +++ b/ape-server/deps/js/src/ref-config/SunOS5.5.1.mk @@ -0,0 +1,44 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config stuff for SunOS5.5.1 +# + +include $(DEPTH)/config/SunOS5.5.mk diff --git a/ape-server/deps/js/src/ref-config/SunOS5.5.mk b/ape-server/deps/js/src/ref-config/SunOS5.5.mk new file mode 100755 index 0000000..e26b3a3 --- /dev/null +++ b/ape-server/deps/js/src/ref-config/SunOS5.5.mk @@ -0,0 +1,87 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config stuff for SunOS5.5 +# + +AS = /usr/ccs/bin/as +ifndef NS_USE_NATIVE +CC = gcc +CCC = g++ +CFLAGS += -Wall -Wno-format +else +CC = cc +CCC = CC +endif + +RANLIB = echo + +#.c.o: +# $(CC) -c -MD $*.d $(CFLAGS) $< + +CPU_ARCH = sparc +GFX_ARCH = x + +OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -DSOLARIS -DHAVE_LOCALTIME_R +OS_LIBS = -lsocket -lnsl -ldl + +ASFLAGS += -P -L -K PIC -D_ASM -D__STDC__=0 + +HAVE_PURIFY = 1 + +NOSUCHFILE = /solaris-rm-f-sucks + +ifeq ($(OS_CPUARCH),sun4u) # ultra sparc? +ifeq ($(CC),gcc) # using gcc? +ifndef JS_NO_ULTRA # do we want ultra? +ifdef JS_THREADSAFE # only in thread-safe mode +DEFINES += -DULTRA_SPARC +DEFINES += -Wa,-xarch=v8plus,-DULTRA_SPARC +else +ASFLAGS += -xarch=v8plus -DULTRA_SPARC +endif +endif +endif +endif + +MKSHLIB = $(LD) -G + +# Use the editline library to provide line-editing support. +JS_EDITLINE = 1 diff --git a/ape-server/deps/js/src/ref-config/SunOS5.6.mk b/ape-server/deps/js/src/ref-config/SunOS5.6.mk new file mode 100755 index 0000000..efe1152 --- /dev/null +++ b/ape-server/deps/js/src/ref-config/SunOS5.6.mk @@ -0,0 +1,89 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config stuff for SunOS5.5 +# + +AS = /usr/ccs/bin/as +ifndef NS_USE_NATIVE + CC = gcc + CCC = g++ + CFLAGS += -Wall -Wno-format +else + CC = cc + CCC = CC + CFLAGS += -mt -KPIC +# LD = CC +endif + +RANLIB = echo + +#.c.o: +# $(CC) -c -MD $*.d $(CFLAGS) $< + +CPU_ARCH = sparc +GFX_ARCH = x + +OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -DSOLARIS -DHAVE_LOCALTIME_R +OS_LIBS = -lsocket -lnsl -ldl + +ASFLAGS += -P -L -K PIC -D_ASM -D__STDC__=0 + +HAVE_PURIFY = 1 + +NOSUCHFILE = /solaris-rm-f-sucks + +ifeq ($(OS_CPUARCH),sun4u) # ultra sparc? +ifeq ($(CC),gcc) # using gcc? +ifndef JS_NO_ULTRA # do we want ultra? +ifdef JS_THREADSAFE # only in thread-safe mode +DEFINES += -DULTRA_SPARC +DEFINES += -Wa,-xarch=v8plus,-DULTRA_SPARC +else +ASFLAGS += -xarch=v8plus -DULTRA_SPARC +endif +endif +endif +endif + +MKSHLIB = $(LD) -G + +# Use the editline library to provide line-editing support. +JS_EDITLINE = 1 diff --git a/ape-server/deps/js/src/ref-config/SunOS5.7.mk b/ape-server/deps/js/src/ref-config/SunOS5.7.mk new file mode 100755 index 0000000..2cb02f2 --- /dev/null +++ b/ape-server/deps/js/src/ref-config/SunOS5.7.mk @@ -0,0 +1,44 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1999 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config stuff for SunOS5.7 +# + +include $(DEPTH)/config/SunOS5.5.mk diff --git a/ape-server/deps/js/src/ref-config/SunOS5.8.mk b/ape-server/deps/js/src/ref-config/SunOS5.8.mk new file mode 100755 index 0000000..dd8a32d --- /dev/null +++ b/ape-server/deps/js/src/ref-config/SunOS5.8.mk @@ -0,0 +1,44 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1999 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config stuff for SunOS5.8 +# + +include $(DEPTH)/config/SunOS5.5.mk diff --git a/ape-server/deps/js/src/ref-config/SunOS5.9.mk b/ape-server/deps/js/src/ref-config/SunOS5.9.mk new file mode 100755 index 0000000..b01ec9c --- /dev/null +++ b/ape-server/deps/js/src/ref-config/SunOS5.9.mk @@ -0,0 +1,44 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1999 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config stuff for SunOS5.9 +# + +include $(DEPTH)/config/SunOS5.5.mk diff --git a/ape-server/deps/js/src/ref-config/WINNT4.0.mk b/ape-server/deps/js/src/ref-config/WINNT4.0.mk new file mode 100755 index 0000000..1d36f91 --- /dev/null +++ b/ape-server/deps/js/src/ref-config/WINNT4.0.mk @@ -0,0 +1,118 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config for Windows NT using MS Visual C++ (version?) +# + +CC = cl +CXX = cl + +RANLIB = echo + +PDBFILE = $(basename $(@F)).pdb + +#.c.o: +# $(CC) -c -MD $*.d $(CFLAGS) $< + +CPU_ARCH = x86 # XXX fixme +GFX_ARCH = win32 + +# MSVC compiler options for both debug/optimize +# -nologo - suppress copyright message +# -W3 - Warning level 3 +# -Gm - enable minimal rebuild +# -Z7 - put debug info into the executable, not in .pdb file +# -Zi - put debug info into .pdb file +# -YX - automatic precompiled headers +# -GX - enable C++ exception support +WIN_CFLAGS = -nologo -W3 + +# MSVC compiler options for debug builds linked to MSVCRTD.DLL +# -MDd - link with MSVCRTD.LIB (Dynamically-linked, multi-threaded, debug C-runtime) +# -Od - minimal optimization +WIN_IDG_CFLAGS = -MDd -Od -Z7 + +# MSVC compiler options for debug builds linked to MSVCRT.DLL +# -MD - link with MSVCRT.LIB (Dynamically-linked, multi-threaded, debug C-runtime) +# -Od - minimal optimization +WIN_DEBUG_CFLAGS = -MD -Od -Zi -Fd$(OBJDIR)/$(PDBFILE) + +# MSVC compiler options for release (optimized) builds +# -MD - link with MSVCRT.LIB (Dynamically-linked, multi-threaded, C-runtime) +# -O2 - Optimize for speed +# -G5 - Optimize for Pentium +WIN_OPT_CFLAGS = -MD -O2 + +ifdef BUILD_OPT +OPTIMIZER = $(WIN_OPT_CFLAGS) +else +ifdef BUILD_IDG +OPTIMIZER = $(WIN_IDG_CFLAGS) +else +OPTIMIZER = $(WIN_DEBUG_CFLAGS) +endif +endif + +OS_CFLAGS = -D_X86_=1 -DXP_WIN -DXP_WIN32 -DWIN32 -D_WINDOWS -D_WIN32 $(WIN_CFLAGS) +JSDLL_CFLAGS = -DEXPORT_JS_API +OS_LIBS = -lm -lc + +PREBUILT_CPUCFG = 1 +USE_MSVC = 1 + +LIB_LINK_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib \ + winmm.lib \ + -nologo\ + -subsystem:windows -dll -debug -pdb:$(OBJDIR)/$(PDBFILE)\ + -machine:I386\ + -opt:ref -opt:noicf + +EXE_LINK_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib -nologo\ + -subsystem:console -debug -pdb:$(OBJDIR)/$(PDBFILE)\ + -machine:I386\ + -opt:ref -opt:noicf + +# CAFEDIR = t:/cafe +# JCLASSPATH = $(CAFEDIR)/Java/Lib/classes.zip +# JAVAC = $(CAFEDIR)/Bin/sj.exe +# JAVAH = $(CAFEDIR)/Java/Bin/javah.exe +# JCFLAGS = -I$(CAFEDIR)/Java/Include -I$(CAFEDIR)/Java/Include/win32 diff --git a/ape-server/deps/js/src/ref-config/WINNT5.0.mk b/ape-server/deps/js/src/ref-config/WINNT5.0.mk new file mode 100755 index 0000000..7681e01 --- /dev/null +++ b/ape-server/deps/js/src/ref-config/WINNT5.0.mk @@ -0,0 +1,118 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config for Windows NT using MS Visual C++ (version?) +# + +CC = cl +CXX = cl + +RANLIB = echo + +PDBFILE = $(basename $(@F)).pdb + +#.c.o: +# $(CC) -c -MD $*.d $(CFLAGS) $< + +CPU_ARCH = x86 # XXX fixme +GFX_ARCH = win32 + +# MSVC compiler options for both debug/optimize +# -nologo - suppress copyright message +# -W3 - Warning level 3 +# -Gm - enable minimal rebuild +# -Z7 - put debug info into the executable, not in .pdb file +# -Zi - put debug info into .pdb file +# -YX - automatic precompiled headers +# -GX - enable C++ exception support +WIN_CFLAGS = -nologo -W3 + +# MSVC compiler options for debug builds linked to MSVCRTD.DLL +# -MDd - link with MSVCRTD.LIB (Dynamically-linked, multi-threaded, debug C-runtime) +# -Od - minimal optimization +WIN_IDG_CFLAGS = -MDd -Od -Z7 + +# MSVC compiler options for debug builds linked to MSVCRT.DLL +# -MD - link with MSVCRT.LIB (Dynamically-linked, multi-threaded, debug C-runtime) +# -Od - minimal optimization +WIN_DEBUG_CFLAGS = -MD -Od -Zi -Fd$(OBJDIR)/$(PDBFILE) + +# MSVC compiler options for release (optimized) builds +# -MD - link with MSVCRT.LIB (Dynamically-linked, multi-threaded, C-runtime) +# -O2 - Optimize for speed +# -G5 - Optimize for Pentium +WIN_OPT_CFLAGS = -MD -O2 + +ifdef BUILD_OPT +OPTIMIZER = $(WIN_OPT_CFLAGS) +else +ifdef BUILD_IDG +OPTIMIZER = $(WIN_IDG_CFLAGS) +else +OPTIMIZER = $(WIN_DEBUG_CFLAGS) +endif +endif + +OS_CFLAGS = -D_X86_=1 -DXP_WIN -DXP_WIN32 -DWIN32 -D_WINDOWS -D_WIN32 -DWINVER=0x500 -D_WIN32_WINNT=0x500 $(WIN_CFLAGS) +JSDLL_CFLAGS = -DEXPORT_JS_API +OS_LIBS = -lm -lc + +PREBUILT_CPUCFG = 1 +USE_MSVC = 1 + +LIB_LINK_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib \ + winmm.lib \ + -nologo\ + -subsystem:windows -dll -debug -pdb:$(OBJDIR)/$(PDBFILE)\ + -machine:I386\ + -opt:ref -opt:noicf + +EXE_LINK_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib -nologo\ + -subsystem:console -debug -pdb:$(OBJDIR)/$(PDBFILE)\ + -machine:I386\ + -opt:ref -opt:noicf + +# CAFEDIR = t:/cafe +# JCLASSPATH = $(CAFEDIR)/Java/Lib/classes.zip +# JAVAC = $(CAFEDIR)/Bin/sj.exe +# JAVAH = $(CAFEDIR)/Java/Bin/javah.exe +# JCFLAGS = -I$(CAFEDIR)/Java/Include -I$(CAFEDIR)/Java/Include/win32 diff --git a/ape-server/deps/js/src/ref-config/WINNT5.1.mk b/ape-server/deps/js/src/ref-config/WINNT5.1.mk new file mode 100755 index 0000000..7681e01 --- /dev/null +++ b/ape-server/deps/js/src/ref-config/WINNT5.1.mk @@ -0,0 +1,118 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config for Windows NT using MS Visual C++ (version?) +# + +CC = cl +CXX = cl + +RANLIB = echo + +PDBFILE = $(basename $(@F)).pdb + +#.c.o: +# $(CC) -c -MD $*.d $(CFLAGS) $< + +CPU_ARCH = x86 # XXX fixme +GFX_ARCH = win32 + +# MSVC compiler options for both debug/optimize +# -nologo - suppress copyright message +# -W3 - Warning level 3 +# -Gm - enable minimal rebuild +# -Z7 - put debug info into the executable, not in .pdb file +# -Zi - put debug info into .pdb file +# -YX - automatic precompiled headers +# -GX - enable C++ exception support +WIN_CFLAGS = -nologo -W3 + +# MSVC compiler options for debug builds linked to MSVCRTD.DLL +# -MDd - link with MSVCRTD.LIB (Dynamically-linked, multi-threaded, debug C-runtime) +# -Od - minimal optimization +WIN_IDG_CFLAGS = -MDd -Od -Z7 + +# MSVC compiler options for debug builds linked to MSVCRT.DLL +# -MD - link with MSVCRT.LIB (Dynamically-linked, multi-threaded, debug C-runtime) +# -Od - minimal optimization +WIN_DEBUG_CFLAGS = -MD -Od -Zi -Fd$(OBJDIR)/$(PDBFILE) + +# MSVC compiler options for release (optimized) builds +# -MD - link with MSVCRT.LIB (Dynamically-linked, multi-threaded, C-runtime) +# -O2 - Optimize for speed +# -G5 - Optimize for Pentium +WIN_OPT_CFLAGS = -MD -O2 + +ifdef BUILD_OPT +OPTIMIZER = $(WIN_OPT_CFLAGS) +else +ifdef BUILD_IDG +OPTIMIZER = $(WIN_IDG_CFLAGS) +else +OPTIMIZER = $(WIN_DEBUG_CFLAGS) +endif +endif + +OS_CFLAGS = -D_X86_=1 -DXP_WIN -DXP_WIN32 -DWIN32 -D_WINDOWS -D_WIN32 -DWINVER=0x500 -D_WIN32_WINNT=0x500 $(WIN_CFLAGS) +JSDLL_CFLAGS = -DEXPORT_JS_API +OS_LIBS = -lm -lc + +PREBUILT_CPUCFG = 1 +USE_MSVC = 1 + +LIB_LINK_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib \ + winmm.lib \ + -nologo\ + -subsystem:windows -dll -debug -pdb:$(OBJDIR)/$(PDBFILE)\ + -machine:I386\ + -opt:ref -opt:noicf + +EXE_LINK_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib -nologo\ + -subsystem:console -debug -pdb:$(OBJDIR)/$(PDBFILE)\ + -machine:I386\ + -opt:ref -opt:noicf + +# CAFEDIR = t:/cafe +# JCLASSPATH = $(CAFEDIR)/Java/Lib/classes.zip +# JAVAC = $(CAFEDIR)/Bin/sj.exe +# JAVAH = $(CAFEDIR)/Java/Bin/javah.exe +# JCFLAGS = -I$(CAFEDIR)/Java/Include -I$(CAFEDIR)/Java/Include/win32 diff --git a/ape-server/deps/js/src/ref-config/WINNT5.2.mk b/ape-server/deps/js/src/ref-config/WINNT5.2.mk new file mode 100755 index 0000000..5fbcbfe --- /dev/null +++ b/ape-server/deps/js/src/ref-config/WINNT5.2.mk @@ -0,0 +1,118 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config for Windows NT using MS Visual C++ (version?) +# + +CC = cl +CXX = cl + +RANLIB = echo + +PDBFILE = $(basename $(@F)).pdb + +#.c.o: +# $(CC) -c -MD $*.d $(CFLAGS) $< + +CPU_ARCH = x86 # XXX fixme +GFX_ARCH = win32 + +# MSVC compiler options for both debug/optimize +# -nologo - suppress copyright message +# -W3 - Warning level 3 +# -Gm - enable minimal rebuild +# -Z7 - put debug info into the executable, not in .pdb file +# -Zi - put debug info into .pdb file +# -YX - automatic precompiled headers +# -GX - enable C++ exception support +WIN_CFLAGS = -nologo -W3 + +# MSVC compiler options for debug builds linked to MSVCRTD.DLL +# -MDd - link with MSVCRTD.LIB (Dynamically-linked, multi-threaded, debug C-runtime) +# -Od - minimal optimization +WIN_IDG_CFLAGS = -MDd -Od -Z7 + +# MSVC compiler options for debug builds linked to MSVCRT.DLL +# -MD - link with MSVCRT.LIB (Dynamically-linked, multi-threaded, debug C-runtime) +# -Od - minimal optimization +WIN_DEBUG_CFLAGS = -MD -Od -Zi -Fd$(OBJDIR)/$(PDBFILE) + +# MSVC compiler options for release (optimized) builds +# -MD - link with MSVCRT.LIB (Dynamically-linked, multi-threaded, C-runtime) +# -O2 - Optimize for speed +# -G5 - Optimize for Pentium +WIN_OPT_CFLAGS = -MD -O2 + +ifdef BUILD_OPT +OPTIMIZER = $(WIN_OPT_CFLAGS) +else +ifdef BUILD_IDG +OPTIMIZER = $(WIN_IDG_CFLAGS) +else +OPTIMIZER = $(WIN_DEBUG_CFLAGS) +endif +endif + +OS_CFLAGS = -D_X86_=1 -DXP_WIN -DXP_WIN32 -DWIN32 -D_WINDOWS -D_WIN32 -DWINVER=0x500 -D_WIN32_WINNT=0x500 $(WIN_CFLAGS) -DAVMPLUS_WIN32 -DAVMPLUS_IA32 +JSDLL_CFLAGS = -DEXPORT_JS_API +OS_LIBS = -lm -lc + +PREBUILT_CPUCFG = 1 +USE_MSVC = 1 + +LIB_LINK_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib \ + winmm.lib \ + -nologo\ + -subsystem:windows -dll -debug -pdb:$(OBJDIR)/$(PDBFILE)\ + -machine:I386\ + -opt:ref -opt:noicf + +EXE_LINK_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib -nologo\ + -subsystem:console -debug -pdb:$(OBJDIR)/$(PDBFILE)\ + -machine:I386\ + -opt:ref -opt:noicf + +# CAFEDIR = t:/cafe +# JCLASSPATH = $(CAFEDIR)/Java/Lib/classes.zip +# JAVAC = $(CAFEDIR)/Bin/sj.exe +# JAVAH = $(CAFEDIR)/Java/Bin/javah.exe +# JCFLAGS = -I$(CAFEDIR)/Java/Include -I$(CAFEDIR)/Java/Include/win32 diff --git a/ape-server/deps/js/src/ref-config/WINNT6.0.mk b/ape-server/deps/js/src/ref-config/WINNT6.0.mk new file mode 100755 index 0000000..7681e01 --- /dev/null +++ b/ape-server/deps/js/src/ref-config/WINNT6.0.mk @@ -0,0 +1,118 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config for Windows NT using MS Visual C++ (version?) +# + +CC = cl +CXX = cl + +RANLIB = echo + +PDBFILE = $(basename $(@F)).pdb + +#.c.o: +# $(CC) -c -MD $*.d $(CFLAGS) $< + +CPU_ARCH = x86 # XXX fixme +GFX_ARCH = win32 + +# MSVC compiler options for both debug/optimize +# -nologo - suppress copyright message +# -W3 - Warning level 3 +# -Gm - enable minimal rebuild +# -Z7 - put debug info into the executable, not in .pdb file +# -Zi - put debug info into .pdb file +# -YX - automatic precompiled headers +# -GX - enable C++ exception support +WIN_CFLAGS = -nologo -W3 + +# MSVC compiler options for debug builds linked to MSVCRTD.DLL +# -MDd - link with MSVCRTD.LIB (Dynamically-linked, multi-threaded, debug C-runtime) +# -Od - minimal optimization +WIN_IDG_CFLAGS = -MDd -Od -Z7 + +# MSVC compiler options for debug builds linked to MSVCRT.DLL +# -MD - link with MSVCRT.LIB (Dynamically-linked, multi-threaded, debug C-runtime) +# -Od - minimal optimization +WIN_DEBUG_CFLAGS = -MD -Od -Zi -Fd$(OBJDIR)/$(PDBFILE) + +# MSVC compiler options for release (optimized) builds +# -MD - link with MSVCRT.LIB (Dynamically-linked, multi-threaded, C-runtime) +# -O2 - Optimize for speed +# -G5 - Optimize for Pentium +WIN_OPT_CFLAGS = -MD -O2 + +ifdef BUILD_OPT +OPTIMIZER = $(WIN_OPT_CFLAGS) +else +ifdef BUILD_IDG +OPTIMIZER = $(WIN_IDG_CFLAGS) +else +OPTIMIZER = $(WIN_DEBUG_CFLAGS) +endif +endif + +OS_CFLAGS = -D_X86_=1 -DXP_WIN -DXP_WIN32 -DWIN32 -D_WINDOWS -D_WIN32 -DWINVER=0x500 -D_WIN32_WINNT=0x500 $(WIN_CFLAGS) +JSDLL_CFLAGS = -DEXPORT_JS_API +OS_LIBS = -lm -lc + +PREBUILT_CPUCFG = 1 +USE_MSVC = 1 + +LIB_LINK_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib \ + winmm.lib \ + -nologo\ + -subsystem:windows -dll -debug -pdb:$(OBJDIR)/$(PDBFILE)\ + -machine:I386\ + -opt:ref -opt:noicf + +EXE_LINK_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib -nologo\ + -subsystem:console -debug -pdb:$(OBJDIR)/$(PDBFILE)\ + -machine:I386\ + -opt:ref -opt:noicf + +# CAFEDIR = t:/cafe +# JCLASSPATH = $(CAFEDIR)/Java/Lib/classes.zip +# JAVAC = $(CAFEDIR)/Bin/sj.exe +# JAVAH = $(CAFEDIR)/Java/Bin/javah.exe +# JCFLAGS = -I$(CAFEDIR)/Java/Include -I$(CAFEDIR)/Java/Include/win32 diff --git a/ape-server/deps/js/src/ref-config/dgux.mk b/ape-server/deps/js/src/ref-config/dgux.mk new file mode 100755 index 0000000..3b5967e --- /dev/null +++ b/ape-server/deps/js/src/ref-config/dgux.mk @@ -0,0 +1,64 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Config stuff for Data General DG/UX +# + +# +# Initial DG/UX port by Marc Fraioli (fraioli@dg-rtp.dg.com) +# + +AS = as +CC = gcc +CCC = g++ + +RANLIB = echo + +# +# _DGUX_SOURCE is needed to turn on a lot of stuff in the headers if +# you're not using DG's compiler. It shouldn't hurt if you are. +# +# _POSIX4A_DRAFT10_SOURCE is needed to pick up localtime_r, used in +# prtime.c +# +OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -DDGUX -D_DGUX_SOURCE -D_POSIX4A_DRAFT10_SOURCE -DHAVE_LOCALTIME_R +OS_LIBS = -lsocket -lnsl + +NOSUCHFILE = /no-such-file diff --git a/ape-server/deps/js/src/resource.h b/ape-server/deps/js/src/resource.h new file mode 100755 index 0000000..59dbde3 --- /dev/null +++ b/ape-server/deps/js/src/resource.h @@ -0,0 +1,15 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by js3240.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/ape-server/deps/js/src/rules.mk b/ape-server/deps/js/src/rules.mk new file mode 100755 index 0000000..5a1e053 --- /dev/null +++ b/ape-server/deps/js/src/rules.mk @@ -0,0 +1,206 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998-1999 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Michael Ang +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# JSRef GNUmake makefile rules +# + +ifdef USE_MSVC +LIB_OBJS = $(addprefix $(OBJDIR)/, $(LIB_CPPFILES:.cpp=.obj)) +PROG_OBJS = $(addprefix $(OBJDIR)/, $(PROG_CPPFILES:.cpp=.obj)) +else +LIB_OBJS = $(addprefix $(OBJDIR)/, $(LIB_CPPFILES:.cpp=.o)) +LIB_OBJS += $(addprefix $(OBJDIR)/, $(LIB_ASFILES:.s=.o)) +PROG_OBJS = $(addprefix $(OBJDIR)/, $(PROG_CPPFILES:.cpp=.o)) +endif + +CPPFILES = $(LIB_CPPFILES) $(PROG_CPPFILES) +OBJS = $(LIB_OBJS) $(PROG_OBJS) + +ifdef USE_MSVC +# TARGETS = $(LIBRARY) # $(PROGRAM) not supported for MSVC yet +TARGETS += $(SHARED_LIBRARY) $(PROGRAM) # it is now +else +TARGETS += $(LIBRARY) $(SHARED_LIBRARY) $(PROGRAM) +endif + +all: + +$(LOOP_OVER_PREDIRS) +ifneq "$(strip $(TARGETS))" "" + $(MAKE) -f Makefile.ref $(TARGETS) +endif + +$(LOOP_OVER_DIRS) + +$(OBJDIR)/%: %.cpp + @$(MAKE_OBJDIR) + $(CXX) -o $@ $(CFLAGS) $(OPTIMIZER) $< $(LDFLAGS) + +# This rule must come before the rule with no dep on header +$(OBJDIR)/%.o: %.cpp %.h + @$(MAKE_OBJDIR) + $(CXX) -o $@ -c $(CFLAGS) $(OPTIMIZER) $< + +$(OBJDIR)/jsinterp.o: jsinterp.cpp jsinterp.h + @$(MAKE_OBJDIR) + $(CXX) -o $@ -c $(CFLAGS) $(INTERP_OPTIMIZER) jsinterp.cpp + +$(OBJDIR)/jsbuiltins.o: jsbuiltins.cpp jsinterp.h + @$(MAKE_OBJDIR) + $(CXX) -o $@ -c $(CFLAGS) $(BUILTINS_OPTIMIZER) jsbuiltins.cpp + +$(OBJDIR)/%.o: %.cpp + @$(MAKE_OBJDIR) + $(CXX) -o $@ -c $(CFLAGS) $(OPTIMIZER) $< + +$(OBJDIR)/%.o: %.s + @$(MAKE_OBJDIR) + $(AS) -o $@ $(ASFLAGS) $< + +# This rule must come before rule with no dep on header +$(OBJDIR)/%.obj: %.cpp %.h + @$(MAKE_OBJDIR) + $(CXX) -Fo$(OBJDIR)/ -c $(CFLAGS) $(JSDLL_CFLAGS) $(OPTIMIZER) $< + +$(OBJDIR)/jsinterp.obj: jsinterp.cpp jsinterp.h + @$(MAKE_OBJDIR) + $(CXX) -Fo$(OBJDIR)/ -c $(CFLAGS) $(JSDLL_CFLAGS) $(INTERP_OPTIMIZER) jsinterp.cpp + +$(OBJDIR)/jsbuiltins.obj: jsbuiltins.cpp jsinterp.h + @$(MAKE_OBJDIR) + $(CXX) -Fo$(OBJDIR)/ -c $(CFLAGS) $(JSDLL_CFLAGS) $(BUILTINS_OPTIMIZER) jsbuiltins.cpp + +$(OBJDIR)/%.obj: %.cpp + @$(MAKE_OBJDIR) + $(CXX) -Fo$(OBJDIR)/ -c $(CFLAGS) $(JSDLL_CFLAGS) $(OPTIMIZER) $< + +$(OBJDIR)/js.obj: js.cpp + @$(MAKE_OBJDIR) + $(CXX) -Fo$(OBJDIR)/ -c $(CFLAGS) $(OPTIMIZER) $< + +ifeq ($(OS_ARCH),OS2) +$(LIBRARY): $(LIB_OBJS) + $(AR) $@ $? $(AR_OS2_SUFFIX) + $(RANLIB) $@ +else +ifdef USE_MSVC +$(SHARED_LIBRARY): $(LIB_OBJS) + link.exe $(LIB_LINK_FLAGS) /base:0x61000000 $(OTHER_LIBS) \ + /out:"$@" /pdb:none\ + /implib:"$(OBJDIR)/$(@F:.dll=.lib)" $^ +else +$(LIBRARY): $(LIB_OBJS) + $(AR) rv $@ $? + $(RANLIB) $@ + +$(SHARED_LIBRARY): $(LIB_OBJS) + $(MKSHLIB) -o $@ $(LIB_OBJS) $(LDFLAGS) $(OTHER_LIBS) +endif +endif + +# Java stuff +$(CLASSDIR)/$(OBJDIR)/$(JARPATH)/%.class: %.java + mkdir -p $(@D) + $(JAVAC) $(JAVAC_FLAGS) $< + +define MAKE_OBJDIR +if test ! -d $(@D); then rm -rf $(@D); mkdir -p $(@D); fi +endef + +ifdef DIRS +LOOP_OVER_DIRS = \ + @for d in $(DIRS); do \ + if test -d $$d; then \ + set -e; \ + echo "cd $$d; $(MAKE) -f Makefile.ref $@"; \ + cd $$d; $(MAKE) -f Makefile.ref $@; cd ..; \ + set +e; \ + else \ + echo "Skipping non-directory $$d..."; \ + fi; \ + done +endif + +ifdef PREDIRS +LOOP_OVER_PREDIRS = \ + @for d in $(PREDIRS); do \ + if test -d $$d; then \ + set -e; \ + echo "cd $$d; $(MAKE) -f Makefile.ref $@"; \ + cd $$d; $(MAKE) -f Makefile.ref $@; cd ..; \ + set +e; \ + else \ + echo "Skipping non-directory $$d..."; \ + fi; \ + done +endif + +export: + +$(LOOP_OVER_PREDIRS) + mkdir -p $(DIST)/include $(DIST)/$(LIBDIR) $(DIST)/bin +ifneq "$(strip $(HFILES))" "" + $(CP) $(HFILES) $(DIST)/include +endif +ifneq "$(strip $(LIBRARY))" "" + $(CP) $(LIBRARY) $(DIST)/$(LIBDIR) +endif +ifneq "$(strip $(JARS))" "" + $(CP) $(JARS) $(DIST)/$(LIBDIR) +endif +ifneq "$(strip $(SHARED_LIBRARY))" "" + $(CP) $(SHARED_LIBRARY) $(DIST)/$(LIBDIR) +endif +ifneq "$(strip $(PROGRAM))" "" + $(CP) $(PROGRAM) $(DIST)/bin +endif + +$(LOOP_OVER_DIRS) + +clean: + +$(LOOP_OVER_PREDIRS) + rm -rf $(OBJS) $(GARBAGE) + +clobber: + +$(LOOP_OVER_PREDIRS) + rm -rf $(OBJS) $(TARGETS) $(DEPENDENCIES) $(GARBAGE) + if test -d $(OBJDIR); then rmdir $(OBJDIR); fi + +tar: + tar cvf $(TARNAME) $(TARFILES) + gzip $(TARNAME) + diff --git a/ape-server/deps/js/src/time.sh b/ape-server/deps/js/src/time.sh new file mode 100755 index 0000000..05b3499 --- /dev/null +++ b/ape-server/deps/js/src/time.sh @@ -0,0 +1,13 @@ +#!/bin/bash +echo -n interp:' ' +for i in 1 2 3 4 5; do + INTERP=`Darwin_OPT.OBJ/js -e 'var d = Date.now(); load("'$1'"); print(Date.now() - d);'` + echo -n $INTERP' ' +done +echo -ne '\njit: ' +for i in 1 2 3 4 5; do + JIT=`Darwin_OPT.OBJ/js -j -e 'var d = Date.now(); load("'$1'"); print(Date.now() - d);'` + echo -n $JIT' ' +done +echo -ne '\njit factor: ' +(echo scale=2; echo $INTERP / $JIT ) | bc diff --git a/ape-server/deps/js/src/unallmakefiles b/ape-server/deps/js/src/unallmakefiles new file mode 100755 index 0000000..83fd624 --- /dev/null +++ b/ape-server/deps/js/src/unallmakefiles @@ -0,0 +1 @@ +Makefile config/Makefile config/autoconf.mk config/mkdepend/Makefile editline/Makefile diff --git a/ape-server/deps/js/src/vprof/readme.txt b/ape-server/deps/js/src/vprof/readme.txt new file mode 100755 index 0000000..d9b04de --- /dev/null +++ b/ape-server/deps/js/src/vprof/readme.txt @@ -0,0 +1,130 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http:#www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is [Open Source Virtual Machine.]. +# +# The Initial Developer of the Original Code is +# Adobe System Incorporated. +# Portions created by the Initial Developer are Copyright (C) 2008 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Adobe AS3 Team +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +The two files vprof.h and vprof.cpp implement a simple value-profiling mechanism. By including these two files in avmplus (or any other project), you can value profile data as you wish (currently integers). + +Usage: +#include "vprof.h" // in the source file you want to use it + +_vprof (value); + +At the end of the execution, for each probe you'll get the data associated with the probe, such as: + +File line avg [min : max] total count +..\..\pcre\pcre_valid_utf8.cpp 182 50222.75916 [0 : 104947] 4036955604 80381 + +The probe is defined at line 182 of file pcre_vali_utf8.cpp. It was called 80381 times. The min value of the probe was 0 while its max was 10497 and its average was 50222.75916. The total sum of all values of the probe is 4036955604. Later, I plan to add more options on the spectrum of data among others. + +A few typical uses +------------------ + +To see how many times a given function gets executed do: + +void f() +{ + _vprof(1); + ... +} + +void f() +{ + _vprof(1); + ... + if (...) { + _vprof(1); + ... + } else { + _vprof(1); + ... + } +} + +Here are a few examples of using the value-profiling utility: + + _vprof (e); + at the end of program execution, you'll get a dump of the source location of this probe, + its min, max, average, the total sum of all instances of e, and the total number of times this probe was called. + + _vprof (x > 0); + shows how many times and what percentage of the cases x was > 0, + that is the probablitiy that x > 0. + + _vprof (n % 2 == 0); + shows how many times n was an even number + as well as th probablitiy of n being an even number. + + _hprof (n, 4, 1000, 5000, 5001, 10000); + gives you the histogram of n over the given 4 bucket boundaries: + # cases < 1000 + # cases >= 1000 and < 5000 + # cases >= 5000 and < 5001 + # cases >= 5001 and < 10000 + # cases >= 10000 + + _nvprof ("event name", value); + all instances with the same name are merged + so, you can call _vprof with the same event name at difference places + + _vprof (e, myProbe); + value profile e and call myProbe (void* vprofID) at the profiling point. + inside the probe, the client has the predefined variables: + _VAL, _COUNT, _SUM, _MIN, _MAX, and the general purpose registers + _IVAR1, ..., IVAR4 general integer registrs + _I64VAR1, ..., I64VAR4 general integer64 registrs + _DVAR1, ..., _DVAR4 general double registers + _GENPTR a generic pointer that can be used by the client + the number of registers can be changed in vprof.h + +Named Events +------------ +_nvprof ("event name", value); + all instances with the same name are merged + so, you can call _vprof with the same event name at difference places + + +Custom Probes +-------------- +You can call your own custom probe at the profiling point. +_vprof (v, myProbe); + value profile v and call myProbe (void* vprofID) at the profiling point + inside the probe, the client has the predefined variables: + _VAL, _COUNT, _SUM, _MIN, _MAX, and the general purpose registers + _IVAR1, ..., IVAR4 general integer registrs + _I64VAR1, ..., I64VAR4 general integer64 registrs + _DVAR1, ..., _DVAR4 general double registers + the number of registers can be changed in vprof.h + _GENPTR a generic pointer that can be used for almost anything diff --git a/ape-server/deps/js/src/vprof/vprof.cpp b/ape-server/deps/js/src/vprof/vprof.cpp new file mode 100755 index 0000000..68c7c04 --- /dev/null +++ b/ape-server/deps/js/src/vprof/vprof.cpp @@ -0,0 +1,410 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Value-Profiling Utility. + * + * The Initial Developer of the Original Code is + * Intel Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mohammad R. Haghighat [mohammad.r.haghighat@intel.com] + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "VMPI.h" + +// Note, this is not supported in configurations with more than one AvmCore running +// in the same process. + +#ifdef WIN32 +#include "windows.h" +#else +#define __cdecl +#include +#include +#endif + +#include "vprof.h" + +#define MIN(x,y) ((x) <= (y) ? x : y) +#define MAX(x,y) ((x) >= (y) ? x : y) + +#ifndef MAXINT +#define MAXINT int(unsigned(-1)>>1) +#endif + +#ifndef MAXINT64 +#define MAXINT64 int64_t(uint64_t(-1)>>1) +#endif + +#ifndef __STDC_WANT_SECURE_LIB__ +#define sprintf_s(b,size,fmt,...) sprintf((b),(fmt),__VA_ARGS__) +#endif + +#if THREADED +#define DO_LOCK(lock) Lock(lock); { +#define DO_UNLOCK(lock) }; Unlock(lock) +#else +#define DO_LOCK(lock) { (void)(lock); +#define DO_UNLOCK(lock) } +#endif + +#if THREAD_SAFE +#define LOCK(lock) DO_LOCK(lock) +#define UNLOCK(lock) DO_UNLOCK(lock) +#else +#define LOCK(lock) { (void)(lock); +#define UNLOCK(lock) } +#endif + +static entry* entries = NULL; +static bool notInitialized = true; +static long glock = LOCK_IS_FREE; + +#define Lock(lock) while (_InterlockedCompareExchange(lock, LOCK_IS_TAKEN, LOCK_IS_FREE) == LOCK_IS_TAKEN){}; +#define Unlock(lock) _InterlockedCompareExchange(lock, LOCK_IS_FREE, LOCK_IS_TAKEN); + +#if defined(WIN32) && !defined(UNDER_CE) + static void vprof_printf(const char* format, ...) + { + va_list args; + va_start(args, format); + + char buf[1024]; + vsnprintf(buf, sizeof(buf), format, args); + + va_end(args); + + printf(buf); + ::OutputDebugStringA(buf); + } +#else + #define vprof_printf printf +#endif + +inline static entry* reverse (entry* s) +{ + entry_t e, n, p; + + p = NULL; + for (e = s; e; e = n) { + n = e->next; + e->next = p; + p = e; + } + + return p; +} + +static char* f (double d) +{ + static char s[80]; + char* p; + sprintf_s (s, sizeof(s), "%lf", d); + p = s+VMPI_strlen(s)-1; + while (*p == '0') { + *p = '\0'; + p--; + if (p == s) break; + } + if (*p == '.') *p = '\0'; + return s; +} + +static void dumpProfile (void) +{ + entry_t e; + + entries = reverse(entries); + vprof_printf ("event avg [min : max] total count\n"); + for (e = entries; e; e = e->next) { + vprof_printf ("%s", e->file); + if (e->line >= 0) { + vprof_printf (":%d", e->line); + } + vprof_printf (" %s [%lld : %lld] %lld %lld ", + f(((double)e->sum)/((double)e->count)), (long long int)e->min, (long long int)e->max, (long long int)e->sum, (long long int)e->count); + if (e->h) { + int j = MAXINT; + for (j = 0; j < e->h->nbins; j ++) { + vprof_printf ("(%lld < %lld) ", (long long int)e->h->count[j], (long long int)e->h->lb[j]); + } + vprof_printf ("(%lld >= %lld) ", (long long int)e->h->count[e->h->nbins], (long long int)e->h->lb[e->h->nbins-1]); + } + if (e->func) { + int j; + for (j = 0; j < NUM_EVARS; j++) { + if (e->ivar[j] != 0) { + vprof_printf ("IVAR%d %d ", j, e->ivar[j]); + } + } + for (j = 0; j < NUM_EVARS; j++) { + if (e->i64var[j] != 0) { + vprof_printf ("I64VAR%d %lld ", j, (long long int)e->i64var[j]); + } + } + for (j = 0; j < NUM_EVARS; j++) { + if (e->dvar[j] != 0) { + vprof_printf ("DVAR%d %lf ", j, e->dvar[j]); + } + } + } + vprof_printf ("\n"); + } + entries = reverse(entries); +} + + +int _profileEntryValue (void* id, int64_t value) +{ + entry_t e = (entry_t) id; + long* lock = &(e->lock); + LOCK (lock); + e->value = value; + e->sum += value; + e->count ++; + e->min = MIN (e->min, value); + e->max = MAX (e->max, value); + if (e->func) e->func (e); + UNLOCK (lock); + + return 0; +} + +inline static entry_t findEntry (char* file, int line) +{ + for (entry_t e = entries; e; e = e->next) { + if ((e->line == line) && (VMPI_strcmp (e->file, file) == 0)) { + return e; + } + } + return NULL; +} + +int profileValue(void** id, char* file, int line, int64_t value, ...) +{ + DO_LOCK (&glock); + entry_t e = (entry_t) *id; + if (notInitialized) { + atexit (dumpProfile); + notInitialized = false; + } + + if (e == NULL) { + e = findEntry (file, line); + if (e) { + *id = e; + } + } + + if (e == NULL) { + va_list va; + e = (entry_t) malloc (sizeof(entry)); + e->lock = LOCK_IS_FREE; + e->file = file; + e->line = line; + e->value = value; + e->sum = value; + e->count = 1; + e->min = value; + e->max = value; + + va_start (va, value); + e->func = (void (__cdecl*)(void*)) va_arg (va, void*); + va_end (va); + + e->h = NULL; + + e->genptr = NULL; + + VMPI_memset (&e->ivar, 0, sizeof(e->ivar)); + VMPI_memset (&e->i64var, 0, sizeof(e->i64var)); + VMPI_memset (&e->dvar, 0, sizeof(e->dvar)); + + e->next = entries; + entries = e; + + if (e->func) e->func (e); + + *id = e; + } else { + long* lock = &(e->lock); + LOCK (lock); + e->value = value; + e->sum += value; + e->count ++; + e->min = MIN (e->min, value); + e->max = MAX (e->max, value); + if (e->func) e->func (e); + UNLOCK (lock); + } + DO_UNLOCK (&glock); + + return 0; +} + +int _histEntryValue (void* id, int64_t value) +{ + entry_t e = (entry_t) id; + long* lock = &(e->lock); + hist_t h = e->h; + int nbins = h->nbins; + int64_t* lb = h->lb; + int b; + + for (b = 0; b < nbins; b ++) { + if (value < lb[b]) break; + } + + LOCK (lock); + e->value = value; + e->sum += value; + e->count ++; + e->min = MIN (e->min, value); + e->max = MAX (e->max, value); + h->count[b] ++; + UNLOCK (lock); + + return 0; +} + +int histValue(void** id, char* file, int line, int64_t value, int nbins, ...) +{ + DO_LOCK (&glock); + entry_t e = (entry_t) *id; + if (notInitialized) { + atexit (dumpProfile); + notInitialized = false; + } + + if (e == NULL) { + e = findEntry (file, line); + if (e) { + *id = e; + } + } + + if (e == NULL) { + va_list va; + hist_t h; + int b, n, s; + int64_t* lb; + + e = (entry_t) malloc (sizeof(entry)); + e->lock = LOCK_IS_FREE; + e->file = file; + e->line = line; + e->value = value; + e->sum = value; + e->count = 1; + e->min = value; + e->max = value; + e->func = NULL; + e->h = h = (hist_t) malloc (sizeof(hist)); + n = 1+MAX(nbins,0); + h->nbins = n-1; + s = n*sizeof(int64_t); + lb = (int64_t*) malloc (s); + h->lb = lb; + VMPI_memset (h->lb, 0, s); + h->count = (int64_t*) malloc (s); + VMPI_memset (h->count, 0, s); + + va_start (va, nbins); + for (b = 0; b < nbins; b++) { + //lb[b] = va_arg (va, int64_t); + lb[b] = va_arg (va, int); + } + lb[b] = MAXINT64; + va_end (va); + + for (b = 0; b < nbins; b ++) { + if (value < lb[b]) break; + } + h->count[b] ++; + + e->genptr = NULL; + + VMPI_memset (&e->ivar, 0, sizeof(e->ivar)); + VMPI_memset (&e->i64var, 0, sizeof(e->i64var)); + VMPI_memset (&e->dvar, 0, sizeof(e->dvar)); + + e->next = entries; + entries = e; + *id = e; + } else { + int b; + long* lock = &(e->lock); + hist_t h=e->h; + int64_t* lb = h->lb; + + LOCK (lock); + e->value = value; + e->sum += value; + e->count ++; + e->min = MIN (e->min, value); + e->max = MAX (e->max, value); + for (b = 0; b < nbins; b ++) { + if (value < lb[b]) break; + } + h->count[b] ++; + UNLOCK (lock); + } + DO_UNLOCK (&glock); + + return 0; +} + +#if defined(_MSC_VER) && defined(_M_IX86) +inline uint64_t _rdtsc() +{ + // read the cpu cycle counter. 1 tick = 1 cycle on IA32 + _asm rdtsc; +} +#elif defined(__GNUC__) && (__i386__ || __x86_64__) +inline uint64_t _rdtsc() +{ + uint32_t lo, hi; + __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); + return (uint64_t(hi) << 32) | lo; +} +#else +// add stub for platforms without it, so fat builds don't fail +inline uint64_t _rdtsc() { return 0; } +#endif + +void* _tprof_before_id=0; +static uint64_t _tprof_before = 0; +int64_t _tprof_time() +{ + uint64_t now = _rdtsc(); + uint64_t v = _tprof_before ? now-_tprof_before : 0; + _tprof_before = now; + return v/2600; // v = microseconds on a 2.6ghz cpu +} + diff --git a/ape-server/deps/js/src/vprof/vprof.h b/ape-server/deps/js/src/vprof/vprof.h new file mode 100755 index 0000000..ac016a6 --- /dev/null +++ b/ape-server/deps/js/src/vprof/vprof.h @@ -0,0 +1,271 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Value-Profiling Utility. + * + * The Initial Developer of the Original Code is + * Intel Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mohammad R. Haghighat [mohammad.r.haghighat@intel.com] + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +// +// Here are a few examples of using the value-profiling utility: +// +// _vprof (e); +// at the end of program execution, you'll get a dump of the source location of this probe, +// its min, max, average, the total sum of all instances of e, and the total number of times this probe was called. +// +// _vprof (x > 0); +// shows how many times and what percentage of the cases x was > 0, +// that is the probablitiy that x > 0. +// +// _vprof (n % 2 == 0); +// shows how many times n was an even number +// as well as th probablitiy of n being an even number. +// +// _hprof (n, 4, 1000, 5000, 5001, 10000); +// gives you the histogram of n over the given 4 bucket boundaries: +// # cases < 1000 +// # cases >= 1000 and < 5000 +// # cases >= 5000 and < 5001 +// # cases >= 5001 and < 10000 +// # cases >= 10000 +// +// _nvprof ("event name", value); +// all instances with the same name are merged +// so, you can call _vprof with the same event name at difference places +// +// _vprof (e, myProbe); +// value profile e and call myProbe (void* vprofID) at the profiling point. +// inside the probe, the client has the predefined variables: +// _VAL, _COUNT, _SUM, _MIN, _MAX, and the general purpose registers +// _IVAR1, ..., IVAR4 general integer registrs +// _I64VAR1, ..., I64VAR4 general integer64 registrs +// _DVAR1, ..., _DVAR4 general double registers +// _GENPTR a generic pointer that can be used by the client +// the number of registers can be changed in vprof.h +// + +#ifndef __VPROF__ +#define __VPROF__ +// +// If the application for which you want to use vprof is threaded, THREADED must be defined as 1, otherwise define it as 0 +// +// If your application is not threaded, define THREAD_SAFE 0, +// otherwise, you have the option of setting THREAD_SAFE to 1 which results in exact counts or to 0 which results in a much more efficient but non-exact counts +// +#define THREADED 0 +#define THREAD_SAFE 0 + +#include "VMPI.h" + +// Note, this is not supported in configurations with more than one AvmCore running +// in the same process. + +// portable align macro +#if defined(_MSC_VER) + #define vprof_align8(t) __declspec(align(8)) t +#elif defined(__GNUC__) + #define vprof_align8(t) t __attribute__ ((aligned (8))) +#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) + #define vprof_align8(t) t __attribute__ ((aligned (8))) +#elif defined(VMCFG_SYMBIAN) + #define vprof_align8(t) t __attribute__ ((aligned (8))) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +int profileValue (void** id, char* file, int line, int64_t value, ...); +int _profileEntryValue (void* id, int64_t value); +int histValue(void** id, char* file, int line, int64_t value, int nbins, ...); +int _histEntryValue (void* id, int64_t value); +int64_t _tprof_time(); +extern void* _tprof_before_id; + +#ifdef __cplusplus +} +#endif + +//#define DOPROF + +#ifndef DOPROF +#ifndef VMCFG_SYMBIAN +#define _vprof(v,...) +#define _nvprof(e,v,...) +#define _hprof(h,n,...) +#define _nhprof(e,v,n,...) +#define _ntprof(e) +#define _tprof_end() +#endif // ! VMCFG_SYMBIAN +#else + +#define _vprof(v,...) \ +{ \ + static void* id = 0; \ + (id != 0) ? \ + _profileEntryValue (id, (int64_t) (v)) \ + : \ + profileValue (&id, __FILE__, __LINE__, (int64_t) (v), ##__VA_ARGS__, NULL) \ + ;\ +} + +#define _nvprof(e,v,...) \ +{ \ + static void* id = 0; \ + (id != 0) ? \ + _profileEntryValue (id, (int64_t) (v)) \ + : \ + profileValue (&id, (char*) (e), -1, (int64_t) (v), ##__VA_ARGS__, NULL) \ + ; \ +} + +#define _hprof(v,n,...) \ +{ \ + static void* id = 0; \ + (id != 0) ? \ + _histEntryValue (id, (int64_t) (v)) \ + : \ + histValue (&id, __FILE__, __LINE__, (int64_t) (v), (int) (n), ##__VA_ARGS__) \ + ; \ +} + +#define _nhprof(e,v,n,...) \ +{ \ + static void* id = 0; \ + (id != 0) ? \ + _histEntryValue (id, (int64_t) (v)) \ + : \ + histValue (&id, (char*) (e), -1, (int64_t) (v), (int) (n), ##__VA_ARGS__) \ + ; \ +} + +#define _ntprof(e) \ +{ \ + uint64_t v = _tprof_time();\ + (_tprof_before_id != 0) ? \ + _profileEntryValue(_tprof_before_id, v)\ + : 0;\ + static void* id = 0; \ + (id != 0) ? \ + _profileEntryValue (id, (int64_t) 0) \ + : \ + profileValue (&id, (char*)(e), -1, (int64_t) 0, NULL) \ + ;\ + _tprof_before_id = id;\ +} + +#define _tprof_end() \ +{\ + uint64_t v = _tprof_time();\ + if (_tprof_before_id)\ + _profileEntryValue(_tprof_before_id, v);\ + _tprof_before_id = 0;\ +} + +#endif + +#define NUM_EVARS 4 + +enum { + LOCK_IS_FREE = 0, + LOCK_IS_TAKEN = 1 +}; + +extern +#ifdef __cplusplus +"C" +#endif +long _InterlockedCompareExchange ( + long volatile * Destination, + long Exchange, + long Comperand +); + +typedef struct hist hist; + +typedef struct hist { + int nbins; + int64_t* lb; + int64_t* count; +} *hist_t; + +typedef struct entry entry; + +typedef struct entry { + long lock; + char* file; + int line; + int64_t value; + int64_t count; + int64_t sum; + int64_t min; + int64_t max; + void (*func)(void*); + hist* h; + + entry* next; + + // exposed to the clients + void* genptr; + int ivar[NUM_EVARS]; + vprof_align8(int64_t) i64var[NUM_EVARS]; + vprof_align8(double) dvar[NUM_EVARS]; + // + + char pad[128]; // avoid false sharing +} *entry_t; + +#define _VAL ((entry_t)vprofID)->value +#define _COUNT ((entry_t)vprofID)->count +#define _SUM ((entry_t)vprofID)->sum +#define _MIN ((entry_t)vprofID)->min +#define _MAX ((entry_t)vprofID)->max + +#define _GENPTR ((entry_t)vprofID)->genptr + +#define _IVAR0 ((entry_t)vprofID)->ivar[0] +#define _IVAR1 ((entry_t)vprofID)->ivar[1] +#define _IVAR2 ((entry_t)vprofID)->ivar[2] +#define _IVAR3 ((entry_t)vprofID)->ivar[3] + +#define _I64VAR0 ((entry_t)vprofID)->i64var[0] +#define _I64VAR1 ((entry_t)vprofID)->i64var[1] +#define _I64VAR2 ((entry_t)vprofID)->i64var[2] +#define _I64VAR3 ((entry_t)vprofID)->i64var[3] + +#define _DVAR0 ((entry_t)vprofID)->dvar[0] +#define _DVAR1 ((entry_t)vprofID)->dvar[1] +#define _DVAR2 ((entry_t)vprofID)->dvar[2] +#define _DVAR3 ((entry_t)vprofID)->dvar[3] + +#endif /* __VPROF__ */ diff --git a/ape-server/deps/udns-0.0.9/.cvsignore b/ape-server/deps/udns-0.0.9/.cvsignore new file mode 100755 index 0000000..81946db --- /dev/null +++ b/ape-server/deps/udns-0.0.9/.cvsignore @@ -0,0 +1,14 @@ +udns*.tar.gz +udns_codes.c +udns.3.html +*.lo +*_s +libudns.so.* +dnsget +ex-rdns +rblcheck +Makefile +config.h +config.status +config.log +build-stamp diff --git a/ape-server/deps/udns-0.0.9/COPYING.LGPL b/ape-server/deps/udns-0.0.9/COPYING.LGPL new file mode 100755 index 0000000..b1e3f5a --- /dev/null +++ b/ape-server/deps/udns-0.0.9/COPYING.LGPL @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +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 and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/ape-server/deps/udns-0.0.9/Makefile b/ape-server/deps/udns-0.0.9/Makefile new file mode 100644 index 0000000..b0b50d8 --- /dev/null +++ b/ape-server/deps/udns-0.0.9/Makefile @@ -0,0 +1,196 @@ +#! /usr/bin/make -rf +# $Id: Makefile.in,v 1.11 2007/01/15 21:19:08 mjt Exp $ +# libudns Makefile +# +# Copyright (C) 2005 Michael Tokarev +# This file is part of UDNS library, an async DNS stub resolver. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library, in file named COPYING.LGPL; if not, +# write to the Free Software Foundation, Inc., 59 Temple Place, +# Suite 330, Boston, MA 02111-1307 USA + +NAME = udns +VERS = 0.0.9 +SRCS = udns_dn.c udns_dntosp.c udns_parse.c udns_resolver.c udns_init.c \ + udns_misc.c udns_XtoX.c \ + udns_rr_a.c udns_rr_ptr.c udns_rr_mx.c udns_rr_txt.c udns_bl.c \ + udns_rr_srv.c udns_rr_naptr.c udns_codes.c +USRCS = dnsget.c rblcheck.c ex-rdns.c +DEB = debian/copyright debian/changelog debian/control debian/rules +DIST = COPYING.LGPL udns.h udns.3 dnsget.1 rblcheck.1 $(SRCS) $(USRCS) \ + NEWS TODO NOTES Makefile.in configure configure.lib \ + inet_XtoX.c getopt.c + +OBJS = $(SRCS:.c=.o) $(GEN:.c=.o) +LIB = lib$(NAME).a +LIBFL = -L. -l$(NAME) + +SOVER = 0 +SOBJS = $(OBJS:.o=.lo) +SOLIB = lib$(NAME)_s.so +SOLIBV = lib$(NAME).so.$(SOVER) +SOLIBFL= -L. -l$(NAME)_s + +LIBS = $(LIB) $(SOLIBV) + +UTILS = $(USRCS:.c=) +UOBJS = $(USRCS:.c=.o) +SOUTILS = $(USRCS:.c=_s) + +NAMEPFX = $(NAME)-$(VERS) + +CC = gcc +CFLAGS = -Wall -W -O2 -pipe +CDEFS = -DHAVE_CONFIG_H +PICFLAGS = -fPIC +AWK = awk + +all: static + +.SUFFIXES: .c .o .lo + +static: $(LIB) $(UTILS) +staticlib: $(LIB) +$(LIB): $(OBJS) + -rm -f $@ + $(AR) rv $@ $(OBJS) +.c.o: + $(CC) $(CFLAGS) $(CDEFS) -c $< + +shared: $(SOLIBV) $(SOUTILS) +sharedlib: $(SOLIBV) + +$(SOLIBV): $(SOBJS) + $(CC) -shared -Wl,--soname,$(SOLIBV) -o $@ $(SOBJS) +$(SOLIB): $(SOLIBV) + rm -f $@ + ln -s $(SOLIBV) $@ +.c.lo: + $(CC) $(CFLAGS) $(PICFLAGS) $(CDEFS) -o $@ -c $< + +# udns_codes.c is generated from udns.h +udns_codes.c: udns.h + @echo Generating $@ + @set -e; exec >$@.tmp; \ + set T type C class R rcode; \ + echo "/* Automatically generated. */"; \ + echo "#include \"udns.h\""; \ + while [ "$$1" ]; do \ + echo; \ + echo "const struct dns_nameval dns_$${2}tab[] = {"; \ + $(AWK) "/^ DNS_$${1}_[A-Z0-9_]+[ ]*=/ \ + { printf \" {%s,\\\"%s\\\"},\\n\", \$$1, substr(\$$1,7) }" \ + udns.h ; \ + echo " {0,0}};"; \ + echo "const char *dns_$${2}name(enum dns_$${2} code) {"; \ + echo " static char nm[20];"; \ + echo " switch(code) {"; \ + $(AWK) "BEGIN{i=0} \ + /^ DNS_$${1}_[A-Z0-9_]+[ ]*=/ \ + {printf \" case %s: return dns_$${2}tab[%d].name;\\n\",\$$1,i++}\ + " udns.h ; \ + echo " }"; \ + echo " return _dns_format_code(nm,\"$$2\",code);"; \ + echo "}"; \ + shift 2; \ + done + @mv $@.tmp $@ + +udns.3.html: udns.3 + groff -man -Thtml udns.3 > $@.tmp + mv $@.tmp $@ + +dist: $(NAMEPFX).tar.gz +$(NAMEPFX).tar.gz: $(DIST) $(DEB) + mkdir $(NAMEPFX) $(NAMEPFX)/debian + ln $(DIST) $(NAMEPFX) + ln $(DEB) $(NAMEPFX)/debian + tar cvfz $@ $(NAMEPFX) + rm -rf $(NAMEPFX) +subdist: + cp -p $(DIST) $(TARGET)/ + +clean: + rm -f $(OBJS) + rm -f $(SOBJS) + rm -f $(UOBJS) + rm -f build-stamp config.log +distclean: clean + rm -f $(LIBS) $(SOLIB) udns.3.html + rm -f $(UTILS) $(SOUTILS) + rm -f config.status config.h Makefile + + +Makefile: configure configure.lib Makefile.in + ./configure + @echo + @echo Please rerun make >&2 + @exit 1 + +.PHONY: all static staticlib shared sharedlib dist clean distclean subdist \ + depend dep deps + +depend dep deps: $(SRCS) $(USRC) + @echo Generating deps for: + @echo \ $(SRCS) + @echo \ $(USRCS) + @sed '/^# depend/q' Makefile.in > Makefile.tmp + @set -e; \ + for f in $(SRCS) $(USRCS); do \ + echo $${f%.c}.o $${f%.c}.lo: $$f \ + `sed -n 's/^#[ ]*include[ ]*"\(.*\)".*/\1/p' $$f`; \ + done >> Makefile.tmp; \ + for f in $(USRCS:.c=.o); do \ + echo "$${f%.?}: $$f \$$(LIB)"; \ + echo " \$$(CC) \$$(CFLAGS) -o \$$@ $$f \$$(LIBFL)"; \ + echo "$${f%.?}_s: $$f \$$(SOLIB)"; \ + echo " \$$(CC) \$$(CFLAGS) -o \$$@ $$f \$$(SOLIBFL)"; \ + done >> Makefile.tmp ; \ + if cmp Makefile.tmp Makefile.in >/dev/null 2>&1 ; then \ + echo Makefile.in unchanged; rm -f Makefile.tmp; \ + else \ + echo Updating Makfile.in; mv -f Makefile.tmp Makefile.in ; \ + fi + +# depend +udns_dn.o udns_dn.lo: udns_dn.c udns.h +udns_dntosp.o udns_dntosp.lo: udns_dntosp.c udns.h +udns_parse.o udns_parse.lo: udns_parse.c udns.h +udns_resolver.o udns_resolver.lo: udns_resolver.c config.h udns.h +udns_init.o udns_init.lo: udns_init.c config.h udns.h +udns_misc.o udns_misc.lo: udns_misc.c udns.h +udns_XtoX.o udns_XtoX.lo: udns_XtoX.c config.h udns.h inet_XtoX.c +udns_rr_a.o udns_rr_a.lo: udns_rr_a.c udns.h +udns_rr_ptr.o udns_rr_ptr.lo: udns_rr_ptr.c udns.h +udns_rr_mx.o udns_rr_mx.lo: udns_rr_mx.c udns.h +udns_rr_txt.o udns_rr_txt.lo: udns_rr_txt.c udns.h +udns_bl.o udns_bl.lo: udns_bl.c udns.h +udns_rr_srv.o udns_rr_srv.lo: udns_rr_srv.c udns.h +udns_rr_naptr.o udns_rr_naptr.lo: udns_rr_naptr.c udns.h +udns_codes.o udns_codes.lo: udns_codes.c udns.h +dnsget.o dnsget.lo: dnsget.c config.h udns.h getopt.c +rblcheck.o rblcheck.lo: rblcheck.c udns.h getopt.c +ex-rdns.o ex-rdns.lo: ex-rdns.c udns.h +dnsget: dnsget.o $(LIB) + $(CC) $(CFLAGS) -o $@ dnsget.o $(LIBFL) +dnsget_s: dnsget.o $(SOLIB) + $(CC) $(CFLAGS) -o $@ dnsget.o $(SOLIBFL) +rblcheck: rblcheck.o $(LIB) + $(CC) $(CFLAGS) -o $@ rblcheck.o $(LIBFL) +rblcheck_s: rblcheck.o $(SOLIB) + $(CC) $(CFLAGS) -o $@ rblcheck.o $(SOLIBFL) +ex-rdns: ex-rdns.o $(LIB) + $(CC) $(CFLAGS) -o $@ ex-rdns.o $(LIBFL) +ex-rdns_s: ex-rdns.o $(SOLIB) + $(CC) $(CFLAGS) -o $@ ex-rdns.o $(SOLIBFL) diff --git a/ape-server/deps/udns-0.0.9/Makefile.in b/ape-server/deps/udns-0.0.9/Makefile.in new file mode 100755 index 0000000..2519eb4 --- /dev/null +++ b/ape-server/deps/udns-0.0.9/Makefile.in @@ -0,0 +1,196 @@ +#! /usr/bin/make -rf +# $Id: Makefile.in,v 1.11 2007/01/15 21:19:08 mjt Exp $ +# libudns Makefile +# +# Copyright (C) 2005 Michael Tokarev +# This file is part of UDNS library, an async DNS stub resolver. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library, in file named COPYING.LGPL; if not, +# write to the Free Software Foundation, Inc., 59 Temple Place, +# Suite 330, Boston, MA 02111-1307 USA + +NAME = udns +VERS = 0.0.9 +SRCS = udns_dn.c udns_dntosp.c udns_parse.c udns_resolver.c udns_init.c \ + udns_misc.c udns_XtoX.c \ + udns_rr_a.c udns_rr_ptr.c udns_rr_mx.c udns_rr_txt.c udns_bl.c \ + udns_rr_srv.c udns_rr_naptr.c udns_codes.c +USRCS = dnsget.c rblcheck.c ex-rdns.c +DEB = debian/copyright debian/changelog debian/control debian/rules +DIST = COPYING.LGPL udns.h udns.3 dnsget.1 rblcheck.1 $(SRCS) $(USRCS) \ + NEWS TODO NOTES Makefile.in configure configure.lib \ + inet_XtoX.c getopt.c + +OBJS = $(SRCS:.c=.o) $(GEN:.c=.o) +LIB = lib$(NAME).a +LIBFL = -L. -l$(NAME) + +SOVER = 0 +SOBJS = $(OBJS:.o=.lo) +SOLIB = lib$(NAME)_s.so +SOLIBV = lib$(NAME).so.$(SOVER) +SOLIBFL= -L. -l$(NAME)_s + +LIBS = $(LIB) $(SOLIBV) + +UTILS = $(USRCS:.c=) +UOBJS = $(USRCS:.c=.o) +SOUTILS = $(USRCS:.c=_s) + +NAMEPFX = $(NAME)-$(VERS) + +CC = @CC@ +CFLAGS = @CFLAGS@ +CDEFS = @CDEFS@ +PICFLAGS = -fPIC +AWK = awk + +all: static + +.SUFFIXES: .c .o .lo + +static: $(LIB) $(UTILS) +staticlib: $(LIB) +$(LIB): $(OBJS) + -rm -f $@ + $(AR) rv $@ $(OBJS) +.c.o: + $(CC) $(CFLAGS) $(CDEFS) -c $< + +shared: $(SOLIBV) $(SOUTILS) +sharedlib: $(SOLIBV) + +$(SOLIBV): $(SOBJS) + $(CC) -shared -Wl,--soname,$(SOLIBV) -o $@ $(SOBJS) +$(SOLIB): $(SOLIBV) + rm -f $@ + ln -s $(SOLIBV) $@ +.c.lo: + $(CC) $(CFLAGS) $(PICFLAGS) $(CDEFS) -o $@ -c $< + +# udns_codes.c is generated from udns.h +udns_codes.c: udns.h + @echo Generating $@ + @set -e; exec >$@.tmp; \ + set T type C class R rcode; \ + echo "/* Automatically generated. */"; \ + echo "#include \"udns.h\""; \ + while [ "$$1" ]; do \ + echo; \ + echo "const struct dns_nameval dns_$${2}tab[] = {"; \ + $(AWK) "/^ DNS_$${1}_[A-Z0-9_]+[ ]*=/ \ + { printf \" {%s,\\\"%s\\\"},\\n\", \$$1, substr(\$$1,7) }" \ + udns.h ; \ + echo " {0,0}};"; \ + echo "const char *dns_$${2}name(enum dns_$${2} code) {"; \ + echo " static char nm[20];"; \ + echo " switch(code) {"; \ + $(AWK) "BEGIN{i=0} \ + /^ DNS_$${1}_[A-Z0-9_]+[ ]*=/ \ + {printf \" case %s: return dns_$${2}tab[%d].name;\\n\",\$$1,i++}\ + " udns.h ; \ + echo " }"; \ + echo " return _dns_format_code(nm,\"$$2\",code);"; \ + echo "}"; \ + shift 2; \ + done + @mv $@.tmp $@ + +udns.3.html: udns.3 + groff -man -Thtml udns.3 > $@.tmp + mv $@.tmp $@ + +dist: $(NAMEPFX).tar.gz +$(NAMEPFX).tar.gz: $(DIST) $(DEB) + mkdir $(NAMEPFX) $(NAMEPFX)/debian + ln $(DIST) $(NAMEPFX) + ln $(DEB) $(NAMEPFX)/debian + tar cvfz $@ $(NAMEPFX) + rm -rf $(NAMEPFX) +subdist: + cp -p $(DIST) $(TARGET)/ + +clean: + rm -f $(OBJS) + rm -f $(SOBJS) + rm -f $(UOBJS) + rm -f build-stamp config.log +distclean: clean + rm -f $(LIBS) $(SOLIB) udns.3.html + rm -f $(UTILS) $(SOUTILS) + rm -f config.status config.h Makefile + + +Makefile: configure configure.lib Makefile.in + ./configure + @echo + @echo Please rerun make >&2 + @exit 1 + +.PHONY: all static staticlib shared sharedlib dist clean distclean subdist \ + depend dep deps + +depend dep deps: $(SRCS) $(USRC) + @echo Generating deps for: + @echo \ $(SRCS) + @echo \ $(USRCS) + @sed '/^# depend/q' Makefile.in > Makefile.tmp + @set -e; \ + for f in $(SRCS) $(USRCS); do \ + echo $${f%.c}.o $${f%.c}.lo: $$f \ + `sed -n 's/^#[ ]*include[ ]*"\(.*\)".*/\1/p' $$f`; \ + done >> Makefile.tmp; \ + for f in $(USRCS:.c=.o); do \ + echo "$${f%.?}: $$f \$$(LIB)"; \ + echo " \$$(CC) \$$(CFLAGS) -o \$$@ $$f \$$(LIBFL)"; \ + echo "$${f%.?}_s: $$f \$$(SOLIB)"; \ + echo " \$$(CC) \$$(CFLAGS) -o \$$@ $$f \$$(SOLIBFL)"; \ + done >> Makefile.tmp ; \ + if cmp Makefile.tmp Makefile.in >/dev/null 2>&1 ; then \ + echo Makefile.in unchanged; rm -f Makefile.tmp; \ + else \ + echo Updating Makfile.in; mv -f Makefile.tmp Makefile.in ; \ + fi + +# depend +udns_dn.o udns_dn.lo: udns_dn.c udns.h +udns_dntosp.o udns_dntosp.lo: udns_dntosp.c udns.h +udns_parse.o udns_parse.lo: udns_parse.c udns.h +udns_resolver.o udns_resolver.lo: udns_resolver.c config.h udns.h +udns_init.o udns_init.lo: udns_init.c config.h udns.h +udns_misc.o udns_misc.lo: udns_misc.c udns.h +udns_XtoX.o udns_XtoX.lo: udns_XtoX.c config.h udns.h inet_XtoX.c +udns_rr_a.o udns_rr_a.lo: udns_rr_a.c udns.h +udns_rr_ptr.o udns_rr_ptr.lo: udns_rr_ptr.c udns.h +udns_rr_mx.o udns_rr_mx.lo: udns_rr_mx.c udns.h +udns_rr_txt.o udns_rr_txt.lo: udns_rr_txt.c udns.h +udns_bl.o udns_bl.lo: udns_bl.c udns.h +udns_rr_srv.o udns_rr_srv.lo: udns_rr_srv.c udns.h +udns_rr_naptr.o udns_rr_naptr.lo: udns_rr_naptr.c udns.h +udns_codes.o udns_codes.lo: udns_codes.c udns.h +dnsget.o dnsget.lo: dnsget.c config.h udns.h getopt.c +rblcheck.o rblcheck.lo: rblcheck.c udns.h getopt.c +ex-rdns.o ex-rdns.lo: ex-rdns.c udns.h +dnsget: dnsget.o $(LIB) + $(CC) $(CFLAGS) -o $@ dnsget.o $(LIBFL) +dnsget_s: dnsget.o $(SOLIB) + $(CC) $(CFLAGS) -o $@ dnsget.o $(SOLIBFL) +rblcheck: rblcheck.o $(LIB) + $(CC) $(CFLAGS) -o $@ rblcheck.o $(LIBFL) +rblcheck_s: rblcheck.o $(SOLIB) + $(CC) $(CFLAGS) -o $@ rblcheck.o $(SOLIBFL) +ex-rdns: ex-rdns.o $(LIB) + $(CC) $(CFLAGS) -o $@ ex-rdns.o $(LIBFL) +ex-rdns_s: ex-rdns.o $(SOLIB) + $(CC) $(CFLAGS) -o $@ ex-rdns.o $(SOLIBFL) diff --git a/ape-server/deps/udns-0.0.9/NEWS b/ape-server/deps/udns-0.0.9/NEWS new file mode 100755 index 0000000..3234a6b --- /dev/null +++ b/ape-server/deps/udns-0.0.9/NEWS @@ -0,0 +1,85 @@ +$Id: NEWS,v 1.11 2007/01/15 21:19:08 mjt Exp $ + +User-visible changes in udns library. Recent changes on top. + +0.0.9 (16 Jan 2007) + + - incompat: minor API changes in dns_init() &friends. dns_init() + now requires extra `struct dns_ctx *' argument. Not bumped + soversion yet - I only expect one "release" with this change, + 0.1 will have more changes and will increment so version + + - many small bugfixes, here and there + + - more robust FORMERR replies handling - not only such replies are now + recognized, but udns retries queries without EDNS0 extensions if tried + with, but server reported FORMERR + + - portability changes, udns now includes getopt() implementation fo + the systems lacking it (mostly windows), and dns_ntop()&dns_pton(), + which are either just wrappers for system functions or reimplementations. + + - build is now based on autoconf-like configuration + + - NAPTR (RFC3403) RR decoding support + + - new file NOTES which complements TODO somewhat, and includes some + important shortcomings + + - many internal cleanups, including some preparations for better error + recovery, security and robustness (and thus API changes) + + - removed some #defines which are now unused (like DNS_MAXSRCH) + + - changed WIN32 to WINDOWS everywhere in preprocessor tests, + to be able to build it on win64 as well + +0.0.8 (12 Sep 2005) + + - added SRV records (rfc2782) parsing, + thanks to Thadeu Lima de Souza Cascardo for implementation. + + - bugfixes: + o use uninitialized value when no reply, library died with assertion: + assert((status < 0 && result == 0) || (status >= 0 && result != 0)). + o on some OSes, struct sockaddr_in has additional fields, so + memcmp'ing two sockaddresses does not work. + + - rblcheck(.1) + +0.0.7 (20 Apr 2005) + + - dnsget.1 manpage and several enhancements to dnsget. + + - allow nameserver names for -n option of dnsget. + + - API change: all dns_submit*() routines now does not expect + last `now' argument, since requests aren't sent immediately + anymore. + + - API change: different application timer callback mechanism. + Udns now uses single per-context timer instead of per-query. + + - don't assume DNS replies only contain backward DN pointers, + allow forward pointers too. Change parsing API. + + - debianize + +0.0.6 (08 Apr 2005) + + - use double sorted list for requests (sorted by deadline). + This should significantly speed up timeout processing for + large number of requests. + + - changed debugging interface, so it is finally useable + (still not documented). + + - dnsget routine is now Officially Useable, and sometimes + even more useable than `host' from BIND distribution + (and sometimes not - dnsget does not have -C option + and TCP mode) + + - Debian packaging in debian/ -- udns is now maintained as a + native Debian package. + + - alot (and I really mean alot) of code cleanups all over. diff --git a/ape-server/deps/udns-0.0.9/NOTES b/ape-server/deps/udns-0.0.9/NOTES new file mode 100755 index 0000000..6c24682 --- /dev/null +++ b/ape-server/deps/udns-0.0.9/NOTES @@ -0,0 +1,222 @@ +Assorted notes about udns (library). + +UDP-only mode +~~~~~~~~~~~~~ + +First of all, since udns is (currently) UDP-only, there are some +shortcomings. + +It assumes that a reply will fit into a UDP buffer. With adoption of EDNS0, +and general robustness of IP stacks, in most cases it's not an issue. But +in some cases there may be problems: + + - if an RRset is "very large" so it does not fit even in buffer of size + requested by the library (current default is 4096; some servers limits + it further), we will not see the reply, or will only see "damaged" + reply (depending on the server). + + - many DNS servers ignores EDNS0 option requests. In this case, no matter + which buffer size udns library will request, such servers reply is limited + to 512 bytes (standard pre-EDNS0 DNS packet size). (Udns falls back to + non-EDNO0 query if EDNS0-enabled one received FORMERR or NOTIMPL error). + +The problem is that with this, udns currently will not consider replies with +TC (truncation) bit set, and will treat such replies the same way as it +treats SERVFAIL replies, thus trying next server, or temp-failing the query +if no more servers to try. In other words, if the reply is really large, or +if the servers you're using don't support EDNS0, your application will be +unable to resolve a given name. + +Yet it's not common situation - in practice, it's very rare. + +Implementing TCP mode isn't difficult, but it complicates API significantly. +Currently udns uses only single UDP socket (or - maybe in the future - two, +see below), but in case of TCP, it will need to open and close sockets for +TCP connections left and right, and that have to be integrated into an +application's event loop in an easy and efficient way. Plus all the +timeouts - different for connect(), write, and several stages of read. + +IPv6 vs IPv4 usage +~~~~~~~~~~~~~~~~~~ + +This is only relevant for nameservers reachable over IPv6, NOT for IPv6 +queries. I.e., if you've IPv6 addresses in 'nameservers' line in your +/etc/resolv.conf file. Even more: if you have BOTH IPv6 AND IPv4 addresses +there. Or pass them to udns initialization routines. + +Since udns uses a single UDP socket to communicate with all nameservers, +it should support both v4 and v6 communications. Most current platforms +supports this mode - using PF_INET6 socket and V4MAPPED addresses, i.e, +"tunnelling" IPv4 inside IPv6. But not all systems supports this. And +more, it has been said that such mode is deprecated. + +So, list only IPv4 or only IPv6 addresses, but don't mix them, in your +/etc/resolv.conf. + +An alternative is to use two sockets instead of 1 - one for IPv6 and one +for IPv4. For now I'm not sure if it's worth the complexity - again, of +the API, not the library itself (but this will not simplify library either). + +Single socket for all queries +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Using single UDP socket for sending queries to all nameservers has obvious +advantages. First it's, again, trivial, simple to use API. And simple +library too. Also, after sending queries to all nameservers (in case first +didn't reply in time), we will be able to receive late reply from first +nameserver and accept it. + +But this mode has disadvantages too. Most important is that it's much easier +to send fake reply to us, as the UDP port where we expects the reply to come +to is constant during the whole lifetime of an application. More secure +implementations uses random port for every single query. While port number +(16 bits integer) can not hold much randomness, it's still of some help. +Ok, udns is a stub resolver, so it expects sorta friendly environment, but +on LAN it's usually much easier to fire an attack, due to the speed of local +network, where a bad guy can generate alot of packets in a short time. + +Choosing of DNS QueryID +~~~~~~~~~~~~~~~~~~~~~~~ + +Currently, udns uses sequential number for query IDs. Which simplifies +attacks even more (c.f. the previous item about single UDP port), making +them nearly trivial. The library should use random number for query ID. +But there's no portable way to get random numbers, even on various flavors +of Unix. It's possible to use low bits from tv_nsec field returned by +gettimeofday() (current time, nanoseconds), but I wrote the library in +a way to avoid making system calls where possible, because many syscalls +means many context switches and slow processes as a result. Maybe use some +application-supplied callback to get random values will be a better way, +defaulting to gettimeofday() method. + +Note that a single query - even if (re)sent to different nameservers, several +times (due to no reply received in time), uses the same qID assigned when it +was first dispatched. So we have: single UDP socket (fixed port number), +sequential (= trivially predictable) qIDs, and long lifetime of those qIDs. +This all makes (local) attacks against the library really trivial. + +See also comments in udns_resolver.c, udns_newid(). + +And note that at least some other stub resolvers out there (like c-ares +for example) also uses sequential qID. + +Assumptions about RRs returned +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Currently udns processes records in the reply it received sequentially. +This means that order of the records is significant. For example, if +we asked for foo.bar A, but the server returned that foo.bar is a CNAME +(alias) for bar.baz, and bar.baz, in turn, has address 1.2.3.4, when +the CNAME should come first in reply, followed by A. While DNS specs +does not say anything about order of records - it's an rrSET - unordered, - +I think an implementation which returns the records in "wrong" order is +somewhat insane... + +CNAME recursion +~~~~~~~~~~~~~~~ + +Another interesting point is the handling of CNAMEs returned as replies +to non-CNAME queries. If we asked for foo.bar A, but it's a CNAME, udns +expects BOTH the CNAME itself and the target DN to be present in the reply. +In other words, udns DOES NOT RECURSE CNAMES. If we asked for foo.bar A, +but only record in reply was that foo.bar is a CNAME for bar.baz, udns will +return no records to an application (NXDOMAIN). Strictly speaking, udns +should repeat the query asking for bar.baz A, and recurse. But since it's +stub resolver, recursive resolver should recurse for us instead. + +It's not very difficult to implement, however. Probably with some (global?) +flag to en/dis-able the feature. Provided there's some demand for it. + +To clarify: udns handles CNAME recursion in a single reply packet just fine. + +Note also that standard gethostbyname() routine does not recurse in this +situation, too. + +Error reporting +~~~~~~~~~~~~~~~ + +Too many places in the code (various failure paths) sets generic "TEMPFAIL" +error condition. For example, if no nameserver replied to our query, an +application will get generic TEMPFAIL, instead of something like TIMEDOUT. +This probably should be fixed, but most applications don't care about the +exact reasons of failure - 4 common cases are already too much: + - query returned some valid data + - NXDOMAIN + - valid domain but no data of requested type - =NXDOMAIN in most cases + - temporary error - this one sometimes (incorrectly!) treated as NXDOMAIN + by (naive) applications. +DNS isn't yes/no, it's at least 3 variants, temp err being the 3rd important +case! And adding more variations for the temp error case is complicating things +even more - again, from an application writer standpoint. For diagnostics, +such more specific error cases are of good help. + +Planned API changes +~~~~~~~~~~~~~~~~~~~ + +At least one thing I want to change for 0.1 version is a way how queries are +submitted and how replies are handled. + +I want to made dns_query object to be owned by an application. So that instead +of udns library allocating it for the lifetime of query, it will be pre- +allocated by an application. This simplifies and enhances query submitting +interface, and complicates it a bit too, in simplest cases. + +Currently, we have: + +dns_submit_dn(dn, cls, typ, flags, parse, cbck, data) +dns_submit_p(name, cls, typ, flags, parse, cbck, data) +dns_submit_a4(ctx, name, flags, cbck, data) + +and so on -- with many parameters missed for type-specific cases, but generic +cases being too complex for most common usage. + +Instead, with dns_query being owned by an app, we will be able to separately +set up various parts of the query - domain name (various forms), type&class, +parser, flags, callback... and even change them at runtime. And we will also +be able to reuse query structures, instead of allocating/freeing them every +time. So the whole thing will look something like: + + q = dns_alloc_query(); + dns_submit(dns_q_flags(dns_q_a4(q, name, cbck), DNS_F_NOSRCH), data); + +The idea is to have a set of functions accepting struct dns_query* and +returning it (so the calls can be "nested" like the above), to set up +relevant parts of the query - specific type of callback, conversion from +(type-specific) query parameters into a domain name (this is for type- +specific query initializers), and setting various flags and options and +type&class things. + +One example where this is almost essential - if we want to support +per-query set of nameservers (which isn't at all useless: imagine a +high-volume mail server, were we want to direct DNSBL queries to a separate +set of nameservers, and rDNS queries to their own set and so on). Adding +another argument (set of nameservers to use) to EVERY query submitting +routine is.. insane. Especially since in 99% cases it will be set to +default NULL. But with such "nesting" of query initializers, it becomes +trivial. + +Another way to do the same is to manipulate query object right after a +query has been submitted, but before any events processing (during this +time, query object is allocated and initialized, but no actual network +packets were sent - it will happen on the next event processing). But +this way it become impossible to perform syncronous resolver calls, since +those calls hide query objects they use internally. + +Speaking of replies handling - the planned change is to stop using dynamic +memory (malloc) inside the library. That is, instead of allocating a buffer +for a reply dynamically in a parsing routine (or memdup'ing the raw reply +packet if no parsing routine is specified), I want udns to return the packet +buffer it uses internally, and change parsing routines to expect a buffer +for result. When parsing, a routine will return true amount of memory it +will need to place the result, regardless of whenever it has enough room +or not, so that an application can (re)allocate properly sized buffer and +call a parsing routine again. + +Another modification I plan to include is to have an ability to work in +terms of domain names (DNs) as used with on-wire DNS packets, not only +with asciiz representations of them. For this to work, the above two +changes (query submission and result passing) have to be completed first +(esp. the query submission part), so that it will be possible to specify +some additional query flags (for example) to request domain names instead +of the text strings, and to allow easy query submissions with either DNs +or text strings. diff --git a/ape-server/deps/udns-0.0.9/TODO b/ape-server/deps/udns-0.0.9/TODO new file mode 100755 index 0000000..41299bc --- /dev/null +++ b/ape-server/deps/udns-0.0.9/TODO @@ -0,0 +1,69 @@ +$Id: TODO,v 1.13 2007/01/15 21:19:08 mjt Exp $ + +The following is mostly an internal, not user-visible stuff. + +* rearrange an API to make dns_query object owned by application, + so that it'll look like this: + struct dns_query *q; + q = udns_query_alloc(ctx); + udns_query_set(q, options, domain_name, flags, ...); + udns_query_submit(ctx, q); + or + udns_query_resolve(ctx, q); + +* allow NULL callbacks? Or provide separate resolver + context list of queries which are done but wich did not + have callback, and dns_pick() routine to retrieve results + from this query, i.e. allow non-callback usage? The + non-callback usage may be handy sometimes (any *good* + example?), but it will be difficult to provide type-safe + non-callback interface due to various RR-specific types + in use. + +* DNS_OPT_FLAGS should be DNS_OPT_ADDFLAGS and DNS_OPT_SETFLAGS. + Currently one can't add a single flag bit but preserve + existing bits... at least not without retrieving all current + flags before, which isn't that bad anyway. + +* dns_set_opts() may process flags too (such as aaonly etc) + +* a way to disable $NSCACHEIP et al processing? + (with now separate dns_init() and dns_reset(), it has finer + control, but still no way to init from system files but ignore + environment variables and the like) + +* initialize/open the context automatically, and be more + liberal about initialization in general? + +* dns_init(ctx, do_open) - make the parameter opposite, aka + dns_init(ctx, skip_open) ? + +* allow TCP queue? + +* And oh, qID should really be random. Or... not. + See notes in udns_resolver.c, dns_newid(). + +* more accurate error reporting. Currently, udns always returns TEMPFAIL, + but don't specify why it happened (ENOMEM, timeout, etc). + +* check the error value returned by recvfrom() and + sendto() and determine which errors to ignore. + +* maybe merge dns_timeouts() and dns_ioevent(), to have + only one entry point for everything? For traditional + select-loop-based eventloop it may be easier, but for + callback-driven event loops the two should be separate. + Provide an option, or a single dns_events() entry point + for select-loop approach, or just call dns_ioevent() + from within dns_timeouts() (probably after renaming + it to be dns_events()) ? + +* implement /etc/hosts lookup too, ala [c-]ares?? + +* sortlist support? + +* windows port? Oh no please!.. At least, I can't do it myself + because of the lack of platform. + Ok ok, the Windows port is in progress. Christian Prahauser + from cosy.sbg.ac.at is helping with that. + Other folks done some more work in this area. diff --git a/ape-server/deps/udns-0.0.9/config.h b/ape-server/deps/udns-0.0.9/config.h new file mode 100755 index 0000000..95b802d --- /dev/null +++ b/ape-server/deps/udns-0.0.9/config.h @@ -0,0 +1,6 @@ +/* automatically generated by configure. */ + +#define HAVE_GETOPT 1 +#define HAVE_INET_PTON_NTOP 1 +#define HAVE_IPv6 1 +#define HAVE_POLL 1 diff --git a/ape-server/deps/udns-0.0.9/config.status b/ape-server/deps/udns-0.0.9/config.status new file mode 100644 index 0000000..51a14ec --- /dev/null +++ b/ape-server/deps/udns-0.0.9/config.status @@ -0,0 +1,3 @@ +# automatically generated by configure to hold command-line options + +# (no options encountered) diff --git a/ape-server/deps/udns-0.0.9/configure b/ape-server/deps/udns-0.0.9/configure new file mode 100755 index 0000000..1cbac6b --- /dev/null +++ b/ape-server/deps/udns-0.0.9/configure @@ -0,0 +1,167 @@ +#! /bin/sh +# $Id: configure,v 1.4 2007/01/07 23:19:40 mjt Exp $ +# autoconf-style configuration script +# + +set -e + +name=udns + +if [ -f udns.h -a -f udns_resolver.c ] ; then : +else + echo "configure: error: sources not found at `pwd`" >&2 + exit 1 +fi + +options="ipv6" + +for opt in $options; do + eval enable_$opt= +done + +if [ -f config.status ]; then + . ./config.status +fi + +enable() { + opt=`echo "$1" | sed 's/^--[^-]*-//'` + case "$opt" in + ipv6|stats|master_dump|zlib|dso) ;; + master-dump) opt=master_dump ;; + *) echo "configure: unrecognized option \`$1'" >&2; exit 1;; + esac + eval enable_$opt=$2 +} + +while [ $# -gt 0 ]; do + case "$1" in + --disable-*|--without-*|--no-*) enable "$1" n;; + --enable-*|--with-*) enable "$1" y;; + --help | --hel | --he | --h | -help | -hel | -he | -h ) + cat <&2; exit 1 ;; + esac + shift +done + +. ./configure.lib + +ac_msg "configure" +ac_result "$name package" + +ac_prog_c_compiler_v +ac_prog_ranlib_v + +ac_ign ac_yesno "for getopt()" ac_have GETOPT ac_link < +extern int optind; +extern char *optarg; +extern int getopt(int, char **, char *); +int main(int argc, char **argv) { + getopt(argc, argv, "abc"); + return optarg ? optind : 0; +} +EOF + +ac_ign \ + ac_yesno "for inet_pton() && inet_ntop()" \ + ac_have INET_PTON_NTOP \ + ac_link < +#include +#include +int main() { + char buf[64]; + long x = 0; + inet_pton(AF_INET, &x, buf); + return inet_ntop(AF_INET, &x, buf, sizeof(buf)); +} +EOF + +if ac_yesno "for socklen_t" ac_compile < +#include +int foo() { socklen_t len; len = 0; return len; } +EOF +then : +else + ac_define socklen_t int +fi + +if ac_library_find_v 'socket and connect' "" "-lsocket -lnsl" < +#include +#include +int main() { + struct sockaddr_in6 sa; + sa.sin6_family = AF_INET6; + return 0; +} +EOF +then : +elif [ "$enable_ipv6" ]; then + ac_fatal "IPv6 is requested but not available" +fi +fi # !disable_ipv6? + +if ac_yesno "for poll()" ac_have POLL ac_link < +#include +int main() { + struct pollfd pfd[2]; + return poll(pfd, 2, 10); +} +EOF +then : +else + ac_ign ac_yesno "for sys/select.h" ac_have SYS_SELECT_H ac_cpp < +#include +EOF +fi + +ac_config_h +ac_output Makefile +ac_msg "creating config.status" +rm -f config.status +{ +echo "# automatically generated by configure to hold command-line options" +echo +found= +for opt in $options; do + eval val=\$enable_$opt + if [ -n "$val" ]; then + echo enable_$opt=$val + found=y + fi +done +if [ ! "$found" ]; then + echo "# (no options encountered)" +fi +} > config.status +ac_result ok + +ac_result "all done." +exit 0 diff --git a/ape-server/deps/udns-0.0.9/configure.lib b/ape-server/deps/udns-0.0.9/configure.lib new file mode 100755 index 0000000..541177a --- /dev/null +++ b/ape-server/deps/udns-0.0.9/configure.lib @@ -0,0 +1,268 @@ +# configure.lib +# a library of shell routines for simple autoconf system +# + +set -e +ac_substitutes= +rm -f conftest* config.log +exec 5>config.log +cat <&5 +This file contains any messages produced by compilers etc while +running configure, to aid debugging if configure script makes a mistake. + +EOF + +case `echo "a\c"` in + *c*) ac_en=-n ac_ec= ;; + *) ac_en= ac_ec='\c' ;; +esac + +##### Messages +ac_msg() { + echo $ac_en "$*... $ac_ec" + echo ">>> $*" >&5 +} +ac_checking() { + echo $ac_en "checking $*... $ac_ec" + echo ">>> checking $*" >&5 +} +ac_result() { + echo "$1" + echo "=== $1" >&5 +} +ac_fatal() { + echo "configure: fatal: $*" >&2 + echo "=== FATAL: $*" >&5 + exit 1 +} +ac_warning() { + echo "configure: warning: $*" >&2 + echo "=== WARNING: $*" >&5 +} +ac_ign() { + "$@" || : +} + +# ac_run command... +# captures output in conftest.out +ac_run() { + # apparently UnixWare (for one) /bin/sh optimizes the following "if" + # "away", by checking if there's such a command BEFORE redirecting + # output. So error message (like "gcc: command not found") goes + # to stderr instead of to conftest.out, and `cat conftest.out' below + # fails. + if "$@" >conftest.out 2>&1; then + return 0 + else + echo "==== Command invocation failed. Command line was:" >&5 + echo "$*" >&5 + echo "==== compiler input was:" >&5 + cat conftest.c >&5 + echo "==== output was:" >&5 + cat conftest.out >&5 + echo "====" >&5 + return 1 + fi +} + +# common case for ac_verbose: yes/no result +ac_yesno() { + ac_checking "$1" + shift + if "$@"; then + ac_result yes + return 0 + else + ac_result no + return 1 + fi +} + +ac_subst() { + ac_substitutes="$ac_substitutes $*" +} + +ac_define() { + CDEFS="$CDEFS -D$1=${2:-1}" +} + +ac_have() { + ac_what=$1; shift + if "$@"; then + ac_define HAVE_$ac_what + eval ac_have_$ac_what=yes + return 0 + else + eval ac_have_$ac_what=no + return 1 + fi +} + +##### Compiling, linking + +# run a compiler +ac_run_compiler() { + rm -f conftest*; cat >conftest.c + ac_run $CC $CFLAGS $CDEFS "$@" conftest.c +} + +ac_compile() { + ac_run_compiler -c +} + +ac_link() { + ac_run_compiler -o conftest $LIBS "$@" +} + +ac_cpp() { + ac_run_compiler -E "$@" +} + +### check for C compiler. Set $CC, $CFLAGS etc +ac_prog_c_compiler_v() { + ac_checking "for C compiler" + rm -f conftest* + echo 'int main(int argc, char **argv) { return 0; }' >conftest.c + + if [ -n "$CC" ]; then + if ac_run $CC -o conftest conftest.c && ac_run ./conftest; then + ac_result "\$CC ($CC)" + else + ac_result no + ac_fatal "\$CC ($CC) is not a working compiler" + fi + else + for cc in gcc cc ; do + if ac_run $cc -o conftest conftest.c && ac_run ./conftest; then + ac_result "$cc" + CC=$cc + break + fi + done + if [ -z "$CC" ]; then + ac_result no + ac_fatal "no working C compiler found in \$PATH. please set \$CC variable" + fi + fi + if [ -z "$CFLAGS" ]; then + if ac_yesno "whenever C compiler ($CC) is GNU CC" \ + ac_grep_cpp yEs_mAsTeR <conftest.c + for lib in "$@"; do + if ac_run $CC $CFLAGS $LDFLAGS conftest.c -o conftest $LIBS $lib; then + found=y + break + fi + done + if [ ! "$found" ]; then + ac_result "not found" + return 1 + fi + if [ -z "$lib" ]; then + ac_result "ok (none needed)" + else + ac_result "ok ($lib)" + LIBS="$LIBS $lib" + fi +} + +ac_compile_run() { + ac_link "$@" && ac_run ./conftest +} + +ac_grep_cpp() { + pattern="$1"; shift + ac_cpp "$@" && grep "$pattern" conftest.out >/dev/null +} + +ac_output() { + for var in $ac_substitutes; do + eval echo "\"s|@$var@|\$$var|\"" + done >conftest.sed + for file in "$@"; do + ac_msg "creating $file" + if [ -f $file.in ]; then + sed -f conftest.sed $file.in > $file.tmp + mv -f $file.tmp $file + ac_result ok + else + ac_result failed + ac_fatal "$file.in not found" + fi + done + rm -f conftest* +} + +ac_config_h() { + h=${1:-config.h} + ac_msg "creating $h" + rm -f $1.tmp + echo "/* automatically generated by configure. */" > $h.tmp + echo "$CDEFS" | tr ' ' ' +' | sed -e 's/^-D/#define /' -e 's/=/ /' >> $h.tmp + if [ -f $h ] && cmp -s $h.tmp $h ; then + rm -f $h.tmp + ac_result unchanged + else + mv -f $h.tmp $h + ac_result ok + fi + CDEFS=-DHAVE_CONFIG_H +} diff --git a/ape-server/deps/udns-0.0.9/dnsget b/ape-server/deps/udns-0.0.9/dnsget new file mode 100755 index 0000000000000000000000000000000000000000..ca2c8f6edae23cc8f6fc5e91161993dd60c39365 GIT binary patch literal 45553 zcmeIbdtemR**`v;EU;j70z{1(b=9DuLQDYBg@EL;T$Df{1W~~dav>=piP_lzDjL{b z$aGvxRn)d#pjvCIUrW6psAvK-0Y#07N`Y#sXm?H8#!Dey+WbDBb7qpw0=|7OzyCTg zIrE(Joaa2}InQ~{b1pM`r+em9o6V+JSBw&?5b84Qj&TUL+#^%QDNbdOlA>IooTm&# z6w}7l0|$3Io_P_?8D(}ivPj%`TnHNP1-lxqf5P8W5^9;jH+6lN>cc!mj^SdXW+w`b?Y1kO>+?^}G zo4La^d<4=`aVO)Z49>)T0q!{5kr(A~32sYfPBZaBgy-Tu$Am9J$TCUyM`qk*X2`&S zo4oN#kpac0B;h^}H+dh7do=F<$;G;!eu|8vqT^&ANJfY zk6R)^s=S`D9uP8dVH7-)#LbK8Sy6BVs5p_H8WBPKPjOop;Y}8RkZ@TPd}UPr>!RR! zQTYBS__Zkbtf>5DQShx%_-RovX`X{Oc&VFseLHRQe@R>1q^wa}<1E zRQ?a6;MgeqpGBp=5d~L8!5LBc?}`*$yV=M6 zU)fDKUilvI?uhhUz)cbGKL9Jk`tmD zLa(Q^MDbJ?FDuoQva(9Ar(7vrS)mDx8n0HYc(mH8;^j3;b&Xb01yrrJwz#}jsi-Q| z3ahm08i`P|N&&V<@sw7U7HcxKMysv@3q>AJDT*&Es;E?!F4roSmokf1y-X=Cs;sOQ zB~+FYv$VFhs=5$SMOp>2)|M8PC`(IGhNje1S5_ihNwrr~mX|IsMtRF+ZXlFa)d9J< zuB^6tIr%KAT}oV#WA^fDk5*NLQp;A0;>v1IDe|CvFvW^_N~=n= zYNf2WN@Iz&pi*56<|sTfiQs7ivciv)RzG;q|~bc?7+~E0WkDm z18)%WbTb62-!r5k2i_nkpq(M;I2dAph+_z28_y5}N&>^du*D2vu#*_VfIAt+VQgav z&Qch{SdU@|^8w80e-j#9)@g5Ch|Mh8Q$vGQ^;f#}EU+Jcbwm z7cfLeS;!EB*dWOm9cML}; zN&`a-I(IR|pxVd~gJ^;NX}+)19)iS_o7U`ldcC3;_kQ2g(-YXQ4G7(g*w9FbGjyN= zJl?c-SI?E5kZ{k4F8pPlo)K(;az2LJk*#M0TcVt4;Wh!YMatRC;U)nqD7l-pOmgf5*5Z*+1lYplaev)vbfM*ikLbzVQd4#tSt`RUbsJw~rVgXZ=%J&e? z6EHQZyp8Y_0WT(ekZ_uSO9;0Ub_%$haENfcfGY`i5mp3TL%5r8*FRZ*YG672m2kU& zsfpzd!fgVkMwZ7DZW3@k;Y7k)1nej5B)mz$4TS0Lh8qQZ7vVI*^#X1rd^O=3!0CUz zS^u-|Xjk66x#dk?+EG*dnKQpU?{2#z)B}XC8~a_RzSmE(!+DaM%&JZy`Dl8$!Z zPjh!na6ymjkmthSR@6}mX4xJiMo&V3$r9alsP-={14zN<+ikyT(XL>f5_|={O7tgR zr(<(Ga05-+Ft(F|hrzA(WXAQh?cEj4&-MBT^YisxzRoyTo9c^0&4M+yFf|5bH@Bm@ z>ek_UPO@FyI?(rVZ1czVrVyT+2Jck2uHE##-J#o)L%Y#7!R#1UtGe!w$SJbPJlQeo z*0X#qu}vZ5R*|py_@7opYYrjU_T5l0w3Jljrby@@#$boKC1X31}j;rgh zg;GQ!`r2cg+re)ywR5cUAd^Ypm28IWCyV?$+RwK;?xvdyA>!v@z4}#bpsyX(u?I{p zJ6;ZImzwr0VRIfD$5`ZO1?>j79w@`Hc{fB!S%qNVg6??68SXe1-)6`}{quFE=&QfiTQ@&M ze!SiK`}(4<15GRQ^n~Ql%o~C4?lPv?$!RJ^I(?ThO|ZSD5VpCXOJA5APqtPbL$&55 zCxCjKs4nZ%%a)yWoND)V+v-O72F#tVeKWvK`&4=a%om`StVj=d9^W!7yvwVmDmm;tJHy7)V}!`7K1(^cV%N`u~cd z*4JbckUr4YV^cRgizwzm1_gp)5c=Eocn+}b2?m1#+tOSZFi_kkN2IdPQGfN1RR53B)NE1YpE7WOBlHv00bK6V1xM=L zwbXxNv!ExV3R;AM^oPlMnEj-z(KlL@6V3p{@?}mb9+nb1EQFx?&x<6hpr(=h4+Pop zH=x?++d&BpQ1AW(qA@lS5G3`SjZgb_+H?^Ueha9wUZIH#fOjTa4FvZ{or;5A*@a+M ze*+WSv}QNk{08>JVA1!xdijJrw5g^n6bEE+$9z*3JJp7tqowsTH$jTwLKgSD=)b01 zW%IH9!n7M@jrA*S%1ea3zcVxaNW}Lc5<~STAU3?Zj~?3{iX}AblRjaGzJ_*(zwg5f zMWbwZkpwrgnGBI^*w;c*dVpMkwqO^ekzENbD-gu) zehO(p_il<&vJV7doT2KWsS;_!tlnDxfaLeW4L?Eh;WeaRC-ktcuXlzj(SgFxF-_|K zb|;uhZwl@hh5r2gS5FxMYE1euV+X_L&;UKqg$LhXO#cJ26wMB74l~HCg!W8fmG01t zjYL(SdSxSt>F%RJ@ep($ZFSRuQ_OD6;erZ4<{S)Vp|H>F2EYABFJ*mK=m?;|VZG&! zb!>C1#C2Iv&0A1Nqj4t3l5JGGz;`*U*7Ahv}g0PIhiykuW1zva*T36CD28yvR zJd`o$zpd^AEI+-c)qRi;t?nZ-XlGD{mCJ+<~@!w-TgjUWDct( zUU5-04k(m}4J4UQhIk4V95faNo~BTcf%*?`!5&&VSN4Gi5MhYS?BatVTuUQ})@Rrr z5cqnF>YoN2V`?0M4}IAGWx)ZGWEW)^=7F(=1p*t+U{ zeOz+rtuLWXJu7eY3GQstkh%n*-ZcYt4?lx(TG%`Hfk=fr6J+$(j$Mtc|H!`P4kaB! zre(||`x9y`iia+xQcP$f?_WI!basX2e}P6_D0|X0P?G~d8#FRp1TN5T4dG?5 zKb)IpN!V0A{R7B2G#n(zO#3%h_Mfuj2x$^A{Ci^kwJREH84KxxR`iwNzpmlB{GDoSAj>c1!852^ zf#HrbgE)OA#u2I58(32hX!%|xrAz9vj743pMMvt+FKbk{#zJr?G+-5aEfA5o;!`1b zq=-?5>^OxeGQ@O7G6iq58{Q`5=lgEZOS8d%y8bpOGW9Dcar0?b35Q)vdPRJe$o`a6~WYtb0S<8WYTkvANtwba$us72|v{ z^T_QC9KPdBi_44IXyo!%P{wE-x`!tBbYfzBcXz5Aegh6r{4E9(1=S#BOGX>^; zb;AJge*)1FAR6v~I%5J8%o^x|(J1It*QEki-Fl<_hq~}fxnPWa*Pi^WCF9Tj#^Sqt zO#@_|)OB}*c6v|8nMOe;rtDViD#ZF);kPT=XZpWh9)vmqq2qMeu@0NB#pe62M{9$0 z^gq7a{iIF%oU;d%^E#`zmSq6>Jy}0@S8!1$07v8Mz1axa81Mz$1$AC?lzKq3K(Cf7!w~MAGJA1Wy7kDR^r=QCOglLZu%hJrBLfZV7<(2h9+tXf0o zPAQ#j{Z!%Q&%xa-bQr|=CGBav8h8#-Xrq2!1B!-pd+qg<8KR<#t%}x=)TJZ^e?fE> z+8MOl+A>6fqgb#iI71lGTz}}t(2qbxba=aErQSsgoX~h*Ni?J2Na!F1{0tpSluN5O z?B9`Z78IHCeVx9?qAqk_d+<66@gyC{5-a4KZB`;XMphygozfOu6oO#h>Q|2?VbNB> z>8R-x+^42n5Te2RlKAnk8Hq+74Ss@=#{o9p$m4QDoRJsL^$B>c#*_pUb{K^XsD#A^ z8}`sYLMIVXo5cZSJ_YxG#X-&gh64{cpi=$ie0MGI#RL8+&K+pqhHW*TtO#B94H z>dcpzE13Q&vbu7G(yW^U4^`bdG?+<)VQYgnc+L+eagL7p1N=MA&&&jMtsU`FUsfE2 z%B3H+=v^ts-D7v06F9tW3%Jk*Y~u(`Zw}+pj=u;_LVtieX=Z;joCjJ$k77XIvQxrH zVWdI5&~+(vbFlgLEU8CpIrm?5#I*=^s*aN+2x9~0_PxTOai(>8wVx~|f`jlE@Y;Lr z=SjxV6GpgzS?K8~D4N6mW8qCE<|in^V($YyTe&chT25hVf z6ifjGu7kMm0vFDbx;2X(5*;&(9THleMK>M&jicT#E%NVuiYx}QKsUlQsQwA|rK!RlG+ij@{mE05|R4SD6FRz@;=f9NH)N8kVo z!8nGew%(_Q%qR8_;Sph-_i40+uUo-78b>|0Fq-swc&gZRIV{$gCXtTne++*o1`eVg zs$b57kKozYJ;3|C5hUfnUhi*%8xP`%<|w!^gk-B;Cf{;SBQ_1txBd)9X<%OkWg|fJ ziGfbi4?K++bNNs8zYZW6AYD*%n69H(-ETjg^|NXf9tmf-(Qv~cI@WwkJnal4Kqg%~ z)j&QHeFE1e_94CVjg8E?4LA2{r&;A~_$}FyBA@*UbtvA*62)Z_!nOxkACJd1yV>Sq zGO`VYnEg}V36#)$nhic&fYk7JC_$3` z4tf|-bVTcinvj$A7mLQmbO#omzY&dhUlZnk7c~PGa#1C`Qx8&0^o`Vz4prJ0{gB=r zdH@qGOF<0LO5VSO*+86se#-qey(K^W@S4t1=y?#{^xm~vY9O3LP694P1wyMo2BkaV zgFDy`9jBEw>fd55=_=B{UFzE9pbrJqcinl^J8aEvQq&I$sfu|62xXAa0TAk!YIZ}p ztFoqZA@Uik9fmh9(1R&2dW7LlVCO<>rKm#QQ!R;x{>+9<1nB}R>ODlMFAgm8b;qdw za^%L-5Y^wpA?C#WTAc|cu01g9Pq0_9gheUWloUP0mY(>zY8;FQOR4CLt z8S`~34eK9Z$oC!Uybw||F%|+Nl!F8lJ{xoS&^U4~TNG;??u1x(B9>VLO;9V<{~8)U zxUmb*LAaH-L8V{8vmRu>4F4YexeRszexKbXGpv1|eOw};$2n+6?8(znj*!xxkC1s6 za-a<%VMVeF#A_f~y+tO#ubW~{z>xB*EHv47XNxtEJI}+Kh**rcb10W3xE68epqFwH zK>sm`LLt`D$XwQK0m}2ayX;Ua|JUHm_jg;L;S@EYrQgW9vVR$(UhHL%0~`E7oU4JF z+?@z3gnFcr=HsBbW;c6<>`T7Y@#m@Q(zt#*)uFz4a|}x8=ewKwMd!V&`3RVs`14ps5Pe+8gN?fp=Qo^Aw9Dcysq<+ZXq@9I&lZYSlhzOmC9?9!y^Dc8CKYTTwBd??CxDVgqLtT#hsl7UP1Ru6>lE+4Ity`}XZS{&Dl?vB9jgV8T7N{UhIL7#Pe< zYrvehJLO)Mnk;x@Psa$~&g&cUY%NpRv_qRdx7U9= zxb6}}`ogw`yjTtyf7C8%QJ9L;KfZ4VuXqzYbi9E-5h&h2$u<5mA@`{BOektTyd|k2 z(@Z`&=s{HuL#bnIn7FH3hXgY}%$Ts^3`ApD`zX#O3AM@mC}UCGNZ&3ykX-Mt_&{o4 zQ!v{$$%Y~Moc$NIad*MDE2iU5&{L>H$9pH}8{2!4!&n^`yE@*;#gH6};otvH2xww% zoHo?;jp`c?y>GFlay`D8tOavosb83!2tQ0{U<7|{|1|l_0)OhdKY9CX)_t$Q7h zEw+Uq^wLN?9{aegIpn~JD>QmTCMXZz)-Srj{r7Dp#4G)K3R)ZH``E{9c080df9`0K zZ9WGIIou+Jrmwd(r9gGl8s>2Oz_qBW?o$1W@b$=K$~SmRjO%!9rep*tC>mHR4uLKG z3sgQ33tZRRbr-hS6s#az?vCrYkYaOvT2!?ApKZGU47GH~&frAE1$jG*ta_Y*2dazqyHjZB{3Cv^F4^>AW(oH+Sjs&m`NwVOvnjR-2icv#>kBi zpf!f}aIeLG2uv7@5;(2@9UnQ_h_|(37sR#Nq57)9r2Z9H?6_Fy(W1_}+cFk5s{Utz z+z^ZYihiZRXC z8VbIwn_T~`YlSPAP_J%ve~CJv0y%kYtfIvDsxFBbXG@H)m?%2Qih3ZIH44e-JHzz& zWC(|Ej60pX5UuPm)9?lI~-W!jzqUfvog;s^w@W#|6*5@ONx7ipN%!_l)PtwK)?WgI} z?7fJnITzlx1Iw72sN)nVG%4c1C;PcE2st|1^<#bfi}Ha9&HLWS76tKqSo(fU9pO>L zxw4bAAuPU~tH3*h_A$mHFe=ZGn)-oau#kfxJ2#OhTv>$#Jyz%bL!OXDaD`pWWFRTm z7kYlYo}E;?Slx=W>&1cqlsam$CqG%;It{YCK34a{=^i-|IuXm+nk}WAEu`G9!jMc{ z7wj@6^kXyuLi+*2_T)b!2>t!-lbyE^{$>)E!;F}T?s(P52|1=kCVT5&0vKD65S(t) z3qsHlb?cWn*XRF=Sc?+GkvNp(?xK^>k>SdXuk&DU0LzoN)4|lrU`yA0b`A8R`7_7h zE?@U)b@^y-TPr+P+NraHv*C1rq;~u9mL|~fzT*4WH7l+NW~2k7vG&qn&RFbshicso z!|CrNCELEjBwbEZA3=qHwPIT%hSF_RKIsAAxVqSN=vXTT3VHfe_}38V>O>CH@Dn4) zXR)0TaupUYBAT*M{97})#Ev9Mtxqsr+`W~Dpc)c15)8x?)}CFD7Zy<~Nq zk>&91ObOmC&VS&vLT)=;4xyJw&cg78{VUi@AfvltaYWCGN2AFN_}j6lfR#;5$7v6Z z7^-A0%rG+rm}wCfS{y;okcl;Rb-f2M@KwYXSiV#-{Q*uXvE>D=;>I)gSs$Qz=3W^_ zj#F-wJ;3uKK*DgIKUjZ}%!}MX`&s$}T-T@BUe9wZ6SGisH^hl*8Cgka^#rkS2v(ov z*(i()!_X=ic8yFYp}T-PalK%|Uyo;s>Bb}u+Mjc3`x-eOpa%W~z_>OcuvZQ2$D<_^ z?4v>+LM#-CyIr_#LNa58b-kn7SWk8*3hul`B%gc4SkDT%vJ&-clhk!Q$*5-~hIx3R zkqBAI;5%;OdLgaAk4=8DAeEV5GlVQ$a}xDTc`C4O7>DA#L~%s01D+%<-|kVC-eK_x zHW3l#XbyV;e9%9`sQ((~+Ome*Hl2k7{8m)I`6pO{@|{5&yVlIYebKm=n8k{U7_Ybj z?v87XK@}A}%Wjp>JHLXxlPnBBrr5MIjrvaTvDZBBB>3p~3<8d;vWYX@j$Eu`&~p4s zT8^$BSdJIo;1CHmh5d`7*M4$(t4w;+PeQNs2k4>GH6D~C>y!xoo-*nE1Qm_u@63Mm zQuxBbUj1E~<`x>~tZ~MzX*$k12}NDGM9O0c7xaEfC0s|5@W?58p)p(+ppk8F&Au9` z6e<8cp1Zt9UZ|IefrTuZvAkLY)|uGS=-z0$K!f1a8&|A5R?YmRnl!#yTn~ z`FVK{&C;qXa6rnt69XeF4F2@4?d-{+S71iadn3k@=f$~x<9@0nnsgk`hi8t18KR}* zyfM*OM^0IFoV6hrIPb%Q7NSE!C_VcjrKiN171ldZX?ti0j>t zxk|1C@)Eu0g}MGM{Z+EoO{pZReqvLwaN_@{Pmizt1Z0h=cH=rHC<3oJ7G^dEYJY8t zG1so=*^PX=9%I}e0(Zf6NAcIy<~_Yb6LOntceQC1u4_~7{F*h2(dXE8n?Bv1-lPwS zv}cQswq)BC{iBxb0gA6xZOI-u(wLginmriI;GAzZ*5>t{SO{pyAJmZTkfLaravB>r z?1F_g{c3$@$Ac_av~qYBM9pS$n_Yu%v;wwFPAXM8tF?DO>uvM>G2`0!i4By3=L$jR zRZ2_<#~UABOMV*lE&*#RASF1T6Ahx*hK(#|YnGTf@YF$;b7Ho`GZqJ(US_gVmvSsC z(I%*Q)s77EY0YqZgnvQ5BP(7%$O%c~_kF1m_S*Efyuv?N z6i;XBelTGS#8!`=EuX%B`%wtS_bRQDq6QwpUzbZ8hZQDYbAs>NLEbNLMs$_Fvn+jo z;PA@0vh>3vxRO42mwo~K6demre4$M8W4}lLA~r4%0>QxHU`7D66l3Vh4<;rBIuTdv znD}TH!n#XCL;rw-nd}6u%2`0_c%E{2m4l9W^HkE)&*^w5^q1YenCu)KKVe%|#>nAg z5*j6p=PkaKsQZ7dT@761Is;378;Y`1e1D0*=`r8BXeqHSEUlBlI&-3cZHM zMQjuPVyjzS&vh>K#o=YXZ-;0(_=}CPpCf8nQxeXL#v8GmufUXN#TnTSw3CsI8BBt) zM&yTL$Op0dVCwCEkuy6Uo`jK|z}ZlCA{49RSt+OOLT=fRri5DsX|RHqBjoaij1T={ z7X@B~1YvVJ)|l}=5r4K7A1*Vy4E865e@`zbLd-&hD%yo&J_XK=Y>E!hWy41Jt9mQ? z*SmN>6Q0n-^?M-Uy7R-7JJzL){A5gk^z3ApNKn`Q0`at?JTGKi=g?b%c86RrH~?Ip zTON2UJ1)k%H<)u;mT=T*f|21%6l`QCj~*?Sc`1P z&Jex3A%9Y9wiCrEt=XdMwr1150>S|f24Vu#+x?ywb{e4Rc}Y3;&DS{J!!#}q{s0x{ zUXVBz3%z1RjeCUyfEPaY0%DK!i&)A+KM0Y^@?oJ6%~LQV+Xl2U4Z`L!M5qAgOW{9U z6zdaN#9yogr8o6=3?NS{DfG)`tEb6^m*;R_7J5_Mt2e=W_n^e>beQ!I^scX-((h#n z9lvHfZ%4;P=vZ(PU5MQ&b>S2^qFa6h7EU~WFYHr>;dM|B17aYife;ft1UZVb`~8W~ z)Oa2?Zvua1&2PiURsH-@iSBLFzesPwLVpVMNA|(%fnMfYbuOB|OkZ`baX&M`68oBy zTs!Mh)EDO@vdenkIVlp(W)g43LADz|KH_>;U0cUGi(#KiK`9KCp&ey$8;Y(%Xc20t z??S!?<^z@;BaK2wZmg7#oh=HuejhzK?AIY0HEHN15kw%D$VQ^f_B{wPeIH*y)-iEFX)^t~JizJPla%sq%HFegiFL%f2URQpAX z`}3X_cSnzOY4G~b!6Cvt-w1Zn_je>Ta_n-qCAmb)tLykuq~UHW^Q}yb@qU6A^In{C zZkg^r#Pyg=IB^H{Y)mqaVAX_^ylki8J_@?XA)|&kw1{Gr) zn===U_U^k^dwO1DRm8v@z7GWhCCWQoM-L{%fm3N!jRa3C`wuF&-; zK*|jMKu>7T4uIk-QQ>JQP8y;w7QNt%ZOA!ys z&^%3~%}42mAvEea{%sX_Y!KHM+GT8itUh08*bF#za0Br-hUe-p?9Jx-!uu!VUTSG8 zzK+3+Fnjy8^9&!=kCkO{rklf#(X+o*BoVRIM_na?Z+EWlej z(A`a%%*-CM9s)87zTZo8AD`YPoEOtyG3_-iwF9#_8f#dg4ag|0(A_K^hZ#XCbP;O9 z7n8Lmdh7PdXrr%w)ml#plCxkv<)ZsS;;)pERnb0awfd;&yjpz}hJ2z3c!jP3A>Ahi zrh|Ns9IJlvRr0b%*&WJ(Kux*NRKPd-YsySc@os$u+(dpq;rib8*H%G=^p8S+L9a@0 zl6810=%dBg#W)f43C^?avS9*_#R&5mOE7EMHb@NcxC-@G-FjjDcTUyMZ*$duH(2%a z#o_wzPFMX&;sJ-hxFhOEA7Y60$98hX7j!W?p52%+Nz2Ad128&W3BJ{xgUpG3ZkI6_ z(_MCkT5u}Xs-WFGG!&v{PC{eh)X5+4-G!?%CV008Z@&twlV@X#MSmBYEDO^2TIy}r z)mQiXT5Z9ZS#1&{V{lzq%yjQD5`=HK7oekUBne+!Z*#rAq6|wt zy8T?#mGy{&z#B!SWAJ2_BkKMonh|vua`AsE0MwtyXTX4&o3Fehv-d4Tg*qCY)$x(;iNvr>9fBP^Gzp>=_*KF7}FOi{`W6#{pH@bM>OKQf^q`x@+{2zUzM_=fCEY$2XDuq?glxEpPNckQ#|%>CST^m{pR zpd+x3Mj4x|`u)N>%LRyxhU|Z5#$arqsSgT=%n-{WKC$439b0VSV9DeWF?1}jYolF< zv?_RMm}P5ariXTp>7j8}V|r-sSPoi}<)Fcz%192?AI6dCsd04^gX?J0!`Gm7LG4&r6GvLnCjb|CfpOqam`!QrjF-je_hCWnEjO=0M4Uf0Pc93oaLKh z-OyhgLafg`FGY3=93j@90H^2*9%Q^ce&8R=-Nlq&N zCQAOS?<8UEM(~SGsq7S2mwGqnuTYCGa2)&~-*giMafHVEo^dzz8xG(UL(q5OpsfEs zQiYwpOH#+%?&%-N1^+O}S>}Z8~ z02w88H>!%EQ!uol8ZyK=(So>V{hor@Q0A6}|F)O$={8Y$!4gb1!YBNx6Ou4j7*CS@tPnd3fpo=7o6qvlj=QF<@B-e9R^wvZ0 zjlmBS=;x?4PE%|6p##9y6l4=S(aL9~)>t_F?q3AXgY=u`0f+rvtk2_pm4}3R6_e`0 z;2r*wu}xTNe0_yJ5*9M#;ypfVve6H5s{U;l5<@e84_(N01ic6J7w$sznr5M5!A*G9 z13W$|KIrj4mkaE0*SPvl9_`6&2BEk6!+fS3jTgjG*~zD=fuSUc-e+}-*=Dy0 zP$sIMW{*8%DMGF}9##WfPhkgv)Ljn>ML7>@Uf^svQYtdz|MolYfHbtFO!O{?&LvwCmQXwh7sjydUy& zBty`|hHP%UH-+~JFs5SXlmxM0`ct_@q53aG#o*8yFR&?OQI-%XCNw-iDNEW_Y&0X< z5@gT*@JFznrkSVU9$HMQvW5`+@bm&PtFY&L0f;4m9!J@dnmEQ(Wu z4^cG^U0x6uz8_DPMVYjP3KJI|M@*b`&7Qm+T8EDs=306Sak9T;|4v3OK0&YC%xjAM&OG|TagqCCOJMcLTblC8wIMs)C~ zZit1J`MP6O|4x9uZoBHgg}h&vU~D8ugYy%)=Y8|{U@wRX9YKsj)ng9 zet{lXLE3%NQT@AEu#gRfB*X);oS+7ln4oSHm8l2NEqszdE;5O&hVai=(@6To|5N&4 zvZ7;7&60LIeJGBuDUL>|i!ufC))1p~$MwL+i4GQR<5UW-N0}JJ zGEs%3?Ogn0$vZd>;JtmP-tyHG-e-jWh+@~=k-*JYb={rhFgqzA-Xl;0{OaUj@l5rL zlRd={Rn~9gX({-jTjAM&*1PF z&dd8h#)BNT+3~vp;Up#c!HbMvb+zjQHE=nqvu2g)PH`&A&l+S3SkD}19quySy-lbD z_IrdMippS}WI5!|9SE^n$SKe1{}7_sxia5aPk#X`!694kv{H8;t=JiY1(dewD1~Q$ zs99;eT09Am_&nF6!VrOC$1x!}tbE=}vt^#NX%vTH!e>G-C_e6T)BD$f1Ky&>1_bc3G*XNkCHJx0$+0b%5-OX=P-$=D#pxy zQyEO4&3Vj3d|3v|a2OPQyOXgJcN#l08oqCS|G%Vp9_xorU90LN_2E^sb%{bP?ZgWyf0I4BrI(kox*6o_eC=A%fPx5kjoN3kUF|`rGhq_U8a&SR?M6k>*@kZh)s? zwK!~M^5leojEy3SZjDNeZz!{t2)Q)8g*1pU*UHcpGKj#Y~=c46| zjpQzXQB`mh7?1XCaeu}2t9L28M;@o_RR8%b{Na9*LrvLk4*|n?LCD)`U9{f#j{dFt zzL14B3g=6JWr+5njLwL_vs(GL0@wRK;4pbOSMZ>vpS=7emM`fY3f+yqCmRWMV)e7t@Ql`z+Q4bj4{JqrTc2O$w7|ENfPwS!B% zur5jto!zh8@DngGX8!Gc`BneRpku~2F&>|h2VFIA5Md( z6OZ^h*+=cc+E0Lqz`A|de~vSHdkZEvFWkcX~*|`ciH@VwRClB+=ao+N7Yx~!OMuT zzR#WNtM&_EMcaLD2bNwPB8&G;X$bjUHo6Tl5$A<0f5MKbFb?M1XX@IX{h3_DR$ z13n9L8v3k%ulFy3V=Xb41aGyiT!gi#FR>2;At|r&tw7QLb-aYpb^J3NqQ)zM=&0^4 zZ)b~rkP=L~P$21hj(^tgWkLO&hFF}v=OMIr5TPNDhr4G=fiW4!p7?;cP#zaye=d>VjughTWfc!$vt zu==}qgE{fyFvmVLr5<>kOl{=fZS=k2e*9K~Mu5a{ehi;)akhDnJrBM-&qLDrpEOmN6|oPhrbh;h#Z?Ciw547P`f3CFrc7N`F1VNo8u?AROQ znF|Idv<; z?p!F9n7-Uf>h|Qt*!&KCi_HN14oGsKeZT-moC^2PZ&({Gw=@mw!?*2wcz!87Fz zA2b1T?ndvy&2fUFX;(Je&2o~>Fv$!PMA*N-!!n@!bL96`>MDKRm#Y3Zr2nR_+X<;o z`VN0Y`>Otzkbv*1tlJOkG>LE4nDJv|{QCWv?@ju?FP@(To5WX}C0>BP&b9fKL6kDa z8DBT{`*>VTUB`7&w34hhRyl#kay}N(XPWiGLYjDRX`fEjgrpfR(p|ATq#s{t?ev*a1xl@$dxOumT zY2183z?(G@lr?Vlo9UG@KF5lmC*kKsw#^B>^pZ_F8D{0m`{c z!p}>xPm2KjZhl2Z9Fakez+(J7@zm(+j@ROS-9xcP4b!)IpG4a&v!h@7x|6jy;Mbk8 zxl)!;B7=oi{_~mt0yQv(`Nzq`Q6doYpzbTQvnO$dMmyWreYqOoI>6@pB-%qVcwAze z?LEXQ?;**{eKNR91mHK8<>3QDJ7F3(m&nA0B9P^sZRNL1>fcM0uVwy8-0s)TYOFnX z5;s;G)xfQ!b421Ekik9?^!l!RfPI6H27y@qoA};EXf`UgX4mN#K&3k4LeuWYaQ1Q{ z2Q?r6j%;o2iNV@HGQJ~k?)64`P(AlOM=ZYS{jb^&edc+5BPkg^8_L7_h-bz_tf zo(WE6f^&pth@#c5axNa>agHv(%vn}l>nty=tWmsG_@%SzrBxNTmX=4$yQfd>lx)7QG&>o_{FxmqRNUACx7Oxy2^ z1))N{MX-25c!+|Zb8}XgIq{2b)wQdXg(K3&ti<19WrSyhckl=g|Km9oxt+?0l2q%z za=niZpLk z=}O37D`n#qSC^Cug2*&_^5p66%pCXJjEvD{5_D_EoC5dU8RCaL_MMb46H{>I;EYHDg~>W~%XrM0DI*YG%tYD*^!L9cX zbZX@l9??6UMO7tE{(K^m>yW&p)Ct8dWpPXJJA;V5MaEW^qEq(8YO=XULt8Y9Wp?V6 z%u&wFO#H#GbU914;*p?G?37(P5?RKi%s}gul$I5FE47iMoY~VyIj853a{h?9icoIp zE#3-r^3p04SM40FIWuS706vjg<%CQ|JMmkP&ZQM~r2u;^1p9eKl}E#GF|wzc)4 z-;M+!_GeKv=%9Gfy-gly!P!+RU;|DRsPoFP$nGtvS>i1-2|!xZZ}47_ljlTy8EOJ0 zE_J%0*=f#-GUwzg)5k#0Cu<9`CD^X6DlUbg=<8!qeW$p(N-JHdvG4TZP(+n=r4f37 z{=q(?Oq7MAX{;`q0`|x$l3@*LQ-z^Je}(NpH!g)n2@5UlA4u0!SD_aCwN9tRsi|Bg z!3bOCsrJ?um-eM|NHLpx1$uo&N!4W<1P^6Sfn=*IOGXlRMNw@P^k;%|Ubb@y8VLF{ z3ca*?35KOrkb7}8O@men#!x8gJPJcyk>(YZ8|ADhVFeIp)`#Y=tg>jSX9DG8nnH;p zt(y64nKl#bwQq!@jdY(*f_YONOdD$&QJIS+_FbEPYImI9h~McmLq1z~+DYP=rXaq_ z#M=^qzrcK+jlfT`W6_dgSf!=q z6+d29xxA{n=9XHI=B-0`!@yK?+hSGz91g4DMC9zRfq+Y0&oL-)Un7fIt} zc}^A=ndkrdndR+y*6v8dZH0XPq5I#(i==U~JSU5b%=3T!%<=|dsD`^)YcHy;DN|KBvc^XXol&)=T5VC4M-e}8jGsy- zyh^bKMMaEr_$}pHO^IMi7&zyp^p^@}%$hOJoih@@+bhkYGsBtYy!29-OAkhO7MC{C zdHd~7E14CENT%e%b-KEI=FgkXSN7a z&4wy0Uc!7A7Z-6Tz~AE1N~dR4RdH=~Rkhdjv@ibe&@c&~j0nq>MF`a~F~u{~sZcn< z0;0oTNRLoL-X+T`w8EN5prlGbQk`T1bV59cAEIVa;zy3b=C9;;${@ye|H?9A-x?iqOsCvf@hx%1s~^Oc#ivomMr%+Af6F-wVX zG8O0G(}yS?Z*ehP^U^YJrPFj3_)Fw)AhhaY%pv5DfyXuom8YDctfBF?GQ2T!-zB#kd#X&ci(&_Y~aHvegzA!KzX2`1SMj@hLEd zmya3A?#xb~jc(7L)IS})(oCOMh)GB$epCB@o?qk@wzjZnd?YYVspXti5mOk2OBu?H zS@Sbz&d5=6@qel!X5UKw>`Wy$OUa$43flkc? zkNBI3N$(BHjWQBsrYqBtWwux*P^QkFn~Nuf8aWln%B%&ZKF*pwZ$@t3Oz7p*f_!(5 za)Xs{!-BbU^WF1+X@;}saXJl>^KP6y%dKQ(=8zQx*ST+C06K1Vw%%*yU+s>1+zq%J zaX*NA6YeK*Z^69{cN6YCxM!W@ZbRHb+%>p+iAFsbSR-b?{|X_y`oFNbO&f79VA_ED z5z;>V4BWeb)qY=^?Y0Q{*lvpv_R{?U8pXh4`!7St_V*xU``?a` zbOH$3{y~In|BVRQ{*NFO?T?V{{|rL5|5k)-*H;m;UH^cP?Ya*kg8y9qk-&c>@E-~M zM*{zmz<(t0zmx#>IXT2*&L%@Xb1dihOW!L^w+r8EA%6CmV|nEoyW=1K>Zxv8E9ZO% z0(LQ`e+`&pw-=e|yD>j~E?)EnLf9pxZ!iVv^vO7`(+BHXZU;pvR7M z^^=GPYt`flC)ca51&qi4_LYG0txR>O(>mPu<9-77R@^PP-^cwK?qj$I{>1J$3-_hC z$K#%gdp_lq zPvG8)y9M|AxIe>f@yR;#o|c_G!I_d%x}*Zz5#v(Frj8puex!U*lvK~EJk-ipeS(G?|1Dj{rolq;zvtExaxhFYym zF*nWn0fk7bEv+QJe5k3^lvFOPDXC&xA+-zulBzjzRZ>gKv9y7`9woI{tFHB+02%za z7=%Cs%V5PQpc>inCsk0BIhS~VjXC*pteRVb`8PuP@01;NE&)2rkIPc8tcz*9=%>>c zC0+{fQjp-pvvpDDd8OmF@TLQAI`EQ3iu_~YO$SI{m-N_IDuFi-H~n5-#N(ZV8*idi z6OVnV2@m7&9E~>%aqLgTV;}n(5AC?g3&;vu>}&IJv!4-+xnpus;!x zed$^dT8L+>T*mWWhMWD5&+LOw0?&!($Z`QHx8kPnPP{$9+XI9YJX`cwS1Pm*w}r<( z-vzuWV2XI0+gW&b;hDTLp0+0qnbPoVUBu(H9=CK6T zn3YRSBm|TluLp5kc(ip3J&MA)pLJQ~K7t71v8a0B)dTNp%g`atiJR9GxGlVGI1;z5 zLGmLK@yDXK#l&OV@g#}TDCI?vmxaf7tVoY|4&XUXf%j?zUOe#PfoIV`Qt$IFJX6M` zN86YHyf!?eS;WOWY`1;5t#Xe7@90VB5%--4JlgsY@SaATbrFa6A>053F*8<3RQ6|?E(OYdD`Ae{a!i)6tPuO5gy|6|3j3Rc z*>iz!j#;q^S7%^P&@ULqDqN*eL_c8|YxXww2MM!Pa6w-f#47Xvq<%AuH7z#vSip?zK_ z@&>!(Q3OnX3t?UazaIeJg!JE->Fo&li@LHtiAo6Yya@ci16~jTcLUxOfzNfUEl=7V zGoZRGj~^FH`<31CH*^KUg8@5{KNWbC*D%2C5855Hb4))E@TQ;H9c!VggfAgJ=;>y9 z8er>U{U=1h9LM>q7VOxhpBt5aBVhgn0yUfR;b-x!i}ZYe`SB2HCd}iB|1LlXvEqeUsne#QfSl?s*4#51tx0TMXPx6yK)H|jR1v~}tGp2klMCgvd zzZ!7YPK?7QekR~{q~B$h&r`_!vM%+S{1gM`=Uv%T3EvJluf^^-Waj?~V1DwGJ)P-~ z0p_<3t@^wIm|v@NnCb5U=J(vJbhaZu3TEZ!mznvgDhqZ3UJQJzzAnK08jh7d7qAns zCC_Dm`F5_wzaKE)y|nm$7_fD*eSQPD<`cW)CPY$xuL0&emb9F#&mRD{A^mNHO#dT7 z>tg;-qu?*1;C}+f&xkpwC(O@}t1QNPu*LtyfcdV01@q@Mt&8-h6Ca1GsXx@eJit7H zZ@~)y^YFC=F9yurpU&GSs@5{&&FqX(^5rLm|H&rvDCp8iHZ0KLlIzvE9LbPB;;8`)<3V6!IZ_8Q}P^ z-BD?I97xCw*gMuZ+Fav-ZH%%7zKY0*c$JKVZ7^#N{^94GIEk0 z=EQ)vyk~b@Vans;sPu7wH@%GWHfH(P0A3ujJ027H&rtFJ*FfKtcJo0(dd_zZK6cpYxk~=r8RX z;di3&KLNbuExY3rlRoD;*box^gZlMP<_ACH%=|Q*jS=*Qik^)`k&gK(;C7^A`&g7W z2Jr5P`sGC7&yRx30oNcu`z!g?0B>r;cxm#t2JnNR$MS~8Bq|&56ldC(p)tdiUjWWS zedd|@9|znPp&!o@Mt{B0Oy3FEx+uQ`OvkyTe6zd{0dLu7cl-!xgfpYy{3y6A3a$g(4t=xq#SggY4ZGuG z&?0{u06%WZD<1ay=TZ5e2F%wqE&H+^aM$y82giNp-xY;_2ypvzcE|5b`kw->hka`{ z;jg0b2cRLH5&eHS8`iv7-%9{bfxfN8GxaSEa9RZYiGcH7vOBU&e%#C-VGr^EHy*&+ zm6^T>@Pp?`{a}5U0B(=aKaKg#i{<+Pr?uK07n%8g3b^S_yW?)8vHrh^!hafYJK8(N zOn)gVeIMY(e}n#@Lo@#$qtZL0V6j*d!A}Am2h;x?it+VQ!0{NLEc-JKaAbQ<1l)-B zqQAiMrUG94BKFNqcrM^g5&f|UuywKiUgGZozwir~{|@3~{N@*iS-uXq9s2)1aGCy7 zz)f5G=syhDx=8Q;0sax}Bjbj}oT2OnJO!`JJPG--ecJ%%-EDWQfu4}wdw{L^+Ayr( z9tPb0zFq7aGyOBBNAzE%xK_it$+9w>6Dcgro;Q1L;mjHN^9lJEu*i#ZY33Q*lGH1&y3(aEp|GN4C7|)?idg+CEb%U1zRD`cJu62PGj%Qw zSrl3ji?N>jN)fAuq7)oP!C4k?E6(6A89z?ZstXyERFv|-&QhKT;z<*E^u|1FuL#J4 zw^+2YYA?bP;^#!9*xPDYCYYHMX^z2_j#)s$4BeDg`G z_S66prHUx);9+55-dy**c{dbJEtn-JH*Q>d z`Y969PLVKXe403K(|1bkT#88?8(a*b5*+36fUYm!DPEgS>XgLR`#okDisgj4HDWe4z-$UB#y}mU0hz_ zDYjH~fmXf1dams|7-k;MIw=!l$;8QIHg*i^BTC>QE)Vs8dFq(-t6VaKk_$L(oKiS# z=IpG@nT4~bPQ^i{!g-llGu^Z;tW{xAZEex2LTG6J2pmDhN!7xf8*ut+Mz+FzfI>5_ z5dFBSy0EgkxQM6arc9YOb4FHn;h5AkrJ@Q)3#+v1n#f2Vtc-*yMVu2x$u&iV=7CI{ zwJnwBDS0?C;;?1Zkwk2(NSk6kL=rA6#LGyUO<}otHdG!mMK!=tad~Yd zKd_OdRdtaulCR>TN(e799(k*ZYgR=@k!$lPu?Gh&t6<8bli6=!8zDm+TE)r7BCSxI z=Mxo{d=-INDHYLpg?zOtnoQAnt-7SDkQIoofuOXkqNcBvik6hpXwyLP;A~+b#Z#-{ z5OqamG~tTsQoPW_lh8#aC!}$kie!q$$21F!ED;;ow!1)>zfC@U#C zK}zBfqpVYSjt+pe!f|)dpK|qI%{b*}}pI-7hPv^m@u8iy&%A z6%K^L5b&9$De^tSXf*TKt(2ZPbIH9zS??8KNj$Riu(sebI=>|yv?;hnH;6>QYn)LE zgyuw_io{`2)mj8qUH}r0kbQ~eH$bG2fb6fH(n`Ex8A-di5-;>cMwG3nt2bX{o+!vGQtn&muzq zi)xofb7*ye8l|vQY)z9>>WV3VqH*Yd=Cg&1GtYPvu8yy~MU(42@^8NLKx)>Z|Dy4d x6kjQg%!grf{MF-AA@stUV!UjCQ|75lRtk}^-fSNlSw)PlB0A#L1@paw{|_B}M2i3b literal 0 HcmV?d00001 diff --git a/ape-server/deps/udns-0.0.9/dnsget.1 b/ape-server/deps/udns-0.0.9/dnsget.1 new file mode 100755 index 0000000..711f297 --- /dev/null +++ b/ape-server/deps/udns-0.0.9/dnsget.1 @@ -0,0 +1,182 @@ +.\" $Id: dnsget.1,v 1.3 2005/04/20 00:55:34 mjt Exp $ +.\" dnsget manpage +.\" +.\" Copyright (C) 2005 Michael Tokarev +.\" This file is part of UDNS library, an async DNS stub resolver. +.\" +.\" This library is free software; you can redistribute it and/or +.\" modify it under the terms of the GNU Lesser General Public +.\" License as published by the Free Software Foundation; either +.\" version 2.1 of the License, or (at your option) any later version. +.\" +.\" This library is distributed in the hope that it will be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +.\" Lesser General Public License for more details. +.\" +.\" You should have received a copy of the GNU Lesser General Public +.\" License along with this library, in file named COPYING.LGPL; if not, +.\" write to the Free Software Foundation, Inc., 59 Temple Place, +.\" Suite 330, Boston, MA 02111-1307 USA + +.TH dnsget 1 "Apr 2005" "User Utilities" + +.SH NAME +dnsget \- DNS lookup utility + +.SH SYNOPSYS +.B dnsget +.RB [\| \-v \||\| \-q \|] +.RB [\| \-c +.IR class \|] +.RB [\| \-t +.IR type \|] +.RB [\| \-o +.IR option : value \] +.IR name \|.\|.\|. + +.SH DESCRIPTION +.B dnsget +is a simple command-line to perform DNS lookups, similar to +.BR host (1) +and +.BR dig (1). +It is useable for both interactive/debugging scenarious and +in scripts. +The program is implemented using +.BR udns (3) +library. + +.PP +By default, +.B dnsget +produces a human-readable output, similar to +.RS +.nf +alias.example.com. CNAME www.example.com. +www.example.com. A 192.168.1.1 +www.example.com. MX 10 mx.example.com. +.fi +.RE +which is just sufficient to see how a given name resolves. +Output format is controllable with +.B \-v +and +.B \-q +options -- the former increases verbosity level up to printing +the whole DNS contents of all packets sent and received, which +is suitable for debugging DNS problems, while the latter reduces +the level, making output more quiet, up to bare result with no +error messages, which is good for scripts. + +.SH OPTIONS + +The following options are recognized by +.BR dnsget : + +.TP +.B \-v +produce more detailed output. More +.BR \-v 's +means more details will be produced. With single +.BR \-v , dnsget +will print contents of all received DNS packets (in a readable format), +while with +.BR \-vv , +it will output all outgoing DNS packets too. + +.TP +.B \-q +the opposite for \fB\-v\fR -- produce less detailed output. +With single +.BR \-q , dnsget +will only show (decoded) data from final DNS resource records (RR), +while +.B \-qq +also suppresses error messages. + +.TP +\fB\-t \fItype\fR +request record(s) of the given type \fItype\fR. By default, +.B dnsget +will ask for IPv4 address (A) record, or for PTR record if the +argument in question is an IPv4 or IPv6 address. Recognized +types include A, AAAA, MX, TXT, CNAME, PTR, NS, SOA, ANY and +others. + +.TP +\fB\-c \fIclass\fR +request DNS record(s) of the given class \fIclass\fR. By +default +.B dnsget +uses IN class. Valid classes include IN, CH, HS, ANY. + +.TP +.B \-a +(compatibility option). Equivalent to setting query type to +.B ANY +and increasing verbosity level +.RB ( \-v ). + +.TP +.B \-C +(planned) + +.TP +.B \-x +(planned) + +.TP +\fB\-o \fIoption\fR:\fIvalue\fR +Set resolver option \fIoption\fR to the value \fIvalue\fR +(may be specified several times). The same as setting +.RB $ RES_OPTIONS +environment variable. The following options are recognized: +.RS +.TP +\fBtimeout\fR:\fIsec\fR +Set initial query timeout to \fIsec\fR. +.TP +\fBattempts\fR:\fInum\fR +(re)try every query \fInum\fR times before failing. +.TP +\fBudpbuf\fR:\fIbytes\fR +set DNS UDP buffer size to \fIbytes\fR bytes. Valid values +are from 512 to 65535. If \fIbytes\fR is greather than 512, +EDNS0 (RFC 2671) extensions will be used. +.TP +\fBport\fR:\fInum\fR +Use given UDP port number \fInum\fR instead of the default port 53 (domain). +.RE + +.TP +\fB\-n \fInameserver\fR +Use the given nameserver(s) (may be specified more than once) +instead of the default. Using this option has the same same effect as +.RB $ NSCACHEIP +or +.RB $ NAMESERVERS +environment variables, with the only difference that only IPv4 addresses +are recognized for now, and it is possible to specify names (which will +be resolved using default settings) instead of IP addresses. + +.TP +.B \-h +print short help and exit. + +.SH "RETURN VALUE" +When all names where resovled successefully, +.B dnsget +exits with zero exit status. If at least one name was not found, +.B dnsget +will exit with return code 100. If some other error occured during +name resolution, it will exit with code 99. In case of usage or +initialization error, +.B dnsget +will return 1. + +.SH "SEE ALSO" +.BR host (1) +.BR dig (1) +.BR resolv.conf (5) +.BR udns (3). diff --git a/ape-server/deps/udns-0.0.9/dnsget.c b/ape-server/deps/udns-0.0.9/dnsget.c new file mode 100755 index 0000000..68c5295 --- /dev/null +++ b/ape-server/deps/udns-0.0.9/dnsget.c @@ -0,0 +1,726 @@ +/* $Id: dnsget.c,v 1.31 2007/01/08 01:14:44 mjt Exp $ + simple host/dig-like application using UDNS library + + Copyright (C) 2005 Michael Tokarev + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef WINDOWS +#include +#include +#else +#include +#include +#include +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include "udns.h" + +#ifndef HAVE_GETOPT +# include "getopt.c" +#endif + +#ifndef AF_INET6 +# define AF_INET6 10 +#endif + +static char *progname; +static int verbose = 1; +static int errors; +static int notfound; + +/* verbosity level: + * <0 - bare result + * 0 - bare result and error messages + * 1 - readable result + * 2 - received packet contents and `trying ...' stuff + * 3 - sent and received packet contents + */ + +static void die(int errnum, const char *fmt, ...) { + va_list ap; + fprintf(stderr, "%s: ", progname); + va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); + if (errnum) fprintf(stderr, ": %s\n", strerror(errnum)); + else putc('\n', stderr); + fflush(stderr); + exit(1); +} + +static const char *dns_xntop(int af, const void *src) { + static char buf[6*5+4*4]; + return dns_ntop(af, src, buf, sizeof(buf)); +} + +struct query { + const char *name; /* original query string */ + unsigned char *dn; /* the DN being looked up */ + enum dns_type qtyp; /* type of the query */ +}; + +static void query_free(struct query *q) { + free(q->dn); + free(q); +} + +static struct query * +query_new(const char *name, const unsigned char *dn, enum dns_type qtyp) { + struct query *q = malloc(sizeof(*q)); + unsigned l = dns_dnlen(dn); + unsigned char *cdn = malloc(l); + if (!q || !cdn) die(0, "out of memory"); + memcpy(cdn, dn, l); + q->name = name; + q->dn = cdn; + q->qtyp = qtyp; + return q; +} + +static enum dns_class qcls = DNS_C_IN; + +static void +dnserror(struct query *q, int errnum) { + if (verbose >= 0) + fprintf(stderr, "%s: unable to lookup %s record for %s: %s\n", progname, + dns_typename(q->qtyp), dns_dntosp(q->dn), dns_strerror(errnum)); + if (errnum == DNS_E_NXDOMAIN || errnum == DNS_E_NODATA) + ++notfound; + else + ++errors; + query_free(q); +} + +static const unsigned char * +printtxt(const unsigned char *c) { + unsigned n = *c++; + const unsigned char *e = c + n; + if (verbose > 0) while(c < e) { + if (*c < ' ' || *c >= 127) printf("\\%02x", *c); + else if (*c == '\\' || *c == '"') printf("\\%c", *c); + else putchar(*c); + ++c; + } + else + fwrite(c, n, 1, stdout); + return e; +} + +static void +printhex(const unsigned char *c, const unsigned char *e) { + while(c < e) + printf("%02x", *c++); +} + +static unsigned char to_b64[] = +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static void +printb64(const unsigned char *c, const unsigned char *e) { + while(c < e) { + putchar(to_b64[c[0] >> 2]); + if (c+1 < e) { + putchar(to_b64[(c[0] & 0x3) << 4 | c[1] >> 4]); + if (c+2 < e) { + putchar(to_b64[(c[1] & 0xf) << 2 | c[2] >> 6]); + putchar(to_b64[c[2] & 0x3f]); + } + else { + putchar(to_b64[(c[1] & 0xf) << 2]); + putchar('='); + break; + } + } + else { + putchar(to_b64[(c[0] & 0x3) << 4]); + putchar('='); + putchar('='); + break; + } + c += 3; + } +} + +static void +printdate(time_t time) { + struct tm *tm = gmtime(&time); + printf("%04d%02d%02d%02d%02d%02d", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); +} + +static void +printrr(const struct dns_parse *p, struct dns_rr *rr) { + const unsigned char *pkt = p->dnsp_pkt; + const unsigned char *end = p->dnsp_end; + const unsigned char *dptr = rr->dnsrr_dptr; + const unsigned char *dend = rr->dnsrr_dend; + unsigned char *dn = rr->dnsrr_dn; + const unsigned char *c; + unsigned n; + + if (verbose > 0) { + if (verbose > 1) { + if (!p->dnsp_rrl && !rr->dnsrr_dn[0] && rr->dnsrr_typ == DNS_T_OPT) { + printf(";EDNS0 OPT record (UDPsize: %d): %d bytes\n", + rr->dnsrr_cls, rr->dnsrr_dsz); + return; + } + n = printf("%s.", dns_dntosp(rr->dnsrr_dn)); + printf("%s%u\t%s\t%s\t", + n > 15 ? "\t" : n > 7 ? "\t\t" : "\t\t\t", + rr->dnsrr_ttl, + dns_classname(rr->dnsrr_cls), + dns_typename(rr->dnsrr_typ)); + } + else + printf("%s. %s ", dns_dntosp(rr->dnsrr_dn), dns_typename(rr->dnsrr_typ)); + } + + switch(rr->dnsrr_typ) { + + case DNS_T_CNAME: + case DNS_T_PTR: + case DNS_T_NS: + case DNS_T_MB: + case DNS_T_MD: + case DNS_T_MF: + case DNS_T_MG: + case DNS_T_MR: + if (dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN) <= 0) goto xperr; + printf("%s.", dns_dntosp(dn)); + break; + + case DNS_T_A: + if (rr->dnsrr_dsz != 4) goto xperr; + printf("%d.%d.%d.%d", dptr[0], dptr[1], dptr[2], dptr[3]); + break; + + case DNS_T_AAAA: + if (rr->dnsrr_dsz != 16) goto xperr; + printf("%s", dns_xntop(AF_INET6, dptr)); + break; + + case DNS_T_MX: + c = dptr + 2; + if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 || c != dend) goto xperr; + printf("%d %s.", dns_get16(dptr), dns_dntosp(dn)); + break; + + case DNS_T_TXT: + /* first verify it */ + for(c = dptr; c < dend; c += n) { + n = *c++; + if (c + n > dend) goto xperr; + } + c = dptr; n = 0; + while (c < dend) { + if (verbose > 0) printf(n++ ? "\" \"":"\""); + c = printtxt(c); + } + if (verbose > 0) putchar('"'); + break; + + case DNS_T_HINFO: /* CPU, OS */ + c = dptr; + n = *c++; if ((c += n) >= dend) goto xperr; + n = *c++; if ((c += n) != dend) goto xperr; + c = dptr; + if (verbose > 0) putchar('"'); + c = printtxt(c); + if (verbose > 0) printf("\" \""); else putchar(' '); + printtxt(c); + if (verbose > 0) putchar('"'); + break; + + case DNS_T_WKS: + c = dptr; + if (dptr + 4 + 2 >= end) goto xperr; + printf("%s %d", dns_xntop(AF_INET, dptr), dptr[4]); + c = dptr + 5; + for (n = 0; c < dend; ++c, n += 8) { + if (*c) { + unsigned b; + for (b = 0; b < 8; ++b) + if (*c & (1 << (7-b))) printf(" %d", n + b); + } + } + break; + + case DNS_T_SRV: /* prio weight port targetDN */ + c = dptr; + c += 2 + 2 + 2; + if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 || c != dend) goto xperr; + c = dptr; + printf("%d %d %d %s.", + dns_get16(c+0), dns_get16(c+2), dns_get16(c+4), + dns_dntosp(dn)); + break; + + case DNS_T_NAPTR: /* order pref flags serv regexp repl */ + c = dptr; + c += 4; /* order, pref */ + for (n = 0; n < 3; ++n) + if (c >= dend) goto xperr; + else c += *c + 1; + if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 || c != dend) goto xperr; + c = dptr; + printf("%u %u", dns_get16(c+0), dns_get16(c+2)); + c += 4; + for(n = 0; n < 3; ++n) { + putchar(' '); + if (verbose > 0) putchar('"'); + c = printtxt(c); + if (verbose > 0) putchar('"'); + } + printf(" %s.", dns_dntosp(dn)); + break; + + case DNS_T_KEY: /* flags(2) proto(1) algo(1) pubkey */ + c = dptr; + if (c + 2 + 1 + 1 > dend) goto xperr; + printf("%d %d %d", dns_get16(c), c[2], c[3]); + c += 2 + 1 + 1; + if (c < dend) { + putchar(' '); + printb64(c, dend); + } + break; + + case DNS_T_SIG: + /* type(2) algo(1) labels(1) ottl(4) sexp(4) sinc(4) tag(2) sdn sig */ + c = dptr; + c += 2 + 1 + 1 + 4 + 4 + 4 + 2; + if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0) goto xperr; + printf("%d %u %u %u ", + dns_get16(dptr), dptr[2], dptr[3], dns_get32(dptr+4)); + printdate(dns_get32(dptr+8)); + putchar(' '); + printdate(dns_get32(dptr+12)); + printf(" %d %s. ", dns_get16(dptr+10), dns_dntosp(dn)); + printb64(c, dend); + break; + +#if 0 /* unused RR types? */ + case DNS_T_DS: + c = dptr; + if (c + 2 + 2 >= dend) goto xperr; + printf("%u %u %u ", dns_get16(c), c[2], c[3]); + printhex(c + 4, dend); + break; + + case DNS_T_NSEC: + c = dptr; + if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0) goto xperr; + printf("%s.", dns_dntosp(dn)); + unfinished. + break; +#endif + + + case DNS_T_SOA: + c = dptr; + if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 || + dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 || + c + 4*5 != dend) + goto xperr; + dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN); + printf("%s. ", dns_dntosp(dn)); + dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN); + printf("%s. ", dns_dntosp(dn)); + printf("%u %u %u %u %u", + dns_get32(dptr), dns_get32(dptr+4), dns_get32(dptr+8), + dns_get32(dptr+12), dns_get32(dptr+16)); + break; + + case DNS_T_MINFO: + c = dptr; + if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 || + dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 || + c != dend) + goto xperr; + dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN); + printf("%s. ", dns_dntosp(dn)); + dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN); + printf("%s.", dns_dntosp(dn)); + break; + + case DNS_T_NULL: + default: + printhex(dptr, dend); + break; + } + putchar('\n'); + return; + +xperr: + printf("\n"); + ++errors; +} + +static int +printsection(struct dns_parse *p, int nrr, const char *sname) { + struct dns_rr rr; + int r; + if (!nrr) return 0; + if (verbose > 1) printf("\n;; %s section (%d):\n", sname, nrr); + + p->dnsp_rrl = nrr; + while((r = dns_nextrr(p, &rr)) > 0) + printrr(p, &rr); + if (r < 0) printf("<>\n"); + return r; +} + +/* dbgcb will only be called if verbose > 1 */ +static void +dbgcb(int code, const struct sockaddr *sa, unsigned slen, + const unsigned char *pkt, int r, + const struct dns_query *unused_q, void *unused_data) { + struct dns_parse p; + const unsigned char *cur, *end; + int numqd; + + if (code > 0) { + printf(";; trying %s.\n", dns_dntosp(dns_payload(pkt))); + printf(";; sending %d bytes query to ", r); + } + else + printf(";; received %d bytes response from ", r); + if (sa->sa_family == AF_INET && slen >= sizeof(struct sockaddr_in)) + printf("%s port %d\n", + dns_xntop(AF_INET, &((struct sockaddr_in*)sa)->sin_addr), + htons(((struct sockaddr_in*)sa)->sin_port)); +#ifdef HAVE_IPv6 + else if (sa->sa_family == AF_INET6 && slen >= sizeof(struct sockaddr_in6)) + printf("%s port %d\n", + dns_xntop(AF_INET6, &((struct sockaddr_in6*)sa)->sin6_addr), + htons(((struct sockaddr_in6*)sa)->sin6_port)); +#endif + else + printf("<>\n", sa->sa_family); + if (code > 0 && verbose < 3) { + putchar('\n'); + return; + } + + if (code == -2) printf(";; reply from unexpected source\n"); + if (code == -5) printf(";; reply to a query we didn't sent (or old)\n"); + if (r < DNS_HSIZE) { + printf(";; short packet (%d bytes)\n", r); + return; + } + if (dns_opcode(pkt) != 0) + printf(";; unexpected opcode %d\n", dns_opcode(pkt)); + if (dns_tc(pkt) != 0) + printf(";; warning: TC bit set, probably incomplete reply\n"); + + printf(";; ->>HEADER<<- opcode: "); + switch(dns_opcode(pkt)) { + case 0: printf("QUERY"); break; + case 1: printf("IQUERY"); break; + case 2: printf("STATUS"); break; + default: printf("UNKNOWN(%u)", dns_opcode(pkt)); break; + } + printf(", status: %s, id: %d, size: %d\n;; flags:", + dns_rcodename(dns_rcode(pkt)), dns_qid(pkt), r); + if (dns_qr(pkt)) printf(" qr"); + if (dns_rd(pkt)) printf(" rd"); + if (dns_ra(pkt)) printf(" ra"); + if (dns_aa(pkt)) printf(" aa"); + if (dns_tc(pkt)) printf(" tc"); + numqd = dns_numqd(pkt); + printf("; QUERY: %d, ANSWER: %d, AUTHORITY: %d, ADDITIONAL: %d\n", + numqd, dns_numan(pkt), dns_numns(pkt), dns_numar(pkt)); + if (numqd != 1) + printf(";; unexpected number of entries in QUERY section: %d\n", + numqd); + printf("\n;; QUERY SECTION (%d):\n", numqd); + cur = dns_payload(pkt); + end = pkt + r; + while(numqd--) { + if (dns_getdn(pkt, &cur, end, p.dnsp_dnbuf, DNS_MAXDN) <= 0 || + cur + 4 > end) { + printf("; invalid query section\n"); + return; + } + r = printf(";%s.", dns_dntosp(p.dnsp_dnbuf)); + printf("%s%s\t%s\n", + r > 23 ? "\t" : r > 15 ? "\t\t" : r > 7 ? "\t\t\t" : "\t\t\t\t", + dns_classname(dns_get16(cur+2)), dns_typename(dns_get16(cur))); + cur += 4; + } + + p.dnsp_pkt = pkt; + p.dnsp_cur = p.dnsp_ans = cur; + p.dnsp_end = end; + p.dnsp_qdn = NULL; + p.dnsp_qcls = p.dnsp_qtyp = 0; + p.dnsp_ttl = 0xffffffffu; + p.dnsp_nrr = 0; + + r = printsection(&p, dns_numan(pkt), "ANSWER"); + if (r == 0) + r = printsection(&p, dns_numns(pkt), "AUTHORITY"); + if (r == 0) + r = printsection(&p, dns_numar(pkt), "ADDITIONAL"); + putchar('\n'); +} + +static void dnscb(struct dns_ctx *ctx, void *result, void *data) { + int r = dns_status(ctx); + struct query *q = data; + struct dns_parse p; + struct dns_rr rr; + unsigned nrr; + unsigned char dn[DNS_MAXDN]; + const unsigned char *pkt, *cur, *end; + if (!result) { + dnserror(q, r); + return; + } + pkt = result; end = pkt + r; cur = dns_payload(pkt); + dns_getdn(pkt, &cur, end, dn, sizeof(dn)); + dns_initparse(&p, NULL, pkt, cur, end); + p.dnsp_qcls = p.dnsp_qtyp = 0; + nrr = 0; + while((r = dns_nextrr(&p, &rr)) > 0) { + if (!dns_dnequal(dn, rr.dnsrr_dn)) continue; + if ((qcls == DNS_C_ANY || qcls == rr.dnsrr_cls) && + (q->qtyp == DNS_T_ANY || q->qtyp == rr.dnsrr_typ)) + ++nrr; + else if (rr.dnsrr_typ == DNS_T_CNAME && !nrr) { + if (dns_getdn(pkt, &rr.dnsrr_dptr, end, + p.dnsp_dnbuf, sizeof(p.dnsp_dnbuf)) <= 0 || + rr.dnsrr_dptr != rr.dnsrr_dend) { + r = DNS_E_PROTOCOL; + break; + } + else { + if (verbose == 1) { + printf("%s.", dns_dntosp(dn)); + printf(" CNAME %s.\n", dns_dntosp(p.dnsp_dnbuf)); + } + dns_dntodn(p.dnsp_dnbuf, dn, sizeof(dn)); + } + } + } + if (!r && !nrr) + r = DNS_E_NODATA; + if (r < 0) { + dnserror(q, r); + free(result); + return; + } + if (verbose < 2) { /* else it is already printed by dbgfn */ + dns_rewind(&p, NULL); + p.dnsp_qtyp = q->qtyp == DNS_T_ANY ? 0 : q->qtyp; + p.dnsp_qcls = qcls == DNS_C_ANY ? 0 : qcls; + while(dns_nextrr(&p, &rr)) + printrr(&p, &rr); + } + free(result); + query_free(q); +} + +int main(int argc, char **argv) { + int i; + int fd; + fd_set fds; + struct timeval tv; + time_t now; + char *ns[DNS_MAXSERV]; + int nns = 0; + struct query *q; + enum dns_type qtyp = 0; + struct dns_ctx *nctx = NULL; + + if (!(progname = strrchr(argv[0], '/'))) progname = argv[0]; + else argv[0] = ++progname; + + if (argc <= 1) + die(0, "try `%s -h' for help", progname); + + if (dns_init(NULL, 0) < 0 || !(nctx = dns_new(NULL))) + die(errno, "unable to initialize dns library"); + /* we keep two dns contexts: one may be needed to resolve + * nameservers if given as names, using default options. + */ + + while((i = getopt(argc, argv, "vqt:c:an:o:h")) != EOF) switch(i) { + case 'v': ++verbose; break; + case 'q': --verbose; break; + case 't': + if (optarg[0] == '*' && !optarg[1]) + i = DNS_T_ANY; + else if ((i = dns_findtypename(optarg)) <= 0) + die(0, "unrecognized query type `%s'", optarg); + qtyp = i; + break; + case 'c': + if (optarg[0] == '*' && !optarg[1]) + i = DNS_C_ANY; + else if ((i = dns_findclassname(optarg)) < 0) + die(0, "unrecognized query class `%s'", optarg); + qcls = i; + break; + case 'a': + qtyp = DNS_T_ANY; + ++verbose; + break; + case 'n': + if (nns >= DNS_MAXSERV) + die(0, "too many nameservers, %d max", DNS_MAXSERV); + ns[nns++] = optarg; + break; + case 'o': + if (dns_set_opts(NULL, optarg) != 0) + die(0, "invalid option string: `%s'", optarg); + break; + case 'h': + printf( +"%s: simple DNS query tool (using udns version %s)\n" +"Usage: %s [options] domain-name...\n" +"where options are:\n" +" -h - print this help and exit\n" +" -v - be more verbose\n" +" -q - be less verbose\n" +" -t type - set query type (A, AAA, PTR etc)\n" +" -c class - set query class (IN (default), CH, HS, *)\n" +" -a - equivalent to -t ANY -v\n" +" -n ns - use given nameserver(s) instead of default\n" +" (may be specified multiple times)\n" +" -o option:value - set resovler option (the same as setting $RES_OPTIONS):\n" +" timeout:sec - initial query timeout\n" +" attempts:num - number of attempt to resovle a query\n" +" ndots:num - if name has more than num dots, lookup it before search\n" +" port:num - port number for queries instead of default 53\n" +" udpbuf:num - size of UDP buffer (use EDNS0 if >512)\n" +" (may be specified more than once)\n" + , progname, dns_version(), progname); + return 0; + default: + die(0, "try `%s -h' for help", progname); + } + + argc -= optind; argv += optind; + if (!argc) + die(0, "no name(s) to query specified"); + + if (nns) { + /* if nameservers given as names, resolve them. + * We only allow IPv4 nameservers as names for now. + * Ok, it is easy enouth to try both AAAA and A, + * but the question is what to do by default. + */ + struct sockaddr_in sin; + int j, r = 0, opened = 0; + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(dns_set_opt(NULL, DNS_OPT_PORT, -1)); + dns_add_serv(NULL, NULL); + for(i = 0; i < nns; ++i) { + if (dns_pton(AF_INET, ns[i], &sin.sin_addr) <= 0) { + struct dns_rr_a4 *rr; + if (!opened) { + if (dns_open(nctx) < 0) + die(errno, "unable to initialize dns context"); + opened = 1; + } + rr = dns_resolve_a4(nctx, ns[i], 0); + if (!rr) + die(0, "unable to resolve nameserver %s: %s", + ns[i], dns_strerror(dns_status(nctx))); + for(j = 0; j < rr->dnsa4_nrr; ++j) { + sin.sin_addr = rr->dnsa4_addr[j]; + if ((r = dns_add_serv_s(NULL, (struct sockaddr *)&sin)) < 0) + break; + } + free(rr); + } + else + r = dns_add_serv_s(NULL, (struct sockaddr *)&sin); + if (r < 0) + die(errno, "unable to add nameserver %s", + dns_xntop(AF_INET, &sin.sin_addr)); + } + } + dns_free(nctx); + + fd = dns_open(NULL); + if (fd < 0) + die(errno, "unable to initialize dns context"); + + if (verbose > 1) + dns_set_dbgfn(NULL, dbgcb); + + for (i = 0; i < argc; ++i) { + char *name = argv[i]; + union { + struct in_addr addr; + struct in6_addr addr6; + } a; + unsigned char dn[DNS_MAXDN]; + enum dns_type l_qtyp = 0; + int abs; + if (dns_pton(AF_INET, name, &a.addr) > 0) { + dns_a4todn(&a.addr, 0, dn, sizeof(dn)); + l_qtyp = DNS_T_PTR; + abs = 1; + } +#ifdef HAVE_IPv6 + else if (dns_pton(AF_INET6, name, &a.addr6) > 0) { + dns_a6todn(&a.addr6, 0, dn, sizeof(dn)); + l_qtyp = DNS_T_PTR; + abs = 1; + } +#endif + else if (!dns_ptodn(name, strlen(name), dn, sizeof(dn), &abs)) + die(0, "invalid name `%s'\n", name); + else + l_qtyp = DNS_T_A; + if (qtyp) l_qtyp = qtyp; + q = query_new(name, dn, l_qtyp); + if (abs) abs = DNS_NOSRCH; + if (!dns_submit_dn(NULL, dn, qcls, l_qtyp, abs, 0, dnscb, q)) + dnserror(q, dns_status(NULL)); + } + + FD_ZERO(&fds); + now = 0; + while((i = dns_timeouts(NULL, -1, now)) > 0) { + FD_SET(fd, &fds); + tv.tv_sec = i; + tv.tv_usec = 0; + i = select(fd+1, &fds, 0, 0, &tv); + now = time(NULL); + if (i > 0) dns_ioevent(NULL, now); + } + + return errors ? 1 : notfound ? 100 : 0; +} diff --git a/ape-server/deps/udns-0.0.9/ex-rdns b/ape-server/deps/udns-0.0.9/ex-rdns new file mode 100755 index 0000000000000000000000000000000000000000..ce5c9094589fcbabc239ce91391192de629296fd GIT binary patch literal 29947 zcmcJ24PX@2wf}6gz=Eq2Fly8&i$)u&#sm;qi3(X3MF}DhwJNM3A0#y-ad$QW1r6>l zWIC>K zQB0fG0vy~2@XUJ@ZaeNw+}R+Kz0U67eLP^^Oe1aH;zC{pck4BZ!khV(ESWCiK0+FA zWavksESKpS2p$Ih!${-Z1DH3{SqATX`cEr8%YNBa zTYhu;SxEaX?n$^QgA;I{hCBVD9L~l~nemz^1IS)cPR5;M!s8IKOc!p_=5?kF#9xQ` z{2hd6n9t)8o{IYv+~j>M?#Z~n?P8sdcCpUK9wp%z#O0^NQ*OLYlEH|-PCzPd%8>HR z!<~!!MBK;WwyrVe19>5iC3{A8W}_{wUpMRizSWsJOXGQv!|t+Ip>8 zq&5b%3MHWV>nm0^Dh-WVZGELuTVJL5>a~VOnb5de(P~##0RSPH*r+wsqtx<1pb8~c zm)F)2Rcp9e26amHiYhIjRFv1%H3;%`Rm89I`|BHgh$`1=LBL;CUa0^P#VU;rb)-?* z5Y&{FRVyn{=t`Lz2vzk>K(1)2_BX5~ljZ&u#05EKuW1Nq_2po&y4hc=RWXYEHdI%Z zuU0DR8Uj_wgYv;)MU7vnHftZKs;|@e2FbbV+4e-YxZwo^hx-df+zE*}%zPlKLUK>Lg@%tIVKt99}M*m@kV-;l+ zLktp+F@#ZmoFNSEuNi{F%?x2=pJWIl+`$l)c%C5!icW^;_&XV5FnWO@2AtgtF?jVb zgrRt~2T?K6fLjDdQW~?dVvhD8_wX4Gs=QdbLq;KuY`_h%3Izt0>pquxrQQ z1sW&~p0x^pnP>2_ zI|a-ZuX%{@W&yM1Yc>(yBw%Vl&EtgI1pEf!&4gP7yoYcH;YI=PC)`Q+CIM4JYF;3` zP{7obnjXTl1Wb*o*+aNcz|@?Y1B6`yrUuo-31(a!W_2~ z2LwzFtKl$|=n*hAttN|bCt&1LivGMz|6}N2|H37UY7V@N)PDTA`kI;p@Q>p+ppehn zMqH+jwp?5ZQc7@^aY79qI_`&?dR`m;Y#+#oUN`988<-evC;e!N?PtUo%#JWwqPzF{ z|I|H-6l|fL_8XS&h&CzF=V2*C9|-k1e)14-Bc0j=%2vU{*qA+!af3a(c8uUBIzP`2 z_6KkI3Y@L$n*sBoul;HpT}QpNUtju}9vm==jPKcFbMow{xB^0?8UOS=5>{J(#ke_n zjy?=@`i*&ZeOg}p0|+NHFsO#NA~7_uQkxVS@N08AnBOSvH^uDFh@~?J8 zwX+2Y7Rq7}C_kaG)9rcjYe7qoB_EDH35zI;qUu_B7CT|sv$Qy2V|B0=UxCELwa8W7 z_LDr&CBKs-F{@6Gll?h)&UiD*6O1MnAw%2D3tY&cM%drXvYul#;;+NtR>!G$p_B}u0Kp-l|)&cL)$U#t{bP@>B3SqW$G|E`wiPm%G#cva- zT4p!KiY-i3)W{}aS}Y3Y61T9PMwHw#Ga##``g{oQ6j*2@LbjR8x(gP(2U-3vVP4J3 z>pS8%0E+CB6%S@b4$E>-xy0!pY8D~&2c+%szryGz_iQE2qJ0uM@fm6!_4diiuq0xl z2pDc9zDy|H?NbaOl=guiN8vqaH;DlRvgV9C3WUxVhDNa^Y>8u0xG6H^U@iL5+Cmdp zhmELOB7tJ9R^;4}2`%SJo!V{c#xRYcyx(Kq--yPN`iWk24F(~X+U`Bx2s*(}HXbJ+ z%tAPx{2|OD6Qf>5tJ?GGz$2m^@qhgsz2-c@a^$d9O%$d9WdbncTfcw^=Q<;uw@)hC z*JjMkGVBk9cG!?S9?76!kN*o$+{>JIy#JF~GNVugrWAos_RN!k5!&gxVHs?OurG#J z^s);mJ5)J(j_hKhU}QW&l6DSNxMrAuup_81NWj7zXS93<^|a`Aw19)5C@Au zJAm(X>UWx1niN3KGK&?}X7+c|FUvw|EJLVXiQOt8x{VEv^iW@pWa0S3icLGgXz2qV zyUh9tK9V0nn9jRj6TQe$3RR+TPm}V`>Va^#519S!X!H&oo!*a3dY2!C-Ywrkugs~p z?U5v#lobA6GU@#Q6;0=F(g=F_u*;EM`s=XGdN<6qQ8LZAwNSsC($u~EtCmZ7ERVnh z{}e(p)>9L8OQ4aG(7g0X9i`cm)HU5S$b|>eBcPQ%-auLp-xgeAgRH(Qcbq7 z*Rl~Gp5nNX7Q1;Z%*pIgdD#fFtao&^L4f?Ks`*Selq; z#ami|^lPutyU_*D)yw*fwtkAFG#&#@NVNnI0|x`6w9lCB{3Si{;Fy*xGHepEE1DgU zy}M+T%~;x}KJ6)vUhk~dGdQ}TE)I_SFhHTs684yVMS#lsqV`OT14ijVqvR6(Ro!MR z%MQit>%IhWr3ogWG%f~}*AGNWa@Fv9G_mmjd7J!}`#p6-58#`<@!aRwVWV?K=b#mn z<7odSma%>(63`UcXdB8CkfN{2bLdMj;=;BV>#3yVC-ygv2&>NzC2hej)1rYtz5f{+ z_W1K0ns=VtLEwgEK8Q`)E#m>IB>LqvA(m;dso5wX3&eAb_2iUQ=gJFWoW|0AkQN;h zLK*CZoSr4bdg?u|A26=W8tch69_d7!`=zD}A+Fco-$snc!kpl#i8t|N+8?ksK&j-Y zVPaFT+imW*^~2)%3j$Bx#;V_H29DT?k{$FPADZjMz_etK8(A(ks$hXs%> zo2km|8l0U0woFbbReHf^^hc<#`m5J0)^~*ZoW>PdGg~Qz!1;oX7#W3df?0_>$xoZ! zFJP@1QqqBL5Z#|JN?fs$EIcZqPMcohn(1)_rWWlAJ;!9FDgSUuj!jSts>x#VY0)`< zBprQ^P!m&c;&)@%2<>!AVM%{cPlU&{e7*9ba{wNMne0c!XG zFky^-+@^-v@)5DUp3@_%T*zT_^sA z21cnHevb$B>Eo+IUyRe{;BST+`7xqy?#zuYv1J*VdWnOkyu@jE9B3!QZHx}nb?NfA5o?9fScm50FX-5NSdE-il-W|0&E@s;_ zzX9BqiZ)l)2bckAMdV;&bMD>0Agx-k#!E1(=1aP3~PI+)pB0E;?P^k4WZ^aqTk4o=nJ za<2V~io?a>!d1i)07V#IeilWYujsfIpCB=XH7pteqhW~>Yzn>F&Q!%7B(fc^Pe};2z{Vl!! z(_iWLv4rF=*v@a8=JUFv=z{uJsSDqLBf4c0EF_N|G2a#b0eWCD>=WfMDg$B~4KdL} zkfSKOAJ>WCV(T$ueJA*<-u^0lTs2%xHiJF-Cq+WjGSD(H*hg~-K+EI~qNWA7fY_~H}g!^^%&L-Ab4Et0HNW;Oc8H9F} z#aTFAg?KG$sP90&R_0?WMjC~V+(aoKJ6jZR%WgTza3C5rY3(Bs5Jj#bqRbWtL8k9U zJjOxxgpDFf7fyg0{u6Q->vvN*9+AdM_wLq8j0ZUWnxk-+8miPj1nyC^bWc%dOw4rA zhW5}vuK$y6@5h7P-sGTkX)y2KGftT2>%mS@Z!)`$W0$ul*DYFJU0;HD!`o9GT9uO# z{6OEKK0WJ{YTdin@Z{>V;l%CHJ(#rY*FCw0_kiwk8JJFc@(gcW_vFI`(mm(W3HKDr ze(+PXA7r3nY-4lgav$y8n7+ZmZLEqIxD)rIV4ygyEJ;W$C5telZzMF#g8J02TV6iObj=(lHoXf8y|;1#?7g=6EHn(C_eIgrvf(5>w89 zpadf%JWCPJI>4_NX?~^|LZhDG-RJlo!cB=?+ zrXe04UoiiSdIw08nb~9RgMf^(uXfSgXBG7b=f#|ln)aHO+QHQ|7;9LeyOB{?p?g@o zJ#PS{;-{fDkxqSwwp@=ra|zn$(|?P#P=e$v+CsVLp}6=fWn@)!SX!|V6`fZsL}AD$ zihx)AQV`NZVqn_C6*-I|Vo`2Kb(`!CH9(*?J!vZ7OCvR9T{&?+B`)jMA zLi&60KcQC@b;>&YDjK52*Tpyy4GGRYc5)Q+IB2|L9w$mckj)Jf42@iyhz+SoYL(-( z2C+<{%WAN_IHxh&E7?N-8th~Xb=1yS_a%oP?G#!AF7N3}9l}{j*iZ>EKC!mHGHd(J z$lBh;+CCH?Quu8smg^6uzw4#H%RyolvSH$k#R&7c%wgQgwn1XVKqB>5ZJ*fkrArNS zPqXFAv1<4%@o=mf<}PT62+`xQDyUfOLXc~V>P#hNGN?Ss+j&kn8a z8)HuNvA8-G(_MCk+rcSjaGV~(i=k9c?35 z_~Lqx`^DyJtP<$xQK|A7zXzEjqnZOh~;B>a4H5!BP2+|wHFT~yfLBA(aSi6XLi$N5L8YS zRMfla>C-<5#m4XfjO6e*x5V z$uIu!TXDnybUtw6-xtJB1PMtzcUW;mwTGyKtKu2IbjdFCMO@BaGXSo1T-0J!7%ovQjY3ifGAv46XNJuRB*|21osbk6H z5ixXJXV=bi@73zzrD2w>Rhu5#$)<e`m(L0o(XW_}a+23VWeJ?7l_s17tj+`h41~sU~G{ehA zE}jS%X8Z@=igPPHqd&ht5~pD#oqs3JF5(Dpke*HAWc;xV;0S-F5Ce!2%%Mxe`L^im zqSZ{7Ms2016ZL+E?#0D@CufYNcVjsQW-o@~a8nzdxWXvKGH!4V)l3shZSV$Npa9n} z7XkdlYy?iI5CVrJ#}So=AX6;l6$)j*8A;kLZAEn5?0Y`}S0pN@r@8x>WS_mkME6$&@>W@;#q8o&!dHdbyVRUUMzYPoz z2XiEUi8g>BhqW8QuQti>!lF*MaiU{}LxGvxQ?%G}Y-pPlO zF_;ICQQ`)wilI|5baw+}SlwonIVbl=dRL8gd!6dt+&a^}&P45yytj$r=kG)ME*xmF z>XcXsB>E#5Kd}5GrtD6x;KK#nrQKl!X1HHbBNWHF+X`FSu<0271pp%xtDMF&ta9j= z^df9>;S**mLvtHH6mnk2zwRz=9;%Hgmp0q|h*|ZoO}F0CMG{5|Ox~?s*d1+hJ*Ro7 z9)fQ%rAR(Ot(j$NjeXPzbH0t8DEyw(8ViSKS&+vXY&+;T-2@!=cj+&!>w8$3S23v` z3*O-`86Cn>52~&7k+6^<7w;p7^^IYO6MLK(664ps3tcF6M1ybWPu+#+b=!rCMK|GD zk35gRQjg$yM7_JCYvV?gx#0-0D~YZ-i&8Dg7Q2R6a|a}P*PSe$X8nW%Vo->*AfMVw zJv1VuVQk^w%q@%$bvoTKeamJ%VyWJrD>2meg`?E=S@!J*9AmpeU)a?3FEck*Le156 zHCzb^OXO(jZbQ(5sN{kVdOXnO0z2F_uD;8iP2?FAdfS`G!z1NrJjJ?(E;&Yxj3-I- zKC4??Wp;}QWuk^@_SiF4Amp0kJ~hJi6m}3u-TjbIlvAPREknIz$nFbOS+|poC3*?; zUx-s}pPljeDm5~RMWcFRM49=ovOw&pC{k8xyPC{4+v8NXnfwDJ&$zNU_-ErO(XQ=t zZ5Mei4!(<(g2*^Du_2qgy(BYqK*gAPZXjy0=Qw?_k3=P=T@Ga#1%4}mJIU2h-2aDd3W!O0| zN}c*%Fc$wDK6YY0VyM@a1D}LgjR^Z!bj!=EkSHFlkj0VJY*MszKU-GM&Wpc}vjkA{ z*4HUnZ$B1p+#s0Yxk5W?s9D2@J)SkqMc75`KEV3hRFME=m)xT>yCvZn0!R0VD&e20?u_NY&n zy%LL17Z9)Lk;hr>0n8aG-;hYKg*tQHQKAIKSGyOU5=37jn!7PK_>S5xQihdngpvvM zN<56R!NsZS`YLjP8ScG;i(Y->W1s`w6`EZ73arC0FV%Xqi_ARLioeOc6n=~?%od6; zA=fPwSq%kHVPgxU)TblNSna;-l;F!Ziz?(kgBHZjtHapBaUeP;!=-nB`t#s#h5v|R z*WI4Y%~y5(9puoH8;MW>)kq`2vErHP=afCg5LMQ1%f$cyKeQbKf8v?Xkq;Z-d*e_4 zi6ep-y;S@Z=bfv*eI-q98)1G>zHg^MYjAX4rM=s+QT4PhHx5_ zt~HZTT1wK%Oe#ha^}||UIhQS6>ZG*(2o4Wtz~a1*2SW_o?DzwKiaOa3o@V^&8Sb~# z$T_Iay49vT#i=O79Gfj*efBhSImP1X;kUp>w>e&U`>;;367m-ZhQQz;r##1s-F3Zd z)na1<{RMdf4AwzRD|PR|+O9Y(AjgBKJR!9KM9l*99dS1xoRI3i{ZV0vKrwk(NDeEX z_t9*bJJW6AFiheE6bs_cD^2ff7m8$IsmmCn-~3h_;j_R0hmkn1jimDxJ+!T9IIoPv z>7}RlW8i#keuVY4JWP+km)yQG-I<{|Ob)7wF>}OJ1`}v=9y1f$W>|*9pcvYjcf-=e zG3?A}_~H5e|B^nYl&SQ8g<&Te^rJ zv9e>WZzgVlen@@YN)7GcpJY2V=6O?N+Ry>gHRg@iF%dB@^e-Nl;r6IFl!nR*C-^Zr z!M0C&-R|IO>E<_R12i%B#JWH{oRV(!pSuK3aDPaynv3zq^7qfhksIm>J2=p;T}Aj{W-!Ia=?8v(p)CXjT}I{=EP`h^bhK}`_fxK4eM;H=`)ib) z8a|DM|LX|Jp{8uN$AMuyCFE^MAFVeXTMLwh)w;bx7TUQuUxHYi_C%?a(QzqwRxAHp z;0E6WJQm6kKU?si6&=0&e3mci?Tz1qz9$z?Ab}5iglbJNHi}}&IQ+SvVfoq+ z4a%|fS>UhRNhLDZd=1gU+j=2|XT~5ABmcRnHB!K(-X%+ppERP}#Lr=3%=~W;=U2ne zfsPq}fbqu&x@u$(!c^Lip-v;^^bwZT6IX$3cO*>{6Q#6gR@2ypQlnX}7D7`a`Djp* zEb#fpVMF)xe7_uwqtk&mJt+xCw$(NVqq!M<+$7$urJQ6T9r9Qg<- zQTx5N&dJ-OvkUdtS~GDrFVY)42!2C2zRy#5ZAaeiF0q{#y)^^J$ONO-_}6hZEiewj z;?V4>u|wx}LS3UfB1pk*Li5Lic^mujOncJtFdjg!<6<+MZ-zU~aD^ELWmwj6A0G4% zkwsjP&(cKZD?{xg;7@lC4H$qi9&2!%qf38>c_W8?=M@kMwV0xzx~xEtX+mfORV7cU5j5Vzy9Cxdn@`zXy=^&4ZkOi;1~ND>-uiwYE3E5 zjL*)%d?8b=D1B@Tj#t|;U|el8mzbQ|*_6w5ZwLuHxTFEt?jUT>%RvzT=q0gk>CMK9 z&ta}xM7R769Ul_sv1$bDHwf&}$dl;1Y`>>r`H~BTej5>*8PFV_cY94|4j!)XR7eGx z7NK>ps6E4o^drYiZ&vU;4B4S>TYMGj63xwtM)t#kj4Wv-j#q{Tlsg{MvN$^%T(^@I z7hNK9xPwy$t#6pG_b_%xmpqYe>*5IUcT`N@(ZR_BY|!tcp6sGQJ>ZN^+pK%QVp^R( zz=6)>^=$@Dv`qt#tQ8LE4pq#X6^)o;!Ayz1YFhOIkco-Hbss7r9XwS0WN>WjK3eVW z5hA(6Fl&H3!l8k&+z4*46QpDJFX2E1hV9=o92(Kdpz&}F^u6#wPK13@+dHF6FzKNe z5j-?`Z}bYA{sMHatZ0`o4${4t&;~r5=p07ks}aw9{d3TvfNt3}!Xo5b7+{kF<_XzE<59y)AR*+N|cwJ0?bwS0DM~_K!z_ z=*_*At)ul9Cim!F9kMVHpu|vDVe2(E;-#4MR!?Sfy;*+@R&AyM-v(|uf5kus>(e-k zMc)LmhqkN!MX=}5>j#r(g}Sb7U1;l`#ikwK`LVs_i?K~-A~KY)wJyx$D%RWDncX-_ z&^|u&z8d;sZ1ca<-_It%kn&%Dh7PelU2_~^E)pWN0iTH_`Dc|TV8SW$g*^&{UplDzCv@-W)VW`6n%E5| zZ?^3muQNH3Vsn2UaJG(*-eODsLezWR+AP|clRJ;}K#fxznc?=e&YxhAbc&OzO`|$E z3(ACf7VD$vYo?(V18gmUrV&7nXo+JL*?_rn+&hchh7|@I77QAV9B=4nv_s{aX3l{$W zE5CD&#_y~leiw@${K-Sx7=85@A*kciVHt2)G-qGdY3Yj@b zzP%Ho!I?vzz!q||^;|$gj2`mAHeASkZ*Bb{58>UgX7lygUUgRFmh2y=v&MyoaEG$1b@4k z^81W`g1-d={{(+U|5N_rxAR9-Sy-aM#3_ls zgD3a)W-Ogq9c|biLgwsk_{BmA{$l#4wqHI5+p?o$B{^ud!B~md8l(rk9Y3KNBCw5u zM)^3;IkY*CE+*R2p-&ug`Ot92Ty7M^C$jmr8>6EOo$hOLwW(42G5Tf0z9HI&R zu9b6(qE2ax)cZT(noa7`7w4(%mqC`-X2QwV0}e3*`8t*hKVJ}1#`#TLsfxG*B6Mo7 zs}$w;k%-WK9btQ36@vK8ZQpoaKNkltj10^w)X;yDY*~)U-UcoWWtL?}FSqGsap;KJ zeu$^QJ{5&$TTA@jeh%`^Sj${P2T`krEnN4}rC@pcM~<=mp@CzX7Gqb< z7K6u0J9AR>DmV=wsjWHE-3c1O=R^OvwE6sKaS<@u{AWk!Otrbaalg0qc={{3dA3iv zYaq5oKHiE!9`0pF8-~ITibam#TyLPeo4ivPda-3Ctq26VHiyG3{Jhlh6)YQNad%mE z;GOPFRKE}Y`XtCi&w#x?$D3^-V}!tgLi3pD%QzXi&vHmc`eR~01;5WKafG_^qxbOF zHhS59z4QQF2BDYeL?sx0)Vp6q61o(WAc_|PlyK@kG#4~V?5rp48Gd00mE>v{Gewwb zX+OGhG%#*vqg~w)Kn#41C{=K=R5ATQ?wY9W9%$8KImOi!6p!+drjN8cZC^hm90ON{m?q&Dl(UOT# zx*zg@RHaC8v>zHSB{Nl6$J-l>4PKY4Xlv6BuBp@S6vVLK8RmkB~~NE zc_Xw#9{6_7+#sYC`M$|7Hj8Fw+YBKK_tiQ2Y)1!(C`}VMlrGE>KoXuHE^Ai_ei(mN zfHvQGt~o)2y@LUSAsV(n@Xz8`fzrAiK^*YI&!*x(!(MjuIddPy%+NLVkC5XKRuSaqfU+fxA4OTWT4^}IahGR^8Wm5id9I*IGC)Xt)Jy{9V2hO`B zP+kxy#~Ybe*4C|dU3{@?igJr6@D^W#zjj4seO;j}(ToHwVj&=Fe6-LR<*(BG<@Eva z`X|1aiSTNrT+^ynHsbY8^_30y+t}dO@J=G&n>DMhD_;?Co$Z>lV6kuBf_Y24b1<>W z$IGd-V8B)EDs-KFwhO`u)@dxSaI$O78kd#Knxv#tW|Kj?p>g%M5GUur%+GIXKw{~kdS;WPw3M%6IrE)SQC|NY&4s@eRduew>iP(n0xOrVs5VJeR{8>XSr!HM4cCX*_N7^&7s~Kl{Q2`y;{Jv> zY(jB=L*Pw8_~lXJ?AU5%pA}vzLhhE2LmZ9_iuYr&4DR9ike6i&CxV1QmGugsK75Z@ z%CnRhIx7$!a(}#^A;$Jyo-HTEnG71ss)P`*VtpnDS==` z1uR8Xb+FD=(Xg_yu1dW4tkK_~HB>azx$p+F2ESRdi$%IB8>#}X`UcHawW>CtDd=^s zhH4kyh}M9fhc}{CH~3dpRZ3lial5&`p}F29Q&><~;HE^_JsW7;TzoTI&|d}HC#j42 z1_ckmlh#bu+qE1e)i<~*%e8Wr=&ynb1~e3w7Aqf|JTnfZ^jP0V^d1V=Wn|${BOfaPdi$kqs67?`@e)) z-k93@^Ei&$%l(bzN@i{2bonIoNhkov&7j|Pj%yl+Mp!|iTO7qjGIWC%{0Yu_heAHn zR&cgVTk`8xy90X(${M_@hjyb5?{&p`e#7QFrER$@f_Lm8z7H^cfl6fN{x0+5RdwQ` zU24P|z-VrAkWSmfAKmgt>i2K3iyz=tBAfuY5cl!8Y46~tHOxUgSmY{ z|AUL~tF^KnLY?4MJ0{>h1NRi%c;|>?5$@%<{kZSMeJ}2x;{F}(F5JDi-^YCjcg7Ep zANLu!r{FHZy$JVm+P)&KDMk%OVT@QLPgpCj>u+ss)QZg0rA)c}yxK%=J=RMlXZ~q5t-Ulc_>isaN8i$50aLj&)IIc%6xxXBmhm-c5`I7oM$)dd;f{w}p2(^!#$* z<%tye$HKcDAjehGV}Gav-W=TQ2fT>Kdk$`#LTex%`^#oL6yrG^Zvo=iXNbps)Q5*n zxXBC13R>(>*WhMfA|CtQU0{O!&AP0|Wr$#(As+h+-{HIj&sMpN=Y2D7_Ax%QpUpvm zE_HA=Yr&w@w6|$MrP8p zF5>apfZM{`54`=rn?)wfi+H^6#ckoa(5GAw=8YZ`2?1rt>ml409_`s02wohm6RDCG zA0ILCSkxxqZ35o=6pDGd%<$(3NsoA8th0pCKo;*rv7R^MnRskF@m_7 zg~yi?wtWNM))YLxeDI-fD0c_&C}Wl@-q{Vj9z0uBVY%dEH*Txkx<5gmuN~$cNunOa zeJur#_Ph~zl&N(Qhc{Q$Eqac)-Ql?I==D7SJeE)XT)@i$-gWF)=H)WO4-t}{r6a^k zZ8xUz=9ofS)^#rM{$c?LiNlLwiW(`qm!h39*qw&>GTf{Kb%wf}O0N!UYxi)FGcSvt z1*e9zM}*~D-KE`;uq>JOMZ&TK+7St}bZIXnECoWlAYrzs?Eey$t!a&mnWkp3Kg)F1 zLiSq;%L=f6N|=^M_CpDC)W^GI(cdJ@o~wxW(o6U_0ki)|SQRk)iG(RR*&ig#R*`xy zVUF^6hpssuXPOq9dMwk$%tYv`gryfjy_E2Y41_-+VR{kJBj}HWa|BF%k}%d(olppyr24d zgnYLsTas`M!p$l8>E(0GyDO#qb%1G45Fr0}|38UnUaSw#+wpaW zR{5QP`98-aLgIHLYnSl+!^z#A#DkYsSCgcy`t^DQ0Pl0a*%pciW{ND$dzx=fDe+`(w>9ged zd%!%2Zt?#vV4gF!_&);Jy4XJaEzp6d?2a1{$@=i13{RZWQjp(?fM>Pa9j_u}`c#D0 z#r&71!B?ii-vi8(PZt0FwDdJ;unw5p4;KDU0dss{nYN6Rln%gF|57q0D7(_&{eUh1 z+?H{I@*!ZWKfCbNg?_-+MeE^|CQ?KZo|FbpOM~YC<~BJk-+0Jp3E;+Ub_d6k36Re+ zzz3eRI|fX6CE$e*+8tHU3#P9D+=98f&rH{e|FV=f(|-h*eg~2T{wBbUyX=ljP53_m zbK9HpVfkHvt;>Z6{%XLwTzL2pFzqR$iO*lBZh8#!3+MsM`yvHj$RlOUBz!tx%RW!Q zS03`x(x<1v^8mXZw>!=^>w8UFdJW*7mtZf<@&kmQwL5->vdB+34S!=A{5!z2w&PuK zCjM5yjW5_8Q%!gm;7w?cvrPC+!2Pg4w5z259$@QY{RRLp{CB&9$CjCHmqVgJVR|;; zEbs^SM5LbvcwwjAanOV(19qXkIo31(48W=16Sxepb&;Mg4ZbA}UY7V z`i}$VcqH5NWaU}F{iuJlnf?;-AFw-UM~VLyU`zkAp#KK}_dq_kn)vkRTb>`Lp8*%3 zANooA#r(%u4xWJF*8q0?#P0aOr2k#On^FEW6aNC@gPxWDlC=B_0rU0F7XQoB(zP^r zJz$!>j0WN$5YXitjetrx%%alhJ z?Dfyn@H+tC^n1Hwr^#{==kq5b$Q$qwOYqC=K6^hR;uF-xH_^=EeG) z1$YzIoGf~UfLmU~nul5bMa2Jw-EpD`Uj}&9pJn@z|M`GtrO;mnc+-#Uj!7o|&43Hh zzW0C(>mLMcT`Ye+;Du<9d^|J#{+&bs>*^3F1*04Fu_+_xeCzqB7E`#t3STV;L4S&t#Z5z=7?hEF2a((&w^Nt^*mgP zSTz!*RINJCkA-fzf_V$qEi0BQ%L}GWb-TZ|H^>EXbDszsP$+ldBJYwV-}B8aTj1eN zg3nhlZCX*$HzX8(L&B8lg<`pMcrWSbgr`muXrj(6p4<%YI2}EYd&Ur&c)+$x;OOiX zU5HQ69IZly?jn)39?NiYr|GC@QwrTj&p&nQHxQm$H02v|Ou69b2}R!|+rkSAkDlX# z8Q(x^`qXd8F=OgC)Z;?;)T5U%<-#JVDO|3E9?5o*vhs;F)B19JFQ>7*qDrI@%~T_? znI)FdSJnn9kXJlkr!`z>J^QWgnxhGQ$~4qlWT~vTGFa>JN5#KjI>d3={Hsc4&-Yz5 zckW{E65o>9CG)-Por0}$zrTF7j|=!CBE;8<6yKchVK;4_N8w7U{QeHmMt&%0)~w<0 z@C<#MC^gdjpiycRcRo{L3J{wNK5jSo%pFtghU2q9mS|IUL(}#Qu~scN>ddbMRRn@Q zv2<+mD=27{5NiOZ3(WT5=EhKIX&-3vUD4!QRJ8&dd{ur=9lxT44piOft1ACBNYHwIbx^ zx0I>Bs+k}0VixfYFH{?hrDlQ!z + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "udns.h" + +static int curq; + +static const char *n2ip(const unsigned char *c) { + static char b[sizeof("255.255.255.255")]; + sprintf(b, "%u.%u.%u.%u", c[0], c[1], c[2], c[3]); + return b; +} +static void dnscb(struct dns_ctx *ctx, struct dns_rr_ptr *rr, void *data) { + const char *ip = n2ip((unsigned char *)&data); + int i; + --curq; + if (rr) { + printf("%s", ip); + for(i = 0; i < rr->dnsptr_nrr; ++i) + printf(" %s", rr->dnsptr_ptr[i]); + putchar('\n'); + free(rr); + } + else + fprintf(stderr, "%s: %s\n", ip, dns_strerror(dns_status(ctx))); +} + +int main(int argc, char **argv) { + int c, t; + time_t now; + int maxq = 10; + struct pollfd pfd; + char linebuf[1024]; + char *eol; + int eof; + + if (dns_init(NULL, 1) < 0) { + fprintf(stderr, "unable to initialize dns library\n"); + return 1; + } + while((c = getopt(argc, argv, "m:r")) != EOF) switch(c) { + case 'm': maxq = atoi(optarg); break; + case 'r': + dns_set_opt(0, DNS_OPT_FLAGS, + dns_set_opt(0, DNS_OPT_FLAGS, -1) | DNS_NORD); + break; + default: return 1; + } + if (argc != optind) return 1; + + pfd.fd = dns_sock(0); + pfd.events = POLLIN; + now = time(NULL); + c = optind; + eof = 0; + while(curq || !eof) { + if (!eof && curq < maxq) { + union { struct in_addr a; void *p; } pa; + if (!fgets(linebuf, sizeof(linebuf), stdin)) { + eof = 1; + continue; + } + eol = strchr(linebuf, '\n'); + if (eol) *eol = '\0'; + if (!linebuf[0]) continue; + if (dns_pton(AF_INET, linebuf, &pa.a) <= 0) + fprintf(stderr, "%s: invalid address\n", linebuf); + else if (dns_submit_a4ptr(0, &pa.a, dnscb, pa.p) == 0) + fprintf(stderr, "%s: unable to submit query: %s\n", + linebuf, dns_strerror(dns_status(0))); + else + ++curq; + continue; + } + if (curq) { + t = dns_timeouts(0, -1, now); + t = poll(&pfd, 1, c * 1000); + now = time(NULL); + if (t) dns_ioevent(0, now); + } + } + return 0; +} diff --git a/ape-server/deps/udns-0.0.9/getopt.c b/ape-server/deps/udns-0.0.9/getopt.c new file mode 100755 index 0000000..f5d476a --- /dev/null +++ b/ape-server/deps/udns-0.0.9/getopt.c @@ -0,0 +1,165 @@ +/* $Id: getopt.c,v 1.2 2007/01/07 23:19:19 mjt Exp $ + * Simple getopt() implementation. + * + * Standard interface: + * extern int getopt(int argc, char *const *argv, const char *opts); + * extern int optind; current index in argv[] + * extern char *optarg; argument for the current option + * extern int optopt; the current option + * extern int opterr; to control error printing + * + * Some minor extensions: + * ignores leading `+' sign in opts[] (unemplemented GNU extension) + * handles optional arguments, in form "x::" in opts[] + * if opts[] starts with `:', will return `:' in case of missing required + * argument, instead of '?'. + * + * Compile with -DGETOPT_NO_OPTERR to never print errors internally. + * Compile with -DGETOPT_NO_STDIO to use write() calls instead of fprintf() for + * error reporting (ignored with -DGETOPT_NO_OPTERR). + * Compile with -DGETOPT_CLASS=static to get static linkage. + * Compile with -DGETOPT_MY to redefine all visible symbols to be prefixed + * with "my_", like my_getopt instead of getopt. + * Compile with -DTEST to get a test executable. + * + * Written by Michael Tokarev. Public domain. + */ + +#include + +#ifndef GETOPT_CLASS +# define GETOPT_CLASS +#endif +#ifdef GETOPT_MY +# define optarg my_optarg +# define optind my_optind +# define opterr my_opterr +# define optopt my_optopt +# define getopt my_getopt +#endif + +GETOPT_CLASS char *optarg /* = NULL */; +GETOPT_CLASS int optind = 1; +GETOPT_CLASS int opterr = 1; +GETOPT_CLASS int optopt; + +static char *nextc /* = NULL */; + +#if defined(GETOPT_NO_OPTERR) + +#define printerr(argv, msg) + +#elif defined(GETOPT_NO_STDIO) + +extern int write(int, void *, int); + +static void printerr(char *const *argv, const char *msg) { + if (opterr) { + char buf[64]; + unsigned pl = strlen(argv[0]); + unsigned ml = strlen(msg); + char *p; + if (pl + /*": "*/2 + ml + /*" -- c\n"*/6 > sizeof(buf)) { + write(2, argv[0], pl); + p = buf; + } + else { + memcpy(buf, argv[0], ml); + p = buf + pl; + } + *p++ = ':'; *p++ = ' '; + memcpy(p, msg, ml); p += ml; + *p++ = ' '; *p++ = '-'; *p++ = '-'; *p++ = ' '; + *p++ = optopt; + *p++ = '\n'; + write(2, buf, p - buf); + } +} + +#else + +#include +static void printerr(char *const *argv, const char *msg) { + if (opterr) + fprintf(stderr, "%s: %s -- %c\n", argv[0], msg, optopt); +} + +#endif + +GETOPT_CLASS int getopt(int argc, char *const *argv, const char *opts) { + char *p; + + optarg = 0; + if (*opts == '+') /* GNU extension (permutation) - isn't supported */ + ++opts; + + if (!optind) { /* a way to reset things */ + nextc = 0; + optind = 1; + } + + if (!nextc || !*nextc) { /* advance to the next argv element */ + /* done scanning? */ + if (optind >= argc) + return -1; + /* not an optional argument */ + if (argv[optind][0] != '-') + return -1; + /* bare `-' */ + if (argv[optind][1] == '\0') + return -1; + /* special case `--' argument */ + if (argv[optind][1] == '-' && argv[optind][2] == '\0') { + ++optind; + return -1; + } + nextc = argv[optind] + 1; + } + + optopt = *nextc++; + if (!*nextc) + ++optind; + p = strchr(opts, optopt); + if (!p || optopt == ':') { + printerr(argv, "illegal option"); + return '?'; + } + if (p[1] == ':') { + if (*nextc) { + optarg = nextc; + nextc = NULL; + ++optind; + } + else if (p[2] != ':') { /* required argument */ + if (optind >= argc) { + printerr(argv, "option requires an argument"); + return *opts == ':' ? ':' : '?'; + } + else + optarg = argv[optind++]; + } + } + return optopt; +} + +#ifdef TEST + +#include + +int main(int argc, char **argv) { + int c; + while((c = getopt(argc, argv, "ab:c::")) != -1) switch(c) { + case 'a': + case 'b': + case 'c': + printf("option %c %s\n", c, optarg ? optarg : "(none)"); + break; + default: + return -1; + } + for(c = optind; c < argc; ++c) + printf("non-opt: %s\n", argv[c]); + return 0; +} + +#endif diff --git a/ape-server/deps/udns-0.0.9/inet_XtoX.c b/ape-server/deps/udns-0.0.9/inet_XtoX.c new file mode 100755 index 0000000..174b8ed --- /dev/null +++ b/ape-server/deps/udns-0.0.9/inet_XtoX.c @@ -0,0 +1,327 @@ +/* $Id: inet_XtoX.c,v 1.1 2006/12/04 01:55:39 mjt Exp $ + * Simple implementation of the following functions: + * inet_ntop(), inet_ntoa(), inet_pton(), inet_aton(). + * + * Differences from traditional implementaitons: + * o modifies destination buffers even on error return. + * o no fancy (hex, or 1.2) input support in inet_aton() + * o inet_aton() does not accept junk after an IP address. + * o inet_ntop(AF_INET) requires at least 16 bytes in dest, + * and inet_ntop(AF_INET6) at least 40 bytes + * (traditional inet_ntop() will try to fit anyway) + * + * Compile with -Dinet_XtoX_prefix=pfx_ to have pfx_*() instead of inet_*() + * Compile with -Dinet_XtoX_no_ntop or -Dinet_XtoX_no_pton + * to disable net2str or str2net conversions. + * + * #define inet_XtoX_prototypes and #include "this_file.c" + * to get function prototypes only (but not for inet_ntoa()). + * #define inet_XtoX_decl to be `static' for static visibility, + * or use __declspec(dllexport) or somesuch... + * + * Compile with -DTEST to test against stock implementation. + * + * Written by Michael Tokarev. Public domain. + */ + +#ifdef inet_XtoX_prototypes + +struct in_addr; + +#else + +#include + +#ifdef TEST + +# include +# include +# include +# include +# include +# include +# include +# undef inet_XtoX_prefix +# define inet_XtoX_prefix mjt_inet_ +# undef inet_XtoX_no_ntop +# undef inet_XtoX_no_pton + +#else /* !TEST */ + +struct in_addr { /* declare it here to avoid messing with headers */ + unsigned char x[4]; +}; + +#endif /* TEST */ + +#endif /* inet_XtoX_prototypes */ + +#ifndef inet_XtoX_prefix +# define inet_XtoX_prefix inet_ +#endif +#ifndef inet_XtoX_decl +# define inet_XtoX_decl /*empty*/ +#endif + +#define cc2_(x,y) cc2__(x,y) +#define cc2__(x,y) x##y +#define fn(x) cc2_(inet_XtoX_prefix,x) + +#ifndef inet_XtoX_no_ntop + +inet_XtoX_decl const char * +fn(ntop)(int af, const void *src, char *dst, unsigned size); + +#ifndef inet_XtoX_prototypes + +static int mjt_ntop4(const void *_src, char *dst, int size) { + unsigned i, x, r; + char *p; + const unsigned char *s = _src; + if (size < 4*4) /* for simplicity, disallow non-max-size buffer */ + return 0; + for (i = 0, p = dst; i < 4; ++i) { + if (i) *p++ = '.'; + x = r = s[i]; + if (x > 99) { *p++ = (char)(r / 100 + '0'); r %= 100; } + if (x > 9) { *p++ = (char)(r / 10 + '0'); r %= 10; } + *p++ = (char)(r + '0'); + } + *p = '\0'; + return 1; +} + +static char *hexc(char *p, unsigned x) { + static char hex[16] = "0123456789abcdef"; + if (x > 0x0fff) *p++ = hex[(x >>12) & 15]; + if (x > 0x00ff) *p++ = hex[(x >> 8) & 15]; + if (x > 0x000f) *p++ = hex[(x >> 4) & 15]; + *p++ = hex[x & 15]; + return p; +} + +static int mjt_ntop6(const void *_src, char *dst, int size) { + unsigned i; + unsigned short w[8]; + unsigned bs = 0, cs = 0; + unsigned bl = 0, cl = 0; + char *p; + const unsigned char *s = _src; + + if (size < 40) /* for simplicity, disallow non-max-size buffer */ + return 0; + + for(i = 0; i < 8; ++i, s += 2) { + w[i] = (((unsigned short)(s[0])) << 8) | s[1]; + if (!w[i]) { + if (!cl++) cs = i; + } + else { + if (cl > bl) bl = cl, bs = cs; + } + } + if (cl > bl) bl = cl, bs = cs; + p = dst; + if (bl == 1) + bl = 0; + if (bl) { + for(i = 0; i < bs; ++i) { + if (i) *p++ = ':'; + p = hexc(p, w[i]); + } + *p++ = ':'; + i += bl; + if (i == 8) + *p++ = ':'; + } + else + i = 0; + for(; i < 8; ++i) { + if (i) *p++ = ':'; + if (i == 6 && !bs && (bl == 6 || (bl == 5 && w[5] == 0xffff))) + return mjt_ntop4(s - 4, p, size - (p - dst)); + p = hexc(p, w[i]); + } + *p = '\0'; + return 1; +} + +inet_XtoX_decl const char * +fn(ntop)(int af, const void *src, char *dst, unsigned size) { + switch(af) { + /* don't use AF_*: don't mess with headers */ + case 2: /* AF_INET */ if (mjt_ntop4(src, dst, size)) return dst; break; + case 10: /* AF_INET6 */ if (mjt_ntop6(src, dst, size)) return dst; break; + default: errno = EAFNOSUPPORT; return (char*)0; + } + errno = ENOSPC; + return (char*)0; +} + +inet_XtoX_decl const char * +fn(ntoa)(struct in_addr addr) { + static char buf[4*4]; + mjt_ntop4(&addr, buf, sizeof(buf)); + return buf; +} + +#endif /* inet_XtoX_prototypes */ +#endif /* inet_XtoX_no_ntop */ + +#ifndef inet_XtoX_no_pton + +inet_XtoX_decl int fn(pton)(int af, const char *src, void *dst); +inet_XtoX_decl int fn(aton)(const char *src, struct in_addr *addr); + +#ifndef inet_XtoX_prototypes + +static int mjt_pton4(const char *c, void *dst) { + unsigned char *a = dst; + unsigned n, o; + for (n = 0; n < 4; ++n) { + if (*c < '0' || *c > '9') + return 0; + o = *c++ - '0'; + while(*c >= '0' && *c <= '9') + if ((o = o * 10 + (*c++ - '0')) > 255) + return 0; + if (*c++ != (n == 3 ? '\0' : '.')) + return 0; + *a++ = (unsigned char)o; + } + return 1; +} + +static int mjt_pton6(const char *c, void *dst) { + unsigned short w[8], *a = w, *z, *i; + unsigned v, o; + const char *sc; + unsigned char *d = dst; + if (*c != ':') z = (unsigned short*)0; + else if (*++c != ':') return 0; + else ++c, z = a; + i = 0; + for(;;) { + v = 0; + sc = c; + for(;;) { + if (*c >= '0' && *c <= '9') o = *c - '0'; + else if (*c >= 'a' && *c <= 'f') o = *c - 'a' + 10; + else if (*c >= 'A' && *c <= 'F') o = *c - 'A' + 10; + else break; + v = (v << 4) | o; + if (v > 0xffff) return 0; + ++c; + } + if (sc == c) { + if (z == a && !*c) + break; + else + return 0; + } + if (*c == ':') { + if (a >= w + 8) + return 0; + *a++ = v; + if (*++c == ':') { + if (z) + return 0; + z = a; + if (!*++c) + break; + } + } + else if (!*c) { + if (a >= w + 8) + return 0; + *a++ = v; + break; + } + else if (*c == '.') { + if (a > w + 6) + return 0; + if (!mjt_pton4(sc, d)) + return 0; + *a++ = ((unsigned)(d[0]) << 8) | d[1]; + *a++ = ((unsigned)(d[2]) << 8) | d[3]; + break; + } + else + return 0; + } + v = w + 8 - a; + if ((v && !z) || (!v && z)) + return 0; + for(i = w; ; ++i) { + if (i == z) + while(v--) { *d++ = '\0'; *d++ = '\0'; } + if (i >= a) + break; + *d++ = (unsigned char)((*i >> 8) & 255); + *d++ = (unsigned char)(*i & 255); + } + return 1; +} + +inet_XtoX_decl int fn(pton)(int af, const char *src, void *dst) { + switch(af) { + /* don't use AF_*: don't mess with headers */ + case 2 /* AF_INET */: return mjt_pton4(src, dst); + case 10 /* AF_INET6 */: return mjt_pton6(src, dst); + default: errno = EAFNOSUPPORT; return -1; + } +} + +inet_XtoX_decl int fn(aton)(const char *src, struct in_addr *addr) { + return mjt_pton4(src, addr); +} + +#endif /* inet_XtoX_prototypes */ + +#endif /* inet_XtoX_no_pton */ + +#ifdef TEST + +int main(int argc, char **argv) { + int i; + char n0[16], n1[16]; + char p0[64], p1[64]; + int af = AF_INET; + int pl = sizeof(p0); + int r0, r1; + const char *s0, *s1; + + while((i = getopt(argc, argv, "46a:p:")) != EOF) switch(i) { + case '4': af = AF_INET; break; + case '6': af = AF_INET6; break; + case 'a': case 'p': pl = atoi(optarg); break; + default: return 1; + } + for(i = optind; i < argc; ++i) { + char *a = argv[i]; + + printf("%s:\n", a); + r0 = inet_pton(af, a, n0); + printf(" p2n stock: %s\n", + (r0 < 0 ? "(notsupp)" : !r0 ? "(inval)" : fn(ntop)(af,n0,p0,sizeof(p0)))); + r1 = fn(pton)(af, a, n1); + printf(" p2n this : %s\n", + (r1 < 0 ? "(notsupp)" : !r1 ? "(inval)" : fn(ntop)(af,n1,p1,sizeof(p1)))); + + if ((r0 > 0) != (r1 > 0) || + (r0 > 0 && r1 > 0 && memcmp(n0, n1, af == AF_INET ? 4 : 16) != 0)) + printf(" DIFFER!\n"); + + s0 = inet_ntop(af, n1, p0, pl); + printf(" n2p stock: %s\n", s0 ? s0 : "(inval)"); + s1 = fn(ntop)(af, n1, p1, pl); + printf(" n2p this : %s\n", s1 ? s1 : "(inval)"); + if ((s0 != 0) != (s1 != 0) || + (s0 && s1 && strcmp(s0, s1) != 0)) + printf(" DIFFER!\n"); + + } + return 0; +} + +#endif /* TEST */ diff --git a/ape-server/deps/udns-0.0.9/rblcheck b/ape-server/deps/udns-0.0.9/rblcheck new file mode 100755 index 0000000000000000000000000000000000000000..d2f9822f75c7d2c10a89b3bf1d5893459cf256d3 GIT binary patch literal 37945 zcmcJ24}4U`wg25@fd!+xNYrRkSv6>=iU}aH5;Y_&f`Uc@>Qm?vk`1JWY}`KqR5Z9* z$o0AywOF68w)nKIZGDwiMf{%xnt-B4)JnlWRMGB|)W$yrq_p{czh~}EHY@nrzWz3! zo4IG^%$YN1&YU^(=iYmpch+>9&8Atu3@uY5wBb&>V6kuLVBW+&tLtYKn+fO%PISA86=0{FF`mH)ijnnmZ; zR-Id0Qx{o2E)*CyUgbs6!uzFH%#*z2gL#g}McUc8Sa+uXV*Hs~fBMc8-#If+t@xY! zH=8GXK5zXPq!r>i6BlKWgX=V0$Kgu-k>)sDC*Yz?&cMar>A1dy>)QnI%fodNuCs8R zi;KUpD$u@za5ygV#NXK}5MD>){#0BLsWt*v0j~e;$9f&@$GSU@keFr0otKh!EbdRl zb#h8v>g_22M&ddK7j=d*rrgfK<;KNdKCWC`Y!Ch%DiGNdkFurQGR_!xI$_F&JdFMJ zow<)){!y#vb0c^BmIKE>@!tp7bc24|QXnRp>MyZ`i_+4sNQ39FaPvp}6j1oTI3@+x##?M`8>4(zbx-|Sd($Z5Ki2RI6!~azp{wHa0EDfHOmcKkL zeQ8?yw6yf&($c?|mVQAR+zr^BF~q?>I1;^z{U`4uy9v9rQ$f$3l72n&r@$v6y*LHd ze7?m?19iSoxFQ(#`81fqN}9k)ns4Tnh^?xr^F>1bDlHVKyulyVs;g@wp(UDsc}-Xg z)dy?p!qtLV9|>1#)q#3{ofZlQ>nfMlYk~T3O&v0ZgTcxrK@^0$sz|+7Q|Ax+>cW9~ zg;~EsL*|ecu375WLjGERWmuuqhXZxURS^pL!CG}iO)WEo12<^Zi~Yz@Sy5XXsAOC% zO7;2t!C+m$hp38h4RQwk6;&D#!G=~JsAcA=KqRa!^)IbNnbnoGfskKYs)zv3U)O*< zl?~Owz*6#F5nRkHph_xB0-2ctuZ4`S-?q8${9@$n8#4|6nWt~e4BRc@ZE_E?^^KWDm}$u_fy)T9 z>65Jj&m~NECAmT1YYDpvuNQbe;e5g^0xu*C4bYN}0#^~9Ot@a)C4`F!FBG_z@C?Fb z0@o8RBU~(Sm@sOiB?|;zPIw_3{e;&OUN7(u2|r4>Md0;>HxOHl6HG#JfK1jItZ>;}T z!W{aNdj#H2*g?2U;4Z>0!mR?oML37>27z}Hb`xGN@VkWRawl5^-b1*6aHGI`2~Q?m zFK~izG2w**_Yj^zxJ=+)!exYu0fX=UU*Vs=tN$Uox3_HWoFxlCw4;^r@2abpwA^EN zBt8Q2mo0;SW?yN%Xc~-?7AZDPn1P#33o>cRv2L9n6Sd+vnL88Xe!rLgFR1hl$ab z9b>XW_v{M(v114+*rHqP*Du%{Z_wh;)9q$ojP^Jlsse7THGDkVSi{ZmPJ15X`ntAl zPUk1ulk?Cl(CVC)XaC+_yF;Ir=Xi)VRJZ3P*uHVEi*bfG3xT~q=_g1M0JLEZH6|w3 zTanCMj`|y3FcQhwwi#FupqBU*DGug0M~@uVJ06;gGCf1eSn`5{E5|&ptLtIrW8o-)Zb#xA*q&IczBzoM$_+E5i0O0l?0Rlq z+nx;8C-Eil9uo54bqxUh+l0DTgFOl(AAXC)^;_V&dqZ^#4bsveg^6O@@fc1Qfd zg9nfoG&Yk)M`3Ghzq9F8Z~{7dI8Ox2NdbyZwEmU6Ih%fjWaq|p%-MLz9lo~l&~SZc z_^Ou1LuZ6vX*@I{+~M5lYTD7_Y+4IUpg5c8+5yekbc@`Kkot#%dYj0h&F0)VvS~+T zT!&3d{FK}_F<#%P^a$buK^FBF(1_gM{i%vajH0s@(cm}SiozqmZt32nPs{_HcpMCG zV#BT4!b)HN-36QVw(bX7;!k3PF!gORq(nep@TDF(sINS%zoUEiBpNX$QY<^+B;XW+SuSKkUW~f-jB8 zL1X3N=#V97G@A~M0KaITYp%vm16_n3pi@&l>Z3^-{ainNfy)0~mF&9=&&q zrU-+j(t+ZosyaWsMEajQx;Z=AVe_0%s<-_n+G+Ew?78iM=$3N|Td)7FZ^7ng%TY&t zXW}uo+>UhZyBdm>yc(90`iK6C{$VW0La#yZG9s?n?(k^ia+ls~lxHOlfdaaTzQBS0 z0;web=PTr6JM^NLGMq6yC{WjRX*3D=R>hXyxgsyZActM4DMFC6P(3ygUvzy;fq zt(Urt%xJM?j6|RG?7T!H%8MTSTR0KjV%K+G|LpB3X+iR3G#(mi5WVbFdh?BA=R-=- zAyv|@rCG6Uk^Ta*Y3-=jrsUT{@TK$P^N5ZokUp$4M(2QR7Vd6AF2^A6yrxEF$u`3Yk8TaRN)$ z*RhJp(M%k&khzniaG#hoQEA*m;e(98C$asuQ09rd9P-l!{W!c6$-WhQ++Ub2u*zwZg(mK8Gs$dTT!+v)h z#?AW{hBj*qq%}>vHC_r?`zfjtpWX8;n=$TvEAcKG&myS$O#FpuC?xSPIDEzpEIpP7 zxUlu`7r!*r?SA70Mztk|=&^j)yTb z*?Z$L-gkQ0gc`&nrjz%OC59GJ5xXiAy@tK%p1<@{*Ec7g1Qgq?cic*Mgrhz4BrbDn z+C$Ve6y4-U0fzTo-d-oSJ9+ysYHtn&#=Jdodt=;wyQf`WN5$1+=it9HdLwXPmW;S) z^VBTnn}Rsw1S)y-kj=R^50jYKj^$5(uvyPxchQS7jMD zWySh#tti|L?obbl!MOcFN{QuXmbgqNWe999Ky7wEyA-4&L!P~vCBQv`NuX^3Vwil| z$fwlLD9f61UDho>GtQ?~fQJ)nC0&@)*rB~Qgm+(lhb*{~1f{4p@+bCyj`(f}P zj6vWp5lClf7#K?aBo(cVoS5Qs#dg3Ipggb3f>Bw!9d#>|VaonD+`_<6=#ZdstzCaV zvmmx@(+bGPxt3kiY}i{Uu&&Sh`aE9*r*>Cb{?&XL51mc-0JHJX>E^iNY`Pt=aLMe> zjqQ}eB1&Pi&GUXko^#`4-EG}S6p`9>CU0}FY7*}IY*z69b=H~C161_`Uh zTvKk!a^C5omZP11^4UOrvyvsKJ@kYHtIo8Blt+Sc@+M@6^=+bNIAfy$Y@&7~PrzM^ zGxj{iW~wfmJb*NkNW2aDNgsyJ=s}w^_9jFP`pE-`NJXD+q5qWVzeg%`lQp{y161Pf z0UoDjB|icU)&E4=Xrr}v4anO7rNlyJuoN`86y)Nw-?D6rn{JtDThJL%2y!b2@R4z1 zt6~Re4^2zbh4yDF z6_`o(q7>tN=_&goJ;t}`25*AlK(B`QkyzN8JPp_&cQQE{BAn=(olS303g9Ob1w>nq z2A!fMyMy^A<|VENrDQWosBTGI0u)22MR3O|)D8^4 z&KM@16eHpiBcHC)J2QqFkRglwT5ix z#@Pv(>eTWV!<+2Z$Sc@ z0u#Aj$`g>r*}JT`Ov`f_tEr^q=ka~CB`eO0cH1KDrlkXadhavnw0()^Ieu(8XA=RL z7@OmzaUWF@{c@s+Wg<*`HVU98m}9Iar>r^_!O2CIv7i^Er9(m}eLEm0@qk%Rz3a_A zm@E%3%{G423VhEi4HrOMZ+`d^@ndB)u8OD7Ha2H>_7ymt7kz>2VBDXnqPgl6c zb6NgvU$RCS`c-z_rq8e!w(27w09AL`odtV4O6{8dK}YEjE!yeqC>=W1n4aBPiaD&N zRd<#iiyF7L?Q=Aj4{I)UC{c72AHxPtdN{^Ec_uOS_U`*xuC%hu=h#fYHM<6PK>%AO z=TWNk^d#*&^;LiU>T-Q^v?t5B%r&LC71ToK37r=yF%eG0mAoDI)h&9jz~N<(k`8o( z=)t5>;_fWrY7ghbdWm~VsUtL@a9i{_CTk7(`%7|cLM`I#E+U^6o%06M(RYfPn0k{~ z%e9IvSxQ)Hy>u$byY!uUQC^}2?t_(@Vy|U=DA$NMjFnIn7Z>a90~5y3N8yIEbeUTMzLy#%W@cD*DEYc^;nqclq|$u{#tF%+RKvdE+K_J7G) z>e5ScjM8kqB-bd-fns(4M#Kq9Ea`%5Q+j_8L0C`$TiPaym;ZFoT)$ni2`2C#dO=Z8{#P8h-jAytvg+=Y&FMg@t%M!YO z#&-6YUGCZ==)$nQN*9iTBi-^Nu+V*M`hJYqry|%V%3(+b#55FQqKBYHQFg!g2u*kC zoyO``@K@dTx-5TGlg&t%{#jvb;qLBy=#T1y*8sgby5bZxeYL*g6yrW-f+dc&<$Bs1 z@|{m#mBTI@dERhI4 zWmp7oBoI##_H`-BN@VVy@M*`&M^O&&kj_r{~^=KGeCo73`4J6NUk7#ycXVgBaP0DS<$9#$ovS(fH@scd(d9WAm|$*pK3#lD zweHz@7X)3Iy<# zX(x75m=X4CNBCPtlV8puX5I zihB=|CNs0g7$1QBd55>r+`9_9#Cb8tL(^WxHcj7Mqlbr~n(QQ^GQPL4WD9W-9%rcq~UHV-3VXgr^h{VI}LqU} z>WS>!#9K@mS5dDZ2|^JBPt}}+RM!#Z%q8H_@&m_64PY>F?-@fq2gRiXIChVNT-C^ zk(hO5_uOnSfjgxfWP|V5-GcSWAO9Xwlw;}Q<|S9K6=*U{KmHI<(2a`Rtft?~i31&h z)wE{uGMj#%SZB2WQPf=ekIWd1wKVl{amWl=7KzG&pAAcfnL3tCek4Q3wf6A2o?YQO zcxjkrhpSBw?PSwK7ep2<<`K*W8U+Dp07X<@w_v;kvo2FRT4i<-q z^*6s$B0CC>h;=P+29Ng?P_D63PRd}#nU4S%KcZ z#hp4|l#k^fnX==E5s!1BF1Hz>$)4AoF^Xf=%>^5Nh`qt4p8+s3vC3&Y!zxFAPA|fy z7CvF7GQu9?ZjtjU{`a(pXQJAea)qaQeq>htYtyZFbVB6B5Nl+q&D299MjFNo z{Ga&(M=f+}t+aAa8b{=V1{VH=~CDdG9cQ#c)q6#@a zd%GdDAYQfLgB}lbwZIN{jjQkE(VoX<5WU@zC&<-dK_u-Q$#_8~aEQ3#NCgEedDTXjBXit??9_LKS5Rkzzu_15~n9ryS`* zS%U1_k^BI*(=_uG+z%|eorpK2-Q$t%s-_m*eu7XU`qHzoFsAm^IdGVbvxfO$NET!{ z<39vz`mVF*WhL*!on=w3+ChcMN=_u^yxw@}#m_+NG`&;*-4{P~#)1&E^J(w)>gYi@ z!2bnl;4Z`2bPVOP($N`v610G$885Xm+gES?gfYfb{I9>j!;Sh=n;7!~|Hp35b~fD# ze0DF{bA)Y}WRy;fuSE4kc+v*g*<$a&u)~VEP_Zm)=3p|8Egor>1M8$b;p4PY>}#o3 z!hwV)9Xy)nMh<2=o7w?J589ngH zhI(x|@ZAur5o7;~zwjz6B*mi@TpU@!op{A@ERvvQdHN!Bny7j6o0P1#7YjEY$?m+p z4KeZ3lK48(vCzK{r;_urWu!eI9cR;K7A&%%kVHHX%h%Mv5);&oqJo&H2hgo3!smD^ zTMfzoWKC1)SN^N?!6@mUf&9^x38!;)H*3m%PgUSn!daBGx6AqTyw^Ho)CI)jxY?tu z_Cd@UDc`6h*rKhuo;XoLBda}Sr$o?~h~}x!jlAdFC@BNVHb}`ty^=pf+2G<^&eeW$ zff?>S!o?1K-6Nm_-4#uq{Ti&p058>gyq(NE)r|k$c`5uDFECp)#)MptD6%seLWPYN z80CCA#*EdTOHYZsdV^FU_Zh4Xa2wiqf#X1YT83Ni_~NILUy1*SVprXo&COTm>f6X+ zX>Ke=1$4&vM8a^nr}|~7o?=Lq^_z1s0KgC3h_$KYGhZPeHo$i!p2pSL~l=c zjUCo`!C zNz@PJFW|g6TY7dDrS%7JxIY6H=R+!h4BHTQ^Fcs(GywhJX~wUZ?0Lr-I~&zmwZe3# zI2DDMJEsb)Pn~Ekr&v5axgBhDnB$eV2kRtDA%E^h$!Z~|Jjeb;MA5#y+*m_@0V}}? zTmQ6D_wKD}Phgzoc)*kR5Z;eL)GPq!k*)$HM=XCVh6oh9_lxAP@_8@Kmbo+CB8Oq* zNEnLG`lac8Z9|b{C-vyc-(ObV*HLi9XWx~E^TuF0*V04Vn1=J(V4QR5>D>dIug#C( z{pVqN1isYvmFdp(&tY;{#oY$?MT^l)k=* zyS`2Q*S(P%lWQ(~wV(r}Ys~hon1~FV-?|#dWw<>`=NLSLYpCrbuoQ$>VHt<3rCYaA z@xwCr#JWJDDJ9+NKaU7b|h`)ahj@IIqN$!mqO|9XTMQ&%)55QOv)@biyh$Y%J4At{N zkvv5|86xuyKvQt|lpNXaa6hxZo^ z4}V^CZ1_{)?`y*FaSB?_SV!*g*o@*RHVN(9;r)W^SLKx5Jsp&tv+3I`{J}wzLrvLk zEx<6I5_wy#i`JXeJ-BuEi7dkB;CuMu~pL;mLjBHSjrOyI?)fOs|x#nv~3%@iT z1fCg&M2!6Br0Q!9xYXNK$%&H&m7C<#TV{UGKz?V_bD(3!7c(A@9)qqkwi970?dMUa z!E*WpXL*uWf^0`DO%s!|Y0s>tv0IO+)rioXv3xWrNsmJjY_4B#Q4Q)sd?PjzumtFA z$^@X=P>y?{{f5%d=mF&xrK`!G)7P-XoR6V)K+MUfS$lil0?N8V(T}~3xa5Tca=4uJ zg>S z)E_%ve52^(Wtq`^Zs&{kQ(;B-M7<84R+KD}SCt`jpj^5Q8ERKq)|V`Q!j7php6o)h zbK^0<15u%v#FJ#U<`IZQpKaO}`D1K;=D3 zJYLEz@0J7aca+%9jo*}kV`RdpHU4eF+N}dZ;i1{(!?CyZq?|X4wh!I(4pOk2uxwvn z-n!yH;Xz3Zo11zN33{6zG{eWtFdGPr&&M%vhMg*Y-lmUnqkoJj`9VGllHKk7?IZ9j zZT$lVVC;8tsKX4q^<_D$lVzPyV6R5U;Bj5)A8?+bJRZ1mIF76M9+bcA6_sDC0Zjgp z^M3^!FgQQ9$aB7-e5|6H(^-zBY22K>kJM71x5M~v(mM)2-$-vay8d8#GTzm~BIEEI zW?$?~UPt_?Cvn4NgZK;F*b>iK)>&MJ;80+R_i@HUHNSZK=;=M9IE~hBJ;}2m5#Glc zOy8<4ByO(9;9&Z?t)RS#?llLB#{h)iC*dt_W9sh5l z?+y3k7Ya0DB!=^2dRMd+r@k!agj0LsV$8WZV;g|NUeKLqn3NbZc2{N@WZlD4?wF8E zeLimmG47dwozqbh+k?b}W8I;Ob3Xo{lm{<6^EwxE!QdovPzq;e!l3TLG5g_QQprX1 z2knK)fJ>H8@oY2p(zCIOO5Y7j!C0EY3@cNu!R@m}MmjfQh`q{YF3RPG^PoCed(a&a zI&MHc5ZbRLjAytJBp!Q-P1dU)(qSX8As)e5y)QexF0h5gJbC8x1lnHqxBn|Y`)rDz zKbicL59a3)@{@ZMe(0>3{KOzutN&iD|E^qrCEDXgy~r}_mnEwvv+`sz!#H+KHwHMp zo!;lI6E<;sYiw71?kJ8&k_{~=hrcKu$%01w6TByk$3Jd$Zp1M^x9mQsj&0d1_58LQ z)YQ5C_=8_j-G7f6dBTTggcEtW+1-lwNE@IqnL?=tzGM@Gj~Oa!zyU~X+})stt$$s1In14e{^tWQf5=wzlYVKiI@Yerz*MA|bj+y;PB11L@gxM=m&?^h zuFqYlSxS9O^U+@))11+K9H~B2;X;37eY#%z_ts|vrdp^Es>7q(sDJ!HZd8Cbr5^0K z5GSoSv}1=f^{vzY$;OgpM6vr7IBvCS3yZ>OQ1saTrULQ5V7&kvQhQR)XCV2XY@a=N zOy(c2PsZT-{G*LEP@lX1aP<0&`FGUE^-tAj2TrKtB*4GkK5g*Aj@CZ&uuk!RZlB^$ z|EcyFT%UiweLnl(==F)c@^7e5{hv^uXQWquTn>wZiJQI)bHo$jKf)fy=VDQcv%JXX zV|T?bv*}x*_wx$3iSwqWf5*4bZnoJ5Q2(rNRR?iyOI;XQiA7fXhr#&(USujSI z!!?Zuv4%J@UhFL%qsj4uJkU3;|E?dgo1iafxF1HCEwsjd5E)Bi%c zr!)Lfi#d&I0h4EY9dhb&kbf-BG+u$w9&OLm=k*4!=jjG4QDV;`#9BHxZjF9bxolzd ztKkio$8XN?taLeV|1k%at3rZAakF&*reI0}e;hsBH6 z7qjSv5PP)E8Jq)uJAPeX_n2t=HO*zVj$$_L$ku&!yv3v83`9ngw&t=-?lSy7d`1V3 zdTksT{m>abG%K^24_C`@xmly6<8gi-S-~SaPTcf+N zR+V9s<%1FNsqYtExa>GY<7EVg;TXs=*3=J*7Bq~FZngu-^Sfp5C=G1IgybR{hV7Gg zoQB6G7Hp1g&ggy*dJ2{3ep@p3pKpM^;gvQ#;IJ|)bEONbtk62FKs52m+$poO!XrIj zIioy<)?pimBq$cOeOe~<3m%vFL85^v{3Sj$`6~f`&ed`{;zqmYfOGZBpxR-Z4?@q5 z#qItNt8qT2HDO1iH%|rSQJV%uH+%mqk7Mrl|2ZG6jUjsfhirD-RO7LnR@LTLK_N$3 zq|o$@w$^;8Zb9=^nP3vf1{%)PKTBSNOs0I}H)ePa1g9!SfP$idwQLvI(m&(!3n$&4 z*BeITbuaO$wzIui-J>Zs&sU+W=8^FmZQVbUdaqjP(kSNE16@$#l!@j*`-*(&1fW;T zlLQSzHqjl+#IX+9zd~P=B|nUj9C&NQMb0;!a184IAHR=1c?5pn>gTuoIhthkPz`*2{bdMhRGOS##)6w?fh*)H+#QuSk)RKX zy_4Arb}&q*XDW-rnQ%V9V)^$X54?^A$8oY&9b(Hh40)CTrXVr2#>hT>UXQbB7h@J= zbM07824QC|;<9z}tu!kg&S(Hk>R-UTbe}GIw4kTq=A!v6&L*CWZq7u1MZeP9J+JGV z8_r@ib`_0_{9fNVwl};qdhn8EHzK)tI#d#vdOIHb+`R1fdfV8~qF-IIY+lp0<}0w= z`8h~oA=8+hgLNYuDjANuP6`JPo=9_>PjW$zO=Vejie+#5pDUSyfv4#|ktez(OLR$; z+SJX$C$JE7BD)d#%faxwoLJ%TD2|RU;>gpM5ls}J*UW@DY&>*c^SsX+E^a*JS>}mn zH{$%;=cofJFs-bMRa6*XG~^QFB!%$>6V>B3jXim4BGO!he;&1o<<6un$lLj1iu#!Q;B!E&JPBBcp6xo;E(@>cSE_ z7Ph+4cW~!vTZXL-tD_CrL&%)H4NsI|nE}tM!F10UhGR2BJ4 z*iYak3J&GV`ugw@5FU#^=og1DIw;!Wl zD$no=BIY_gIq(&PXiU%1FLmg01it@J6#FUOI&`VMd(YtYBK3YKS^vcI94KfKNEGhC z85(#5S)S6|@Ce4brFHOl*}!|A)cUEMANIO9=Q7V%~36cHrl z`Ajc&;YpF;Lgz-LU87bq@LCm6Uma^vwb zkuuKLrzzrQh%n31s}@GSheW{k9@tqdjC_S4aqq8=`aD%G4#pW7Q@k!`^w%UiFUMqW z4VUgS=Vixd*z|b`=!kRU=RBqRg%qA`ZNYkbs}Rv$g)@8Z*t`n-2_yx7ItD|(4eRD$8hx%Oowp-Vvt;ZU+s#8Yjov!XvCCyyK_wlG4M6wRKaqkV)_qwSlYR<6k1iT z7S!|~(7YlFSX^Vv8QzWsH=w)ZCPS1?{{h!-fP@)L`EdPdGA{{n`-%Dw_)0LE-8Fn= z{FD+D&7FHx%P7f3tMjRI*>m_D8@IIU$D>tv`fIA25YKl`StCp|HR7JevRvnC*mtau<|J1j#CcG(Lmv3fnzBZu75kpaFE;b1X4?#rh3BdqeX3&< zh-eMRb0{s#5uh8MAb!@aKm0JRJEG0|-&?2bomxT4HW$pSg)*$R(r*-ND(g1lKE8}; zJzhd-s+Hu=$6h={ke(ktDfi~Fn$P9(BU3w!18Z1(AM|_=7sO>OB z6m_zPU_F5E2(-cQMbw;a7iA#!kwG62hoKVj`o`KR3J#UOeZ>)2fH9xWZJ*V7gH z*tyXWpXRW6+JjlPSMWIHTit)EZV9{6*TU1*!TF-n@E(%n`_zm$;rQHb*|uG_c3S;+ zet}ku@f?$}j{mKN@c&VC43!jTl3td@#qg=FxV_6kyN_x`%e&y zp7h^?@53!s9%*hK_N-7IX0P^#_}vJ+*)KfmIo{!YgWdMNE)2&9`yyXPVM#!$&BLb^1JAC#ju5V#>y?_M}2be5o-utJ}E?OZorIvxn( zJ?W`rQsM{UPqnZIIO2QG-8Tj5{M9vJk;Q0uVLNZ!=Po%*xLx9}t=E>^7;ab^y0HAh z%DM}+IVH17XLw65_g#1872a~qJzSe{~BC!Er;#ebL2kxmOteu^@RPa#s0|mNQUaPfCzeH3$$t4E(^NFkWCU zf>FX6P|%ROrWy$=fVmWyA-r(kE$UNKx7b}#9rg!NmtY8utlGl8v?5%&1iUkoRhv3@ zO+C{=fXYirDlzb#`)mPFsi}jqK~P|f=~e#fib!pEL_Ug#9-*bsFClT&zSK~maE=hn z8?5l>xNl8W^AX6Irs?D_yrcp~Ac#)@jMY$rr3m?|!P&&_P`$shrn&}wAU}-(EMg7x z(VDuNa7{&R%}suq2}_5?96&FZ1p^S(%(8w7(ZW=?2daLd*{%DnkZCK3q3n%mlt^)e zcFDc?2G9TDT6%=MZyzCU8{#&l#6M=Tiz;t$ zpLUVEK&uQdKlfs!R{H8HD#Ory_r-|45#Mxh7hU8YNR3q0FN#!arwzoI_}Xdt198CO zmnz&BgY;M}1Y2@(sA62G0^g5VT2s5iebGhk@!E}2;EldOux4>pU2TCX(ToHwVj&=F zytU9774(OL6?N)s75th6;T2j%IP70qk8e`cRR!?BJ`fC}T~pYquB})ca-Yf8_szUw z=3MVI=tn+2x)F|q+(qsJ_nBwH3WT7~EU;j#`<7eWRx+!Sl1`zG1?@on3R7-JB2LzU znV;VfsHt*)XDmpf2s8FfHU|0+D}-Nv{meE=X)0gkBIY~2vZC&+up9p?VO>HiV0Qv_ z0kAHE-Rb`f4O0h>NCb3Pwkk$$O>DWxM>c$Fa3vDIx#6S9mNeBNeD$e7Rh>XesZ;_q zLvG|dC8Q!B;^0l4f0;tTw@JcFsh#~`)uP4KCaJ0_UkG3PpwN!;E5v*<-RM+=FYtG3c4D{j)1RIS&3oU zUmdA+R|b~W*ZSp?E%m`bI8Yg=b*m4ksFKAFx~l^I5X^j-<8lZeajB?K+8tfEtdRHLbp4}v zsWgt1=SXp>dH&t^EYI<_-BJJ6K*;-Vx-9%}!bwj%Ql2BlrRV#1!YprCP2IV4p6nID z`U)+xrhbyT6P*-g;D8$my3ck`L_S;Q6w%YC(ig+z*chFD2 z3z>QLmih6y9{JG*7U31aG;Mr*k#-K}+vLRq^PBDRa-@j}k4IcSE*v%!+lonPz>awE z#vK?9M_>Z$*Psvo3qO8)(8_iQbsBMp-Ej)8@8G%+*JZe_!}UE}H{rSi*8{kIf$LdZ zFXP&UYagydxJDSrkLx?QF2r>iuIq4p57$k&?!ff`u3zAK7T3$TcH!EG>kzIHcOpNo z@8G%+*JZe_!}UE}H{rSi*8{jLo>~7(OG__w=TGx5s;Q`RPaHR4+{AMyja4`J2w`Z& z(s0Ehg!s;ugiEYDeCW#`tk=fD1pCKLEtz?4xMHyhEv}1FA3cgnfLLdT1uM!0WkP-jJ2{n~-Q3%)=Zqp(^uMn4oHv@fP2JrGEMg6nzW&q?kPkQV#wZL15 zOZp1ph{tOhE<8mPARhbFcHGe3r{i6LIQA>zv9EEl!-b2yfUMABpSv0t`y26G=$kGO zVxP2r*6nu@!G1+N_NnzCvW`v%OA@nAKP(R``VB8h>@9Q zskA!^RwdKEC|H$1JECAE9oh>8n*yO-z%Nr;RP}!atJbu}&P-FY*q>E8YoYqBf;p1o zTYvrOv^=UGGR!nviv3N&s>1A73OFF9$pg`b)Ytly@CMe!GkUd1L-qpXYI3kMbH2GQAxkzdK{` z^9tZzr2ooHe-k0UC1$1X2F&luSn!8{`GGJCP5@rN7Ct;GLi%3<-f$Prl_4a|^J#o3 zzlA>(c*QMt#|LIQzjewlNAWNV@%d&oezE}FP~bBF^ZQ2AO?WI|e&fQTR{)r=|F_@^ z0P`~y3eM3k0lY9JeI{UjfJddfw5tGHKgz2DFkkFK%_Becfca8-_6!>=fe!d9!0btc z{~Pex-F63i0%4wWYJJe|_^X+J8(_YC-AezE>3H1|^@{ZW40sRVU%?cJya4l!)E54U zh;Mnw?$~PLj|R-Q%-w00mk*e4G^XCN{HcKXdS3QK!q)-reFQ$KnSTjj5AfNOnZ61z z-$QBD=K;WclOPp}=}!PYCMBJI%fXcV9}^$tS@ix6cuxxdC&8rgO+{9E0bss^#**hu zz}t0=u16&0^&ntA|4Pe9ejf&0kM!3OGW}_U){pr& zr@_RteoWsDc;Pnq=x7J#|07^Nhhp*10fEnfSn$bIB=g7oX8_*w3f^grIOZ=*OP`Vk z&j8Hh99DZ*q@@P{bN|muZwAcqmPOh!PSSn^*s@nz#_`(YX>dDWYffa#I6->_uw@_J z_}cmJ0C&A+chIgJ4}Oz?tskxPkd!f)@F{8VxoPl4fO$-jBg;s%$85mGZ`d6iuSx$J zzy+_`9Ze>@1hA_O>m4S%3~=Mac84GGC;n={8?bicGt=)S{DIvu*@PbkZ2idJ(}4L6 zN=7q(C-F`DO!zgx#k=i}OU(55i2u6XQEbAW0p_tq%A5S#=`@)?H^NbA@L7O)4BkK* z=}!dA@sDC;{waXFHrO3=&@Y%iGbKOyy*>?I3Yh*Vl7R2jLTTx-H28kNWl!22=b7^Q zNm}~TfY-l=`GHw}JJTW0hlT!e+M8+kdjY$k|CgHSpQoie;bOS3cEj<6_4$@^IRFvH z^b~OMF1urbz{hDG!1ZYVF(y16aPRg3dCv#zhP}MpO!ot}ex!FR;Kq%12OleDd3U6x zKLEJ6!|u2SxJ>6KytEX3eipEM2j&ZAdI#XLR=Z=K3BL_E_1oNgncw`e{KIK*mb8pQ zVESo<&GCib`knx|74_%%MSi9MUija3$EjxflmWK-p9}qO0rQ(b(hC4y4|&~$d*;6x zuzRbr&xD)O@b4oG{9+UT7l6zD$L{#ZEbnQ+z34B~&Ge46{5t`!hyGjr|IdIM!T&@P z{}AAUl=3r`3yD&NoS)8MK!cv%{}2JpgP*c~4tFZsEf>1YqqbYXmYFb)54 zzsVE6Crj>k><3BZl8r)?&@KMkJ)eJk4QQZxNTz}AoT zI}>m{)<-RRg@DW6wL418@}~e^2zz0T7iEA8p0GPkG}9LVUOsG~ev1IRQ}izk*!r=& zCcs@ku{%yP^WP14A@sr0|NlzEe++OV+Akk@DbHuq(qB!3|Bwd%1#oc+Kf~bYY)G*` zC(!XpX`fucU63#R8o3;hiG5n&q~DX#mF ztgW^q(HVdKr1KSi|2EqZ(I%fiS@T^w>&lX;vwT-hpI+{r>zg~ZWR{mUNGe_t3|6f0 zaSLxy1dfv6%!zN>e_#)OW~s*g4xfq>HJ7u6*b)Y?s1`ZV=j#GKScVF2f)^KGI%{T0 zsc-za0MTKstsUK+TibeifB_2!A(%v#w z2sV%3_gw7^Xh~`jM6IgB`4U(+-m^4~hu8e!bTq3z6%&Do4xCbo61dr<>>|~bwSiDdUDW8) z-{wM~UuZ%9GSo=uvCmUH;QZ*tLlJNsa*%4{h;*91LRrx7(}}1qBODI49g0D#iUTIX z@*-GS%RW%1p@44*PUymUG2JrdA`z|sI2Xqcb#5!gI?#SB5qHs2_H$O>>45PcYZ% zc%~gPkNU`!2hAo47#m9B;({tZ0eonB1; zIYjd?CQIo*iD;U+lrCQ}NtNn@P6VqWgX+uk>Z#=69ZZ@$X&jozS6_*Pl99R_#w}WI biIm51eWo=L4&{uWco>3`as<;nKl%RvGS*bU literal 0 HcmV?d00001 diff --git a/ape-server/deps/udns-0.0.9/rblcheck.1 b/ape-server/deps/udns-0.0.9/rblcheck.1 new file mode 100755 index 0000000..0e427e4 --- /dev/null +++ b/ape-server/deps/udns-0.0.9/rblcheck.1 @@ -0,0 +1,151 @@ +.\" $Id: rblcheck.1,v 1.1 2005/04/24 23:14:23 mjt Exp $ +.\" rblckeck manpage +.\" +.\" Copyright (C) 2005 Michael Tokarev +.\" This file is part of UDNS library, an async DNS stub resolver. +.\" +.\" This library is free software; you can redistribute it and/or +.\" modify it under the terms of the GNU Lesser General Public +.\" License as published by the Free Software Foundation; either +.\" version 2.1 of the License, or (at your option) any later version. +.\" +.\" This library is distributed in the hope that it will be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +.\" Lesser General Public License for more details. +.\" +.\" You should have received a copy of the GNU Lesser General Public +.\" License along with this library, in file named COPYING.LGPL; if not, +.\" write to the Free Software Foundation, Inc., 59 Temple Place, +.\" Suite 330, Boston, MA 02111-1307 USA + +.TH rblckeck 1 "Apr 2005" "User Utilities" + +.SH NAME +rblckeck \- DNSBL lookup utility + +.SH SYNOPSYS +.B rblcheck +.RB [\| \-s +.IR zone \|] +.RB [\| \-S +.IR zone\-file \|] +.RB [\| \-c \|] +.RB [\| \-tmvq \|] +.RB [\| \-n +.IR nsaddr \|] +.IR address \|.\|.\|. + +.SH DESCRIPTION +.B rblcheck +is a simple command-line to perform DNSBL (DNS-based blocklists) lookups. +For every IP address (or a name, in which case it will be resolved to an +address first), the utility verifies whenever it is listed in a (list of) +DNS blocklists specified with +.B \-s +or +.B \-S +options, optionally obtains text assotiated with the listing (usually it +is either some description about the reason of the listing or an URL +referring to such a description), and displays results on standard output. +.PP +The program is implemented on top of +.BR udns (3) +library. + +.SH OPTIONS + +The following options are recognized by +.BR rblcheck : + +.TP +.B \-s \fIzone\fR +add the given \fIzone\fR DNSBL name to the list of active zones. +.TP +.B \-S \fIzone-file\fR +add list of zones from the named \fIzone-file\fR to the list of +active zones (the file specifies one zone as the first word on a +line, empty lines and lines starting with `#' character are ignored). +.TP +.B \-c +reset active zone list. +.TP +.B \-v +be more verbose, produce more detailed output. +.TP +.B \-q +the opposite for \fB\-v\fR -- produce less detailed output. +.TP +.B \-t +obtain text for listed addresses. +.TP +.B \-n \fInsaddr\fR +Use the given nameserver (given as IPv4 or IPv6 address) instead of the +default. The same effect may be achieved by setting $NSCACHEIP environment +variable. +.TP +.B \-m +stop after first hit, ie after the first address which is found to be +listed. + +.TP +.B \-h +print short help and exit. + +.PP +If no +.BR \-s , +.BR \-S +and +.B \-c +options are given, +.B rblcheck +will try to obtain list of zones using $RBLCHECK_ZONES environment variable, +or ~/.rblcheckrc, or /etc/rblckechrc files, in that order. If no zones are +found, it will exit unsuccessefully. + +.SH "RETURN VALUE" +When no addresses given are listed and no errors occured, +.B rblcheck +exits with code 0. If at least one address is listed, +.B rblcheck +returns 100. In case of DNS errors, +.B rblcheck +returns 2. + +.SH ENVIRONMENT + +.TP +.B $RBLCHECK_ZONES +if no +.BR \-s , +.B \-S +or +.B \-c +option is given, +.B rblcheck +tries this variable to obtain list of DNSBL zones to check against. + +.SH FILES + +.TP +$HOME/.rblcheckrc and /etc/rblcheckrc +if no +.BR \-s , +.B \-S +or +.B \-c +option is given, and no $RBLCHECK_ZONES environment variable is set, +.B rblcheck +will try the two files (the first one that exists) to obtain list of +DNSBL zones to check against. +Each line specifies one zone (only first word in each line is used). +Empty lines and lines starting with `#' character are ignored. + +.SH "SEE ALSO" +.BR dnsget (1) +.BR resolv.conf (5) +.BR udns (3). + +.SH AUTHOR +This program and manual pages are written by Michael Tokarev. diff --git a/ape-server/deps/udns-0.0.9/rblcheck.c b/ape-server/deps/udns-0.0.9/rblcheck.c new file mode 100755 index 0000000..1820d1c --- /dev/null +++ b/ape-server/deps/udns-0.0.9/rblcheck.c @@ -0,0 +1,377 @@ +/* $Id: rblcheck.c,v 1.14 2007/01/10 02:52:51 mjt Exp $ + dnsbl (rbl) checker application + + Copyright (C) 2005 Michael Tokarev + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include +#ifdef WINDOWS +# include +#else +# include +# include +# include +# include +#endif +#include +#include +#include +#include "udns.h" + +#ifndef HAVE_GETOPT +# include "getopt.c" +#endif + +static const char *version = "udns-rblcheck 0.2"; +static char *progname; + +static void error(int die, const char *fmt, ...) { + va_list ap; + fprintf(stderr, "%s: ", progname); + va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); + putc('\n', stderr); + fflush(stderr); + if (die) + exit(1); +} + +struct rblookup { + struct ipcheck *parent; + struct in_addr key; + const char *zone; + struct dns_rr_a4 *addr; + struct dns_rr_txt *txt; +}; + +struct ipcheck { + const char *name; + int naddr; + int listed; + struct rblookup *lookup; +}; + +#define notlisted ((void*)1) + +static int nzones, nzalloc; +static const char **zones; + +static int do_txt; +static int stopfirst; +static int verbose = 1; +/* verbosity level: + * <0 - only bare As/TXTs + * 0 - what RBL result + * 1(default) - what is listed by RBL: result + * 2 - what is[not ]listed by RBL: result, name lookups + */ + +static int listed; +static int failures; + +static void *ecalloc(int size, int cnt) { + void *t = calloc(size, cnt); + if (!t) + error(1, "out of memory"); + return t; +} + +static void addzone(const char *zone) { + if (nzones >= nzalloc) { + const char **zs = (const char**)ecalloc(sizeof(char*), (nzalloc += 16)); + if (zones) { + memcpy(zs, zones, nzones * sizeof(char*)); + free(zones); + } + zones = zs; + } + zones[nzones++] = zone; +} + +static int addzonefile(const char *fname) { + FILE *f = fopen(fname, "r"); + char linebuf[2048]; + if (!f) + return 0; + while(fgets(linebuf, sizeof(linebuf), f)) { + char *p = linebuf, *e; + while(*p == ' ' || *p == '\t') ++p; + if (*p == '#' || *p == '\n') continue; + e = p; + while(*e && *e != ' ' && *e != '\t' && *e != '\n') + ++e; + *e = '\0'; + addzone(p); + } + fclose(f); + return 1; +} + +static void dnserror(struct rblookup *ipl, const char *what) { + char buf[4*4]; + error(0, "unable to %s for %s (%s): %s", + what, dns_ntop(AF_INET, &ipl->key, buf, sizeof(buf)), + ipl->zone, dns_strerror(dns_status(0))); + ++failures; +} + +static void display_result(struct ipcheck *ipc) { + int j; + struct rblookup *l, *le; + char buf[4*4]; + if (!ipc->naddr) return; + for (l = ipc->lookup, le = l + nzones * ipc->naddr; l < le; ++l) { + if (!l->addr) continue; + if (verbose < 2 && l->addr == notlisted) continue; + if (verbose >= 0) { + dns_ntop(AF_INET, &l->key, buf, sizeof(buf)); + if (ipc->name) printf("%s[%s]", ipc->name, buf); + else printf("%s", buf); + } + if (l->addr == notlisted) { + printf(" is NOT listed by %s\n", l->zone); + continue; + } + else if (verbose >= 1) + printf(" is listed by %s: ", l->zone); + else if (verbose >= 0) + printf(" %s ", l->zone); + if (verbose >= 1 || !do_txt) + for (j = 0; j < l->addr->dnsa4_nrr; ++j) + printf("%s%s", j ? " " : "", + dns_ntop(AF_INET, &l->addr->dnsa4_addr[j], buf, sizeof(buf))); + if (!do_txt) ; + else if (l->txt) { + for(j = 0; j < l->txt->dnstxt_nrr; ++j) { + unsigned char *t = l->txt->dnstxt_txt[j].txt; + unsigned char *e = t + l->txt->dnstxt_txt[j].len; + printf("%s\"", verbose > 0 ? "\n\t" : j ? " " : ""); + while(t < e) { + if (*t < ' ' || *t >= 127) printf("\\x%02x", *t); + else if (*t == '\\' || *t == '"') printf("\\%c", *t); + else putchar(*t); + ++t; + } + putchar('"'); + } + free(l->txt); + } + else + printf("%s", verbose > 0 ? "\n\t" : ""); + free(l->addr); + putchar('\n'); + } + free(ipc->lookup); +} + +static void txtcb(struct dns_ctx *ctx, struct dns_rr_txt *r, void *data) { + struct rblookup *ipl = data; + if (r) { + ipl->txt = r; + ++ipl->parent->listed; + } + else if (dns_status(ctx) != DNS_E_NXDOMAIN) + dnserror(ipl, "lookup DNSBL TXT record"); +} + +static void a4cb(struct dns_ctx *ctx, struct dns_rr_a4 *r, void *data) { + struct rblookup *ipl = data; + if (r) { + ipl->addr = r; + ++listed; + if (do_txt) { + if (dns_submit_a4dnsbl_txt(0, &ipl->key, ipl->zone, txtcb, ipl)) + return; + dnserror(ipl, "submit DNSBL TXT record"); + } + ++ipl->parent->listed; + } + else if (dns_status(ctx) != DNS_E_NXDOMAIN) + dnserror(ipl, "lookup DNSBL A record"); + else + ipl->addr = notlisted; +} + +static int +submit_a_queries(struct ipcheck *ipc, + int naddr, const struct in_addr *addr) { + int z, a; + struct rblookup *rl = ecalloc(sizeof(*rl), nzones * naddr); + ipc->lookup = rl; + ipc->naddr = naddr; + for(a = 0; a < naddr; ++a) { + for(z = 0; z < nzones; ++z) { + rl->key = addr[a]; + rl->zone = zones[z]; + rl->parent = ipc; + if (!dns_submit_a4dnsbl(0, &rl->key, rl->zone, a4cb, rl)) + dnserror(rl, "submit DNSBL A query"); + ++rl; + } + } + return 0; +} + +static void namecb(struct dns_ctx *ctx, struct dns_rr_a4 *rr, void *data) { + struct ipcheck *ipc = data; + if (rr) { + submit_a_queries(ipc, rr->dnsa4_nrr, rr->dnsa4_addr); + free(rr); + } + else { + error(0, "unable to lookup `%s': %s", + ipc->name, dns_strerror(dns_status(ctx))); + ++failures; + } +} + +static int submit(struct ipcheck *ipc) { + struct in_addr addr; + if (dns_pton(AF_INET, ipc->name, &addr) > 0) { + submit_a_queries(ipc, 1, &addr); + ipc->name = NULL; + } + else if (!dns_submit_a4(0, ipc->name, 0, namecb, ipc)) { + error(0, "unable to submit name query for %s: %s\n", + ipc->name, dns_strerror(dns_status(0))); + ++failures; + } + return 0; +} + +static void waitdns(struct ipcheck *ipc) { + struct timeval tv; + fd_set fds; + int c; + int fd = dns_sock(NULL); + time_t now = 0; + FD_ZERO(&fds); + while((c = dns_timeouts(NULL, -1, now)) > 0) { + FD_SET(fd, &fds); + tv.tv_sec = c; + tv.tv_usec = 0; + c = select(fd+1, &fds, NULL, NULL, &tv); + now = time(NULL); + if (c > 0) + dns_ioevent(NULL, now); + if (stopfirst && ipc->listed) + break; + } +} + +int main(int argc, char **argv) { + int c; + struct ipcheck ipc; + char *nameserver = NULL; + int zgiven = 0; + + if (!(progname = strrchr(argv[0], '/'))) progname = argv[0]; + else argv[0] = ++progname; + + while((c = getopt(argc, argv, "hqtvms:S:cn:")) != EOF) switch(c) { + case 's': ++zgiven; addzone(optarg); break; + case 'S': + ++zgiven; + if (addzonefile(optarg)) break; + error(1, "unable to read zonefile `%s'", optarg); + case 'c': ++zgiven; nzones = 0; break; + case 'q': --verbose; break; + case 'v': ++verbose; break; + case 't': do_txt = 1; break; + case 'n': nameserver = optarg; break; + case 'm': ++stopfirst; break; + case 'h': + printf("%s: %s (udns library version %s).\n", + progname, version, dns_version()); + printf("Usage is: %s [options] address..\n", progname); + printf( +"Where options are:\n" +" -h - print this help and exit\n" +" -s service - add the service (DNSBL zone) to the serice list\n" +" -S service-file - add the DNSBL zone(s) read from the given file\n" +" -c - clear service list\n" +" -v - increase verbosity level (more -vs => more verbose)\n" +" -q - decrease verbosity level (opposite of -v)\n" +" -t - obtain and print TXT records if any\n" +" -m - stop checking after first address match in any list\n" +" -n ipaddr - use the given nameserver instead of the default\n" +"(if no -s or -S option is given, use $RBLCHECK_ZONES, ~/.rblcheckrc\n" +"or /etc/rblcheckrc in that order)\n" + ); + return 0; + default: + error(1, "use `%s -h' for help", progname); + } + + if (!zgiven) { + char *s = getenv("RBLCHECK_ZONES"); + if (s) { + char *k; + s = strdup(s); + for(k = strtok(s, " \t"); k; k = strtok(NULL, " \t")) + addzone(k); + free(s); + } + else { /* probably worthless on windows? */ + char *path; + char *home = getenv("HOME"); + if (!home) home = "."; + path = malloc(strlen(home) + 1 + sizeof(".rblcheckrc")); + sprintf(path, "%s/.rblcheckrc", home); + if (!addzonefile(path)) + addzonefile("/etc/rblcheckrc"); + free(path); + } + } + if (!nzones) + error(1, "no service (zone) list specified (-s or -S option)"); + + argv += optind; + argc -= optind; + + if (!argc) + return 0; + + if (dns_init(NULL, 0) < 0) + error(1, "unable to initialize DNS library: %s", strerror(errno)); + if (nameserver) { + dns_add_serv(NULL, NULL); + if (dns_add_serv(NULL, nameserver) < 0) + error(1, "wrong IP address for a nameserver: `%s'", nameserver); + } + if (dns_open(NULL) < 0) + error(1, "unable to initialize DNS library: %s", strerror(errno)); + + for (c = 0; c < argc; ++c) { + if (c && (verbose > 1 || (verbose == 1 && do_txt))) putchar('\n'); + memset(&ipc, 0, sizeof(ipc)); + ipc.name = argv[c]; + submit(&ipc); + waitdns(&ipc); + display_result(&ipc); + if (stopfirst > 1 && listed) break; + } + + return listed ? 100 : failures ? 2 : 0; +} diff --git a/ape-server/deps/udns-0.0.9/udns.3 b/ape-server/deps/udns-0.0.9/udns.3 new file mode 100755 index 0000000..f580add --- /dev/null +++ b/ape-server/deps/udns-0.0.9/udns.3 @@ -0,0 +1,1351 @@ +.\" $Id: udns.3,v 1.32 2007/01/15 21:19:08 mjt Exp $ +.\" udns library manpage +.\" +.\" Copyright (C) 2005 Michael Tokarev +.\" This file is part of UDNS library, an async DNS stub resolver. +.\" +.\" This library is free software; you can redistribute it and/or +.\" modify it under the terms of the GNU Lesser General Public +.\" License as published by the Free Software Foundation; either +.\" version 2.1 of the License, or (at your option) any later version. +.\" +.\" This library is distributed in the hope that it will be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +.\" Lesser General Public License for more details. +.\" +.\" You should have received a copy of the GNU Lesser General Public +.\" License along with this library, in file named COPYING.LGPL; if not, +.\" write to the Free Software Foundation, Inc., 59 Temple Place, +.\" Suite 330, Boston, MA 02111-1307 USA + +.TH udns 3 "Jan 2007" "Library Functions" + +.SH NAME +udns \- stub DNS resolver library + +.SH SYNOPSYS +.nf +#include +struct \fBdns_ctx\fR; +struct \fBdns_query\fR; +extern struct dns_ctx \fBdns_defctx\fR; +struct dns_ctx *\fIctx\fR; +typedef void \fBdns_query_fn\fR(\fIctx\fR, void *\fIresult\fR, void *\fIdata\fR); +typedef int +\fBdns_parse_fn\fR(const unsigned char *\fIqnd\fR, + const unsigned char *\fIpkt\fR, + const unsigned char *\fIcur\fR, + const unsigned char *\fIend\fR, + void **\fIresultp\fR); + +\fBcc\fR ... -l\fBudns\fR +.fi + +.SH DESCRIPTION + +.PP +The DNS library, \fBudns\fR, implements thread-safe stub DNS resolver +functionality, which may be used both traditional, syncronous way +and asyncronously, with application-supplied event loop. + +.PP +While DNS works with both TCP and UDP, performing UDP query first and +if the result does not fit in UDP buffer (512 bytes max for original +DNS protocol), retrying the query over TCP, the library uses UDP only, +but uses EDNS0 (RFC2671) extensions which allows larger UDP buffers. + +.PP +The library uses single UDP socket to perform all operations even when +asking multiple nameservers. This way, it is very simple to use the +library in asyncronous event-loop applications: an application should +add only single socket to the set of filedescriptors it monitors for I/O. + +.PP +The library uses two main objects, \fIresolver context\fR of type +\fBstruct\ dns_ctx\fR, and \fIquery structure\fR of type +\fBstruct\ dns_query\fR, both are opaque for an application. +Resolver context holds global information about the resolver, +such as list of nameservers to use, list of active requests and the like. +Query objects holds information about a single DNS query in progress and +are allocated/processed/freed by the library. Pointer to query structure +may be treated as an identifier of an in-progress query and may be used +to cancel the asyncronous query or to wait for it to complete. + +.PP +Asyncronous interface works as follows. An application initializes +resolver context, submits any number of queries for it using one of +supplied \fBdns_submit_\fIXXX\fR() routines (each return the query +identifier as pointer to query structure), waits for input on the +UDP socket used by the library, and gives some control to the library +by calling \fBdns_ioevent\fR() and \fBdns_timeouts\fR() routines when +appropriate. The library performs all necessary processing and executes +application supplied callback routine when a query completes (either +successefully or not), giving it the result if any, pointer to the +resolver context (from which completion status may be obtained), and +the data pointer supplied by an application when the query has been +submitted. When submitting a query, an application requests how to +handle the reply -- to either return raw DNS reply packet for its +own low-level processing, or it may provide an address of \fIparsing +routine\fR of type \fBdns_parse_fn\fR to perform conversion of on-wire +format into easy to use data structure (the library provides parsing +routines for several commonly used resource record types, as well as +type-safe higher-level inteface that requests parsing automatically). +The I/O monitoring and timeout handling may be either traditional +select() or poll() based, or any callback-driven technique may be +used. + +.PP +Additionally, the library provides traditional syncronous interface, +which may be intermixed with asyncronous calls (during syncronous +query processing, other asyncronous queries for the same resolver +context continued to be processed as usual). An application uses +one of numerous \fBdns_resolve_\fIXXX\fR() routines provided by the +library to perform a query. As with asyncronous interface, an +application may either request to return raw DNS packet or type-specific +data structure by providing the parsing routine to handle the reply. +Every routine from \fBdns_resolve_\fIXXX\fR() series return pointer +to result or NULL in case of any error. Query completion status +(or length of the raw DNS packet) is available from the resolver +context using \fBdns_status\fR() routine, the same way as for the +asyncronous interface. + +.PP +Internally, library uses on-wire format of domain names, referred +to as \fIDN format\fR in this manual page. This is a series of domain +\fIlabels\fR whith preceeding length byte, terminated by zero-length +label wich is integral part of the DN format. There are several routines +provided to convert from traditional asciiz string to DN and back. +Higher-level type-specific query interface hides the DN format from +an application. + +.SH "COMMON DEFINITIONS" + +.PP +Every DNS Resource Record (RR) has a \fItype\fR and a \fIclass\fR. +The library defines several integer constants, \fBDNS_C_\fIXXX\fR and +\fBDNS_T_\fIXXX\fR, to use as symbolic names for RR classes and types, +such as \fBDNS_C_IN\fR for Internet class, \fBDNS_T_A\fR for IPv4 +address record type and so on. See udns.h header file for complete list +of all such constants. + +.PP +The following constants are defined in udns.h header file: +.IP "\fBDNS_MAXDN\fR (255 bytes)" +Maximum length of the domain name in internal (on-wire) DN format. +.IP "\fBDNS_MAXLABEL\fR (63 bytes)" +Maximum length of a single label in DN format. +.IP "\fBDNS_MAXNAME\fR (1024 bytes)" +Maximum length of asciiz format of a domain name. +.IP "\fBDNS_HSIZE\fR (12 bytes)" +Size of header in DNS packet. +.IP "\fBDNS_PORT\fR (53)" +Default port to use when contacting a DNS server. +.IP "\fBDNS_MAXSERV\fR (6 servers)" +Maximum number of DNS servers to use. +.IP "\fBDNS_MAXPACKET\fR (512 bytes)" +Maximum length of DNS UDP packet as specified by original DNS protocol +.IP "\fBDNS_EDNS0PACKET\fR (4096 bytes)" +Default length of DNS UDP packet (with EDNS0 extensions) the library uses. +Note that recursive nameservers usually resides near the client asking them +to resolve names, e.g. on the same LAN segment or even on the same host, so +UDP packet fragmentation isn't a problem in most cases. Note also that +the size of actual packets will be as many bytes as actual reply size requires, +which is smaller than this value in almost all cases. + +.PP +Additionally, several constants are defined to simplify work with raw DNS +packets, such as DNS response codes (\fBDNS_R_\fIXXX\fR), DNS header layout +(\fBDNS_H_\fIXXX\fR) and others. Again, see udns.h for complete list. +Library error codes (\fBDNS_E_\fIXXX\fR) are described later in this +manual page. + +.SH "RESOLVER CONTEXT" + +.PP +Resolver context, of type \fBstruct\ dns_ctx\fR, is an object which is +opaque to an application. Several routines provided by the library +to initialize, copy and free resolver contexts. Most other high-level +routines in this library expects a pointer to resolver context, \fIctx\fR, +as the first argument. There is a default resolver context available, +named \fBdns_defctx\fR. When the context pointer \fIctx\fR passed to +a routine is NULL, \fBdns_defctx\fR is used. Several resolver contexts +may be active at the same time, for example, when an application is +multi-threaded and each thread uses resolver. +.PP +In order to use the library, an application should initialize and open +one or more resolver context objects. These are two separate actions, +performed by \fBdns_init\fR() (or \fBdns_reset\fR()), and \fBdns_open\fR(). +Between the two calls, an application is free to pefrorm additional +initialisation, such as setting custom nameservers, options or domain search +lists. Optionally, in case no additional custom initialisation is required, +\fBdns_init\fR() may open the context if \fIdo_open\fR argument (see below) +is non-zero. +.PP +When initializing resolver context, the library uses information from +system file /etc/resolv.conf (see \fBresolv.conf\fR(5)), consults +environment variables \fB$LOCALDOMAIN\fR, \fB$DNSCACHEIP\fR, +\fB$NAMESERVERS\fR and \fB$RES_OPTIONS\fR, and local host name to obtain +list of local nameservers, domain name search list and various resolver +options. +.PP +The following routines to initialize resolver context are available: +.PP +.nf +void \fBdns_reset\fR(\fIctx\fR) +int \fBdns_init\fR(\fIctx\fR, int \fIdo_open\fR) +.fi +.RS +\fBdns_reset\fR() resets a given resolver context to default values, +preparing it to be opened by \fBdns_open\fR(). +It is ok to call this routine against opened and active context - all active +queries will be dropped, sockets will be closed and so on. This routine +does not initialize any parameters from system configuration files, use +\fBdns_init\fR() for this. There's no error return - operation always +succeeds. \fBdns_init\fR() does everything \fBdns_reset\fR() does, +plus initializes various parameters of the context according to system +configuration and process environment variables. If \fIdo_open\fR is +non-zero, \fBdns_init\fR() calls \fIdns_open\fR(), so that the whole +library initialisation is performed in a single step. +.RE +.PP +.nf +struct dns_ctx *\fBdns_new\fR(struct dns_ctx *\fIcopy\fR) +void \fBdns_free\fR(\fIctx\fR) +.fi +.RS +\fBdns_new\fR() allocates new resolver context and copies all parameters +for a given resolver context \fIcopy\fR, or default context if \fIcopy\fR +is NULL, and returns pointer to the newly allocated context. The context +being copied should be initialized. +\fBdns_new\fR() may fail if there's no memory available to make a copy +of \fIcopy\fR, in which case the routine will return NULL pointer. +\fBdns_free\fR() is used to close assotiated socket and free resolver +context resources and cancelling (abandoming) all active queries +assotiated with it. It's an error to free \fBdns_defctx\fR, only +dynamically allocated contexts returned by \fBdns_new\fR() are allowed +to be freed by \fBdns_free\fR(). +.RE +.PP +.nf +int \fBdns_add_serv\fR(\fIctx\fR, const char *\fIservaddr\fR) +int \fBdns_add_serv_s\fR(\fIctx\fR, const struct sockaddr *\fIsa\fR) +int \fBdns_add_srch\fR(\fIctx\fR, const char *\fIsrch\fR) +.fi +.RS +Add an element to list of nameservers (\fBdns_add_serv\fR(), as +asciiz-string \fIservaddr\fR with an IP address of the nameserver, +and \fBdns_add_serv_s\fR(), as initialized socket address \fIsa\fR), +or search list (\fBdns_add_srch\fR(), as a pointer to domain name) +for the given context \fIctx\fR. If the last argument is a NULL +pointer, the corresponding list (search or nameserver) is reset +instead. Upon successeful completion, each routine returns new +number of elements in the list in question. On error, negative +value is returned and global variable \fBerrno\fR is set appropriately. +It is an error to call any of this functions if the context is +opened (after \fBdns_open\fR() or \fBdns_init\fR() with non-zero argument). +.RE +.PP +.nf +int \fBdns_set_opts\fR(\fIctx\fR, const char *\fIopts\fR) +.fi +.RS +set resolver context options from \fIopts\fR string, in the same way as +processing \fBoptions\fR statement in resolv.conf and \fB$RES_OPTIONS\fR +environment variable. +.RE +.PP +.nf +void \fBdns_set_opt\fR(\fIctx\fR, int \fIopt\fR, \fIval\fR) +.fi +.RS +.B TODO +The \fIflags\fR argument is a bitmask with the following bits defined: +.IP \fBDNS_NOSRCH\fR +do not perform domain name search in search list. +.IP \fBDNS_NORD\fR +do not request recursion when performing queries +(i.e. don't set RD flag in querues). +.IP \fBDNS_AAONLY\fR +request authoritative answers only (i.e. set AA +flag in queries). +.RE + +.PP +.nf +int \fBdns_open\fR(\fIctx\fR) +int \fBdns_sock\fR(const \fIctx\fR) +void \fBdns_close\fR(\fIctx\fR) +.fi +.RS +\fBdns_open\fR() opens the UDP socket used for queries if not already +open, and return assotiated filedescriptor (or negative value in case +of error). Before any query can be submitted, the context should be +opened using this routine. And before opening, the context should be +initialized. +\fBdns_sock\fR() return the UDP socket if open, or -1 if not. +\fBdns_close\fR() closes the UDP socket if it was open, and drops all active +queries if any. +.RE + +.PP +.nf +int \fBdns_active\fR(const \fIctx\fR) +.fi +.RS +return number of active queries queued for the given context +\fIctx\fR, or zero if none. +.RE + +.PP +.nf +int \fBdns_status\fR(const \fIctx\fR) +.fi +.RS +return status code from last operation. When using syncronous +interface, this is the query completion status of the last query. +With asyncronous interface, from within the callback routine, +this is the query completion status of the query for which the +callback is being called. When query submission fails, this +is the error code indicating failure reason. All error codes +are negative and are represented by \fBDNS_E_\fIXXX\fR constants +described below. +.RE + +.PP +.nf +void \fBdns_ioevent\fR(\fIctx\fR, time_t \fInow\fR) +.fi +.RS +this routine may be called by an application to process I/O +events on the UDP socket used by the library, as returned +by \fBdns_sock\fR(). The routine tries to receive incoming +UDP datagram from the socket and process it. The socket is +set up to be non-blocking, so it is safe to call the routine +even if there's no data to read. The routine will process +as many datagrams as are queued for the socket, so it is +safe to use it with either level-triggered or edge-triggered +I/O monitoring model. The \fInow\fR argument is either a +current time as returned by \fBtime\fR(), or 0, in which +case the routine will obtain current time by it's own. +.RE + +.PP +.nf +int \fBdns_timeouts\fR(\fIctx\fR, int \fImaxwait\fR, time_t \fInow\fR) +.fi +.RS +process any pending timeouts and return number of secounds +from current time (\fInow\fR if it is not 0) to the time when +the library wants the application to pass it control to process +more queued requests. In case when there are no requests pending, +this time is -1. The routine will not request a time larger than +\fImaxwait\fR secounds if it is greather or equal to zero. If +\fInow\fR is 0, the routine will obtain current time by it's own; +when it is not 0, it should contain current time as returned by +\fBtime\fR(). +.RE + +.PP +.nf +typedef void \fBdns_utm_fn\fR(\fIctx\fR, int \fItimeout\fR, void *\fIdata\fR) +void \fBdns_set_cbck\fR(\fIctx\fR, dns_utm_fn *\fIutmfn\fR, void *\fIdata\fR) +.fi +.RS +An application may use custom callback-based I/O multiplexing mechanism. +Usually such a mechanism have concept of a \fItimer\fR, and an ability +to register a timer event in a form of a callback routine which will +be executed after certain amount of time. In order to use such an +event mechanism, udns provides an ability to register and de-register +timer events necessary for internal processing using whatever event +mechanism an application uses. For this to work, it is possible to +assotiate a pointer to a routine that will perform necessary work for +(de)registering timer events with a given resolver context, and +udns will call that routine at appropriate times. Prototype of +such a routine is shown by \fBdns_utm_fn\fR typedef above. Libudns +assotiates single timer with resolver context. User-supplied \fIutmfn\fR +routine will be called by the library with the following arguments: +.IP "\fIctx\fR == NULL" +delete user timer, at context free time or when an application changes +user timer request routine using \fBdns_set_cbck\fR(); +.IP "\fIctx\fR != NULL, \fItimeout\fR < 0" +don't fire timer anymore, when there are no active requests; +.IP "\fIctx\fR != NULL, \fItimeout\fR == 0" +fire timer at the next possibility, but not immediately; +.IP "\fIctx\fR != NULL, \fItimeout\fR > 0" +fire timer after \fItimeout\fR seconds after now. +.PP +The \fIdata\fR argument passed to the routine will be the same +as passed to \fBdns_set_cbck\fR(). +.PP +When a timer expires, an application should call \fBdns_tmeouts\fR() +routine (see below). Non-callback timer usage is provided too. +.RE + +.PP +.B XXXX TODO: some more resolver context routines, like dns_set_dbgfn() etc. + +.SH "QUERY INTERFACE" + +.PP +There are two ways to perform DNS queries: traditional syncronous +way, when udns performs all the necessary processing and return +control to the application only when the query completes, and +asyncronous way, when an application submits one or more queries +to the library using given resolver context, and waits for completion +by monitoring filedescriptor used by library and calling library +routines to process input on that filedescriptor. Asyncronous mode +works with callback routines: an application supplies an address of +a routine to execute when the query completes, and a data pointer, +which is passed to the callback routine. + +.PP +Queries are submitted to the library in a form of \fBstruct\ dns_query\fR. +To perform asyncronous query, an application calls one of the +\fBdns_submit_\fIXXX\fR() rounines, and provides necessary information +for a callback, together with all the query parameters. +When the query completes, library will call application-supplied callback +routine, giving it the resolver context (wich holds query completion status), +dynamically allocated result (which will be either raw DNS packet or, if +applicatin requested parsing the result by specifying non-NULL parse routine, +ready-to-use type-specific structure), and a data pointer provided by an +application when it submitted the query. It is the application who's +responsible for freeing the result memory. +.PP +Generic query callback routine looks like this: +.nf +typedef void +\fBdns_query_fn\fR(\fIctx\fR, void *\fIresult\fR, void *\fIdata\fR) +.fi +Type-specific query interface expects similar form of callback +routine with the only difference in type of \fBresult\fR argument, +which will be pointer to specific data structure (decoded reply) +instead of this void pointer to raw DNS packet data. + +.PP +Result parsing routine looks like this: +.nf +typedef int +\fBdns_parse_fn\fR(const unsigned char *\fIqdn\fR, + const unsigned char *\fIpkt\fR, + const unsigned char *\fIcur\fR, + const unsigned char *\fIend\fR, + void **\fIresultp\fR); +.fi +When called by the library, the arguments are as follows: +\fIpkt\fR points to the start of the packet received; +\fIend\fR points past the end of the packet received; +\fIcur\fR points past the query DN in the query section of the +packet; +\fIqdn\fR points to the original query DN. +The routine should allocate a single buffer to hold the result, +parse the reply filling in the buffer, and return the buffer +using \fIresultp\fR argument. It returns 0 in case of error, +or udns error code (\fBDNS_E_\fIXXX\fR constants) in case of +error. +Note that by the time when the parse routine is called by the +library, packet is already verified to be a reply to the +original query, by matching query DN, query class and query type. + +.PP +Type-specific query inteface supplies necessary parsing routines +automatically. + +.PP +In case of error, query completion status as returned by +\fBdns_status\fR(\fIctx\fR), will contain one of the following values: +.IP "positive value" +length of raw DNS packet if parsing is not requested. +.IP 0 +the query was successeful and the \fIreply\fR points to type-specific +data structure. +.IP \fBDNS_E_TEMPFAIL\fR +temporary error, the resolver nameserver was not able to +process our query or timed out. +.IP \fBDNS_E_PROTOCOL\fR +protocol error, a nameserver returned malformed reply. +.IP \fBDNS_E_NXDOMAIN\fR +the domain name does not exist. +.IP \fBDNS_E_NODATA\fR +there is no data of requested type found. +.IP \fBDNS_E_NOMEM\fR +out of memory while processing request. +.IP \fBDNS_E_BADQUERY\fR +some aspect of the query (most common is the domain name in question) +is invalid, and the library can't even start a query. + +.PP +Library provides two series of routines which uses similar interface -- +one for asyncronous queries and another for syncronous queries. There +are two general low-level routines in each series to submit (asyncronous +interface) and resolve (syncronous interface) queries, as well as several +type-specific routines with more easy-to-use interfaces. To submit +an asyncronous query, use one of \fBdns_submit_\fIXXX\fR() routine, each +of which accepts query parameters, pointers to callback routine and to +callback data, and optional current time hint. Note type-specific +\fBdns_submit_\fIXXX\fR() routines expects specific type of the callback +routine as well, which accepts reply as a pointer to corresponding +structure, not a void pointer). Every \fBdns_submit_\fIXXX\fR() routine +return pointer to internal query structure of type struct\ dns_query, +used as an identifier for the given query. + +.PP +To resolve a query syncronously, use one of \fBdns_resolve_\fIXXX\fR() +routines, which accepts the same query parameters (but not the +callback pointers) as corresponding \fBdns_submit_\fIXXX\fR(), and +return the query result, which is the same as passed to the callback +routine in case of asyncronous interface. + +.PP +In either case, the result memory (if the query completed successefully) +is dynamically allocated and should be freed by an application. If +the query failed for any reason, the result will be NULL, and error +status will be available from \fBdns_status\fR(\fIctx\fR) routine +as shown above. + +.PP +.nf +struct dns_query * +\fBdns_submit_dn\fR(\fIctx\fR, + const unsigned char *\fIdn\fR, \fIqcls\fR, \fIqtyp\fR, \fIflags\fR, + \fIparse\fR, \fIcbck\fR, \fIdata\fR) +struct dns_query * +\fBdns_submit_p\fR(\fIctx\fR, + const char *\fIname\fR, \fIqcls\fR, \fIqtyp\fR, \fIflags\fR, + \fIparse\fR, \fIcbck\fR, \fIdata\fR) + enum dns_class \fIqcls\fR; + enum dns_type \fIqtyp\fR; + int \fIflags\fR; + dns_parse_fn *\fIparse\fR; + dns_query_fn *\fIcbck\fR; + void *\fIdata\fR; +.fi +.RS +submit a query for processing for the given resolver context \fIctx\fR. +Two routines differs only in 3rd argument, which is domain name in +DN format (\fIdn\fR) or asciiz string (\fIname\fR). The query will be +performed for the given domain name, with type \fIqtyp\fR in class \fIqcls\fR, +using option bits in \fIflags\fR, using RR parsing routine pointed by +\fIparse\fR if not-NULL, and upon completion, \fIcbck\fR function will +be called with the \fIdata\fR argument. +In case of successeful query submission, +the routine return pointer to internal query structure which may be treated +as an identifier of the query as used by the library, and may be used as an +argument for \fBdns_cancel\fR() routine. In case of error, NULL will be +returned, and context error status (available using \fIdns_status\fR() routine) +will be set to corresponding error code, which in this case may be +DNS_E_BADQUERY if the \fIname\fR of \fIdn\fR is invalid, DNS_E_NOMEM if +there's no memory available to allocate query structure, or DNS_E_TEMPFAIL +if an internal error occured. +.RE + +.PP +.nf +void *\fBdns_resolve_dn\fR(\fIctx\fR, + const unsigned char *\fIdn\fR, \fIqcls\fR, \fIqtyp\fR, \fIflags\fR, \fIparse\fR); +void *\fBdns_resolve_p\fR(\fIctx\fR, + const char *\fIname\fR, \fIqcls\fR, \fIqtyp\fR, \fIflags\fR, \fIparse\fR) + enum dns_class \fIqcls\fR; + enum dns_type \fIqtyp\fR; + int \fIflags\fR; + dns_parse_fn *\fIparse\fR; +.fi +.RS +syncronous interface. The routines perform all the steps necessary to resolve +the given query and return the result. If there's no positive result for any +reason, all the routines return NULL, and set context error status (available +using \fBdns_status\fR() routine) to indicate the error code. If the query +was successeful, context status code will contain either the length of the +raw DNS reply packet if \fIparse\fR argument was NULL (in which case the return +value is pointer to the reply DNS packet), or 0 (in which case the return value +is the result of \fIparse\fR routine). If the query successeful (return value +is not NULL), the memory returned was dynamically allocated by the library +and should be free()d by application after use. +.RE + +.PP +.nf +void *\fBdns_resolve\fR(\fIctx\fR, struct dns_query *\fIq\fR) +.fi +.RS +wait for the given query \fIq\fR, as returned by one of +\fBdns_submit_\fIXXX\fR() routines, for completion, and +return the result. The callback routine will not be called +for this query. After completion, the query identifier \fIq\fR +is not valid. Both \fBdns_resolve_dn\fR() and \fBdns_resolve_p\fR() +are just wrappers around corresponding submit routines and this +\fBdns_resolve\fR() routine. +.RE + +.PP +.nf +void \fBdns_cancel\fR(\fIctx\fR, struct dns_query *\fIq\fR) +.fi +.RS +cancel an active query \fIq\fR, without calling a callback routine. +After completion, the query identifier \fIq\fR is not valid. +.RE + +.SH "TYPE-SPECIFIC QUERIES" + +.PP +In addition to the generic low-level query interface, the library provides +a set of routines to perform specific queries in a type-safe manner, as +well as parsers for several well-known resource record types. The library +implements high-level interface for A, AAAA, PTR, MX and TXT records +and DNSBL and RHSBL functionality. These routines returns specific types +as result of a query, instead of raw DNS packets. The following types +and routines are available. + +.PP +.nf +struct \fBdns_rr_null\fR { + char *\fBdnsn_qname\fR; /* original query name */ + char *\fBdnsn_cname\fR; /* canonical name */ + unsigned \fBdnsn_ttl\fR; /* Time-To-Live (TTL) value */ + int \fBdnsn_nrr\fR; /* number of records in the set */ +}; +.fi +.PP +NULL RR set, used as a base for all other RR type structures. +Every RR structure as used by the library have four standard +fields as in struct\ \fBdns_rr_null\fR. + +.SS "IN A Queries" +.PP +.nf +struct \fBdns_rr_a4\fR { /* IN A RRset */ + char *\fBdnsa4_qname\fR; /* original query name */ + char *\fBdnsa4_cname\fR; /* canonical name */ + unsigned \fBdnsa4_ttl\fR; /* Time-To-Live (TTL) value */ + int \fBdnsa4_nrr\fR; /* number of addresses in the set */ + struct in_addr \fBdnsa4_addr\fR[]; /* array of addresses */ +}; +typedef void + \fBdns_query_a4_fn\fR(\fIctx\fR, struct dns_rr_a4 *\fIresult\fR, \fIdata\fR) +dns_parse_fn \fBdns_parse_a4\fB; +struct dns_query * +\fBdns_submit_a4\fB(\fIctx\fR, const char *\fIname\fR, int \fIflags\fR, + dns_query_a4_fn *\fIcbck\fR, \fIdata\fR); +struct dns_rr_a4 * +\fBdns_resolve_a4\fB(\fIctx\fR, const char *\fIname\fR, int \fIflags\fR); +.fi +.PP +The \fBdns_rr_a4\fR structure holds a result of an \fBIN A\fR query, +which is an array of IPv4 addresses. Callback routine for IN A queries +expected to be of type \fBdns_query_a4_fn\fR, which expects pointer to +\fBdns_rr_a4\fR structure as query result instead of raw DNS packet. +The \fBdns_parse_a4\fR() is used to convert raw DNS reply packet into +\fBdns_rr_a4\fR structure (it is used internally and may be used directly too +with generic query interface). Routines \fBdns_submit_a4\fR() and +\fBdns_resolve_a4\fR() are used to perform A IN queries in a type-safe +manner. The \fIname\fR parameter is the domain name in question, and +\fIflags\fR is query flags bitmask, with one bit, DNS_NOSRCH, of practical +interest (if the \fIname\fR is absolute, that is, it ends up with a dot, +DNS_NOSRCH flag will be set automatically). + +.SS "IN AAAA Queries" +.PP +.nf +struct \fBdns_rr_a6\fR { /* IN AAAA RRset */ + char *\fBdnsa6_qname\fR; /* original query name */ + char *\fBdnsa6_cname\fR; /* canonical name */ + unsigned \fBdnsa6_ttl\fR; /* Time-To-Live (TTL) value */ + int \fBdnsa6_nrr\fR; /* number of addresses in the set */ + struct in6_addr \fBdnsa6_addr\fR[]; /* array of addresses */ +}; +typedef void + \fBdns_query_a6_fn\fR(\fIctx\fR, struct dns_rr_a6 *\fIresult\fR, \fIdata\fR) +dns_parse_fn \fBdns_parse_a6\fB; +struct dns_query * +\fBdns_submit_a6\fB(\fIctx\fR, const char *\fIname\fR, int \fIflags\fR, + dns_query_a6_fn *\fIcbck\fR, \fIdata\fR); +struct dns_rr_a6 * +\fBdns_resolve_a6\fB(\fIctx\fR, const char *\fIname\fR, int \fIflags\fR); +.fi +.PP +The \fBdns_rr_a6\fR structure holds a result of an \fBIN AAAA\fR query, +which is an array of IPv6 addresses. Callback routine for IN AAAA queries +expected to be of type \fBdns_query_a6_fn\fR, which expects pointer to +\fBdns_rr_a6\fR structure as query result instead of raw DNS packet. +The \fBdns_parse_a6\fR() is used to convert raw DNS reply packet into +\fBdns_rr_a6\fR structure (it is used internally and may be used directly too +with generic query interface). Routines \fBdns_submit_a6\fR() and +\fBdns_resolve_a6\fR() are used to perform AAAA IN queries in a type-safe +manner. The \fIname\fR parameter is the domain name in question, and +\fIflags\fR is query flags bitmask, with one bit, DNS_NOSRCH, of practical +interest (if the \fIname\fR is absolute, that is, it ends up with a dot, +DNS_NOSRCH flag will be set automatically). + +.SS "IN PTR Queries" +.PP +.nf +struct \fBdns_rr_ptr\fR { /* IN PTR RRset */ + char *\fBdnsptr_qname\fR; /* original query name */ + char *\fBdnsptr_cname\fR; /* canonical name */ + unsigned \fBdnsptr_ttl\fR; /* Time-To-Live (TTL) value */ + int \fBdnsptr_nrr\fR; /* number of domain name pointers */ + char *\fBdnsptr_ptr\fR[]; /* array of domain name pointers */ +}; +typedef void + \fBdns_query_ptr_fn\fR(\fIctx\fR, struct dns_rr_ptr *\fIresult\fR, \fIdata\fR) +dns_parse_fn \fBdns_parse_ptr\fB; +struct dns_query * +\fBdns_submit_a4ptr\fB(\fIctx\fR, const struct in_addr *\fBaddr\fR, + dns_query_ptr_fn *\fIcbck\fR, \fIdata\fR); +struct dns_rr_ptr * +\fBdns_resolve_a4ptr\fB(\fIctx\fR, const struct in_addr *\fBaddr\fR); +struct dns_query * +\fBdns_submit_a6ptr\fB(\fIctx\fR, const struct in6_addr *\fBaddr\fR, + dns_query_ptr_fn *\fIcbck\fR, \fIdata\fR); +struct dns_rr_ptr * +\fBdns_resolve_a6ptr\fB(\fIctx\fR, const struct in6_addr *\fBaddr\fR); +.fi +.PP +The \fBdns_rr_ptr\fR structure holds a result of an IN PTR query, which +is an array of domain name pointers for a given IPv4 or IPv6 address. +Callback routine for IN PTR queries expected to be of type +\fBdns_query_ptr_fn\fR, which expects pointer to \fBdns_rr_ptr\fR +structure as query result instead of raw DNS packet. The \fBdns_parse_ptr\fR() +is used to convert raw DNS reply packet into \fBdns_rr_ptr\fR structure +(it is used internally and may be used directly too with generic query +interface). Routines \fBdns_submit_a4ptr\fR() and \fBdns_resolve_a4ptr\fR() +are used to perform IN PTR queries for IPv4 addresses in a type-safe +manner. Routines \fBdns_submit_a6ptr\fR() and \fBdns_resolve_a6ptr\fR() +are used to perform IN PTR queries for IPv6 addresses. + +.SS "IN MX Queries" +.PP +.nf +struct \fBdns_mx\fR { /* single MX record */ + int \fBpriority\fR; /* priority value of this MX */ + char *\fBname\fR; /* domain name of this MX */ +}; +struct \fBdns_rr_mx\fR { /* IN MX RRset */ + char *\fBdnsmx_qname\fR; /* original query name */ + char *\fBdnsmx_cname\fR; /* canonical name */ + unsigned \fBdnsmx_ttl\fR; /* Time-To-Live (TTL) value */ + int \fBdnsmx_nrr\fR; /* number of mail exchangers in the set */ + struct dns_mx \fBdnsmx_mx\fR[]; /* array of mail exchangers */ +}; +typedef void + \fBdns_query_mx_fn\fR(\fIctx\fR, struct dns_rr_mx *\fIresult\fR, \fIdata\fR) +dns_parse_fn \fBdns_parse_mx\fB; +struct dns_query * +\fBdns_submit_mx\fB(\fIctx\fR, const char *\fIname\fR, int \fIflags\fR, + dns_query_mx_fn *\fIcbck\fR, \fIdata\fR); +struct dns_rr_mx * +\fBdns_resolve_mx\fB(\fIctx\fR, const char *\fIname\fR, int \fIflags\fR); +.fi +.PP +The \fBdns_rr_mx\fR structure holds a result of an IN MX query, which +is an array of mail exchangers for a given domain. Callback routine for IN MX +queries expected to be of type \fBdns_query_mx_fn\fR, which expects pointer to +\fBdns_rr_mx\fR structure as query result instead of raw DNS packet. +The \fBdns_parse_mx\fR() is used to convert raw DNS reply packet into +\fBdns_rr_mx\fR structure (it is used internally and may be used directly too +with generic query interface). Routines \fBdns_submit_mx\fR() and +\fBdns_resolve_mx\fR() are used to perform IN MX queries in a type-safe +manner. The \fIname\fR parameter is the domain name in question, and +\fIflags\fR is query flags bitmask, with one bit, DNS_NOSRCH, of practical +interest (if the \fIname\fR is absolute, that is, it ends up with a dot, +DNS_NOSRCH flag will be set automatically). + +.SS "TXT Queries" +.PP +.nf +struct \fBdns_txt\fR { /* single TXT record */ + int \fBlen\fR; /* length of the text */ + unsigned char *\fBtxt\fR; /* pointer to the text */ +}; +struct \fBdns_rr_txt\fR { /* TXT RRset */ + char *\fBdnstxt_qname\fR; /* original query name */ + char *\fBdnstxt_cname\fR; /* canonical name */ + unsigned \fBdnstxt_ttl\fR; /* Time-To-Live (TTL) value */ + int \fBdnstxt_nrr\fR; /* number of text records in the set */ + struct dns_txt \fBdnstxt_txt\fR[]; /* array of TXT records */ +}; +typedef void + \fBdns_query_txt_fn\fR(\fIctx\fR, struct dns_rr_txt *\fIresult\fR, \fIdata\fR) +dns_parse_fn \fBdns_parse_txt\fB; +struct dns_query * +\fBdns_submit_txt\fB(\fIctx\fR, const char *\fIname\fR, enum dns_class \fIqcls\fR, + int \fIflags\fR, dns_query_txt_fn *\fIcbck\fR, \fIdata\fR); +struct dns_rr_txt * +\fBdns_resolve_txt\fB(\fIctx\fR, const char *\fIname\fR, + enum dns_class \fIqcls\fR, int \fIflags\fR); +.fi +.PP +The \fBdns_rr_txt\fR structure holds a result of a TXT query, which is an +array of text records for a given domain name. Callback routine for TXT +queries expected to be of type \fBdns_query_txt_fn\fR, which expects pointer +to \fBdns_rr_txt\fR structure as query result instead of raw DNS packet. +The \fBdns_parse_txt\fR() is used to convert raw DNS reply packet into +\fBdns_rr_txt\fR structure (it is used internally and may be used directly too +with generic query interface). Routines \fBdns_submit_txt\fR() and +\fBdns_resolve_txt\fR() are used to perform IN MX queries in a type-safe +manner. The \fIname\fR parameter is the domain name in question, and +\fIflags\fR is query flags bitmask, with one bit, DNS_NOSRCH, of practical +interest (if the \fIname\fR is absolute, that is, it ends up with a dot, +DNS_NOSRCH flag will be set automatically). Note that each TXT string +is represented by \fBstruct\ dns_txt\fR, while zero-terminated (and the +len field of the structure does not include the terminator), may contain +embedded null characters -- content of TXT records is not interpreted +by the library in any way. + +.SS "SRV Queries" +.PP +.nf +struct \fBdns_srv\fR { /* single SRV record */ + int \fBpriority\fR; /* priority of the record */ + int \fBweight\fR; /* weight of the record */ + int \fBport\fR; /* the port number to connect to */ + char *\fBname\fR; /* target host name */ +}; +struct \fBdns_rr_srv\fR { /* SRV RRset */ + char *\fBdnssrv_qname\fR; /* original query name */ + char *\fBdnssrv_cname\fR; /* canonical name */ + unsigned \fBdnssrv_ttl\fR; /* Time-To-Live (TTL) value */ + int \fBdnssrv_nrr\fR; /* number of text records in the set */ + struct dns_srv \fBdnssrv_srv\fR[]; /* array of SRV records */ +}; +typedef void + \fBdns_query_srv_fn\fR(\fIctx\fR, struct dns_rr_srv *\fIresult\fR, \fIdata\fR) +dns_parse_fn \fBdns_parse_srv\fB; +struct dns_query * +\fBdns_submit_srv\fB(\fIctx\fR, const char *\fIname\fR, const char *\fIservice\fR, const char *\fIprotocol\fR, + int \fIflags\fR, dns_query_txt_fn *\fIcbck\fR, \fIdata\fR); +struct dns_rr_srv * +\fBdns_resolve_srv\fB(\fIctx\fR, const char *\fIname\fR, const char *\fIservice\fR, const char *\fIprotocol\fR, + int \fIflags\fR); +.fi +.PP +The \fBdns_rr_srv\fR structure holds a result of an IN SRV (rfc2782) query, +which is an array of servers (together with port numbers) which are performing +operations for a given \fIservice\fR using given \fIprotocol\fR on a target +domain \fIname\fR. Callback routine for IN SRV queries expected to be of type +\fBdns_query_srv_fn\fR, which expects pointer to \fBdns_rr_srv\fR structure as +query result instead of raw DNS packet. The \fBdns_parse_srv\fR() is used to +convert raw DNS reply packet into \fBdns_rr_srv\fR structure (it is used +internally and may be used directly too with generic query interface). +Routines \fBdns_submit_srv\fR() and \fBdns_resolve_srv\fR() are used to +perform IN SRV queries in a type-safe manner. The \fIname\fR parameter +is the domain name in question, \fIservice\fR and \fRprotocl\fR specifies the +service and the protocol in question (the library will construct query DN +according to rfc2782 rules) and may be NULL (in this case the library +assumes \fIname\fR parameter holds the complete SRV query), and +\fIflags\fR is query flags bitmask, with one bit, DNS_NOSRCH, of practical +interest (if the \fIname\fR is absolute, that is, it ends up with a dot, +DNS_NOSRCH flag will be set automatically). + +.SS "NAPTR Queries" +.PP +.nf +struct \fBdns_naptr\fR { /* single NAPTR record */ + int \fBorder\fR; /* record order */ + int \fBpreference\fR; /* preference of this record */ + char *\fBflags\fR; /* application-specific flags */ + char *\fBservices\fR; /* service parameters */ + char *\fBregexp\fR; /* substitutional regular expression */ + char *\fBreplacement\fR; /* replacement string */ +}; +struct \fBdns_rr_naptr\fR { /* NAPTR RRset */ + char *\fBdnsnaptr_qname\fR; /* original query name */ + char *\fBdnsnaptr_cname\fR; /* canonical name */ + unsigned \fBdnsnaptr_ttl\fR; /* Time-To-Live (TTL) value */ + int \fBdnsnaptr_nrr\fR; /* number of text records in the set */ + struct dns_naptr \fBdnsnaptr_naptr\fR[]; /* array of NAPTR records */ +}; +typedef void + \fBdns_query_naptr_fn\fR(\fIctx\fR, struct dns_rr_naptr *\fIresult\fR, \fIdata\fR) +dns_parse_fn \fBdns_parse_naptr\fB; +struct dns_query * +\fBdns_submit_naptr\fB(\fIctx\fR, const char *\fIname\fR, int \fIflags\fR, + dns_query_txt_fn *\fIcbck\fR, \fIdata\fR); +struct dns_rr_naptr * +\fBdns_resolve_naptr\fB(\fIctx\fR, const char *\fIname\fR, int \fIflags\fR); +.fi +.PP +The \fBdns_rr_naptr\fR structure holds a result of an IN NAPTR (rfc3403) query. +Callback routine for IN NAPTR queries expected to be of type +\fBdns_query_naptr_fn\fR, expects pointer to \fBdns_rr_naptr\fR +structure as query result instead of raw DNS packet. +The \fBdns_parse_naptr\fR() is used to convert raw DNS reply packet into +\fBdns_rr_naptr\fR structure (it is used +internally and may be used directly too with generic query interface). +Routines \fBdns_submit_naptr\fR() and \fBdns_resolve_naptr\fR() are used to +perform IN NAPTR queries in a type-safe manner. The \fIname\fR parameter +is the domain name in question, and \fIflags\fR is query flags bitmask, +with one bit, DNS_NOSRCH, of practical interest (if the \fIname\fR is +absolute, that is, it ends up with a dot, DNS_NOSRCH flag will be set +automatically). + +.SS "DNSBL Interface" +.PP +A DNS-based blocklists, or a DNSBLs, are in wide use nowadays, especially +to protect mailservers from spammers. The library provides DNSBL interface, +a set of routines to perform queries against DNSBLs. Routines accepts an +IP address (IPv4 and IPv6 are both supported) and a base DNSBL zone as +query parameters, and returns either \fBdns_rr_a4\fR or \fBdns_rr_txt\fR +structure. Note that IPv6 interface return IPv4 RRset. +.PP +.nf +struct dns_query * +\fBdns_submit_a4dnsbl\fR(\fIctx\fR, + const struct in_addr *\fIaddr\fR, const char *\fIdnsbl\fR, + dns_query_a4_fn *\fIcbck\fR, void *\fIdata\fR); +struct dns_query * +\fBdns_submit_a4dnsbl_txt\fR(\fIctx\fR, + const struct in_addr *\fIaddr\fR, const char *\fIdnsbl\fR, + dns_query_txt_fn *\fIcbck\fR, void *\fIdata\fR); +struct dns_query * +\fBdns_submit_a6dnsbl\fR(\fIctx\fR, + const struct in6_addr *\fIaddr\fR, const char *\fIdnsbl\fR, + dns_query_a4_fn *\fIcbck\fR, void *\fIdata\fR); +struct dns_query * +\fBdns_submit_a6dnsbl_txt\fR(\fIctx\fR, + const struct in6_addr *\fIaddr\fR, const char *\fIdnsbl\fR, + dns_query_txt_fn *\fIcbck\fR, void *\fIdata\fR); +struct dns_rr_a4 *\fBdns_resolve_a4dnsbl\fR(\fIctx\fR, + const struct in_addr *\fIaddr\fR, const char *\fIdnsbl\fR) +struct dns_rr_txt *\fBdns_resolve_a4dnsbl_txt\fR(\fIctx\fR, + const struct in_addr *\fIaddr\fR, const char *\fIdnsbl\fR) +struct dns_rr_a4 *\fBdns_resolve_a6dnsbl\fR(\fIctx\fR, + const struct in6_addr *\fIaddr\fR, const char *\fIdnsbl\fR) +struct dns_rr_txt *\fBdns_resolve_a6dnsbl_txt\fR(\fIctx\fR, + const struct in6_addr *\fIaddr\fR, const char *\fIdnsbl\fR) +.fi +Perform (submit or resolve) a DNSBL query for the given \fIdnsbl\fR +domain and an IP \fIaddr\fR in question, requesting either A or TXT +records. + +.SS "RHSBL Interface" +.PP +RHSBL is similar to DNSBL, but instead of an IP address, the +parameter is a domain name. +.PP +.nf +struct dns_query * +\fBdns_submit_rhsbl\fR(\fIctx\fR, const char *\fIname\fR, const char *\fIrhsbl\fR, + dns_query_a4_fn *\fIcbck\fR, void *\fIdata\fR); +struct dns_query * +\fBdns_submit_rhsbl_txt\fR(\fIctx\fR, const char *\fIname\fR, const char *\fIrhsbl\fR, + dns_query_txt_fn *\fIcbck\fR, void *\fIdata\fR); +struct dns_rr_a4 * +\fBdns_resolve_rhsbl\fR(\fIctx\fR, const char *\fIname\fR, const char *\fIrhsbl\fR); +struct dns_rr_txt * +\fBdns_resolve_rhsbl_txt\fR(\fIctx\fR, const char *\fIname\fR, const char *\fIrhsbl\fR); +.fi +Perform (submit or resolve) a RHSBL query for the given \fIrhsbl\fR +domain and \fIname\fR in question, requesting either A or TXT records. + + +.SH "LOW-LEVEL INTERFACE" + +.SS "Domain Names (DNs)" + +.PP +A DN is a series of domain name labels each starts with length byte, +followed by empty label (label with zero length). The following +routines to work with DNs are provided. + +.PP +.nf +unsigned \fBdns_dnlen\fR(const unsigned char *\fIdn\fR) +.fi +.RS +return length of the domain name \fIdn\fR, including the terminating label. +.RE + +.PP +.nf +unsigned \fBdns_dnlabels\fR(const unsigned char *\fIdn\fR) +.fi +.RS +return number of non-zero labels in domain name \fIdn\fR. +.RE + +.PP +.nf +unsigned \fBdns_dnequal\fR(\fIdn1\fR, \fIdn2\fR) + const unsigned char *\fIdn1\fR, *\fIdn2\fR; +.fi +.RS +test whenever the two domain names, \fIdn1\fR and \fIdn2\fR, are +equal (case-insensitive). Return domain name length if equal +or 0 if not. +.RE + +.PP +.nf +unsigned \fBdns_dntodn\fR(\fIsdn\fR, \fIddn\fR, \fIdnsiz\fR) + const unsigned char *\fIsdn\fR; + unsigned char *\fIddn\fR; + unsigned \fIdnsiz\fR; +.fi +.RS +copies the source domain name \fIsdn\fR to destination buffer \fIddn\fR +of size \fIdnsiz\fR. Return domain name length or 0 if \fIddn\fR is +too small. +.RE + +.PP +.nf +int \fBdns_ptodn\fR(\fIname\fR, \fInamelen\fR, \fIdn\fR, \fIdnsiz\fR, \fIisabs\fR) +int \fBdns_sptodn\fR(\fIname\fR, \fIdn\fR, \fIdnsiz\fR) + const char *\fIname\fR; unsigned \fInamelen\fR; + unsigned char *\fIdn\fR; unsigned \fIdnsiz\fR; + int *\fIisabs\fR; +.fi +.RS +convert asciiz name \fIname\fR of length \fInamelen\fR to DN format, +placing result into buffer \fIdn\fR of size \fIdnsiz\fR. Return +length of the DN if successeful, 0 if the \fIdn\fR buffer supplied is +too small, or negative value if \fIname\fR is invalid. If \fIisabs\fR +is non-NULL and conversion was successeful, *\fIisabs\fR will be set to +either 1 or 0 depending whenever \fIname\fR was absolute (i.e. ending with +a dot) or not. Name length, \fInamelength\fR, may be zero, in which case +strlen(\fIname\fR) will be used. Second form, \fBdns_sptodn\fR(), is a +simplified form of \fBdns_ptodn\fR(), equivalent to +.br +.nf +\fBdns_ptodn\fR(\fIname\fR, 0, \fIdn\fR, \fIdnlen\fR, 0). +.fi +.RE + +.PP +.nf +extern const unsigned char \fBdns_inaddr_arpa_dn\fR[] +int \fBdns_a4todn\fR(const struct in_addr *\fIaddr\fR, const unsigned char *\fItdn\fR, + unsigned char *\fIdn\fR, unsigned \fIdnsiz\fR) +int \fBdns_a4ptodn\fR(const struct in_addr *\fIaddr\fR, const char *\fItname\fR, + unsigned char *\fIdn\fR, unsigned \fIdnsiz\fR) +extern const unsigned char \fBdns_ip6_arpa_dn\fR[] +int \fBdns_a6todn\fR(const struct in6_addr *\fIaddr\fR, const unsigned char *\fItdn\fR, + unsigned char *\fIdn\fR, unsigned \fIdnsiz\fR) +int \fBdns_a6ptodn\fR(const struct in6_addr *\fIaddr\fR, const char *\fItname\fR, + unsigned char *\fIdn\fR, unsigned \fIdnsiz\fR) +.fi +.RS +several variants of routines to convert IPv4 and IPv6 address \fIaddr\fR +into reverseDNS-like domain name in DN format, storing result in \fIdn\fR +of size \fIdnsiz\fR. \fItdn\fR (or \fItname\fR) is the base zone name, +like in-addr.arpa for IPv4 or in6.arpa for IPv6. If \fItdn\fR (or \fItname\fR) +is NULL, \fBdns_inaddr_arpa_dn\fR (or \fBdns_ip6_arpa_dn\fR) will be used. +The routines may be used to construct a DN for a DNSBL lookup for example. +All routines return length of the resulting DN on success, -1 if resulting +DN is invalid, or 0 if the \fIdn\fR buffer (\fIdnsiz\fR) is too small. +To hold standard rDNS DN, a buffer of size \fBDNS_A4RSIZE\fR (30 bytes) for +IPv4 address, or \fBDNS_A6RSIZE\fR (74 bytes) for IPv6 address, is sufficient. +.RE + +.PP +.nf +int \fBdns_dntop\fR(\fIdn\fR, \fIname\fR, \fInamesiz\fR) + const unsigned char *\fIdn\fR; + const char *\fIname\fR; unsigned \fInamesiz\fR; +.fi +.RS +convert domain name \fIdn\fR in DN format to asciiz string, placing result +into \fIname\fR buffer of size \fInamesiz\fR. Maximum length of asciiz +representation of domain name is \fBDNS_MAXNAME\fR (1024) bytes. Root +domain is represented as empty string. Return length of the resulting name +(including terminating character, i.e. strlen(name)+1) on success, 0 if the +\fIname\fR buffer is too small, or negative value if \fIdn\fR is invalid +(last case should never happen since all routines in this library which +produce domain names ensure the DNs generated are valid). +.RE + +.PP +.nf +const char *\fBdns_dntosp\fR(const unsigned char *\fIdn\fR) +.fi +.RS +convert domain name \fIdn\fR in DN format to asciiz string using static +buffer. Return the resulting asciiz string on success or NULL on failure. +Note since this routine uses static buffer, it is not thread-safe. +.RE + +.PP +.nf +unsigned \fBdns_dntop_size\fR(const unsigned char *\fIdn\fR) +.fi +.RS +return the buffer size needed to convert the \fIdn\fR domain name +in DN format to asciiz string, for \fBdns_dntop\fR(). The routine +return either the size of buffer required, including the trailing +zero byte, or 0 if \fIdn\fR is invalid. +.RE + +.SS "Working with DNS Packets" + +.PP +The following routines are provided to encode and decode DNS on-wire +packets. This is low-level interface. + +.PP +DNS response codes (returned by \fBdns_rcode\fR() routine) are +defined as constants prefixed with \fBDNS_R_\fR. See udns.h +header file for the complete list. In particular, constants +\fBDNS_R_NOERROR\fR (0), \fBDNS_R_SERVFAIL\fR, \fBDNS_R_NXDOMAIN\fR +may be of interest to an application. + +.PP +.nf +unsigned \fBdns_get16\fR(const unsigned char *\fIp\fR) +unsigned \fBdns_get32\fR(const unsigned char *\fIp\fR) +.fi +.RS +helper routines, convert 16-bit or 32-bit integer in on-wire +format pointed to by \fIp\fR to unsigned. +.RE + +.PP +.nf +unsigned char *\fBdns_put16\fR(unsigned char *\fId\fR, unsigned \fIn\fR) +unsigned char *\fBdns_put32\fR(unsigned char *\fId\fR, unsigned \fIn\fR) +.fi +.RS +helper routine, convert unsigned 16-bit or 32-bit integer \fIn\fR to +on-wire format to buffer pointed to by \fId\fR, return \fId\fR+2 or +\fId\fR+4. +.RE + +.PP +.nf +\fBDNS_HSIZE\fR (12) +.fi +.RS +defines size of DNS header. Data section +in the DNS packet immediately follows the header. In the header, +there are query identifier (id), various flags and codes, +and number of resource records in various data sections. +See udns.h header file for complete list of DNS header definitions. +.RE + +.PP +.nf +unsigned \fBdns_qid\fR(const unsigned char *\fIpkt\fR) +int \fBdns_rd\fR(const unsigned char *\fIpkt\fR) +int \fBdns_tc\fR(const unsigned char *\fIpkt\fR) +int \fBdns_aa\fR(const unsigned char *\fIpkt\fR) +int \fBdns_qr\fR(const unsigned char *\fIpkt\fR) +int \fBdns_ra\fR(const unsigned char *\fIpkt\fR) +unsigned \fBdns_opcode\fR(const unsigned char *\fIpkt\fR) +unsigned \fBdns_rcode\fR(const unsigned char *\fIpkt\fR) +unsigned \fBdns_numqd\fR(const unsigned char *\fIpkt\fR) +unsigned \fBdns_numan\fR(const unsigned char *\fIpkt\fR) +unsigned \fBdns_numns\fR(const unsigned char *\fIpkt\fR) +unsigned \fBdns_numar\fR(const unsigned char *\fIpkt\fR) +const unsigned char *\fBdns_payload\fR(const unsigned char *\fIpkt\fR) +.fi +.RS +return various parts from the DNS packet header \fIpkt\fR: +query identifier (qid), +recursion desired (rd) flag, +truncation occured (tc) flag, +authoritative answer (aa) flag, +query response (qr) flag, +recursion available (ra) flag, +operation code (opcode), +result code (rcode), +number of entries in question section (numqd), +number of answers (numan), +number of authority records (numns), +number of additional records (numar), +and the pointer to the packet data (payload). +.RE + +.PP +.nf +int \fBdns_getdn\fR(\fIpkt\fR, \fIcurp\fR, \fIpkte\fR, \fIdn\fR, \fIdnsiz\fR) +const unsigned char *\fBdns_skipdn\fR(\fIcur\fR, \fIpkte\fR) + const unsigned char *\fIpkt\fR, *\fIpkte\fR, **\fIcurp\fR, *\fIcur\fR; + unsigned char *\fIdn\fR; unsigned \fIdnsiz\fR; +.fi +.RS +\fBdns_getdn\fR() extract DN from DNS packet \fIpkt\fR which ends before +\fIpkte\fR starting at position *\fIcurp\fR into buffer pointed to by +\fIdn\fR of size \fIdnsiz\fR. Upon successeful completion, *\fIcurp\fR +will point to the next byte in the packet after the extracted domain name. +It return positive number (length of the DN if \fIdn\fR) upon successeful +completion, negative value on error (when the packet contains invalid data), +or zero if the \fIdnsiz\fR is too small (maximum length of a domain name is +\fBDNS_MAXDN\fR). \fBdns_skipdn\fR() return pointer to the next byte in +DNS packet which ends up before \fIpkte\fR after a domain name which starts +at the \fIcur\fP byte, or NULL if the packet is invalid. \fBdns_skipdn\fR() +is more or less equivalent to what \fBdns_getdn\fR() does, except it does not +actually extract the domain name in question, and uses simpler interface. +.RE + +.PP +.nf +struct \fBdns_rr\fR { + unsigned char \fBdnsrr_dn\fR[DNS_MAXDN]; /* the RR DN name */ + enum dns_class \fBdnsrr_cls\fR; /* class of the RR */ + enum dns_type \fBdnsrr_typ\fR; /* type of the RR */ + unsigned \fBdnsrr_ttl\fR; /* TTL value */ + unsigned \fBdnsrr_dsz\fR; /* size of data in bytes */ + const unsigned char *\fBdnsrr_dptr\fR; /* pointer to the first data byte */ + const unsigned char *\fBdnsrr_dend\fR; /* next byte after RR */ +}; +.fi +.RS +The \fBdns_rr\fR structure is used to hold information about +single DNS Resource Record (RR) in an easy to use form. +.RE + +.PP +.nf +struct \fBdns_parse\fR { + const unsigned char *\fBdnsp_pkt\fR; /* pointer to the packet being parsed */ + const unsigned char *\fBdnsp_end\fR; /* end of the packet pointer */ + const unsigned char *\fBdnsp_cur\fR; /* current packet positionn */ + const unsigned char *\fBdnsp_ans\fR; /* pointer to the answer section */ + int \fBdnsp_rrl\fR; /* number of RRs left */ + int \fBdnsp_nrr\fR; /* number of relevant RRs seen so far */ + unsigned \fBdnsp_ttl\fR; /* TTL value so far */ + const unsigned char *\fBdnsp_qdn\fR; /* the domain of interest or NULL */ + enum dns_class \fBdnsp_qcls\fR; /* class of interest or 0 for any */ + enum dns_type \fBdnsp_qtyp\fR; /* type of interest or 0 for any */ + unsigned char \fBdnsp_dnbuf\fR[DNS_MAXDN]; /* domain name buffer */ +}; +.fi +.RS +The \fBdns_parse\fR structure is used to parse DNS reply packet. +It holds information about the packet being parsed (dnsp_pkt, dnsp_end and +dnsp_cur fields), number of RRs in the current section left to do, and +the information about specific RR which we're looking for (dnsp_qdn, +dnsp_qcls and dnsp_qtyp fields). +.RE + +.PP +.nf +int \fBdns_initparse\fR(struct dns_parse *\fIp\fR, + const unsigned char *\fIqdn\fR, + const unsigned char *\fIpkt\fR, + const unsigned char *\fIcur\fR, + const unsigned char *\fIend\fR) +.fi +.RS +initializes the RR parsing structure \fIp\fR. Arguments \fIpkt\fR, \fIcur\fR +and \fIend\fR should describe the received packet: \fIpkt\fR is the start of +the packet, \fIend\fR points to the next byte after the end of the packet, +and \fIcur\fR points past the query DN in query section (to query class+type +information). And \fIqdn\fR points to the query DN. This is the arguments +passed to \fBdns_parse_fn\fR() routine. \fBdns_initparse\fR() initializes +\fBdnsp_pkt\fR, \fBdnsp_end\fR and \fBdnsp_qdn\fR fields to the corresponding +arguments, extracts and initializes \fBdnsp_qcls\fR and \fBdnsp_qtyp\fR +fields to the values found at \fIcur\fR pointer, initializes +\fBdnsp_cur\fR and \fBdnsp_ans\fR fields to be \fIcur\fR+4 (to the start of +answer section), and initializes \fBdnsp_rrl\fR field to be number of entries +in answer section. \fBdnsp_ttl\fR will be set to max TTL value, 0xffffffff, +and \fBdnsp_nrr\fR to 0. +.RE + +.PP +.nf +int \fBdns_nextrr\fR(struct dns_parse *\fIp\fR, struct dns_rr *\fIrr\fR); +.fi +.RS +searches for next RR in the packet based on the criteria provided in +the \fIp\fR structure, filling in the \fIrr\fR structure and +advancing \fIp\fR->\fBdnsp_cur\fR to the next RR in the packet. +RR selection is based on dnsp_qdn, dnsp_qcls and dnsp_qtyp fields in +the dns_parse structure. Any (or all) of the 3 fields may be 0, +which means any actual value from the packet is acceptable. In case +the field isn't 0 (or NULL for dnsp_qdn), only RRs with corresponding +characteristics are acceptable. Additionally, when dnsp_qdn is non-NULL, +\fBdns_nextrr\fR() performs automatic CNAME expansion. +Routine will return positive value on success, 0 in case it reached the end +of current section in the packet (\fIp\fR->\fBdnsp_rrl\fR is zero), or +negative value if next RR can not be decoded (packet format is invalid). +The routine updates \fIp\fR->\fBdnsp_qdn\fR automatically when this +field is non-NULL and it encounters appropriate CNAME RRs (saving CNAME +target in \fIp\fR->\fBdnsp_dnbuf\fR), so after end of the process, +\fIp\fR->\fBdnsp_qdn\fR will point to canonical name of the domain +in question. The routine updates \fIp\fR->\fBdnsp_ttl\fR value to +be the minimum TTL of all RRs found. +.RE + +.PP +.nf +void \fBdns_rewind\fR(struct dns_parse *\fIp\fR, const unsigned char *\fIqdn\fR) +.fi +.RS +this routine "rewinds" the packet parse state structure to be at the +same state as after a call to \fBdns_initparse\fR(), i.e. reposition +the parse structure \fIp\fR to the start of answer section and +initialize \fIp\fR->\fBdnsp_rrl\fR to the number of entries in +answer section. +.RE + +.PP +.nf +int \fBdns_stdrr_size\fR(const struct dns_parse *\fIp\fR); +.fi +.RS +return size to hold standard RRset structure information, as shown +in \fBdns_rr_null\fR structure (for the query and canonical +names). Used to calculate amount of memory to allocate for common +part of type-specific RR structures in parsing routines. +.RE + +.PP +.nf +void *\fBdns_stdrr_finish\fR(struct dns_rr_null *\fIret\fR, char *\fIcp\fR, + const struct dns_parse *\fIp\fR); +.fi +.RS +initializes standard RRset fields in \fIret\fR structure using buffer +pointed to by \fIcp\fR, which should have at least as many bytes +as \fBdns_stdrr_size\fR(\fIp\fR) returned. Used to finalize common +part of type-specific RR structures in parsing routines. +.RE + +.PP +See library source for usage examples of all the above low-level routines, +especially source of the parsing routines. + +.SS "Auxilary Routines" + +.PP +.nf +int \fBdns_pton\fR(int \fIaf\fR, const char *\fIsrc\fR, void *\fIdst\fR); +.fi +.RS +privides functionality similar to standard \fBinet_pton\fR() routine, +to convert printable representation of an IP address of family \fIaf\fR +(either \fBAF_INET\fR or \fBAF_INET6\fR) pointed to by \fIsrc\fR into +binary form suitable for socket addresses and transmission over network, +in buffer pointed to by \fIdst\fR. The destination buffer should be +of size 4 for \fBAF_INET\fR family or 16 for \fBAF_INET6\fR. +The return value is positive on success, 0 if \fIsrc\fR is not a valid text +representation of an address of family \fIaf\fR, or negative if the +given address family is not supported. +.RE + +.PP +.nf +const char *\fBdns_ntop\fR(int \fIaf\fR, const void *\fIsrc\fR, + char *\fIdst\fR, int \fIdstsize\fR) +.fi +.RS +privides functionality similar to standard \fBinet_ntop\fR() routine, +to convert binary representation of an IP address of family \fIaf\fR +(either \fBAF_INET\fR or \fBAF_INET6\fR) pointed to by \fIsrc\fR +(either 4 or 16 bytes) into printable form in buffer in buffer pointed +to by \fIdst\fR of size \fIdstsize\fR. The destination buffer should be +at least of size 16 bytes for \fBAF_INET\fR family or 46 bytes for +\fBAF_INET6\fR. The return value is either \fIdst\fR, or NULL pointer +if \fIdstsize\fR is too small to hold this address or if the given +address family is not supported. +.RE + +.SH AUTHOR +.PP +The \fBudns\fR library has been written by Michael Tokarev, mjt@corpit.ru. + +.SH VERSION +.PP +This manual page corresponds to udns version 0.0.9, released Jan-2007. diff --git a/ape-server/deps/udns-0.0.9/udns.h b/ape-server/deps/udns-0.0.9/udns.h new file mode 100755 index 0000000..1186bb2 --- /dev/null +++ b/ape-server/deps/udns-0.0.9/udns.h @@ -0,0 +1,745 @@ +/* $Id: udns.h,v 1.51 2007/01/15 21:19:08 mjt Exp $ + header file for the UDNS library. + + Copyright (C) 2005 Michael Tokarev + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + */ + +#ifndef UDNS_VERSION /* include guard */ + +#define UDNS_VERSION "0.0.9" + +#ifdef WINDOWS +# ifdef UDNS_DYNAMIC_LIBRARY +# ifdef DNS_LIBRARY_BUILD +# define UDNS_API __declspec(dllexport) +# define UDNS_DATA_API __declspec(dllexport) +# else +# define UDNS_API __declspec(dllimport) +# define UDNS_DATA_API __declspec(dllimport) +# endif +# endif +#endif + +#ifndef UDNS_API +# define UDNS_API +#endif +#ifndef UDNS_DATA_API +# define UDNS_DATA_API +#endif + +#include /* for time_t */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* forward declarations if sockets stuff isn't #include'd */ +struct in_addr; +struct in6_addr; +struct sockaddr; + +/**************************************************************************/ +/**************** Common definitions **************************************/ + +UDNS_API const char * +dns_version(void); + +struct dns_ctx; +struct dns_query; + +/* shorthand for [const] unsigned char */ +typedef unsigned char dnsc_t; +typedef const unsigned char dnscc_t; + +#define DNS_MAXDN 255 /* max DN length */ +#define DNS_DNPAD 1 /* padding for DN buffers */ +#define DNS_MAXLABEL 63 /* max DN label length */ +#define DNS_MAXNAME 1024 /* max asciiz domain name length */ +#define DNS_HSIZE 12 /* DNS packet header size */ +#define DNS_PORT 53 /* default domain port */ +#define DNS_MAXSERV 6 /* max servers to consult */ +#define DNS_MAXPACKET 512 /* max traditional-DNS UDP packet size */ +#define DNS_EDNS0PACKET 4096 /* EDNS0 packet size to use */ + +enum dns_class { /* DNS RR Classes */ + DNS_C_INVALID = 0, /* invalid class */ + DNS_C_IN = 1, /* Internet */ + DNS_C_CH = 3, /* CHAOS */ + DNS_C_HS = 4, /* HESIOD */ + DNS_C_ANY = 255 /* wildcard */ +}; + +enum dns_type { /* DNS RR Types */ + DNS_T_INVALID = 0, /* Cookie. */ + DNS_T_A = 1, /* Host address. */ + DNS_T_NS = 2, /* Authoritative server. */ + DNS_T_MD = 3, /* Mail destination. */ + DNS_T_MF = 4, /* Mail forwarder. */ + DNS_T_CNAME = 5, /* Canonical name. */ + DNS_T_SOA = 6, /* Start of authority zone. */ + DNS_T_MB = 7, /* Mailbox domain name. */ + DNS_T_MG = 8, /* Mail group member. */ + DNS_T_MR = 9, /* Mail rename name. */ + DNS_T_NULL = 10, /* Null resource record. */ + DNS_T_WKS = 11, /* Well known service. */ + DNS_T_PTR = 12, /* Domain name pointer. */ + DNS_T_HINFO = 13, /* Host information. */ + DNS_T_MINFO = 14, /* Mailbox information. */ + DNS_T_MX = 15, /* Mail routing information. */ + DNS_T_TXT = 16, /* Text strings. */ + DNS_T_RP = 17, /* Responsible person. */ + DNS_T_AFSDB = 18, /* AFS cell database. */ + DNS_T_X25 = 19, /* X_25 calling address. */ + DNS_T_ISDN = 20, /* ISDN calling address. */ + DNS_T_RT = 21, /* Router. */ + DNS_T_NSAP = 22, /* NSAP address. */ + DNS_T_NSAP_PTR = 23, /* Reverse NSAP lookup (deprecated). */ + DNS_T_SIG = 24, /* Security signature. */ + DNS_T_KEY = 25, /* Security key. */ + DNS_T_PX = 26, /* X.400 mail mapping. */ + DNS_T_GPOS = 27, /* Geographical position (withdrawn). */ + DNS_T_AAAA = 28, /* Ip6 Address. */ + DNS_T_LOC = 29, /* Location Information. */ + DNS_T_NXT = 30, /* Next domain (security). */ + DNS_T_EID = 31, /* Endpoint identifier. */ + DNS_T_NIMLOC = 32, /* Nimrod Locator. */ + DNS_T_SRV = 33, /* Server Selection. */ + DNS_T_ATMA = 34, /* ATM Address */ + DNS_T_NAPTR = 35, /* Naming Authority PoinTeR */ + DNS_T_KX = 36, /* Key Exchange */ + DNS_T_CERT = 37, /* Certification record */ + DNS_T_A6 = 38, /* IPv6 address (deprecates AAAA) */ + DNS_T_DNAME = 39, /* Non-terminal DNAME (for IPv6) */ + DNS_T_SINK = 40, /* Kitchen sink (experimentatl) */ + DNS_T_OPT = 41, /* EDNS0 option (meta-RR) */ + DNS_T_DS = 43, /* DNSSEC */ + DNS_T_NSEC = 47, /* DNSSEC */ + DNS_T_TSIG = 250, /* Transaction signature. */ + DNS_T_IXFR = 251, /* Incremental zone transfer. */ + DNS_T_AXFR = 252, /* Transfer zone of authority. */ + DNS_T_MAILB = 253, /* Transfer mailbox records. */ + DNS_T_MAILA = 254, /* Transfer mail agent records. */ + DNS_T_ANY = 255, /* Wildcard match. */ + DNS_T_ZXFR = 256, /* BIND-specific, nonstandard. */ + DNS_T_MAX = 65536 +}; + +/**************************************************************************/ +/**************** Domain Names (DNs) **************************************/ + +/* return length of the DN */ +UDNS_API unsigned +dns_dnlen(dnscc_t *dn); + +/* return #of labels in a DN */ +UDNS_API unsigned +dns_dnlabels(dnscc_t *dn); + +/* lower- and uppercase single DN char */ +#define DNS_DNLC(c) ((c) >= 'A' && (c) <= 'Z' ? (c) - 'A' + 'a' : (c)) +#define DNS_DNUC(c) ((c) >= 'a' && (c) <= 'z' ? (c) - 'a' + 'A' : (c)) + +/* compare the DNs, return dnlen of equal or 0 if not */ +UDNS_API unsigned +dns_dnequal(dnscc_t *dn1, dnscc_t *dn2); + +/* copy one DN to another, size checking */ +UDNS_API unsigned +dns_dntodn(dnscc_t *sdn, dnsc_t *ddn, unsigned ddnsiz); + +/* convert asciiz string of length namelen (0 to use strlen) to DN */ +UDNS_API int +dns_ptodn(const char *name, unsigned namelen, + dnsc_t *dn, unsigned dnsiz, int *isabs); + +/* simpler form of dns_ptodn() */ +#define dns_sptodn(name,dn,dnsiz) dns_ptodn((name),0,(dn),(dnsiz),0) + +UDNS_DATA_API extern dnscc_t dns_inaddr_arpa_dn[14]; +#define DNS_A4RSIZE 30 +UDNS_API int +dns_a4todn(const struct in_addr *addr, dnscc_t *tdn, + dnsc_t *dn, unsigned dnsiz); +UDNS_API int +dns_a4ptodn(const struct in_addr *addr, const char *tname, + dnsc_t *dn, unsigned dnsiz); +UDNS_API dnsc_t * +dns_a4todn_(const struct in_addr *addr, dnsc_t *dn, dnsc_t *dne); + +UDNS_DATA_API extern dnscc_t dns_ip6_arpa_dn[10]; +#define DNS_A6RSIZE 74 +UDNS_API int +dns_a6todn(const struct in6_addr *addr, dnscc_t *tdn, + dnsc_t *dn, unsigned dnsiz); +UDNS_API int +dns_a6ptodn(const struct in6_addr *addr, const char *tname, + dnsc_t *dn, unsigned dnsiz); +UDNS_API dnsc_t * +dns_a6todn_(const struct in6_addr *addr, dnsc_t *dn, dnsc_t *dne); + +/* convert DN into asciiz string */ +UDNS_API int +dns_dntop(dnscc_t *dn, char *name, unsigned namesiz); + +/* convert DN into asciiz string, using static buffer (NOT thread-safe!) */ +UDNS_API const char * +dns_dntosp(dnscc_t *dn); + +/* return buffer size (incl. null byte) required for asciiz form of a DN */ +UDNS_API unsigned +dns_dntop_size(dnscc_t *dn); + +/* either wrappers or reimplementations for inet_ntop() and inet_pton() */ +UDNS_API const char *dns_ntop(int af, const void *src, char *dst, int size); +UDNS_API int dns_pton(int af, const char *src, void *dst); + +/**************************************************************************/ +/**************** DNS raw packet layout ***********************************/ + +enum dns_rcode { /* reply codes */ + DNS_R_NOERROR = 0, /* ok, no error */ + DNS_R_FORMERR = 1, /* format error */ + DNS_R_SERVFAIL = 2, /* server failed */ + DNS_R_NXDOMAIN = 3, /* domain does not exists */ + DNS_R_NOTIMPL = 4, /* not implemented */ + DNS_R_REFUSED = 5, /* query refused */ + /* these are for BIND_UPDATE */ + DNS_R_YXDOMAIN = 6, /* Name exists */ + DNS_R_YXRRSET = 7, /* RRset exists */ + DNS_R_NXRRSET = 8, /* RRset does not exist */ + DNS_R_NOTAUTH = 9, /* Not authoritative for zone */ + DNS_R_NOTZONE = 10, /* Zone of record different from zone section */ + /*ns_r_max = 11,*/ + /* The following are TSIG extended errors */ + DNS_R_BADSIG = 16, + DNS_R_BADKEY = 17, + DNS_R_BADTIME = 18 +}; + +static __inline unsigned dns_get16(dnscc_t *s) { + return ((unsigned)s[0]<<8) | s[1]; +} +static __inline unsigned dns_get32(dnscc_t *s) { + return ((unsigned)s[0]<<24) | ((unsigned)s[1]<<16) + | ((unsigned)s[2]<<8) | s[3]; +} +static __inline dnsc_t *dns_put16(dnsc_t *d, unsigned n) { + *d++ = (dnsc_t)((n >> 8) & 255); *d++ = (dnsc_t)(n & 255); return d; +} +static __inline dnsc_t *dns_put32(dnsc_t *d, unsigned n) { + *d++ = (dnsc_t)((n >> 24) & 255); *d++ = (dnsc_t)((n >> 16) & 255); + *d++ = (dnsc_t)((n >> 8) & 255); *d++ = (dnsc_t)(n & 255); + return d; +} + +/* return pseudo-random 16bits number */ +UDNS_API unsigned dns_random16(void); + +/* DNS Header layout */ +enum { + /* bytes 0:1 - query ID */ + DNS_H_QID1 = 0, + DNS_H_QID2 = 1, + DNS_H_QID = DNS_H_QID1, +#define dns_qid(pkt) dns_get16((pkt)+DNS_H_QID) + /* byte 2: flags1 */ + DNS_H_F1 = 2, + DNS_HF1_QR = 0x80, /* query response flag */ +#define dns_qr(pkt) ((pkt)[DNS_H_F1]&DNS_HF1_QR) + DNS_HF1_OPCODE = 0x78, /* opcode, 0 = query */ +#define dns_opcode(pkt) (((pkt)[DNS_H_F1]&DNS_HF1_OPCODE)>>3) + DNS_HF1_AA = 0x04, /* auth answer */ +#define dns_aa(pkt) ((pkt)[DNS_H_F1]&DNS_HF1_AA) + DNS_HF1_TC = 0x02, /* truncation flag */ +#define dns_tc(pkt) ((pkt)[DNS_H_F1]&DNS_HF1_TC) + DNS_HF1_RD = 0x01, /* recursion desired (may be set in query) */ +#define dns_rd(pkt) ((pkt)[DNS_H_F1]&DNS_HF1_RD) + /* byte 3: flags2 */ + DNS_H_F2 = 3, + DNS_HF2_RA = 0x80, /* recursion available */ +#define dns_ra(pkt) ((pkt)[DNS_H_F2]&DNS_HF2_RA) + DNS_HF2_Z = 0x70, /* reserved */ + DNS_HF2_RCODE = 0x0f, /* response code, DNS_R_XXX above */ +#define dns_rcode(pkt) ((pkt)[DNS_H_F2]&DNS_HF2_RCODE) + /* bytes 4:5: qdcount, numqueries */ + DNS_H_QDCNT1 = 4, + DNS_H_QDCNT2 = 5, + DNS_H_QDCNT = DNS_H_QDCNT1, +#define dns_numqd(pkt) dns_get16((pkt)+4) + /* bytes 6:7: ancount, numanswers */ + DNS_H_ANCNT1 = 6, + DNS_H_ANCNT2 = 7, + DNS_H_ANCNT = DNS_H_ANCNT1, +#define dns_numan(pkt) dns_get16((pkt)+6) + /* bytes 8:9: nscount, numauthority */ + DNS_H_NSCNT1 = 8, + DNS_H_NSCNT2 = 9, + DNS_H_NSCNT = DNS_H_NSCNT1, +#define dns_numns(pkt) dns_get16((pkt)+8) + /* bytes 10:11: arcount, numadditional */ + DNS_H_ARCNT1 = 10, + DNS_H_ARCNT2 = 11, + DNS_H_ARCNT = DNS_H_ARCNT1, +#define dns_numar(pkt) dns_get16((pkt)+10) +#define dns_payload(pkt) ((pkt)+DNS_HSIZE) +}; + +/* packet buffer: start at pkt, end before pkte, current pos *curp. + * extract a DN and set *curp to the next byte after DN in packet. + * return -1 on error, 0 if dnsiz is too small, or dnlen on ok. + */ +UDNS_API int +dns_getdn(dnscc_t *pkt, dnscc_t **curp, dnscc_t *end, + dnsc_t *dn, unsigned dnsiz); + +/* skip the DN at position cur in packet ending before pkte, + * return pointer to the next byte after the DN or NULL on error */ +UDNS_API dnscc_t * +dns_skipdn(dnscc_t *end, dnscc_t *cur); + +struct dns_rr { /* DNS Resource Record */ + dnsc_t dnsrr_dn[DNS_MAXDN]; /* the DN of the RR */ + enum dns_class dnsrr_cls; /* Class */ + enum dns_type dnsrr_typ; /* Type */ + unsigned dnsrr_ttl; /* Time-To-Live (TTL) */ + unsigned dnsrr_dsz; /* data size */ + dnscc_t *dnsrr_dptr; /* pointer to start of data */ + dnscc_t *dnsrr_dend; /* past end of data */ +}; + +struct dns_parse { /* RR/packet parsing state */ + dnscc_t *dnsp_pkt; /* start of the packet */ + dnscc_t *dnsp_end; /* end of the packet */ + dnscc_t *dnsp_cur; /* current packet position */ + dnscc_t *dnsp_ans; /* start of answer section */ + int dnsp_rrl; /* number of RRs left to go */ + int dnsp_nrr; /* RR count so far */ + unsigned dnsp_ttl; /* TTL value so far */ + dnscc_t *dnsp_qdn; /* the RR DN we're looking for */ + enum dns_class dnsp_qcls; /* RR class we're looking for or 0 */ + enum dns_type dnsp_qtyp; /* RR type we're looking for or 0 */ + dnsc_t dnsp_dnbuf[DNS_MAXDN]; /* domain buffer */ +}; + +/* initialize the parse structure */ +UDNS_API void +dns_initparse(struct dns_parse *p, dnscc_t *qdn, + dnscc_t *pkt, dnscc_t *cur, dnscc_t *end); + +/* search next RR, <0=error, 0=no more RRs, >0 = found. */ +UDNS_API int +dns_nextrr(struct dns_parse *p, struct dns_rr *rr); + +UDNS_API void +dns_rewind(struct dns_parse *p, dnscc_t *qdn); + + +/**************************************************************************/ +/**************** Resolver Context ****************************************/ + +/* default resolver context */ +UDNS_DATA_API extern struct dns_ctx dns_defctx; + +/* reset resolver context to default state, close it if open, drop queries */ +UDNS_API void +dns_reset(struct dns_ctx *ctx); + +/* reset resolver context and read in system configuration */ +UDNS_API int +dns_init(struct dns_ctx *ctx, int do_open); + +/* return new resolver context with the same settings as copy */ +UDNS_API struct dns_ctx * +dns_new(const struct dns_ctx *copy); + +/* free resolver context returned by dns_new(); all queries are dropped */ +UDNS_API void +dns_free(struct dns_ctx *ctx); + +/* add nameserver for a resolver context (or reset nslist if serv==NULL) */ +UDNS_API int +dns_add_serv(struct dns_ctx *ctx, const char *serv); + +/* add nameserver using struct sockaddr structure (with ports) */ +UDNS_API int +dns_add_serv_s(struct dns_ctx *ctx, const struct sockaddr *sa); + +/* add search list element for a resolver context (or reset it if srch==NULL) */ +UDNS_API int +dns_add_srch(struct dns_ctx *ctx, const char *srch); + +/* set options for a resolver context */ +UDNS_API int +dns_set_opts(struct dns_ctx *ctx, const char *opts); + +enum dns_opt { /* options */ + DNS_OPT_FLAGS, /* flags, DNS_F_XXX */ + DNS_OPT_TIMEOUT, /* timeout in secounds */ + DNS_OPT_NTRIES, /* number of retries */ + DNS_OPT_NDOTS, /* ndots */ + DNS_OPT_UDPSIZE, /* EDNS0 UDP size */ + DNS_OPT_PORT, /* port to use */ +}; + +/* set or get (if val<0) an option */ +UDNS_API int +dns_set_opt(struct dns_ctx *ctx, enum dns_opt opt, int val); + +enum dns_flags { + DNS_NOSRCH = 0x00010000, /* do not perform search */ + DNS_NORD = 0x00020000, /* request no recursion */ + DNS_AAONLY = 0x00040000, /* set AA flag in queries */ +}; + +/* set the debug function pointer */ +typedef void +(dns_dbgfn)(int code, const struct sockaddr *sa, unsigned salen, + dnscc_t *pkt, int plen, + const struct dns_query *q, void *data); +UDNS_API void +dns_set_dbgfn(struct dns_ctx *ctx, dns_dbgfn *dbgfn); + +/* open and return UDP socket */ +UDNS_API int +dns_open(struct dns_ctx *ctx); + +/* return UDP socket or -1 if not open */ +UDNS_API int +dns_sock(const struct dns_ctx *ctx); + +/* close the UDP socket */ +UDNS_API void +dns_close(struct dns_ctx *ctx); + +/* return number of requests queued */ +UDNS_API int +dns_active(const struct dns_ctx *ctx); + +/* return status of the last operation */ +UDNS_API int +dns_status(const struct dns_ctx *ctx); +UDNS_API void +dns_setstatus(struct dns_ctx *ctx, int status); + +/* handle I/O event on UDP socket */ +UDNS_API void +dns_ioevent(struct dns_ctx *ctx, time_t now); + +/* process any timeouts, return time in secounds to the + * next timeout (or -1 if none) but not greather than maxwait */ +UDNS_API int +dns_timeouts(struct dns_ctx *ctx, int maxwait, time_t now); + +/* define timer requesting routine to use */ +typedef void dns_utm_fn(struct dns_ctx *ctx, int timeout, void *data); +UDNS_API void +dns_set_tmcbck(struct dns_ctx *ctx, dns_utm_fn *fn, void *data); + +/**************************************************************************/ +/**************** Making Queries ******************************************/ + +/* query callback routine */ +typedef void dns_query_fn(struct dns_ctx *ctx, void *result, void *data); + +/* query parse routine: raw DNS => application structure */ +typedef int +dns_parse_fn(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end, + void **res); + +enum dns_status { + DNS_E_NOERROR = 0, /* ok, not an error */ + DNS_E_TEMPFAIL = -1, /* timeout, SERVFAIL or similar */ + DNS_E_PROTOCOL = -2, /* got garbled reply */ + DNS_E_NXDOMAIN = -3, /* domain does not exists */ + DNS_E_NODATA = -4, /* domain exists but no data of reqd type */ + DNS_E_NOMEM = -5, /* out of memory while processing */ + DNS_E_BADQUERY = -6 /* the query is malformed */ +}; + +/* submit generic DN query */ +UDNS_API struct dns_query * +dns_submit_dn(struct dns_ctx *ctx, + dnscc_t *dn, int qcls, int qtyp, int flags, + dns_parse_fn *parse, dns_query_fn *cbck, void *data); +/* submit generic name query */ +UDNS_API struct dns_query * +dns_submit_p(struct dns_ctx *ctx, + const char *name, int qcls, int qtyp, int flags, + dns_parse_fn *parse, dns_query_fn *cbck, void *data); + +/* cancel the given async query in progress */ +UDNS_API int +dns_cancel(struct dns_ctx *ctx, struct dns_query *q); + +/* resolve a generic query, return the answer */ +UDNS_API void * +dns_resolve_dn(struct dns_ctx *ctx, + dnscc_t *qdn, int qcls, int qtyp, int flags, + dns_parse_fn *parse); +UDNS_API void * +dns_resolve_p(struct dns_ctx *ctx, + const char *qname, int qcls, int qtyp, int flags, + dns_parse_fn *parse); +UDNS_API void * +dns_resolve(struct dns_ctx *ctx, struct dns_query *q); + + +/* Specific RR handlers */ + +#define dns_rr_common(prefix) \ + char *prefix##_cname; /* canonical name */ \ + char *prefix##_qname; /* original query name */ \ + unsigned prefix##_ttl; /* TTL value */ \ + int prefix##_nrr /* number of records */ + +struct dns_rr_null { /* NULL RRset, aka RRset template */ + dns_rr_common(dnsn); +}; + +UDNS_API int +dns_stdrr_size(const struct dns_parse *p); +UDNS_API void * +dns_stdrr_finish(struct dns_rr_null *ret, char *cp, const struct dns_parse *p); + +struct dns_rr_a4 { /* the A RRset */ + dns_rr_common(dnsa4); + struct in_addr *dnsa4_addr; /* array of addresses, naddr elements */ +}; + +UDNS_API dns_parse_fn dns_parse_a4; /* A RR parsing routine */ +typedef void /* A query callback routine */ +dns_query_a4_fn(struct dns_ctx *ctx, struct dns_rr_a4 *result, void *data); + +/* submit A IN query */ +UDNS_API struct dns_query * +dns_submit_a4(struct dns_ctx *ctx, const char *name, int flags, + dns_query_a4_fn *cbck, void *data); + +/* resolve A IN query */ +UDNS_API struct dns_rr_a4 * +dns_resolve_a4(struct dns_ctx *ctx, const char *name, int flags); + + +struct dns_rr_a6 { /* the AAAA RRset */ + dns_rr_common(dnsa6); + struct in6_addr *dnsa6_addr; /* array of addresses, naddr elements */ +}; + +UDNS_API dns_parse_fn dns_parse_a6; /* A RR parsing routine */ +typedef void /* A query callback routine */ +dns_query_a6_fn(struct dns_ctx *ctx, struct dns_rr_a6 *result, void *data); + +/* submit AAAA IN query */ +UDNS_API struct dns_query * +dns_submit_a6(struct dns_ctx *ctx, const char *name, int flags, + dns_query_a6_fn *cbck, void *data); + +/* resolve AAAA IN query */ +UDNS_API struct dns_rr_a6 * +dns_resolve_a6(struct dns_ctx *ctx, const char *name, int flags); + + +struct dns_rr_ptr { /* the PTR RRset */ + dns_rr_common(dnsptr); + char **dnsptr_ptr; /* array of PTRs */ +}; + +UDNS_API dns_parse_fn dns_parse_ptr; /* PTR RR parsing routine */ +typedef void /* PTR query callback */ +dns_query_ptr_fn(struct dns_ctx *ctx, struct dns_rr_ptr *result, void *data); +/* submit PTR IN in-addr.arpa query */ +UDNS_API struct dns_query * +dns_submit_a4ptr(struct dns_ctx *ctx, const struct in_addr *addr, + dns_query_ptr_fn *cbck, void *data); +/* resolve PTR IN in-addr.arpa query */ +UDNS_API struct dns_rr_ptr * +dns_resolve_a4ptr(struct dns_ctx *ctx, const struct in_addr *addr); + +/* the same as above, but for ip6.arpa */ +UDNS_API struct dns_query * +dns_submit_a6ptr(struct dns_ctx *ctx, const struct in6_addr *addr, + dns_query_ptr_fn *cbck, void *data); +UDNS_API struct dns_rr_ptr * +dns_resolve_a6ptr(struct dns_ctx *ctx, const struct in6_addr *addr); + + +struct dns_mx { /* single MX RR */ + int priority; /* MX priority */ + char *name; /* MX name */ +}; +struct dns_rr_mx { /* the MX RRset */ + dns_rr_common(dnsmx); + struct dns_mx *dnsmx_mx; /* array of MXes */ +}; +UDNS_API dns_parse_fn dns_parse_mx; /* MX RR parsing routine */ +typedef void /* MX RR callback */ +dns_query_mx_fn(struct dns_ctx *ctx, struct dns_rr_mx *result, void *data); +/* submit MX IN query */ +UDNS_API struct dns_query * +dns_submit_mx(struct dns_ctx *ctx, const char *name, int flags, + dns_query_mx_fn *cbck, void *data); +/* resolve MX IN query */ +UDNS_API struct dns_rr_mx * +dns_resolve_mx(struct dns_ctx *ctx, const char *name, int flags); + + +struct dns_txt { /* single TXT record */ + int len; /* length of the text */ + dnsc_t *txt; /* pointer to text buffer. May contain nulls. */ +}; +struct dns_rr_txt { /* the TXT RRset */ + dns_rr_common(dnstxt); + struct dns_txt *dnstxt_txt; /* array of TXT records */ +}; +UDNS_API dns_parse_fn dns_parse_txt; /* TXT RR parsing routine */ +typedef void /* TXT RR callback */ +dns_query_txt_fn(struct dns_ctx *ctx, struct dns_rr_txt *result, void *data); +/* submit TXT query */ +UDNS_API struct dns_query * +dns_submit_txt(struct dns_ctx *ctx, const char *name, int qcls, int flags, + dns_query_txt_fn *cbck, void *data); +/* resolve TXT query */ +UDNS_API struct dns_rr_txt * +dns_resolve_txt(struct dns_ctx *ctx, const char *name, int qcls, int flags); + + +struct dns_srv { /* single SRV RR */ + int priority; /* SRV priority */ + int weight; /* SRV weight */ + int port; /* SRV port */ + char *name; /* SRV name */ +}; +struct dns_rr_srv { /* the SRV RRset */ + dns_rr_common(dnssrv); + struct dns_srv *dnssrv_srv; /* array of SRVes */ +}; +UDNS_API dns_parse_fn dns_parse_srv; /* SRV RR parsing routine */ +typedef void /* SRV RR callback */ +dns_query_srv_fn(struct dns_ctx *ctx, struct dns_rr_srv *result, void *data); +/* submit SRV IN query */ +UDNS_API struct dns_query * +dns_submit_srv(struct dns_ctx *ctx, + const char *name, const char *srv, const char *proto, + int flags, dns_query_srv_fn *cbck, void *data); +/* resolve SRV IN query */ +UDNS_API struct dns_rr_srv * +dns_resolve_srv(struct dns_ctx *ctx, + const char *name, const char *srv, const char *proto, + int flags); + +/* NAPTR (RFC3403) RR type */ +struct dns_naptr { /* single NAPTR RR */ + int order; /* NAPTR order */ + int preference; /* NAPTR preference */ + char *flags; /* NAPTR flags */ + char *service; /* NAPTR service */ + char *regexp; /* NAPTR regexp */ + char *replacement; /* NAPTR replacement */ +}; + +struct dns_rr_naptr { /* the NAPTR RRset */ + dns_rr_common(dnsnaptr); + struct dns_naptr *dnsnaptr_naptr; /* array of NAPTRes */ +}; +UDNS_API dns_parse_fn dns_parse_naptr; /* NAPTR RR parsing routine */ +typedef void /* NAPTR RR callback */ +dns_query_naptr_fn(struct dns_ctx *ctx, + struct dns_rr_naptr *result, void *data); +/* submit NAPTR IN query */ +UDNS_API struct dns_query * +dns_submit_naptr(struct dns_ctx *ctx, const char *name, int flags, + dns_query_naptr_fn *cbck, void *data); +/* resolve NAPTR IN query */ +UDNS_API struct dns_rr_naptr * +dns_resolve_naptr(struct dns_ctx *ctx, const char *name, int flags); + + +UDNS_API struct dns_query * +dns_submit_a4dnsbl(struct dns_ctx *ctx, + const struct in_addr *addr, const char *dnsbl, + dns_query_a4_fn *cbck, void *data); +UDNS_API struct dns_query * +dns_submit_a4dnsbl_txt(struct dns_ctx *ctx, + const struct in_addr *addr, const char *dnsbl, + dns_query_txt_fn *cbck, void *data); +UDNS_API struct dns_rr_a4 * +dns_resolve_a4dnsbl(struct dns_ctx *ctx, + const struct in_addr *addr, const char *dnsbl); +UDNS_API struct dns_rr_txt * +dns_resolve_a4dnsbl_txt(struct dns_ctx *ctx, + const struct in_addr *addr, const char *dnsbl); + +UDNS_API struct dns_query * +dns_submit_a6dnsbl(struct dns_ctx *ctx, + const struct in6_addr *addr, const char *dnsbl, + dns_query_a4_fn *cbck, void *data); +UDNS_API struct dns_query * +dns_submit_a6dnsbl_txt(struct dns_ctx *ctx, + const struct in6_addr *addr, const char *dnsbl, + dns_query_txt_fn *cbck, void *data); +UDNS_API struct dns_rr_a4 * +dns_resolve_a6dnsbl(struct dns_ctx *ctx, + const struct in6_addr *addr, const char *dnsbl); +UDNS_API struct dns_rr_txt * +dns_resolve_a6dnsbl_txt(struct dns_ctx *ctx, + const struct in6_addr *addr, const char *dnsbl); + +UDNS_API struct dns_query * +dns_submit_rhsbl(struct dns_ctx *ctx, + const char *name, const char *rhsbl, + dns_query_a4_fn *cbck, void *data); +UDNS_API struct dns_query * +dns_submit_rhsbl_txt(struct dns_ctx *ctx, + const char *name, const char *rhsbl, + dns_query_txt_fn *cbck, void *data); +UDNS_API struct dns_rr_a4 * +dns_resolve_rhsbl(struct dns_ctx *ctx, const char *name, const char *rhsbl); +UDNS_API struct dns_rr_txt * +dns_resolve_rhsbl_txt(struct dns_ctx *ctx, const char *name, const char *rhsbl); + +/**************************************************************************/ +/**************** Names, Names ********************************************/ + +struct dns_nameval { + int val; + const char *name; +}; + +UDNS_DATA_API extern const struct dns_nameval dns_classtab[]; +UDNS_DATA_API extern const struct dns_nameval dns_typetab[]; +UDNS_DATA_API extern const struct dns_nameval dns_rcodetab[]; +UDNS_API int +dns_findname(const struct dns_nameval *nv, const char *name); +#define dns_findclassname(cls) dns_findname(dns_classtab, (cls)) +#define dns_findtypename(type) dns_findname(dns_typetab, (type)) +#define dns_findrcodename(rcode) dns_findname(dns_rcodetab, (rcode)) + +UDNS_API const char *dns_classname(enum dns_class cls); +UDNS_API const char *dns_typename(enum dns_type type); +UDNS_API const char *dns_rcodename(enum dns_rcode rcode); +const char *_dns_format_code(char *buf, const char *prefix, int code); + +UDNS_API const char *dns_strerror(int errnum); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* include guard */ diff --git a/ape-server/deps/udns-0.0.9/udns_XtoX.c b/ape-server/deps/udns-0.0.9/udns_XtoX.c new file mode 100755 index 0000000..cfb6af4 --- /dev/null +++ b/ape-server/deps/udns-0.0.9/udns_XtoX.c @@ -0,0 +1,50 @@ +/* $Id: udns_XtoX.c,v 1.1 2007/01/07 22:20:39 mjt Exp $ + udns_ntop() and udns_pton() routines, which are either + - wrappers for inet_ntop() and inet_pton() or + - reimplementations of those routines. + + Copyright (C) 2005 Michael Tokarev + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include "udns.h" + +#ifdef HAVE_INET_PTON_NTOP + +#include +#include +#include + +const char *dns_ntop(int af, const void *src, char *dst, int size) { + return inet_ntop(af, src, dst, size); +} + +int dns_pton(int af, const char *src, void *dst) { + return inet_pton(af, src, dst); +} + +#else + +#define inet_XtoX_prefix udns_ +#include "inet_XtoX.c" + +#endif diff --git a/ape-server/deps/udns-0.0.9/udns_bl.c b/ape-server/deps/udns-0.0.9/udns_bl.c new file mode 100755 index 0000000..dc6f53b --- /dev/null +++ b/ape-server/deps/udns-0.0.9/udns_bl.c @@ -0,0 +1,160 @@ +/* $Id: udns_bl.c,v 1.10 2005/09/12 10:55:21 mjt Exp $ + DNSBL stuff + + Copyright (C) 2005 Michael Tokarev + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + */ + +#include "udns.h" +#ifndef NULL +# define NULL 0 +#endif + +struct dns_query * +dns_submit_a4dnsbl(struct dns_ctx *ctx, + const struct in_addr *addr, const char *dnsbl, + dns_query_a4_fn *cbck, void *data) { + dnsc_t dn[DNS_MAXDN]; + if (dns_a4ptodn(addr, dnsbl, dn, sizeof(dn)) <= 0) { + dns_setstatus(ctx, DNS_E_BADQUERY); + return NULL; + } + return + dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_A, DNS_NOSRCH, + dns_parse_a4, (dns_query_fn*)cbck, data); +} + +struct dns_query * +dns_submit_a4dnsbl_txt(struct dns_ctx *ctx, + const struct in_addr *addr, const char *dnsbl, + dns_query_txt_fn *cbck, void *data) { + dnsc_t dn[DNS_MAXDN]; + if (dns_a4ptodn(addr, dnsbl, dn, sizeof(dn)) <= 0) { + dns_setstatus(ctx, DNS_E_BADQUERY); + return NULL; + } + return + dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_TXT, DNS_NOSRCH, + dns_parse_txt, (dns_query_fn*)cbck, data); +} + +struct dns_rr_a4 * +dns_resolve_a4dnsbl(struct dns_ctx *ctx, + const struct in_addr *addr, const char *dnsbl) { + return (struct dns_rr_a4 *) + dns_resolve(ctx, dns_submit_a4dnsbl(ctx, addr, dnsbl, 0, 0)); +} + +struct dns_rr_txt * +dns_resolve_a4dnsbl_txt(struct dns_ctx *ctx, + const struct in_addr *addr, const char *dnsbl) { + return (struct dns_rr_txt *) + dns_resolve(ctx, dns_submit_a4dnsbl_txt(ctx, addr, dnsbl, 0, 0)); +} + + +struct dns_query * +dns_submit_a6dnsbl(struct dns_ctx *ctx, + const struct in6_addr *addr, const char *dnsbl, + dns_query_a4_fn *cbck, void *data) { + dnsc_t dn[DNS_MAXDN]; + if (dns_a6ptodn(addr, dnsbl, dn, sizeof(dn)) <= 0) { + dns_setstatus(ctx, DNS_E_BADQUERY); + return NULL; + } + return + dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_A, DNS_NOSRCH, + dns_parse_a4, (dns_query_fn*)cbck, data); +} + +struct dns_query * +dns_submit_a6dnsbl_txt(struct dns_ctx *ctx, + const struct in6_addr *addr, const char *dnsbl, + dns_query_txt_fn *cbck, void *data) { + dnsc_t dn[DNS_MAXDN]; + if (dns_a6ptodn(addr, dnsbl, dn, sizeof(dn)) <= 0) { + dns_setstatus(ctx, DNS_E_BADQUERY); + return NULL; + } + return + dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_TXT, DNS_NOSRCH, + dns_parse_txt, (dns_query_fn*)cbck, data); +} + +struct dns_rr_a4 * +dns_resolve_a6dnsbl(struct dns_ctx *ctx, + const struct in6_addr *addr, const char *dnsbl) { + return (struct dns_rr_a4 *) + dns_resolve(ctx, dns_submit_a6dnsbl(ctx, addr, dnsbl, 0, 0)); +} + +struct dns_rr_txt * +dns_resolve_a6dnsbl_txt(struct dns_ctx *ctx, + const struct in6_addr *addr, const char *dnsbl) { + return (struct dns_rr_txt *) + dns_resolve(ctx, dns_submit_a6dnsbl_txt(ctx, addr, dnsbl, 0, 0)); +} + +static int +dns_rhsbltodn(const char *name, const char *rhsbl, dnsc_t dn[DNS_MAXDN]) +{ + int l = dns_sptodn(name, dn, DNS_MAXDN); + if (l <= 0) return 0; + l = dns_sptodn(rhsbl, dn+l-1, DNS_MAXDN-l+1); + if (l <= 0) return 0; + return 1; +} + +struct dns_query * +dns_submit_rhsbl(struct dns_ctx *ctx, const char *name, const char *rhsbl, + dns_query_a4_fn *cbck, void *data) { + dnsc_t dn[DNS_MAXDN]; + if (!dns_rhsbltodn(name, rhsbl, dn)) { + dns_setstatus(ctx, DNS_E_BADQUERY); + return NULL; + } + return + dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_A, DNS_NOSRCH, + dns_parse_a4, (dns_query_fn*)cbck, data); +} +struct dns_query * +dns_submit_rhsbl_txt(struct dns_ctx *ctx, const char *name, const char *rhsbl, + dns_query_txt_fn *cbck, void *data) { + dnsc_t dn[DNS_MAXDN]; + if (!dns_rhsbltodn(name, rhsbl, dn)) { + dns_setstatus(ctx, DNS_E_BADQUERY); + return NULL; + } + return + dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_TXT, DNS_NOSRCH, + dns_parse_txt, (dns_query_fn*)cbck, data); +} + +struct dns_rr_a4 * +dns_resolve_rhsbl(struct dns_ctx *ctx, const char *name, const char *rhsbl) { + return (struct dns_rr_a4*) + dns_resolve(ctx, dns_submit_rhsbl(ctx, name, rhsbl, 0, 0)); +} + +struct dns_rr_txt * +dns_resolve_rhsbl_txt(struct dns_ctx *ctx, const char *name, const char *rhsbl) +{ + return (struct dns_rr_txt*) + dns_resolve(ctx, dns_submit_rhsbl_txt(ctx, name, rhsbl, 0, 0)); +} diff --git a/ape-server/deps/udns-0.0.9/udns_codes.c b/ape-server/deps/udns-0.0.9/udns_codes.c new file mode 100755 index 0000000..a5ffce7 --- /dev/null +++ b/ape-server/deps/udns-0.0.9/udns_codes.c @@ -0,0 +1,171 @@ +/* Automatically generated. */ +#include "udns.h" + +const struct dns_nameval dns_typetab[] = { + {DNS_T_INVALID,"INVALID"}, + {DNS_T_A,"A"}, + {DNS_T_NS,"NS"}, + {DNS_T_MD,"MD"}, + {DNS_T_MF,"MF"}, + {DNS_T_CNAME,"CNAME"}, + {DNS_T_SOA,"SOA"}, + {DNS_T_MB,"MB"}, + {DNS_T_MG,"MG"}, + {DNS_T_MR,"MR"}, + {DNS_T_NULL,"NULL"}, + {DNS_T_WKS,"WKS"}, + {DNS_T_PTR,"PTR"}, + {DNS_T_HINFO,"HINFO"}, + {DNS_T_MINFO,"MINFO"}, + {DNS_T_MX,"MX"}, + {DNS_T_TXT,"TXT"}, + {DNS_T_RP,"RP"}, + {DNS_T_AFSDB,"AFSDB"}, + {DNS_T_X25,"X25"}, + {DNS_T_ISDN,"ISDN"}, + {DNS_T_RT,"RT"}, + {DNS_T_NSAP,"NSAP"}, + {DNS_T_NSAP_PTR,"NSAP_PTR"}, + {DNS_T_SIG,"SIG"}, + {DNS_T_KEY,"KEY"}, + {DNS_T_PX,"PX"}, + {DNS_T_GPOS,"GPOS"}, + {DNS_T_AAAA,"AAAA"}, + {DNS_T_LOC,"LOC"}, + {DNS_T_NXT,"NXT"}, + {DNS_T_EID,"EID"}, + {DNS_T_NIMLOC,"NIMLOC"}, + {DNS_T_SRV,"SRV"}, + {DNS_T_ATMA,"ATMA"}, + {DNS_T_NAPTR,"NAPTR"}, + {DNS_T_KX,"KX"}, + {DNS_T_CERT,"CERT"}, + {DNS_T_A6,"A6"}, + {DNS_T_DNAME,"DNAME"}, + {DNS_T_SINK,"SINK"}, + {DNS_T_OPT,"OPT"}, + {DNS_T_DS,"DS"}, + {DNS_T_NSEC,"NSEC"}, + {DNS_T_TSIG,"TSIG"}, + {DNS_T_IXFR,"IXFR"}, + {DNS_T_AXFR,"AXFR"}, + {DNS_T_MAILB,"MAILB"}, + {DNS_T_MAILA,"MAILA"}, + {DNS_T_ANY,"ANY"}, + {DNS_T_ZXFR,"ZXFR"}, + {DNS_T_MAX,"MAX"}, + {0,0}}; +const char *dns_typename(enum dns_type code) { + static char nm[20]; + switch(code) { + case DNS_T_INVALID: return dns_typetab[0].name; + case DNS_T_A: return dns_typetab[1].name; + case DNS_T_NS: return dns_typetab[2].name; + case DNS_T_MD: return dns_typetab[3].name; + case DNS_T_MF: return dns_typetab[4].name; + case DNS_T_CNAME: return dns_typetab[5].name; + case DNS_T_SOA: return dns_typetab[6].name; + case DNS_T_MB: return dns_typetab[7].name; + case DNS_T_MG: return dns_typetab[8].name; + case DNS_T_MR: return dns_typetab[9].name; + case DNS_T_NULL: return dns_typetab[10].name; + case DNS_T_WKS: return dns_typetab[11].name; + case DNS_T_PTR: return dns_typetab[12].name; + case DNS_T_HINFO: return dns_typetab[13].name; + case DNS_T_MINFO: return dns_typetab[14].name; + case DNS_T_MX: return dns_typetab[15].name; + case DNS_T_TXT: return dns_typetab[16].name; + case DNS_T_RP: return dns_typetab[17].name; + case DNS_T_AFSDB: return dns_typetab[18].name; + case DNS_T_X25: return dns_typetab[19].name; + case DNS_T_ISDN: return dns_typetab[20].name; + case DNS_T_RT: return dns_typetab[21].name; + case DNS_T_NSAP: return dns_typetab[22].name; + case DNS_T_NSAP_PTR: return dns_typetab[23].name; + case DNS_T_SIG: return dns_typetab[24].name; + case DNS_T_KEY: return dns_typetab[25].name; + case DNS_T_PX: return dns_typetab[26].name; + case DNS_T_GPOS: return dns_typetab[27].name; + case DNS_T_AAAA: return dns_typetab[28].name; + case DNS_T_LOC: return dns_typetab[29].name; + case DNS_T_NXT: return dns_typetab[30].name; + case DNS_T_EID: return dns_typetab[31].name; + case DNS_T_NIMLOC: return dns_typetab[32].name; + case DNS_T_SRV: return dns_typetab[33].name; + case DNS_T_ATMA: return dns_typetab[34].name; + case DNS_T_NAPTR: return dns_typetab[35].name; + case DNS_T_KX: return dns_typetab[36].name; + case DNS_T_CERT: return dns_typetab[37].name; + case DNS_T_A6: return dns_typetab[38].name; + case DNS_T_DNAME: return dns_typetab[39].name; + case DNS_T_SINK: return dns_typetab[40].name; + case DNS_T_OPT: return dns_typetab[41].name; + case DNS_T_DS: return dns_typetab[42].name; + case DNS_T_NSEC: return dns_typetab[43].name; + case DNS_T_TSIG: return dns_typetab[44].name; + case DNS_T_IXFR: return dns_typetab[45].name; + case DNS_T_AXFR: return dns_typetab[46].name; + case DNS_T_MAILB: return dns_typetab[47].name; + case DNS_T_MAILA: return dns_typetab[48].name; + case DNS_T_ANY: return dns_typetab[49].name; + case DNS_T_ZXFR: return dns_typetab[50].name; + case DNS_T_MAX: return dns_typetab[51].name; + } + return _dns_format_code(nm,"type",code); +} + +const struct dns_nameval dns_classtab[] = { + {DNS_C_INVALID,"INVALID"}, + {DNS_C_IN,"IN"}, + {DNS_C_CH,"CH"}, + {DNS_C_HS,"HS"}, + {DNS_C_ANY,"ANY"}, + {0,0}}; +const char *dns_classname(enum dns_class code) { + static char nm[20]; + switch(code) { + case DNS_C_INVALID: return dns_classtab[0].name; + case DNS_C_IN: return dns_classtab[1].name; + case DNS_C_CH: return dns_classtab[2].name; + case DNS_C_HS: return dns_classtab[3].name; + case DNS_C_ANY: return dns_classtab[4].name; + } + return _dns_format_code(nm,"class",code); +} + +const struct dns_nameval dns_rcodetab[] = { + {DNS_R_NOERROR,"NOERROR"}, + {DNS_R_FORMERR,"FORMERR"}, + {DNS_R_SERVFAIL,"SERVFAIL"}, + {DNS_R_NXDOMAIN,"NXDOMAIN"}, + {DNS_R_NOTIMPL,"NOTIMPL"}, + {DNS_R_REFUSED,"REFUSED"}, + {DNS_R_YXDOMAIN,"YXDOMAIN"}, + {DNS_R_YXRRSET,"YXRRSET"}, + {DNS_R_NXRRSET,"NXRRSET"}, + {DNS_R_NOTAUTH,"NOTAUTH"}, + {DNS_R_NOTZONE,"NOTZONE"}, + {DNS_R_BADSIG,"BADSIG"}, + {DNS_R_BADKEY,"BADKEY"}, + {DNS_R_BADTIME,"BADTIME"}, + {0,0}}; +const char *dns_rcodename(enum dns_rcode code) { + static char nm[20]; + switch(code) { + case DNS_R_NOERROR: return dns_rcodetab[0].name; + case DNS_R_FORMERR: return dns_rcodetab[1].name; + case DNS_R_SERVFAIL: return dns_rcodetab[2].name; + case DNS_R_NXDOMAIN: return dns_rcodetab[3].name; + case DNS_R_NOTIMPL: return dns_rcodetab[4].name; + case DNS_R_REFUSED: return dns_rcodetab[5].name; + case DNS_R_YXDOMAIN: return dns_rcodetab[6].name; + case DNS_R_YXRRSET: return dns_rcodetab[7].name; + case DNS_R_NXRRSET: return dns_rcodetab[8].name; + case DNS_R_NOTAUTH: return dns_rcodetab[9].name; + case DNS_R_NOTZONE: return dns_rcodetab[10].name; + case DNS_R_BADSIG: return dns_rcodetab[11].name; + case DNS_R_BADKEY: return dns_rcodetab[12].name; + case DNS_R_BADTIME: return dns_rcodetab[13].name; + } + return _dns_format_code(nm,"rcode",code); +} diff --git a/ape-server/deps/udns-0.0.9/udns_dn.c b/ape-server/deps/udns-0.0.9/udns_dn.c new file mode 100755 index 0000000..1264a1d --- /dev/null +++ b/ape-server/deps/udns-0.0.9/udns_dn.c @@ -0,0 +1,382 @@ +/* $Id: udns_dn.c,v 1.7 2006/11/28 22:45:20 mjt Exp $ + domain names manipulation routines + + Copyright (C) 2005 Michael Tokarev + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + */ + +#include +#include "udns.h" + +unsigned dns_dnlen(dnscc_t *dn) { + register dnscc_t *d = dn; + while(*d) + d += 1 + *d; + return (unsigned)(d - dn) + 1; +} + +unsigned dns_dnlabels(register dnscc_t *dn) { + register unsigned l = 0; + while(*dn) + ++l, dn += 1 + *dn; + return l; +} + +unsigned dns_dnequal(register dnscc_t *dn1, register dnscc_t *dn2) { + register unsigned c; + dnscc_t *dn = dn1; + for(;;) { + if ((c = *dn1++) != *dn2++) + return 0; + if (!c) + return (unsigned)(dn1 - dn); + while(c--) { + if (DNS_DNLC(*dn1) != DNS_DNLC(*dn2)) + return 0; + ++dn1; ++dn2; + } + } +} + +unsigned +dns_dntodn(dnscc_t *sdn, dnsc_t *ddn, unsigned ddnsiz) { + unsigned sdnlen = dns_dnlen(sdn); + if (ddnsiz < sdnlen) + return 0; + memcpy(ddn, sdn, sdnlen); + return sdnlen; +} + +int +dns_ptodn(const char *name, unsigned namelen, + dnsc_t *dn, unsigned dnsiz, int *isabs) +{ + dnsc_t *dp; /* current position in dn (len byte first) */ + dnsc_t *const de /* end of dn: last byte that can be filled up */ + = dn + (dnsiz >= DNS_MAXDN ? DNS_MAXDN : dnsiz) - 1; + dnscc_t *np = (dnscc_t *)name; + dnscc_t *ne = np + (namelen ? namelen : strlen((char*)np)); + dnsc_t *llab; /* start of last label (llab[-1] will be length) */ + unsigned c; /* next input character, or length of last label */ + + if (!dnsiz) + return 0; + dp = llab = dn + 1; + + while(np < ne) { + + if (*np == '.') { /* label delimiter */ + c = dp - llab; /* length of the label */ + if (!c) { /* empty label */ + if (np == (dnscc_t *)name && np + 1 == ne) { + /* special case for root dn, aka `.' */ + ++np; + break; + } + return -1; /* zero label */ + } + if (c > DNS_MAXLABEL) + return -1; /* label too long */ + llab[-1] = (dnsc_t)c; /* update len of last label */ + llab = ++dp; /* start new label, llab[-1] will be len of it */ + ++np; + continue; + } + + /* check whenever we may put out one more byte */ + if (dp >= de) /* too long? */ + return dnsiz >= DNS_MAXDN ? -1 : 0; + if (*np != '\\') { /* non-escape, simple case */ + *dp++ = *np++; + continue; + } + /* handle \-style escape */ + /* note that traditionally, domain names (gethostbyname etc) + * used decimal \dd notation, not octal \ooo (RFC1035), so + * we're following this tradition here. + */ + if (++np == ne) + return -1; /* bad escape */ + else if (*np >= '0' && *np <= '9') { /* decimal number */ + /* we allow not only exactly 3 digits as per RFC1035, + * but also 2 or 1, for better usability. */ + c = *np++ - '0'; + if (np < ne && *np >= '0' && *np <= '9') { /* 2digits */ + c = c * 10 + *np++ - '0'; + if (np < ne && *np >= '0' && *np <= '9') { + c = c * 10 + *np++ - '0'; + if (c > 255) + return -1; /* bad escape */ + } + } + } + else + c = *np++; + *dp++ = (dnsc_t)c; /* place next out byte */ + } + + if ((c = dp - llab) > DNS_MAXLABEL) + return -1; /* label too long */ + if ((llab[-1] = (dnsc_t)c) != 0) { + *dp++ = 0; + if (isabs) + *isabs = 0; + } + else if (isabs) + *isabs = 1; + + return dp - dn; +} + +dnscc_t dns_inaddr_arpa_dn[14] = "\07in-addr\04arpa"; + +dnsc_t * +dns_a4todn_(const struct in_addr *addr, dnsc_t *dn, dnsc_t *dne) { + dnsc_t *p; + unsigned n; + dnscc_t *s = ((dnscc_t *)addr) + 4; + while(--s >= (dnscc_t *)addr) { + n = *s; + p = dn + 1; + if (n > 99) { + if (p + 2 > dne) return 0; + *p++ = n / 100 + '0'; + *p++ = (n % 100 / 10) + '0'; + *p = n % 10 + '0'; + } + else if (n > 9) { + if (p + 1 > dne) return 0; + *p++ = n / 10 + '0'; + *p = n % 10 + '0'; + } + else { + if (p > dne) return 0; + *p = n + '0'; + } + *dn = p - dn; + dn = p + 1; + } + return dn; +} + +int dns_a4todn(const struct in_addr *addr, dnscc_t *tdn, + dnsc_t *dn, unsigned dnsiz) { + dnsc_t *dne = dn + (dnsiz > DNS_MAXDN ? DNS_MAXDN : dnsiz); + dnsc_t *p; + unsigned l; + p = dns_a4todn_(addr, dn, dne); + if (!p) return 0; + if (!tdn) + tdn = dns_inaddr_arpa_dn; + l = dns_dnlen(tdn); + if (p + l > dne) return dnsiz >= DNS_MAXDN ? -1 : 0; + memcpy(p, tdn, l); + return (p + l) - dn; +} + +int dns_a4ptodn(const struct in_addr *addr, const char *tname, + dnsc_t *dn, unsigned dnsiz) { + dnsc_t *p; + int r; + if (!tname) + return dns_a4todn(addr, NULL, dn, dnsiz); + p = dns_a4todn_(addr, dn, dn + dnsiz); + if (!p) return 0; + r = dns_sptodn(tname, p, dnsiz - (p - dn)); + return r != 0 ? r : dnsiz >= DNS_MAXDN ? -1 : 0; +} + +dnscc_t dns_ip6_arpa_dn[10] = "\03ip6\04arpa"; + +dnsc_t * +dns_a6todn_(const struct in6_addr *addr, dnsc_t *dn, dnsc_t *dne) { + unsigned n; + dnscc_t *s = ((dnscc_t *)addr) + 16; + if (dn + 64 > dne) return 0; + while(--s >= (dnscc_t *)addr) { + *dn++ = 1; + n = *s & 0x0f; + *dn++ = n > 9 ? n + 'a' - 10 : n + '0'; + *dn++ = 1; + n = *s >> 4; + *dn++ = n > 9 ? n + 'a' - 10 : n + '0'; + } + return dn; +} + +int dns_a6todn(const struct in6_addr *addr, dnscc_t *tdn, + dnsc_t *dn, unsigned dnsiz) { + dnsc_t *dne = dn + (dnsiz > DNS_MAXDN ? DNS_MAXDN : dnsiz); + dnsc_t *p; + unsigned l; + p = dns_a6todn_(addr, dn, dne); + if (!p) return 0; + if (!tdn) + tdn = dns_ip6_arpa_dn; + l = dns_dnlen(tdn); + if (p + l > dne) return dnsiz >= DNS_MAXDN ? -1 : 0; + memcpy(p, tdn, l); + return (p + l) - dn; +} + +int dns_a6ptodn(const struct in6_addr *addr, const char *tname, + dnsc_t *dn, unsigned dnsiz) { + dnsc_t *p; + int r; + if (!tname) + return dns_a6todn(addr, NULL, dn, dnsiz); + p = dns_a6todn_(addr, dn, dn + dnsiz); + if (!p) return 0; + r = dns_sptodn(tname, p, dnsiz - (p - dn)); + return r != 0 ? r : dnsiz >= DNS_MAXDN ? -1 : 0; +} + +/* return size of buffer required to convert the dn into asciiz string. + * Keep in sync with dns_dntop() below. + */ +unsigned dns_dntop_size(dnscc_t *dn) { + unsigned size = 0; /* the size reqd */ + dnscc_t *le; /* label end */ + + while(*dn) { + /* *dn is the length of the next label, non-zero */ + if (size) + ++size; /* for the dot */ + le = dn + *dn + 1; + ++dn; + do { + switch(*dn) { + case '.': + case '\\': + /* Special modifiers in zone files. */ + case '"': + case ';': + case '@': + case '$': + size += 2; + break; + default: + if (*dn <= 0x20 || *dn >= 0x7f) + /* \ddd decimal notation */ + size += 4; + else + size += 1; + } + } while(++dn < le); + } + size += 1; /* zero byte at the end - string terminator */ + return size > DNS_MAXNAME ? 0 : size; +} + +/* Convert the dn into asciiz string. + * Keep in sync with dns_dntop_size() above. + */ +int dns_dntop(dnscc_t *dn, char *name, unsigned namesiz) { + char *np = name; /* current name ptr */ + char *const ne = name + namesiz; /* end of name */ + dnscc_t *le; /* label end */ + + while(*dn) { + /* *dn is the length of the next label, non-zero */ + if (np != name) { + if (np >= ne) goto toolong; + *np++ = '.'; + } + le = dn + *dn + 1; + ++dn; + do { + switch(*dn) { + case '.': + case '\\': + /* Special modifiers in zone files. */ + case '"': + case ';': + case '@': + case '$': + if (np + 2 > ne) goto toolong; + *np++ = '\\'; + *np++ = *dn; + break; + default: + if (*dn <= 0x20 || *dn >= 0x7f) { + /* \ddd decimal notation */ + if (np + 4 >= ne) goto toolong; + *np++ = '\\'; + *np++ = '0' + (*dn / 100); + *np++ = '0' + ((*dn % 100) / 10); + *np++ = '0' + (*dn % 10); + } + else { + if (np >= ne) goto toolong; + *np++ = *dn; + } + } + } while(++dn < le); + } + if (np >= ne) goto toolong; + *np++ = '\0'; + return np - name; +toolong: + return namesiz >= DNS_MAXNAME ? -1 : 0; +} + +#ifdef TEST +#include +#include + +int main(int argc, char **argv) { + int i; + int sz; + dnsc_t dn[DNS_MAXDN+10]; + dnsc_t *dl, *dp; + int isabs; + + sz = (argc > 1) ? atoi(argv[1]) : 0; + + for(i = 2; i < argc; ++i) { + int r = dns_ptodn(argv[i], 0, dn, sz, &isabs); + printf("%s: ", argv[i]); + if (r < 0) printf("error\n"); + else if (!r) printf("buffer too small\n"); + else { + printf("len=%d dnlen=%d size=%d name:", + r, dns_dnlen(dn), dns_dntop_size(dn)); + dl = dn; + while(*dl) { + printf(" %d=", *dl); + dp = dl + 1; + dl = dp + *dl; + while(dp < dl) { + if (*dp <= ' ' || *dp >= 0x7f) + printf("\\%03d", *dp); + else if (*dp == '.' || *dp == '\\') + printf("\\%c", *dp); + else + putchar(*dp); + ++dp; + } + } + if (isabs) putchar('.'); + putchar('\n'); + } + } + return 0; +} + +#endif /* TEST */ diff --git a/ape-server/deps/udns-0.0.9/udns_dntosp.c b/ape-server/deps/udns-0.0.9/udns_dntosp.c new file mode 100755 index 0000000..933463e --- /dev/null +++ b/ape-server/deps/udns-0.0.9/udns_dntosp.c @@ -0,0 +1,30 @@ +/* $Id: udns_dntosp.c,v 1.5 2005/04/19 21:48:09 mjt Exp $ + dns_dntosp() = convert DN to asciiz string using static buffer + + Copyright (C) 2005 Michael Tokarev + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + */ + +#include "udns.h" + +static char name[DNS_MAXNAME]; + +const char *dns_dntosp(dnscc_t *dn) { + return dns_dntop(dn, name, sizeof(name)) > 0 ? name : 0; +} diff --git a/ape-server/deps/udns-0.0.9/udns_init.c b/ape-server/deps/udns-0.0.9/udns_init.c new file mode 100755 index 0000000..c005731 --- /dev/null +++ b/ape-server/deps/udns-0.0.9/udns_init.c @@ -0,0 +1,231 @@ +/* $Id: udns_init.c,v 1.6 2007/01/08 00:41:38 mjt Exp $ + resolver initialisation stuff + + Copyright (C) 2006 Michael Tokarev + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef WINDOWS +# include /* includes */ +# include /* for dns server addresses etc */ +#else +# include +# include +# include +#endif /* !WINDOWS */ + +#include +#include +#include "udns.h" + +#define ISSPACE(x) (x == ' ' || x == '\t' || x == '\r' || x == '\n') + +static const char space[] = " \t\r\n"; + +static void dns_set_serv_internal(struct dns_ctx *ctx, char *serv) { + dns_add_serv(ctx, NULL); + for(serv = strtok(serv, space); serv; serv = strtok(NULL, space)) + dns_add_serv(ctx, serv); +} + +static void dns_set_srch_internal(struct dns_ctx *ctx, char *srch) { + dns_add_srch(ctx, NULL); + for(srch = strtok(srch, space); srch; srch = strtok(NULL, space)) + dns_add_srch(ctx, srch); +} + +#ifdef WINDOWS + +#ifndef NO_IPHLPAPI +/* Apparently, some systems does not have proper headers for IPHLPAIP to work. + * The best is to upgrade headers, but here's another, ugly workaround for + * this: compile with -DNO_IPHLPAPI. + */ + +typedef DWORD (WINAPI *GetAdaptersAddressesFunc)( + ULONG Family, DWORD Flags, PVOID Reserved, + PIP_ADAPTER_ADDRESSES pAdapterAddresses, + PULONG pOutBufLen); + +static int dns_initns_iphlpapi(struct dns_ctx *ctx) { + HANDLE h_iphlpapi; + GetAdaptersAddressesFunc pfnGetAdAddrs; + PIP_ADAPTER_ADDRESSES pAddr, pAddrBuf; + PIP_ADAPTER_DNS_SERVER_ADDRESS pDnsAddr; + ULONG ulOutBufLen; + DWORD dwRetVal; + int ret = -1; + + h_iphlpapi = LoadLibrary("iphlpapi.dll"); + if (!h_iphlpapi) + return -1; + pfnGetAdAddrs = (GetAdaptersAddressesFunc) + GetProcAddress(h_iphlpapi, "GetAdaptersAddresses"); + if (!pfnGetAdAddrs) goto freelib; + ulOutBufLen = 0; + dwRetVal = pfnGetAdAddrs(AF_UNSPEC, 0, NULL, NULL, &ulOutBufLen); + if (dwRetVal != ERROR_BUFFER_OVERFLOW) goto freelib; + pAddrBuf = malloc(ulOutBufLen); + if (!pAddrBuf) goto freelib; + dwRetVal = pfnGetAdAddrs(AF_UNSPEC, 0, NULL, pAddrBuf, &ulOutBufLen); + if (dwRetVal != ERROR_SUCCESS) goto freemem; + for (pAddr = pAddrBuf; pAddr; pAddr = pAddr->Next) + for (pDnsAddr = pAddr->FirstDnsServerAddress; + pDnsAddr; + pDnsAddr = pDnsAddr->Next) + dns_add_serv_s(ctx, pDnsAddr->Address.lpSockaddr); + ret = 0; +freemem: + free(pAddrBuf); +freelib: + FreeLibrary(h_iphlpapi); + return ret; +} + +#else /* NO_IPHLPAPI */ + +#define dns_initns_iphlpapi(ctx) (-1) + +#endif /* NO_IPHLPAPI */ + +static int dns_initns_registry(struct dns_ctx *ctx) { + LONG res; + HKEY hk; + DWORD type = REG_EXPAND_SZ | REG_SZ; + DWORD len; + char valBuf[1024]; + +#define REGKEY_WINNT "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters" +#define REGKEY_WIN9x "SYSTEM\\CurrentControlSet\\Services\\VxD\\MSTCP" + res = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGKEY_WINNT, 0, KEY_QUERY_VALUE, &hk); + if (res != ERROR_SUCCESS) + res = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGKEY_WIN9x, + 0, KEY_QUERY_VALUE, &hk); + if (res != ERROR_SUCCESS) + return -1; + len = sizeof(valBuf) - 1; + res = RegQueryValueEx(hk, "NameServer", NULL, &type, (BYTE*)valBuf, &len); + if (res != ERROR_SUCCESS || !len || !valBuf[0]) { + len = sizeof(valBuf) - 1; + res = RegQueryValueEx(hk, "DhcpNameServer", NULL, &type, + (BYTE*)valBuf, &len); + } + RegCloseKey(hk); + if (res != ERROR_SUCCESS || !len || !valBuf[0]) + return -1; + valBuf[len] = '\0'; + /* nameservers are stored as a whitespace-seperate list: + * "192.168.1.1 123.21.32.12" */ + dns_set_serv_internal(ctx, valBuf); + return 0; +} + +#else /* !WINDOWS */ + +static int dns_init_resolvconf(struct dns_ctx *ctx) { + char *v; + char buf[2049]; /* this buffer is used to hold /etc/resolv.conf */ + int has_srch = 0; + + /* read resolv.conf... */ + { int fd = open("/etc/resolv.conf", O_RDONLY); + if (fd >= 0) { + int l = read(fd, buf, sizeof(buf) - 1); + close(fd); + buf[l < 0 ? 0 : l] = '\0'; + } + else + buf[0] = '\0'; + } + if (buf[0]) { /* ...and parse it */ + char *line, *nextline; + line = buf; + do { + nextline = strchr(line, '\n'); + if (nextline) *nextline++ = '\0'; + v = line; + while(*v && !ISSPACE(*v)) ++v; + if (!*v) continue; + *v++ = '\0'; + while(ISSPACE(*v)) ++v; + if (!*v) continue; + if (strcmp(line, "domain") == 0) { + dns_set_srch_internal(ctx, strtok(v, space)); + has_srch = 1; + } + else if (strcmp(line, "search") == 0) { + dns_set_srch_internal(ctx, v); + has_srch = 1; + } + else if (strcmp(line, "nameserver") == 0) + dns_add_serv(ctx, strtok(v, space)); + else if (strcmp(line, "options") == 0) + dns_set_opts(ctx, v); + } while((line = nextline) != NULL); + } + + buf[sizeof(buf)-1] = '\0'; + + /* get list of nameservers from env. vars. */ + if ((v = getenv("NSCACHEIP")) != NULL || + (v = getenv("NAMESERVERS")) != NULL) { + strncpy(buf, v, sizeof(buf) - 1); + dns_set_serv_internal(ctx, buf); + } + /* if $LOCALDOMAIN is set, use it for search list */ + if ((v = getenv("LOCALDOMAIN")) != NULL) { + strncpy(buf, v, sizeof(buf) - 1); + dns_set_srch_internal(ctx, buf); + has_srch = 1; + } + if ((v = getenv("RES_OPTIONS")) != NULL) + dns_set_opts(ctx, v); + + /* if still no search list, use local domain name */ + if (has_srch && + gethostname(buf, sizeof(buf) - 1) == 0 && + (v = strchr(buf, '.')) != NULL && + *++v != '\0') + dns_add_srch(ctx, v); + + return 0; +} + +#endif /* !WINDOWS */ + +int dns_init(struct dns_ctx *ctx, int do_open) { + if (!ctx) + ctx = &dns_defctx; + dns_reset(ctx); + +#ifdef WINDOWS + if (dns_initns_iphlpapi(ctx) != 0) + dns_initns_registry(ctx); + /*XXX WINDOWS: probably good to get default domain and search list too... + * And options. Something is in registry. */ + /*XXX WINDOWS: maybe environment variables are also useful? */ +#else + dns_init_resolvconf(ctx); +#endif + + return do_open ? dns_open(ctx) : 0; +} diff --git a/ape-server/deps/udns-0.0.9/udns_misc.c b/ape-server/deps/udns-0.0.9/udns_misc.c new file mode 100755 index 0000000..0db7ffd --- /dev/null +++ b/ape-server/deps/udns-0.0.9/udns_misc.c @@ -0,0 +1,67 @@ +/* $Id: udns_misc.c,v 1.8 2005/04/05 22:51:32 mjt Exp $ + miscellaneous routines + + Copyright (C) 2005 Michael Tokarev + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + */ + +#include "udns.h" + +int dns_findname(const struct dns_nameval *nv, const char *name) { + register const char *a, *b; + for(; nv->name; ++nv) + for(a = name, b = nv->name; ; ++a, ++b) + if (DNS_DNUC(*a) != *b) break; + else if (!*a) return nv->val; + return -1; +} + +const char *_dns_format_code(char *buf, const char *prefix, int code) { + char *bp = buf; + unsigned c, n; + do *bp++ = DNS_DNUC(*prefix); + while(*++prefix); + *bp++ = '#'; + if (code < 0) code = -code, *bp++ = '-'; + n = 0; c = code; + do ++n; + while((c /= 10)); + c = code; + bp[n--] = '\0'; + do bp[n--] = c % 10 + '0'; + while((c /= 10)); + return buf; +} + +const char *dns_strerror(int err) { + if (err >= 0) return "successeful completion"; + switch(err) { + case DNS_E_TEMPFAIL: return "temporary failure in name resolution"; + case DNS_E_PROTOCOL: return "protocol error"; + case DNS_E_NXDOMAIN: return "domain name does not exist"; + case DNS_E_NODATA: return "valid domain but no data of requested type"; + case DNS_E_NOMEM: return "out of memory"; + case DNS_E_BADQUERY: return "malformed query"; + default: return "unknown error"; + } +} + +const char *dns_version(void) { + return UDNS_VERSION; +} diff --git a/ape-server/deps/udns-0.0.9/udns_parse.c b/ape-server/deps/udns-0.0.9/udns_parse.c new file mode 100755 index 0000000..5440d92 --- /dev/null +++ b/ape-server/deps/udns-0.0.9/udns_parse.c @@ -0,0 +1,169 @@ +/* $Id: udns_parse.c,v 1.14 2005/09/12 10:55:21 mjt Exp $ + raw DNS packet parsing routines + + Copyright (C) 2005 Michael Tokarev + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + */ + +#include +#include +#include "udns.h" + +dnscc_t *dns_skipdn(dnscc_t *cur, dnscc_t *end) { + unsigned c; + for(;;) { + if (cur >= end) + return NULL; + c = *cur++; + if (!c) + return cur; + if (c & 192) /* jump */ + return cur + 1 >= end ? NULL : cur + 1; + cur += c; + } +} + +int +dns_getdn(dnscc_t *pkt, dnscc_t **cur, dnscc_t *end, + register dnsc_t *dn, unsigned dnsiz) { + unsigned c; + dnscc_t *pp = *cur; /* current packet pointer */ + dnsc_t *dp = dn; /* current dn pointer */ + dnsc_t *const de /* end of the DN dest */ + = dn + (dnsiz < DNS_MAXDN ? dnsiz : DNS_MAXDN); + dnscc_t *jump = NULL; /* ptr after first jump if any */ + unsigned loop = 100; /* jump loop counter */ + + for(;;) { /* loop by labels */ + if (pp >= end) /* reached end of packet? */ + return -1; + c = *pp++; /* length of the label */ + if (!c) { /* empty label: terminate */ + if (dn >= de) /* can't fit terminator */ + goto noroom; + *dp++ = 0; + /* return next pos: either after the first jump or current */ + *cur = jump ? jump : pp; + return dp - dn; + } + if (c & 192) { /* jump */ + if (pp >= end) /* eop instead of jump pos */ + return -1; + if (!jump) jump = pp + 1; /* remember first jump */ + else if (!--loop) return -1; /* too many jumps */ + c = ((c & ~192) << 8) | *pp; /* new pos */ + if (c < DNS_HSIZE) /* don't allow jump into the header */ + return -1; + pp = pkt + c; + continue; + } + if (c > DNS_MAXLABEL) /* too long label? */ + return -1; + if (pp + c > end) /* label does not fit in packet? */ + return -1; + if (dp + c + 1 > de) /* if enouth room for the label */ + goto noroom; + *dp++ = c; /* label length */ + memcpy(dp, pp, c); /* and the label itself */ + dp += c; + pp += c; /* advance to the next label */ + } +noroom: + return dnsiz < DNS_MAXDN ? 0 : -1; +} + +void dns_rewind(struct dns_parse *p, dnscc_t *qdn) { + p->dnsp_qdn = qdn; + p->dnsp_cur = p->dnsp_ans; + p->dnsp_rrl = dns_numan(p->dnsp_pkt); + p->dnsp_ttl = 0xffffffffu; + p->dnsp_nrr = 0; +} + +void +dns_initparse(struct dns_parse *p, dnscc_t *qdn, + dnscc_t *pkt, dnscc_t *cur, dnscc_t *end) { + p->dnsp_pkt = pkt; + p->dnsp_end = end; + p->dnsp_rrl = dns_numan(pkt); + p->dnsp_qdn = qdn; + assert(cur + 4 <= end); + if ((p->dnsp_qtyp = dns_get16(cur+0)) == DNS_T_ANY) p->dnsp_qtyp = 0; + if ((p->dnsp_qcls = dns_get16(cur+2)) == DNS_C_ANY) p->dnsp_qcls = 0; + p->dnsp_cur = p->dnsp_ans = cur + 4; + p->dnsp_ttl = 0xffffffffu; + p->dnsp_nrr = 0; +} + +int dns_nextrr(struct dns_parse *p, struct dns_rr *rr) { + dnscc_t *cur = p->dnsp_cur; + while(p->dnsp_rrl > 0) { + --p->dnsp_rrl; + if (dns_getdn(p->dnsp_pkt, &cur, p->dnsp_end, + rr->dnsrr_dn, sizeof(rr->dnsrr_dn)) <= 0) + return -1; + if (cur + 10 > p->dnsp_end) + return -1; + rr->dnsrr_typ = dns_get16(cur); + rr->dnsrr_cls = dns_get16(cur+2); + rr->dnsrr_ttl = dns_get32(cur+4); + rr->dnsrr_dsz = dns_get16(cur+8); + rr->dnsrr_dptr = cur = cur + 10; + rr->dnsrr_dend = cur = cur + rr->dnsrr_dsz; + if (cur > p->dnsp_end) + return -1; + if (p->dnsp_qdn && !dns_dnequal(p->dnsp_qdn, rr->dnsrr_dn)) + continue; + if ((!p->dnsp_qcls || p->dnsp_qcls == rr->dnsrr_cls) && + (!p->dnsp_qtyp || p->dnsp_qtyp == rr->dnsrr_typ)) { + p->dnsp_cur = cur; + ++p->dnsp_nrr; + if (p->dnsp_ttl > rr->dnsrr_ttl) p->dnsp_ttl = rr->dnsrr_ttl; + return 1; + } + if (p->dnsp_qdn && rr->dnsrr_typ == DNS_T_CNAME && !p->dnsp_nrr) { + if (dns_getdn(p->dnsp_pkt, &rr->dnsrr_dptr, p->dnsp_end, + p->dnsp_dnbuf, sizeof(p->dnsp_dnbuf)) <= 0 || + rr->dnsrr_dptr != rr->dnsrr_dend) + return -1; + p->dnsp_qdn = p->dnsp_dnbuf; + if (p->dnsp_ttl > rr->dnsrr_ttl) p->dnsp_ttl = rr->dnsrr_ttl; + } + } + p->dnsp_cur = cur; + return 0; +} + +int dns_stdrr_size(const struct dns_parse *p) { + return + dns_dntop_size(p->dnsp_qdn) + + (p->dnsp_qdn == dns_payload(p->dnsp_pkt) ? 0 : + dns_dntop_size(dns_payload(p->dnsp_pkt))); +} + +void *dns_stdrr_finish(struct dns_rr_null *ret, char *cp, + const struct dns_parse *p) { + cp += dns_dntop(p->dnsp_qdn, (ret->dnsn_cname = cp), DNS_MAXNAME); + if (p->dnsp_qdn == dns_payload(p->dnsp_pkt)) + ret->dnsn_qname = ret->dnsn_cname; + else + dns_dntop(dns_payload(p->dnsp_pkt), (ret->dnsn_qname = cp), DNS_MAXNAME); + ret->dnsn_ttl = p->dnsp_ttl; + return ret; +} diff --git a/ape-server/deps/udns-0.0.9/udns_resolver.c b/ape-server/deps/udns-0.0.9/udns_resolver.c new file mode 100755 index 0000000..d083397 --- /dev/null +++ b/ape-server/deps/udns-0.0.9/udns_resolver.c @@ -0,0 +1,1294 @@ +/* $Id: udns_resolver.c,v 1.98 2007/01/10 13:32:33 mjt Exp $ + resolver stuff (main module) + + Copyright (C) 2005 Michael Tokarev + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef WINDOWS +# include /* includes */ +# include /* needed for struct in6_addr */ +#else +# include +# include +# include +# include +# include +# include +# ifdef HAVE_POLL +# include +# else +# ifdef HAVE_SYS_SELECT_H +# include +# endif +# endif +# ifdef HAVE_TIMES +# include +# endif +# define closesocket(sock) close(sock) +#endif /* !WINDOWS */ + +#include +#include +#include +#include +#include +#include +#include "udns.h" + +#ifndef EAFNOSUPPORT +# define EAFNOSUPPORT EINVAL +#endif +#ifndef MSG_DONTWAIT +# define MSG_DONTWAIT 0 +#endif + +struct dns_qlink { + struct dns_query *next, *prev; +}; + +struct dns_query { + struct dns_qlink dnsq_link; /* list entry (should be first) */ + unsigned dnsq_origdnl0; /* original query DN len w/o last 0 */ + unsigned dnsq_flags; /* control flags for this query */ + unsigned dnsq_servi; /* index of next server to try */ + unsigned dnsq_servwait; /* bitmask: servers left to wait */ + unsigned dnsq_servskip; /* bitmask: servers to skip */ + unsigned dnsq_servnEDNS0; /* bitmask: servers refusing EDNS0 */ + unsigned dnsq_try; /* number of tries made so far */ + dnscc_t *dnsq_nxtsrch; /* next search pointer @dnsc_srchbuf */ + time_t dnsq_deadline; /* when current try will expire */ + dns_parse_fn *dnsq_parse; /* parse: raw => application */ + dns_query_fn *dnsq_cbck; /* the callback to call when done */ + void *dnsq_cbdata; /* user data for the callback */ +#ifndef NDEBUG + struct dns_ctx *dnsq_ctx; /* the resolver context */ +#endif + /* char fields at the end to avoid padding */ + dnsc_t dnsq_id[2]; /* query ID */ + dnsc_t dnsq_typcls[4]; /* requested RR type+class */ + dnsc_t dnsq_dn[DNS_MAXDN+DNS_DNPAD]; /* the query DN +alignment */ +}; + +/* working with dns_query lists */ + +static __inline void qlist_init(struct dns_qlink *list) { + list->next = list->prev = (struct dns_query *)list; +} + +static __inline int qlist_isempty(const struct dns_qlink *list) { + return list->next == (const struct dns_query *)list ? 1 : 0; +} + +static __inline struct dns_query *qlist_first(struct dns_qlink *list) { + return list->next == (struct dns_query *)list ? 0 : list->next; +} + +static __inline void qlist_remove(struct dns_query *q) { + q->dnsq_link.next->dnsq_link.prev = q->dnsq_link.prev; + q->dnsq_link.prev->dnsq_link.next = q->dnsq_link.next; +} + +/* insert q between prev and next */ +static __inline void +qlist_insert(struct dns_query *q, + struct dns_query *prev, struct dns_query *next) { + q->dnsq_link.next = next; + q->dnsq_link.prev = prev; + prev->dnsq_link.next = next->dnsq_link.prev = q; +} + +static __inline void +qlist_insert_after(struct dns_query *q, struct dns_query *prev) { + qlist_insert(q, prev, prev->dnsq_link.next); +} + +static __inline void +qlist_insert_before(struct dns_query *q, struct dns_query *next) { + qlist_insert(q, next->dnsq_link.prev, next); +} + +static __inline void +qlist_add_tail(struct dns_query *q, struct dns_qlink *top) { + qlist_insert_before(q, (struct dns_query *)top); +} + +static __inline void +qlist_add_head(struct dns_query *q, struct dns_qlink *top) { + qlist_insert_after(q, (struct dns_query *)top); +} + +#define QLIST_FIRST(list, direction) ((list)->direction) +#define QLIST_ISLAST(list, q) ((q) == (struct dns_query*)(list)) +#define QLIST_NEXT(q, direction) ((q)->dnsq_link.direction) + +#define QLIST_FOR_EACH(list, q, direction) \ + for(q = QLIST_FIRST(list, direction); \ + !QLIST_ISLAST(list, q); q = QLIST_NEXT(q, direction)) + +union sockaddr_ns { + struct sockaddr sa; + struct sockaddr_in sin; +#ifdef HAVE_IPv6 + struct sockaddr_in6 sin6; +#endif +}; + +#define sin_eq(a,b) \ + ((a).sin_port == (b).sin_port && \ + (a).sin_addr.s_addr == (b).sin_addr.s_addr) +#define sin6_eq(a,b) \ + ((a).sin6_port == (b).sin6_port && \ + memcmp(&(a).sin6_addr, &(b).sin6_addr, sizeof(struct in6_addr)) == 0) + +struct dns_ctx { /* resolver context */ + /* settings */ + unsigned dnsc_flags; /* various flags */ + unsigned dnsc_timeout; /* timeout (base value) for queries */ + unsigned dnsc_ntries; /* number of retries */ + unsigned dnsc_ndots; /* ndots to assume absolute name */ + unsigned dnsc_port; /* default port (DNS_PORT) */ + unsigned dnsc_udpbuf; /* size of UDP buffer */ + /* array of nameserver addresses */ + union sockaddr_ns dnsc_serv[DNS_MAXSERV]; + unsigned dnsc_nserv; /* number of nameservers */ + unsigned dnsc_salen; /* length of socket addresses */ + dnsc_t dnsc_srchbuf[1024]; /* buffer for searchlist */ + dnsc_t *dnsc_srchend; /* current end of srchbuf */ + + dns_utm_fn *dnsc_utmfn; /* register/cancel timer events */ + void *dnsc_utmctx; /* user timer context for utmfn() */ + time_t dnsc_utmexp; /* when user timer expires */ + + dns_dbgfn *dnsc_udbgfn; /* debugging function */ + + /* dynamic data */ + unsigned dnsc_nextid; /* next queue ID to use */ + int dnsc_udpsock; /* UDP socket */ + struct dns_qlink dnsc_qactive; /* active list sorted by deadline */ + int dnsc_nactive; /* number entries in dnsc_qactive */ + dnsc_t *dnsc_pbuf; /* packet buffer (udpbuf size) */ + int dnsc_qstatus; /* last query status value */ +}; + +static const struct { + const char *name; + enum dns_opt opt; + unsigned offset; + unsigned min, max; +} dns_opts[] = { +#define opt(name,opt,field,min,max) \ + {name,opt,offsetof(struct dns_ctx,field),min,max} + opt("retrans", DNS_OPT_TIMEOUT, dnsc_timeout, 1,300), + opt("timeout", DNS_OPT_TIMEOUT, dnsc_timeout, 1,300), + opt("retry", DNS_OPT_NTRIES, dnsc_ntries, 1,50), + opt("attempts", DNS_OPT_NTRIES, dnsc_ntries, 1,50), + opt("ndots", DNS_OPT_NDOTS, dnsc_ndots, 0,1000), + opt("port", DNS_OPT_PORT, dnsc_port, 1,0xffff), + opt("udpbuf", DNS_OPT_UDPSIZE, dnsc_udpbuf, DNS_MAXPACKET,65536), +#undef opt +}; +#define dns_ctxopt(ctx,idx) (*((unsigned*)(((char*)ctx)+dns_opts[idx].offset))) + +#define ISSPACE(x) (x == ' ' || x == '\t' || x == '\r' || x == '\n') + +struct dns_ctx dns_defctx; + +#define SETCTX(ctx) if (!ctx) ctx = &dns_defctx +#define SETCTXINITED(ctx) SETCTX(ctx); assert(CTXINITED(ctx)) +#define CTXINITED(ctx) (ctx->dnsc_flags & DNS_INITED) +#define SETCTXFRESH(ctx) SETCTXINITED(ctx); assert(!CTXOPEN(ctx)) +#define SETCTXINACTIVE(ctx) \ + SETCTXINITED(ctx); assert(!ctx->dnsc_nactive) +#define SETCTXOPEN(ctx) SETCTXINITED(ctx); assert(CTXOPEN(ctx)) +#define CTXOPEN(ctx) (ctx->dnsc_udpsock >= 0) + +#if defined(NDEBUG) || !defined(DEBUG) +#define dns_assert_ctx(ctx) +#else +static void dns_assert_ctx(const struct dns_ctx *ctx) { + int nactive = 0; + const struct dns_query *q; + QLIST_FOR_EACH(&ctx->dnsc_qactive, q, next) { + assert(q->dnsq_ctx == ctx); + assert(q->dnsq_link.next->dnsq_link.prev == q); + assert(q->dnsq_link.prev->dnsq_link.next == q); + ++nactive; + } + assert(nactive == ctx->dnsc_nactive); +} +#endif + +enum { + DNS_INTERNAL = 0xffff, /* internal flags mask */ + DNS_INITED = 0x0001, /* the context is initialized */ + DNS_ASIS_DONE = 0x0002, /* search: skip the last as-is query */ + DNS_SEEN_NODATA = 0x0004, /* search: NODATA has been received */ +}; + +int dns_add_serv(struct dns_ctx *ctx, const char *serv) { + union sockaddr_ns *sns; + SETCTXFRESH(ctx); + if (!serv) + return (ctx->dnsc_nserv = 0); + if (ctx->dnsc_nserv >= DNS_MAXSERV) + return errno = ENFILE, -1; + sns = &ctx->dnsc_serv[ctx->dnsc_nserv]; + memset(sns, 0, sizeof(*sns)); + if (dns_pton(AF_INET, serv, &sns->sin.sin_addr) > 0) { + sns->sin.sin_family = AF_INET; + return ++ctx->dnsc_nserv; + } +#ifdef HAVE_IPv6 + if (dns_pton(AF_INET6, serv, &sns->sin6.sin6_addr) > 0) { + sns->sin6.sin6_family = AF_INET6; + return ++ctx->dnsc_nserv; + } +#endif + errno = EINVAL; + return -1; +} + +int dns_add_serv_s(struct dns_ctx *ctx, const struct sockaddr *sa) { + SETCTXFRESH(ctx); + if (!sa) + return (ctx->dnsc_nserv = 0); + if (ctx->dnsc_nserv >= DNS_MAXSERV) + return errno = ENFILE, -1; +#ifdef HAVE_IPv6 + else if (sa->sa_family == AF_INET6) + ctx->dnsc_serv[ctx->dnsc_nserv].sin6 = *(struct sockaddr_in6*)sa; +#endif + else if (sa->sa_family == AF_INET) + ctx->dnsc_serv[ctx->dnsc_nserv].sin = *(struct sockaddr_in*)sa; + else + return errno = EAFNOSUPPORT, -1; + return ++ctx->dnsc_nserv; +} + +int dns_set_opts(struct dns_ctx *ctx, const char *opts) { + unsigned i, v; + SETCTXINACTIVE(ctx); + for(;;) { + while(ISSPACE(*opts)) ++opts; + if (!*opts) break; + for(i = 0; i < sizeof(dns_opts)/sizeof(dns_opts[0]); ++i) { + v = strlen(dns_opts[i].name); + if (strncmp(dns_opts[i].name, opts, v) != 0 || + (opts[v] != ':' && opts[v] != '=')) + continue; + opts += v + 1; + v = 0; + if (*opts < '0' || *opts > '9') break; + do v = v * 10 + (*opts++ - '0'); + while (*opts >= '0' && *opts <= '9'); + if (v < dns_opts[i].min) v = dns_opts[i].min; + if (v > dns_opts[i].max) v = dns_opts[i].max; + dns_ctxopt(ctx, i) = v; + break; + } + while(*opts && !ISSPACE(*opts)) ++opts; + } + return 0; +} + +int dns_set_opt(struct dns_ctx *ctx, enum dns_opt opt, int val) { + int prev; + unsigned i; + SETCTXINACTIVE(ctx); + for(i = 0; i < sizeof(dns_opts)/sizeof(dns_opts[0]); ++i) { + if (dns_opts[i].opt != opt) continue; + prev = dns_ctxopt(ctx, i); + if (val >= 0) { + unsigned v = val; + if (v < dns_opts[i].min || v > dns_opts[i].max) { + errno = EINVAL; + return -1; + } + dns_ctxopt(ctx, i) = v; + } + return prev; + } + if (opt == DNS_OPT_FLAGS) { + prev = ctx->dnsc_flags & ~DNS_INTERNAL; + if (val >= 0) + ctx->dnsc_flags = + (ctx->dnsc_flags & DNS_INTERNAL) | (val & ~DNS_INTERNAL); + return prev; + } + errno = ENOSYS; + return -1; +} + +int dns_add_srch(struct dns_ctx *ctx, const char *srch) { + int dnl; + SETCTXINACTIVE(ctx); + if (!srch) { + memset(ctx->dnsc_srchbuf, 0, sizeof(ctx->dnsc_srchbuf)); + ctx->dnsc_srchend = ctx->dnsc_srchbuf; + return 0; + } + dnl = + sizeof(ctx->dnsc_srchbuf) - (ctx->dnsc_srchend - ctx->dnsc_srchbuf) - 1; + dnl = dns_sptodn(srch, ctx->dnsc_srchend, dnl); + if (dnl > 0) + ctx->dnsc_srchend += dnl; + ctx->dnsc_srchend[0] = '\0'; /* we ensure the list is always ends at . */ + if (dnl > 0) + return 0; + errno = EINVAL; + return -1; +} + +static void dns_drop_utm(struct dns_ctx *ctx) { + if (ctx->dnsc_utmfn) + ctx->dnsc_utmfn(NULL, -1, ctx->dnsc_utmctx); + ctx->dnsc_utmctx = NULL; + ctx->dnsc_utmexp = -1; +} + +static void +_dns_request_utm(struct dns_ctx *ctx, time_t now) { + struct dns_query *q; + time_t deadline; + int timeout; + q = qlist_first(&ctx->dnsc_qactive); + if (!q) + deadline = -1, timeout = -1; + else if (!now || q->dnsq_deadline <= now) + deadline = 0, timeout = 0; + else + deadline = q->dnsq_deadline, timeout = (int)(deadline - now); + if (ctx->dnsc_utmexp == deadline) + return; + ctx->dnsc_utmfn(ctx, timeout, ctx->dnsc_utmctx); + ctx->dnsc_utmexp = deadline; +} + +static __inline void +dns_request_utm(struct dns_ctx *ctx, time_t now) { + if (ctx->dnsc_utmfn) + _dns_request_utm(ctx, now); +} + +void dns_set_dbgfn(struct dns_ctx *ctx, dns_dbgfn *dbgfn) { + SETCTXINITED(ctx); + ctx->dnsc_udbgfn = dbgfn; +} + +void +dns_set_tmcbck(struct dns_ctx *ctx, dns_utm_fn *fn, void *data) { + SETCTXINITED(ctx); + dns_drop_utm(ctx); + ctx->dnsc_utmfn = fn; + ctx->dnsc_utmctx = data; + if (CTXOPEN(ctx)) + dns_request_utm(ctx, 0); +} + +unsigned dns_random16(void) { +#ifdef WINDOWS + FILETIME ft; + GetSystemTimeAsFileTime(&ft); +#define x (ft.dwLowDateTime) +#else + struct timeval tv; + gettimeofday(&tv, NULL); +#define x (tv.tv_usec) +#endif + return ((unsigned)x ^ ((unsigned)x >> 16)) & 0xffff; +#undef x +} + +void dns_close(struct dns_ctx *ctx) { + struct dns_query *q; + SETCTX(ctx); + if (CTXINITED(ctx)) { + if (ctx->dnsc_udpsock >= 0) + closesocket(ctx->dnsc_udpsock); + ctx->dnsc_udpsock = -1; + if (ctx->dnsc_pbuf) + free(ctx->dnsc_pbuf); + ctx->dnsc_pbuf = NULL; + while((q = qlist_first(&ctx->dnsc_qactive)) != NULL) { + qlist_remove(q); + free(q); + } + ctx->dnsc_nactive = 0; + dns_drop_utm(ctx); + } +} + +void dns_reset(struct dns_ctx *ctx) { + SETCTX(ctx); + dns_close(ctx); + memset(ctx, 0, sizeof(*ctx)); + ctx->dnsc_timeout = 4; + ctx->dnsc_ntries = 3; + ctx->dnsc_ndots = 1; + ctx->dnsc_udpbuf = DNS_EDNS0PACKET; + ctx->dnsc_port = DNS_PORT; + ctx->dnsc_udpsock = -1; + ctx->dnsc_srchend = ctx->dnsc_srchbuf; + qlist_init(&ctx->dnsc_qactive); + ctx->dnsc_nextid = dns_random16(); + ctx->dnsc_flags = DNS_INITED; +} + +struct dns_ctx *dns_new(const struct dns_ctx *copy) { + struct dns_ctx *ctx; + SETCTXINITED(copy); + dns_assert_ctx(copy); + ctx = malloc(sizeof(*ctx)); + if (!ctx) + return NULL; + *ctx = *copy; + ctx->dnsc_udpsock = -1; + qlist_init(&ctx->dnsc_qactive); + ctx->dnsc_nactive = 0; + ctx->dnsc_pbuf = NULL; + ctx->dnsc_qstatus = 0; + ctx->dnsc_utmfn = NULL; + ctx->dnsc_utmctx = NULL; + ctx->dnsc_nextid = dns_random16(); + return ctx; +} + +void dns_free(struct dns_ctx *ctx) { + assert(ctx != NULL && ctx != &dns_defctx); + dns_reset(ctx); + free(ctx); +} + +int dns_open(struct dns_ctx *ctx) { + int sock; + unsigned i; + int port; + union sockaddr_ns *sns; +#ifdef HAVE_IPv6 + unsigned have_inet6 = 0; +#endif + + SETCTXINITED(ctx); + assert(!CTXOPEN(ctx)); + + port = htons((unsigned short)ctx->dnsc_port); + /* ensure we have at least one server */ + if (!ctx->dnsc_nserv) { + sns = ctx->dnsc_serv; + sns->sin.sin_family = AF_INET; + sns->sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + ctx->dnsc_nserv = 1; + } + + for (i = 0; i < ctx->dnsc_nserv; ++i) { + sns = &ctx->dnsc_serv[i]; + /* set port for each sockaddr */ +#ifdef HAVE_IPv6 + if (sns->sa.sa_family == AF_INET6) { + if (!sns->sin6.sin6_port) sns->sin6.sin6_port = (unsigned short)port; + ++have_inet6; + } + else +#endif + { + assert(sns->sa.sa_family == AF_INET); + if (!sns->sin.sin_port) sns->sin.sin_port = (unsigned short)port; + } + } + +#ifdef HAVE_IPv6 + if (have_inet6 && have_inet6 < ctx->dnsc_nserv) { + /* convert all IPv4 addresses to IPv6 V4MAPPED */ + struct sockaddr_in6 sin6; + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + /* V4MAPPED: ::ffff:1.2.3.4 */ + sin6.sin6_addr.s6_addr[10] = 0xff; + sin6.sin6_addr.s6_addr[11] = 0xff; + for(i = 0; i < ctx->dnsc_nserv; ++i) { + sns = &ctx->dnsc_serv[i]; + if (sns->sa.sa_family == AF_INET) { + sin6.sin6_port = sns->sin.sin_port; + ((struct in_addr*)&sin6.sin6_addr)[3] = sns->sin.sin_addr; + sns->sin6 = sin6; + } + } + } + + ctx->dnsc_salen = have_inet6 ? + sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); + + if (have_inet6) + sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); + else + sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); +#else /* !HAVE_IPv6 */ + sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + ctx->dnsc_salen = sizeof(struct sockaddr_in); +#endif /* HAVE_IPv6 */ + + if (sock < 0) { + ctx->dnsc_qstatus = DNS_E_TEMPFAIL; + return -1; + } +#ifdef WINDOWS + { unsigned long on = 1; + if (ioctlsocket(sock, FIONBIO, &on) == SOCKET_ERROR) { + closesocket(sock); + ctx->dnsc_qstatus = DNS_E_TEMPFAIL; + return -1; + } + } +#else /* !WINDOWS */ + if (fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK) < 0 || + fcntl(sock, F_SETFD, FD_CLOEXEC) < 0) { + closesocket(sock); + ctx->dnsc_qstatus = DNS_E_TEMPFAIL; + return -1; + } +#endif /* WINDOWS */ + /* allocate the packet buffer */ + if ((ctx->dnsc_pbuf = malloc(ctx->dnsc_udpbuf)) == NULL) { + closesocket(sock); + ctx->dnsc_qstatus = DNS_E_NOMEM; + errno = ENOMEM; + return -1; + } + + ctx->dnsc_udpsock = sock; + dns_request_utm(ctx, 0); + return sock; +} + +int dns_sock(const struct dns_ctx *ctx) { + SETCTXINITED(ctx); + return ctx->dnsc_udpsock; +} + +int dns_active(const struct dns_ctx *ctx) { + SETCTXINITED(ctx); + dns_assert_ctx(ctx); + return ctx->dnsc_nactive; +} + +int dns_status(const struct dns_ctx *ctx) { + SETCTX(ctx); + return ctx->dnsc_qstatus; +} +void dns_setstatus(struct dns_ctx *ctx, int status) { + SETCTX(ctx); + ctx->dnsc_qstatus = status; +} + +/* End the query: disconnect it from the active list, free it, + * and return the result to the caller. + */ +static void +dns_end_query(struct dns_ctx *ctx, struct dns_query *q, + int status, void *result) { + dns_query_fn *cbck = q->dnsq_cbck; + void *cbdata = q->dnsq_cbdata; + ctx->dnsc_qstatus = status; + assert((status < 0 && result == 0) || (status >= 0 && result != 0)); + assert(cbck != 0); /*XXX callback may be NULL */ + assert(ctx->dnsc_nactive > 0); + --ctx->dnsc_nactive; + qlist_remove(q); + /* force the query to be unconnected */ + /*memset(q, 0, sizeof(*q));*/ +#ifndef NDEBUG + q->dnsq_ctx = NULL; +#endif + free(q); + cbck(ctx, result, cbdata); +} + +#define DNS_DBG(ctx, code, sa, slen, pkt, plen) \ + do { \ + if (ctx->dnsc_udbgfn) \ + ctx->dnsc_udbgfn(code, (sa), slen, pkt, plen, 0, 0); \ + } while(0) +#define DNS_DBGQ(ctx, q, code, sa, slen, pkt, plen) \ + do { \ + if (ctx->dnsc_udbgfn) \ + ctx->dnsc_udbgfn(code, (sa), slen, pkt, plen, q, q->dnsq_cbdata); \ + } while(0) + +static void dns_newid(struct dns_ctx *ctx, struct dns_query *q) { + /* this is how we choose an identifier for a new query (qID). + * For now, it's just sequential number, incremented for every query, and + * thus obviously trivial to guess. + * There are two choices: + * a) use sequential numbers. It is plain insecure. In DNS, there are two + * places where random numbers are (or can) be used to increase security: + * random qID and random source port number. Without this randomness + * (udns uses fixed port for all queries), or when the randomness is weak, + * it's trivial to spoof query replies. With randomness however, it + * becomes a bit more difficult task. Too bad we only have 16 bits for + * our security, as qID is only two bytes. It isn't a security per se, + * to rely on those 16 bits - an attacker can just flood us with fake + * replies with all possible qIDs (only 65536 of them), and in this case, + * even if we'll use true random qIDs, we'll be in trouble (not protected + * against spoofing). Yes, this is only possible on a high-speed network + * (probably on the LAN only, since usually a border router for a LAN + * protects internal machines from packets with spoofed local addresses + * from outside, and usually a nameserver resides on LAN), but it's + * still very well possible to send us fake replies. + * In other words: there's nothing a DNS (stub) resolver can do against + * spoofing attacks, unless DNSSEC is in use, which helps here alot. + * Too bad that DNSSEC isn't widespread, so relying on it isn't an + * option in almost all cases... + * b) use random qID, based on some random-number generation mechanism. + * This way, we increase our protection a bit (see above - it's very weak + * still), but we also increase risk of qID reuse and matching late replies + * that comes to queries we've sent before against new queries. There are + * some more corner cases around that, as well - for example, normally, + * udns tries to find the query for a given reply by qID, *and* by + * verifying that the query DN and other parameters are also the same + * (so if the new query is against another domain name, old reply will + * be ignored automatically). But certain types of replies which we now + * handle - for example, FORMERR reply from servers which refuses to + * process EDNS0-enabled packets - comes without all the query parameters + * but the qID - so we're forced to use qID only when determining which + * query the given reply corresponds to. This makes us even more + * vulnerable to spoofing attacks, because an attacker don't even need to + * know which queries we perform to spoof the replies - he only needs to + * flood us with fake FORMERR "replies". + * + * That all to say: using sequential (or any other trivially guessable) + * numbers for qIDs is insecure, but the whole thing is inherently insecure + * as well, and this "extra weakness" that comes from weak qID choosing + * algorithm adds almost nothing to the underlying problem. + * + * It CAN NOT be made secure. Period. That's it. + * Unless we choose to implement DNSSEC, which is a whole different story. + * Forcing TCP mode makes it better, but who uses TCP for DNS anyway? + * (and it's hardly possible because of huge impact on the recursive + * nameservers). + * + * Note that ALL stub resolvers (again, unless they implement and enforce + * DNSSEC) suffers from this same problem. + * + * So, instead of trying to be more secure (which actually is not - false + * sense of security is - I think - is worse than no security), I'm trying + * to be more robust here (by preventing qID reuse, which helps in normal + * conditions). And use sequential qID generation scheme. + */ + dns_put16(q->dnsq_id, ctx->dnsc_nextid++); + /* reset all parameters relevant for previous query lifetime */ + q->dnsq_try = 0; + q->dnsq_servi = 0; + /*XXX probably should keep dnsq_servnEDNS0 bits? + * See also comments in dns_ioevent() about FORMERR case */ + q->dnsq_servwait = q->dnsq_servskip = q->dnsq_servnEDNS0 = 0; +} + +/* Find next search suffix and fills in q->dnsq_dn. + * Return 0 if no more to try. */ +static int dns_next_srch(struct dns_ctx *ctx, struct dns_query *q) { + unsigned dnl; + + for(;;) { + if (q->dnsq_nxtsrch > ctx->dnsc_srchend) + return 0; + dnl = dns_dnlen(q->dnsq_nxtsrch); + if (dnl + q->dnsq_origdnl0 <= DNS_MAXDN && + (*q->dnsq_nxtsrch || !(q->dnsq_flags & DNS_ASIS_DONE))) + break; + q->dnsq_nxtsrch += dnl; + } + memcpy(q->dnsq_dn + q->dnsq_origdnl0, q->dnsq_nxtsrch, dnl); + if (!*q->dnsq_nxtsrch) + q->dnsq_flags |= DNS_ASIS_DONE; + q->dnsq_nxtsrch += dnl; + dns_newid(ctx, q); /* new ID for new qDN */ + return 1; +} + +/* find the server to try for current iteration. + * Note that current dnsq_servi may point to a server we should skip -- + * in that case advance to the next server. + * Return true if found, false if all tried. + */ +static int dns_find_serv(const struct dns_ctx *ctx, struct dns_query *q) { + while(q->dnsq_servi < ctx->dnsc_nserv) { + if (!(q->dnsq_servskip & (1 << q->dnsq_servi))) + return 1; + ++q->dnsq_servi; + } + return 0; +} + +/* format and send the query to a given server. + * In case of network problem (sendto() fails), return -1, + * else return 0. + */ +static int +dns_send_this(struct dns_ctx *ctx, struct dns_query *q, + unsigned servi, time_t now) { + unsigned qlen; + unsigned tries; + + { /* format the query buffer */ + dnsc_t *p = ctx->dnsc_pbuf; + memset(p, 0, DNS_HSIZE); + if (!(q->dnsq_flags & DNS_NORD)) p[DNS_H_F1] |= DNS_HF1_RD; + if (q->dnsq_flags & DNS_AAONLY) p[DNS_H_F1] |= DNS_HF1_AA; + p[DNS_H_QDCNT2] = 1; + memcpy(p + DNS_H_QID, q->dnsq_id, 2); + p = dns_payload(p); + /* copy query dn */ + p += dns_dntodn(q->dnsq_dn, p, DNS_MAXDN); + /* query type and class */ + memcpy(p, q->dnsq_typcls, 4); p += 4; + /* add EDNS0 size record */ + if (ctx->dnsc_udpbuf > DNS_MAXPACKET && + !(q->dnsq_servnEDNS0 & (1 << servi))) { + *p++ = 0; /* empty (root) DN */ + p = dns_put16(p, DNS_T_OPT); + p = dns_put16(p, ctx->dnsc_udpbuf); + /* EDNS0 RCODE & VERSION; rest of the TTL field; RDLEN */ + memset(p, 0, 2+2+2); p += 2+2+2; + ctx->dnsc_pbuf[DNS_H_ARCNT2] = 1; + } + qlen = p - ctx->dnsc_pbuf; + assert(qlen <= ctx->dnsc_udpbuf); + } + + /* send the query */ + tries = 10; + while (sendto(ctx->dnsc_udpsock, (void*)ctx->dnsc_pbuf, qlen, 0, + &ctx->dnsc_serv[servi].sa, ctx->dnsc_salen) < 0) { + /*XXX just ignore the sendto() error for now and try again. + * In the future, it may be possible to retrieve the error code + * and find which operation/query failed. + *XXX try the next server too? (if ENETUNREACH is returned immediately) + */ + if (--tries) continue; + /* if we can't send the query, fail it. */ + dns_end_query(ctx, q, DNS_E_TEMPFAIL, 0); + return -1; + } + DNS_DBGQ(ctx, q, 1, + &ctx->dnsc_serv[servi].sa, sizeof(union sockaddr_ns), + ctx->dnsc_pbuf, qlen); + q->dnsq_servwait |= 1 << servi; /* expect reply from this ns */ + + q->dnsq_deadline = now + + (dns_find_serv(ctx, q) ? 1 : ctx->dnsc_timeout << q->dnsq_try); + + /* move the query to the proper place, according to the new deadline */ + qlist_remove(q); + { /* insert from the tail */ + struct dns_query *p; + QLIST_FOR_EACH(&ctx->dnsc_qactive, p, prev) + if (p->dnsq_deadline <= q->dnsq_deadline) + break; + qlist_insert_after(q, p); + } + + return 0; +} + +/* send the query out using next available server + * and add it to the active list, or, if no servers available, + * end it. + */ +static void +dns_send(struct dns_ctx *ctx, struct dns_query *q, time_t now) { + + /* if we can't send the query, return TEMPFAIL even when searching: + * we can't be sure whenever the name we tried to search exists or not, + * so don't continue searching, or we may find the wrong name. */ + + if (!dns_find_serv(ctx, q)) { + /* no more servers in this iteration. Try the next cycle */ + q->dnsq_servi = 0; /* reset */ + q->dnsq_try++; /* next try */ + if (q->dnsq_try >= ctx->dnsc_ntries || + !dns_find_serv(ctx, q)) { + /* no more servers and tries, fail the query */ + /* return TEMPFAIL even when searching: no more tries for this + * searchlist, and no single definitive reply (handled in dns_ioevent() + * in NOERROR or NXDOMAIN cases) => all nameservers failed to process + * current search list element, so we don't know whenever the name exists. + */ + dns_end_query(ctx, q, DNS_E_TEMPFAIL, 0); + return; + } + } + + dns_send_this(ctx, q, q->dnsq_servi++, now); +} + +static void dns_dummy_cb(struct dns_ctx *ctx, void *result, void *data) { + if (result) free(result); + data = ctx = 0; /* used */ +} + +/* The (only, main, real) query submission routine. + * Allocate new query structure, initialize it, check validity of + * parameters, and add it to the head of the active list, without + * trying to send it (to be picked up on next event). + * Error return (without calling the callback routine) - + * no memory or wrong parameters. + *XXX The `no memory' case probably should go to the callback anyway... + */ +struct dns_query * +dns_submit_dn(struct dns_ctx *ctx, + dnscc_t *dn, int qcls, int qtyp, int flags, + dns_parse_fn *parse, dns_query_fn *cbck, void *data) { + struct dns_query *q; + SETCTXOPEN(ctx); + dns_assert_ctx(ctx); + + q = calloc(sizeof(*q), 1); + if (!q) { + ctx->dnsc_qstatus = DNS_E_NOMEM; + return NULL; + } + +#ifndef NDEBUG + q->dnsq_ctx = ctx; +#endif + q->dnsq_parse = parse; + q->dnsq_cbck = cbck ? cbck : dns_dummy_cb; + q->dnsq_cbdata = data; + + q->dnsq_origdnl0 = dns_dntodn(dn, q->dnsq_dn, sizeof(q->dnsq_dn)); + assert(q->dnsq_origdnl0 > 0); + --q->dnsq_origdnl0; /* w/o the trailing 0 */ + dns_put16(q->dnsq_typcls+0, qtyp); + dns_put16(q->dnsq_typcls+2, qcls); + q->dnsq_flags = (flags | ctx->dnsc_flags) & ~DNS_INTERNAL; + + if (flags & DNS_NOSRCH || + dns_dnlabels(q->dnsq_dn) > ctx->dnsc_ndots) { + q->dnsq_nxtsrch = flags & DNS_NOSRCH ? + ctx->dnsc_srchend /* end of the search list if no search requested */ : + ctx->dnsc_srchbuf /* beginning of the list, but try as-is first */; + q->dnsq_flags |= DNS_ASIS_DONE; + dns_newid(ctx, q); + } + else { + q->dnsq_nxtsrch = ctx->dnsc_srchbuf; + dns_next_srch(ctx, q); + } + + qlist_add_head(q, &ctx->dnsc_qactive); + ++ctx->dnsc_nactive; + dns_request_utm(ctx, 0); + + return q; +} + +struct dns_query * +dns_submit_p(struct dns_ctx *ctx, + const char *name, int qcls, int qtyp, int flags, + dns_parse_fn *parse, dns_query_fn *cbck, void *data) { + int isabs; + SETCTXOPEN(ctx); + if (dns_ptodn(name, 0, ctx->dnsc_pbuf, DNS_MAXDN, &isabs) <= 0) { + ctx->dnsc_qstatus = DNS_E_BADQUERY; + return NULL; + } + if (isabs) + flags |= DNS_NOSRCH; + return + dns_submit_dn(ctx, ctx->dnsc_pbuf, qcls, qtyp, flags, parse, cbck, data); +} + +/* process readable fd condition. + * To be usable in edge-triggered environment, the routine + * should consume all input so it should loop over. + * Note it isn't really necessary to loop here, because + * an application may perform the loop just fine by it's own, + * but in this case we should return some sensitive result, + * to indicate when to stop calling and error conditions. + * Note also we may encounter all sorts of recvfrom() + * errors which aren't fatal, and at the same time we may + * loop forever if an error IS fatal. + */ +void dns_ioevent(struct dns_ctx *ctx, time_t now) { + int r; + unsigned servi; + struct dns_query *q; + dnsc_t *pbuf; + dnscc_t *pend, *pcur; + void *result; + union sockaddr_ns sns; + socklen_t slen; + + SETCTX(ctx); + if (!CTXOPEN(ctx)) + return; + dns_assert_ctx(ctx); + pbuf = ctx->dnsc_pbuf; + + if (!now) now = time(NULL); + +again: /* receive the reply */ + + slen = sizeof(sns); + r = recvfrom(ctx->dnsc_udpsock, (void*)pbuf, ctx->dnsc_udpbuf, + MSG_DONTWAIT, &sns.sa, &slen); + if (r < 0) { + /*XXX just ignore recvfrom() errors for now. + * in the future it may be possible to determine which + * query failed and requeue it. + * Note there may be various error conditions, triggered + * by both local problems and remote problems. It isn't + * quite trivial to determine whenever an error is local + * or remote. On local errors, we should stop, while + * remote errors should be ignored (for now anyway). + */ +#ifdef WINDOWS + if (WSAGetLastError() == WSAEWOULDBLOCK) +#else + if (errno == EAGAIN) +#endif + { + dns_request_utm(ctx, now); + return; + } + goto again; + } + + pend = pbuf + r; + pcur = dns_payload(pbuf); + + /* check reply header */ + if (pcur > pend || dns_numqd(pbuf) > 1 || dns_opcode(pbuf) != 0) { + DNS_DBG(ctx, -1/*bad reply*/, &sns.sa, slen, pbuf, r); + goto again; + } + + /* find the matching query, by qID */ + for (q = QLIST_FIRST(&ctx->dnsc_qactive, next);; q = QLIST_NEXT(q, next)) { + if (QLIST_ISLAST(&ctx->dnsc_qactive, q)) { + /* no more requests: old reply? */ + DNS_DBG(ctx, -5/*no matching query*/, &sns.sa, slen, pbuf, r); + goto again; + } + if (pbuf[DNS_H_QID1] == q->dnsq_id[0] && + pbuf[DNS_H_QID2] == q->dnsq_id[1]) + break; + } + + /* if we have numqd, compare with our query qDN */ + if (dns_numqd(pbuf)) { + /* decode the qDN */ + dnsc_t dn[DNS_MAXDN]; + if (dns_getdn(pbuf, &pcur, pend, dn, sizeof(dn)) < 0 || + pcur + 4 > pend) { + DNS_DBG(ctx, -1/*bad reply*/, &sns.sa, slen, pbuf, r); + goto again; + } + if (!dns_dnequal(dn, q->dnsq_dn) || + memcmp(pcur, q->dnsq_typcls, 4) != 0) { + /* not this query */ + DNS_DBG(ctx, -5/*no matching query*/, &sns.sa, slen, pbuf, r); + goto again; + } + /* here, query match, and pcur points past qDN in query section in pbuf */ + } + /* if no numqd, we only allow FORMERR rcode */ + else if (dns_rcode(pbuf) != DNS_R_FORMERR) { + /* treat it as bad reply if !FORMERR */ + DNS_DBG(ctx, -1/*bad reply*/, &sns.sa, slen, pbuf, r); + goto again; + } + else { + /* else it's FORMERR, handled below */ + } + + /* find server */ +#ifdef HAVE_IPv6 + if (sns.sa.sa_family == AF_INET6 && slen >= sizeof(sns.sin6)) { + for(servi = 0; servi < ctx->dnsc_nserv; ++servi) + if (sin6_eq(ctx->dnsc_serv[servi].sin6, sns.sin6)) + break; + } + else +#endif + if (sns.sa.sa_family == AF_INET && slen >= sizeof(sns.sin)) { + for(servi = 0; servi < ctx->dnsc_nserv; ++servi) + if (sin_eq(ctx->dnsc_serv[servi].sin, sns.sin)) + break; + } + else + servi = ctx->dnsc_nserv; + + /* check if we expect reply from this server. + * Note we can receive reply from first try if we're already at next */ + if (!(q->dnsq_servwait & (1 << servi))) { /* if ever asked this NS */ + DNS_DBG(ctx, -2/*wrong server*/, &sns.sa, slen, pbuf, r); + goto again; + } + + /* we got (some) reply for our query */ + + DNS_DBGQ(ctx, q, 0, &sns.sa, slen, pbuf, r); + q->dnsq_servwait &= ~(1 << servi); /* don't expect reply from this serv */ + + /* process the RCODE */ + switch(dns_rcode(pbuf)) { + + case DNS_R_NOERROR: + if (dns_tc(pbuf)) { + /* possible truncation. We can't deal with it. */ + /*XXX for now, treat TC bit the same as SERVFAIL. + * It is possible to: + * a) try to decode the reply - may be ANSWER section is ok; + * b) check if server understands EDNS0, and if it is, and + * answer still don't fit, end query. + */ + break; + } + if (!dns_numan(pbuf)) { /* no data of requested type */ + if (dns_next_srch(ctx, q)) { + /* if we're searching, try next searchlist element, + * but remember NODATA reply. */ + q->dnsq_flags |= DNS_SEEN_NODATA; + dns_send(ctx, q, now); + } + else + /* else - nothing to search any more - finish the query. + * It will be NODATA since we've seen a NODATA reply. */ + dns_end_query(ctx, q, DNS_E_NODATA, 0); + } + /* we've got a positive reply here */ + else if (q->dnsq_parse) { + /* if we have parsing routine, call it and return whatever it returned */ + /* don't try to re-search if NODATA here. For example, + * if we asked for A but only received CNAME. Unless we'll + * someday do recursive queries. And that's problematic too, since + * we may be dealing with specific AA-only nameservers for a given + * domain, but CNAME points elsewhere... + */ + r = q->dnsq_parse(q->dnsq_dn, pbuf, pcur, pend, &result); + dns_end_query(ctx, q, r, r < 0 ? NULL : result); + } + /* else just malloc+copy the raw DNS reply */ + else if ((result = malloc(r)) == NULL) + dns_end_query(ctx, q, DNS_E_NOMEM, NULL); + else { + memcpy(result, pbuf, r); + dns_end_query(ctx, q, r, result); + } + goto again; + + case DNS_R_NXDOMAIN: /* Non-existing domain. */ + if (dns_next_srch(ctx, q)) + /* more search entries exists, try them. */ + dns_send(ctx, q, now); + else + /* nothing to search anymore. End the query, returning either NODATA + * if we've seen it before, or NXDOMAIN if not. */ + dns_end_query(ctx, q, + q->dnsq_flags & DNS_SEEN_NODATA ? DNS_E_NODATA : DNS_E_NXDOMAIN, 0); + goto again; + + case DNS_R_FORMERR: + case DNS_R_NOTIMPL: + /* for FORMERR and NOTIMPL rcodes, if we tried EDNS0-enabled query, + * try w/o EDNS0. */ + if (ctx->dnsc_udpbuf > DNS_MAXPACKET && + !(q->dnsq_servnEDNS0 & (1 << servi))) { + /* we always trying EDNS0 first if enabled, and retry a given query + * if not available. Maybe it's better to remember inavailability of + * EDNS0 in ctx as a per-NS flag, and never try again for this NS. + * For long-running applications.. maybe they will change the nameserver + * while we're running? :) Also, since FORMERR is the only rcode we + * allow to be header-only, and in this case the only check we do to + * find a query it belongs to is qID (not qDN+qCLS+qTYP), it's much + * easier to spoof and to force us to perform non-EDNS0 queries only... + */ + q->dnsq_servnEDNS0 |= 1 << servi; + dns_send_this(ctx, q, servi, now); + goto again; + } + /* else we handle it the same as SERVFAIL etc */ + + case DNS_R_SERVFAIL: + case DNS_R_REFUSED: + /* for these rcodes, advance this request + * to the next server and reschedule */ + default: /* unknown rcode? hmmm... */ + break; + } + + /* here, we received unexpected reply */ + q->dnsq_servskip |= (1 << servi); /* don't retry this server */ + + /* we don't expect replies from this server anymore. + * But there may be other servers. Some may be still processing our + * query, and some may be left to try. + * We just ignore this reply and wait a bit more if some NSes haven't + * replied yet (dnsq_servwait != 0), and let the situation to be handled + * on next event processing. Timeout for this query is set correctly, + * if not taking into account the one-second difference - we can try + * next server in the same iteration sooner. + */ + + /* try next server */ + if (!q->dnsq_servwait) { + /* next retry: maybe some other servers will reply next time. + * dns_send() will end the query for us if no more servers to try. + * Note we can't continue with the next searchlist element here: + * we don't know if the current qdn exists or not, there's no definitive + * answer yet (which is seen in cases above). + *XXX standard resolver also tries as-is query in case all nameservers + * failed to process our query and if not tried before. We don't do it. + */ + dns_send(ctx, q, now); + } + else { + /* else don't do anything - not all servers replied yet */ + } + goto again; + +} + +/* handle all timeouts */ +int dns_timeouts(struct dns_ctx *ctx, int maxwait, time_t now) { + /* this is a hot routine */ + struct dns_query *q; + + SETCTX(ctx); + dns_assert_ctx(ctx); + + /* Pick up first entry from query list. + * If its deadline has passed, (re)send it + * (dns_send() will move it next in the list). + * If not, this is the query which determines the closest deadline. + */ + + q = qlist_first(&ctx->dnsc_qactive); + if (!q) + return maxwait; + if (!now) + now = time(NULL); + do { + if (q->dnsq_deadline > now) { /* first non-expired query */ + int w = (int)(q->dnsq_deadline - now); + if (maxwait < 0 || maxwait > w) + maxwait = w; + break; + } + else { + /* process expired deadline */ + dns_send(ctx, q, now); + } + } while((q = qlist_first(&ctx->dnsc_qactive)) != NULL); + + dns_request_utm(ctx, now); /* update timer with new deadline */ + return maxwait; +} + +struct dns_resolve_data { + int dnsrd_done; + void *dnsrd_result; +}; + +static void dns_resolve_cb(struct dns_ctx *ctx, void *result, void *data) { + struct dns_resolve_data *d = data; + d->dnsrd_result = result; + d->dnsrd_done = 1; + ctx = ctx; +} + +void *dns_resolve(struct dns_ctx *ctx, struct dns_query *q) { + time_t now; + struct dns_resolve_data d; + int n; + SETCTXOPEN(ctx); + + if (!q) + return NULL; + + assert(ctx == q->dnsq_ctx); + dns_assert_ctx(ctx); + /* do not allow re-resolving syncronous queries */ + assert(q->dnsq_cbck != dns_resolve_cb && "can't resolve syncronous query"); + if (q->dnsq_cbck == dns_resolve_cb) { + ctx->dnsc_qstatus = DNS_E_BADQUERY; + return NULL; + } + q->dnsq_cbck = dns_resolve_cb; + q->dnsq_cbdata = &d; + d.dnsrd_done = 0; + + now = time(NULL); + while(!d.dnsrd_done && (n = dns_timeouts(ctx, -1, now)) >= 0) { +#ifdef HAVE_POLL + struct pollfd pfd; + pfd.fd = ctx->dnsc_udpsock; + pfd.events = POLLIN; + n = poll(&pfd, 1, n * 1000); +#else + fd_set rfd; + struct timeval tv; + FD_ZERO(&rfd); + FD_SET(ctx->dnsc_udpsock, &rfd); + tv.tv_sec = n; tv.tv_usec = 0; + n = select(ctx->dnsc_udpsock + 1, &rfd, NULL, NULL, &tv); +#endif + now = time(NULL); + if (n > 0) + dns_ioevent(ctx, now); + } + + return d.dnsrd_result; +} + +void *dns_resolve_dn(struct dns_ctx *ctx, + dnscc_t *dn, int qcls, int qtyp, int flags, + dns_parse_fn *parse) { + return + dns_resolve(ctx, + dns_submit_dn(ctx, dn, qcls, qtyp, flags, parse, NULL, NULL)); +} + +void *dns_resolve_p(struct dns_ctx *ctx, + const char *name, int qcls, int qtyp, int flags, + dns_parse_fn *parse) { + return + dns_resolve(ctx, + dns_submit_p(ctx, name, qcls, qtyp, flags, parse, NULL, NULL)); +} + +int dns_cancel(struct dns_ctx *ctx, struct dns_query *q) { + SETCTX(ctx); + dns_assert_ctx(ctx); + assert(q->dnsq_ctx == ctx); + /* do not allow cancelling syncronous queries */ + assert(q->dnsq_cbck != dns_resolve_cb && "can't cancel syncronous query"); + if (q->dnsq_cbck == dns_resolve_cb) + return (ctx->dnsc_qstatus = DNS_E_BADQUERY); + qlist_remove(q); + --ctx->dnsc_nactive; + dns_request_utm(ctx, 0); + return 0; +} + diff --git a/ape-server/deps/udns-0.0.9/udns_rr_a.c b/ape-server/deps/udns-0.0.9/udns_rr_a.c new file mode 100755 index 0000000..ca99dfe --- /dev/null +++ b/ape-server/deps/udns-0.0.9/udns_rr_a.c @@ -0,0 +1,123 @@ +/* $Id: udns_rr_a.c,v 1.16 2007/01/09 04:44:51 mjt Exp $ + parse/query A/AAAA IN records + + Copyright (C) 2005 Michael Tokarev + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + */ + +#include +#include +#include +#ifndef WINDOWS +# include +# include +#endif +#include "udns.h" + +/* here, we use common routine to parse both IPv4 and IPv6 addresses. + */ + +/* this structure should match dns_rr_a[46] */ +struct dns_rr_a { + dns_rr_common(dnsa); + unsigned char *dnsa_addr; +}; + +static int +dns_parse_a(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end, + void **result, unsigned dsize) { + struct dns_rr_a *ret; + struct dns_parse p; + struct dns_rr rr; + int r; + + /* first, validate and count number of addresses */ + dns_initparse(&p, qdn, pkt, cur, end); + while((r = dns_nextrr(&p, &rr)) > 0) + if (rr.dnsrr_dsz != dsize) + return DNS_E_PROTOCOL; + if (r < 0) + return DNS_E_PROTOCOL; + else if (!p.dnsp_nrr) + return DNS_E_NODATA; + + ret = malloc(sizeof(*ret) + dsize * p.dnsp_nrr + dns_stdrr_size(&p)); + if (!ret) + return DNS_E_NOMEM; + + ret->dnsa_nrr = p.dnsp_nrr; + ret->dnsa_addr = (unsigned char*)(ret+1); + + /* copy the RRs */ + for (dns_rewind(&p, qdn), r = 0; dns_nextrr(&p, &rr); ++r) + memcpy(ret->dnsa_addr + dsize * r, rr.dnsrr_dptr, dsize); + + dns_stdrr_finish((struct dns_rr_null *)ret, + (char *)(ret->dnsa_addr + dsize * p.dnsp_nrr), &p); + *result = ret; + return 0; +} + +int +dns_parse_a4(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end, + void **result) { +#ifdef AF_INET + assert(sizeof(struct in_addr) == 4); +#endif + assert(dns_get16(cur+2) == DNS_C_IN && dns_get16(cur+0) == DNS_T_A); + return dns_parse_a(qdn, pkt, cur, end, result, 4); +} + +struct dns_query * +dns_submit_a4(struct dns_ctx *ctx, const char *name, int flags, + dns_query_a4_fn *cbck, void *data) { + return + dns_submit_p(ctx, name, DNS_C_IN, DNS_T_A, flags, + dns_parse_a4, (dns_query_fn*)cbck, data); +} + +struct dns_rr_a4 * +dns_resolve_a4(struct dns_ctx *ctx, const char *name, int flags) { + return (struct dns_rr_a4 *) + dns_resolve_p(ctx, name, DNS_C_IN, DNS_T_A, flags, dns_parse_a4); +} + +int +dns_parse_a6(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end, + void **result) { +#ifdef AF_INET6 + assert(sizeof(struct in6_addr) == 16); +#endif + assert(dns_get16(cur+2) == DNS_C_IN && dns_get16(cur+0) == DNS_T_AAAA); + return dns_parse_a(qdn, pkt, cur, end, result, 16); +} + +struct dns_query * +dns_submit_a6(struct dns_ctx *ctx, const char *name, int flags, + dns_query_a6_fn *cbck, void *data) { + return + dns_submit_p(ctx, name, DNS_C_IN, DNS_T_AAAA, flags, + dns_parse_a6, (dns_query_fn*)cbck, data); +} + +struct dns_rr_a6 * +dns_resolve_a6(struct dns_ctx *ctx, const char *name, int flags) { + return (struct dns_rr_a6 *) + dns_resolve_p(ctx, name, DNS_C_IN, DNS_T_AAAA, flags, dns_parse_a6); +} diff --git a/ape-server/deps/udns-0.0.9/udns_rr_mx.c b/ape-server/deps/udns-0.0.9/udns_rr_mx.c new file mode 100755 index 0000000..cf83483 --- /dev/null +++ b/ape-server/deps/udns-0.0.9/udns_rr_mx.c @@ -0,0 +1,91 @@ +/* $Id: udns_rr_mx.c,v 1.13 2005/04/20 06:44:34 mjt Exp $ + parse/query MX IN records + + Copyright (C) 2005 Michael Tokarev + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + */ + +#include +#include +#include +#include "udns.h" + +int +dns_parse_mx(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end, + void **result) { + struct dns_rr_mx *ret; + struct dns_parse p; + struct dns_rr rr; + int r, l; + char *sp; + dnsc_t mx[DNS_MAXDN]; + + assert(dns_get16(cur+2) == DNS_C_IN && dns_get16(cur+0) == DNS_T_MX); + + /* first, validate the answer and count size of the result */ + l = 0; + dns_initparse(&p, qdn, pkt, cur, end); + while((r = dns_nextrr(&p, &rr)) > 0) { + cur = rr.dnsrr_dptr + 2; + r = dns_getdn(pkt, &cur, end, mx, sizeof(mx)); + if (r <= 0 || cur != rr.dnsrr_dend) + return DNS_E_PROTOCOL; + l += dns_dntop_size(mx); + } + if (r < 0) + return DNS_E_PROTOCOL; + if (!p.dnsp_nrr) + return DNS_E_NODATA; + + /* next, allocate and set up result */ + l += dns_stdrr_size(&p); + ret = malloc(sizeof(*ret) + sizeof(struct dns_mx) * p.dnsp_nrr + l); + if (!ret) + return DNS_E_NOMEM; + ret->dnsmx_nrr = p.dnsp_nrr; + ret->dnsmx_mx = (struct dns_mx *)(ret+1); + + /* and 3rd, fill in result, finally */ + sp = (char*)(ret->dnsmx_mx + p.dnsp_nrr); + for (dns_rewind(&p, qdn), r = 0; dns_nextrr(&p, &rr); ++r) { + ret->dnsmx_mx[r].name = sp; + cur = rr.dnsrr_dptr; + ret->dnsmx_mx[r].priority = dns_get16(cur); + cur += 2; + dns_getdn(pkt, &cur, end, mx, sizeof(mx)); + sp += dns_dntop(mx, sp, DNS_MAXNAME); + } + dns_stdrr_finish((struct dns_rr_null *)ret, sp, &p); + *result = ret; + return 0; +} + +struct dns_query * +dns_submit_mx(struct dns_ctx *ctx, const char *name, int flags, + dns_query_mx_fn *cbck, void *data) { + return + dns_submit_p(ctx, name, DNS_C_IN, DNS_T_MX, flags, + dns_parse_mx, (dns_query_fn *)cbck, data); +} + +struct dns_rr_mx * +dns_resolve_mx(struct dns_ctx *ctx, const char *name, int flags) { + return (struct dns_rr_mx *) + dns_resolve_p(ctx, name, DNS_C_IN, DNS_T_MX, flags, dns_parse_mx); +} diff --git a/ape-server/deps/udns-0.0.9/udns_rr_naptr.c b/ape-server/deps/udns-0.0.9/udns_rr_naptr.c new file mode 100755 index 0000000..ec4f514 --- /dev/null +++ b/ape-server/deps/udns-0.0.9/udns_rr_naptr.c @@ -0,0 +1,128 @@ +/* $Id: udns_rr_naptr.c,v 1.1 2006/11/28 22:58:04 mjt Exp $ + parse/query NAPTR IN records + + Copyright (C) 2005 Michael Tokarev + Copyright (C) 2006 Mikael Magnusson + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + */ + +#include +#include +#include +#include "udns.h" + +/* Get a single string for NAPTR record, pretty much like a DN label. + * String length is in first byte in *cur, so it can't be >255. + */ +static int dns_getstr(dnscc_t **cur, dnscc_t *ep, char *buf) +{ + unsigned l; + dnscc_t *cp = *cur; + + l = *cp++; + if (cp + l > ep) + return DNS_E_PROTOCOL; + if (buf) { + memcpy(buf, cp, l); + buf[l] = '\0'; + } + cp += l; + + *cur = cp; + return l + 1; +} + +int +dns_parse_naptr(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end, + void **result) { + struct dns_rr_naptr *ret; + struct dns_parse p; + struct dns_rr rr; + int r, l; + char *sp; + dnsc_t dn[DNS_MAXDN]; + + assert(dns_get16(cur+2) == DNS_C_IN && dns_get16(cur+0) == DNS_T_NAPTR); + + /* first, validate the answer and count size of the result */ + l = 0; + dns_initparse(&p, qdn, pkt, cur, end); + while((r = dns_nextrr(&p, &rr)) > 0) { + int i; + dnscc_t *ep = rr.dnsrr_dend; + + /* first 4 bytes: order & preference */ + cur = rr.dnsrr_dptr + 4; + + /* flags, services and regexp */ + for (i = 0; i < 3; i++) { + r = dns_getstr(&cur, ep, NULL); + if (r < 0) + return r; + l += r; + } + /* replacement */ + r = dns_getdn(pkt, &cur, end, dn, sizeof(dn)); + if (r <= 0 || cur != rr.dnsrr_dend) + return DNS_E_PROTOCOL; + l += dns_dntop_size(dn); + } + if (r < 0) + return DNS_E_PROTOCOL; + if (!p.dnsp_nrr) + return DNS_E_NODATA; + + /* next, allocate and set up result */ + l += dns_stdrr_size(&p); + ret = malloc(sizeof(*ret) + sizeof(struct dns_naptr) * p.dnsp_nrr + l); + if (!ret) + return DNS_E_NOMEM; + ret->dnsnaptr_nrr = p.dnsp_nrr; + ret->dnsnaptr_naptr = (struct dns_naptr *)(ret+1); + + /* and 3rd, fill in result, finally */ + sp = (char*)(&ret->dnsnaptr_naptr[p.dnsp_nrr]); + for (dns_rewind(&p, qdn), r = 0; dns_nextrr(&p, &rr); ++r) { + cur = rr.dnsrr_dptr; + ret->dnsnaptr_naptr[r].order = dns_get16(cur); cur += 2; + ret->dnsnaptr_naptr[r].preference = dns_get16(cur); cur += 2; + sp += dns_getstr(&cur, end, (ret->dnsnaptr_naptr[r].flags = sp)); + sp += dns_getstr(&cur, end, (ret->dnsnaptr_naptr[r].service = sp)); + sp += dns_getstr(&cur, end, (ret->dnsnaptr_naptr[r].regexp = sp)); + dns_getdn(pkt, &cur, end, dn, sizeof(dn)); + sp += dns_dntop(dn, (ret->dnsnaptr_naptr[r].replacement = sp), DNS_MAXNAME); + } + dns_stdrr_finish((struct dns_rr_null *)ret, sp, &p); + *result = ret; + return 0; +} + +struct dns_query * +dns_submit_naptr(struct dns_ctx *ctx, const char *name, int flags, + dns_query_naptr_fn *cbck, void *data) { + return + dns_submit_p(ctx, name, DNS_C_IN, DNS_T_NAPTR, flags, + dns_parse_naptr, (dns_query_fn *)cbck, data); +} + +struct dns_rr_naptr * +dns_resolve_naptr(struct dns_ctx *ctx, const char *name, int flags) { + return (struct dns_rr_naptr *) + dns_resolve_p(ctx, name, DNS_C_IN, DNS_T_NAPTR, flags, dns_parse_naptr); +} diff --git a/ape-server/deps/udns-0.0.9/udns_rr_ptr.c b/ape-server/deps/udns-0.0.9/udns_rr_ptr.c new file mode 100755 index 0000000..410af65 --- /dev/null +++ b/ape-server/deps/udns-0.0.9/udns_rr_ptr.c @@ -0,0 +1,109 @@ +/* $Id: udns_rr_ptr.c,v 1.15 2005/09/12 11:21:06 mjt Exp $ + parse/query PTR records + + Copyright (C) 2005 Michael Tokarev + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + */ + +#include +#include +#include "udns.h" + +int +dns_parse_ptr(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end, + void **result) { + struct dns_rr_ptr *ret; + struct dns_parse p; + struct dns_rr rr; + int r, l, c; + char *sp; + dnsc_t ptr[DNS_MAXDN]; + + assert(dns_get16(cur+2) == DNS_C_IN && dns_get16(cur+0) == DNS_T_PTR); + + /* first, validate the answer and count size of the result */ + l = c = 0; + dns_initparse(&p, qdn, pkt, cur, end); + while((r = dns_nextrr(&p, &rr)) > 0) { + cur = rr.dnsrr_dptr; + r = dns_getdn(pkt, &cur, end, ptr, sizeof(ptr)); + if (r <= 0 || cur != rr.dnsrr_dend) + return DNS_E_PROTOCOL; + l += dns_dntop_size(ptr); + ++c; + } + if (r < 0) + return DNS_E_PROTOCOL; + if (!c) + return DNS_E_NODATA; + + /* next, allocate and set up result */ + ret = malloc(sizeof(*ret) + sizeof(char **) * c + l + dns_stdrr_size(&p)); + if (!ret) + return DNS_E_NOMEM; + ret->dnsptr_nrr = c; + ret->dnsptr_ptr = (char **)(ret+1); + + /* and 3rd, fill in result, finally */ + sp = (char*)(ret->dnsptr_ptr + c); + c = 0; + dns_rewind(&p, qdn); + while((r = dns_nextrr(&p, &rr)) > 0) { + ret->dnsptr_ptr[c] = sp; + cur = rr.dnsrr_dptr; + dns_getdn(pkt, &cur, end, ptr, sizeof(ptr)); + sp += dns_dntop(ptr, sp, DNS_MAXNAME); + ++c; + } + dns_stdrr_finish((struct dns_rr_null *)ret, sp, &p); + *result = ret; + return 0; +} + +struct dns_query * +dns_submit_a4ptr(struct dns_ctx *ctx, const struct in_addr *addr, + dns_query_ptr_fn *cbck, void *data) { + dnsc_t dn[DNS_A4RSIZE]; + dns_a4todn(addr, 0, dn, sizeof(dn)); + return + dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_PTR, DNS_NOSRCH, + dns_parse_ptr, (dns_query_fn *)cbck, data); +} + +struct dns_rr_ptr * +dns_resolve_a4ptr(struct dns_ctx *ctx, const struct in_addr *addr) { + return (struct dns_rr_ptr *) + dns_resolve(ctx, dns_submit_a4ptr(ctx, addr, NULL, NULL)); +} + +struct dns_query * +dns_submit_a6ptr(struct dns_ctx *ctx, const struct in6_addr *addr, + dns_query_ptr_fn *cbck, void *data) { + dnsc_t dn[DNS_A6RSIZE]; + dns_a6todn(addr, 0, dn, sizeof(dn)); + return + dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_PTR, DNS_NOSRCH, + dns_parse_ptr, (dns_query_fn *)cbck, data); +} + +struct dns_rr_ptr * +dns_resolve_a6ptr(struct dns_ctx *ctx, const struct in6_addr *addr) { + return (struct dns_rr_ptr *) + dns_resolve(ctx, dns_submit_a6ptr(ctx, addr, NULL, NULL)); +} diff --git a/ape-server/deps/udns-0.0.9/udns_rr_srv.c b/ape-server/deps/udns-0.0.9/udns_rr_srv.c new file mode 100755 index 0000000..56bbbf7 --- /dev/null +++ b/ape-server/deps/udns-0.0.9/udns_rr_srv.c @@ -0,0 +1,154 @@ +/* $Id: udns_rr_srv.c,v 1.2 2005/09/12 12:26:22 mjt Exp $ + parse/query SRV IN (rfc2782) records + + Copyright (C) 2005 Michael Tokarev + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + Copyright 2005 Thadeu Lima de Souza Cascardo + + 2005-09-11: + Changed MX parser file into a SRV parser file + + */ + +#include +#include +#include +#include "udns.h" + +int +dns_parse_srv(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end, + void **result) { + struct dns_rr_srv *ret; + struct dns_parse p; + struct dns_rr rr; + int r, l; + char *sp; + dnsc_t srv[DNS_MAXDN]; + + assert(dns_get16(cur+2) == DNS_C_IN && dns_get16(cur+0) == DNS_T_SRV); + + /* first, validate the answer and count size of the result */ + l = 0; + dns_initparse(&p, qdn, pkt, cur, end); + while((r = dns_nextrr(&p, &rr)) > 0) { + cur = rr.dnsrr_dptr + 6; + r = dns_getdn(pkt, &cur, end, srv, sizeof(srv)); + if (r <= 0 || cur != rr.dnsrr_dend) + return DNS_E_PROTOCOL; + l += dns_dntop_size(srv); + } + if (r < 0) + return DNS_E_PROTOCOL; + if (!p.dnsp_nrr) + return DNS_E_NODATA; + + /* next, allocate and set up result */ + l += dns_stdrr_size(&p); + ret = malloc(sizeof(*ret) + sizeof(struct dns_srv) * p.dnsp_nrr + l); + if (!ret) + return DNS_E_NOMEM; + ret->dnssrv_nrr = p.dnsp_nrr; + ret->dnssrv_srv = (struct dns_srv *)(ret+1); + + /* and 3rd, fill in result, finally */ + sp = (char*)(ret->dnssrv_srv + p.dnsp_nrr); + for (dns_rewind(&p, qdn), r = 0; dns_nextrr(&p, &rr); ++r) { + ret->dnssrv_srv[r].name = sp; + cur = rr.dnsrr_dptr; + ret->dnssrv_srv[r].priority = dns_get16(cur); + ret->dnssrv_srv[r].weight = dns_get16(cur+2); + ret->dnssrv_srv[r].port = dns_get16(cur+4); + cur += 6; + dns_getdn(pkt, &cur, end, srv, sizeof(srv)); + sp += dns_dntop(srv, sp, DNS_MAXNAME); + } + dns_stdrr_finish((struct dns_rr_null *)ret, sp, &p); + *result = ret; + return 0; +} + +/* Add a single service or proto name prepending an undescore (_), + * according to rfc2782 rules. + * Return 0 or the label length. + * Routing assumes dn holds enouth space for a single DN label. */ +static unsigned add_sname(dnsc_t *dn, const char *sn) { + unsigned l; + l = dns_ptodn(sn, 0, dn + 1, DNS_MAXLABEL-1, NULL); + if (l <= 1 || l - 2 != dn[1]) + /* Should we really check if sn is exactly one label? Do we care? */ + return 0; + dn[0] = l - 1; + dn[1] = '_'; + return l; +} + +/* Construct a domain name for SRV query from the given name, service and + * protocol (service may be NULL in which case protocol isn't used). + * Return negative value on error (malformed query), + * or addition query flag(s) to use. + */ +static int +build_srv_dn(dnsc_t *dn, const char *name, const char *srv, const char *proto) +{ + unsigned p = 0, l; + int isabs; + if (srv) { + l = add_sname(dn + p, srv); + if (!l) + return -1; + p += l; + l = add_sname(dn + p, proto); + if (!l) + return -1; + p += l; + } + l = dns_ptodn(name, 0, dn + p, DNS_MAXDN - p, &isabs); + if (!l) + return -1; + return isabs ? DNS_NOSRCH : 0; +} + +struct dns_query * +dns_submit_srv(struct dns_ctx *ctx, + const char *name, const char *srv, const char *proto, + int flags, dns_query_srv_fn *cbck, void *data) { + dnsc_t dn[DNS_MAXDN]; + int r = build_srv_dn(dn, name, srv, proto); + if (r < 0) { + dns_setstatus (ctx, DNS_E_BADQUERY); + return NULL; + } + return + dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_SRV, flags | r, + dns_parse_srv, (dns_query_fn *)cbck, data); +} + +struct dns_rr_srv * +dns_resolve_srv(struct dns_ctx *ctx, + const char *name, const char *srv, const char *proto, int flags) +{ + dnsc_t dn[DNS_MAXDN]; + int r = build_srv_dn(dn, name, srv, proto); + if (r < 0) { + dns_setstatus(ctx, DNS_E_BADQUERY); + return NULL; + } + return (struct dns_rr_srv *) + dns_resolve_dn(ctx, dn, DNS_C_IN, DNS_T_SRV, flags | r, dns_parse_srv); +} diff --git a/ape-server/deps/udns-0.0.9/udns_rr_txt.c b/ape-server/deps/udns-0.0.9/udns_rr_txt.c new file mode 100755 index 0000000..ecc71f0 --- /dev/null +++ b/ape-server/deps/udns-0.0.9/udns_rr_txt.c @@ -0,0 +1,98 @@ +/* $Id: udns_rr_txt.c,v 1.15 2006/11/28 22:45:20 mjt Exp $ + parse/query TXT records + + Copyright (C) 2005 Michael Tokarev + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + */ + +#include +#include +#include +#include "udns.h" + +int +dns_parse_txt(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end, + void **result) { + struct dns_rr_txt *ret; + struct dns_parse p; + struct dns_rr rr; + int r, l; + dnsc_t *sp; + dnscc_t *cp, *ep; + + assert(dns_get16(cur+0) == DNS_T_TXT); + + /* first, validate the answer and count size of the result */ + l = 0; + dns_initparse(&p, qdn, pkt, cur, end); + while((r = dns_nextrr(&p, &rr)) > 0) { + cp = rr.dnsrr_dptr; ep = rr.dnsrr_dend; + while(cp < ep) { + r = *cp++; + if (cp + r > ep) + return DNS_E_PROTOCOL; + l += r; + cp += r; + } + } + if (r < 0) + return DNS_E_PROTOCOL; + if (!p.dnsp_nrr) + return DNS_E_NODATA; + + /* next, allocate and set up result */ + l += (sizeof(struct dns_txt) + 1) * p.dnsp_nrr + dns_stdrr_size(&p); + ret = malloc(sizeof(*ret) + l); + if (!ret) + return DNS_E_NOMEM; + ret->dnstxt_nrr = p.dnsp_nrr; + ret->dnstxt_txt = (struct dns_txt *)(ret+1); + + /* and 3rd, fill in result, finally */ + sp = (dnsc_t*)(ret->dnstxt_txt + p.dnsp_nrr); + for(dns_rewind(&p, qdn), r = 0; dns_nextrr(&p, &rr) > 0; ++r) { + ret->dnstxt_txt[r].txt = sp; + cp = rr.dnsrr_dptr; ep = rr.dnsrr_dend; + while(cp < ep) { + l = *cp++; + memcpy(sp, cp, l); + sp += l; + cp += l; + } + ret->dnstxt_txt[r].len = sp - ret->dnstxt_txt[r].txt; + *sp++ = '\0'; + } + dns_stdrr_finish((struct dns_rr_null *)ret, (char*)sp, &p); + *result = ret; + return 0; +} + +struct dns_query * +dns_submit_txt(struct dns_ctx *ctx, const char *name, int qcls, int flags, + dns_query_txt_fn *cbck, void *data) { + return + dns_submit_p(ctx, name, qcls, DNS_T_TXT, flags, + dns_parse_txt, (dns_query_fn *)cbck, data); +} + +struct dns_rr_txt * +dns_resolve_txt(struct dns_ctx *ctx, const char *name, int qcls, int flags) { + return (struct dns_rr_txt *) + dns_resolve_p(ctx, name, qcls, DNS_T_TXT, flags, dns_parse_txt); +} diff --git a/ape-server/doc/RFC b/ape-server/doc/RFC new file mode 100755 index 0000000..a11f865 --- /dev/null +++ b/ape-server/doc/RFC @@ -0,0 +1 @@ +See http://www.ape-project.org/ diff --git a/ape-server/modules/Makefile b/ape-server/modules/Makefile new file mode 100755 index 0000000..d3643e4 --- /dev/null +++ b/ape-server/modules/Makefile @@ -0,0 +1,29 @@ +include ./plateform.mk +include ./mysql.mk + +CC=gcc -D_GNU_SOURCE + +ifeq ($(HAS_MYSQL), yes) + MYSQL_FLAGS = -L./deps/mysac/ -I./deps/mysac/ -lmysac -lmysqlclient_r +endif + +ifdef DARWIN_BUILD + +all: modules + +modules: libape-spidermonkey.c + + $(CC) -Wall -g -bundle -undefined suppress -flat_namespace -o lib/libmod_spidermonkey.so libape-spidermonkey.c -I../deps/js/src/dist/include/ -L../deps/js/src/ -ljs_static -lstdc++ $(MYSQL_FLAGS) + +endif +ifdef LINUX_BUILD + +CFLAGS = -Wall -O2 -shared -fPIC -rdynamic + +all: modules + +modules: libape-spidermonkey.c + + $(CC) $(CFLAGS) -Wl,-soname,libmod_spidermonkey.so -o lib/libmod_spidermonkey.so libape-spidermonkey.c -I../deps/js/src/dist/include/ -L../deps/js/src/ -ljs_static -lstdc++ $(MYSQL_FLAGS) + +endif diff --git a/ape-server/modules/conf/inlinepush.conf b/ape-server/modules/conf/inlinepush.conf new file mode 100755 index 0000000..513ed1b --- /dev/null +++ b/ape-server/modules/conf/inlinepush.conf @@ -0,0 +1 @@ +password = testpasswd diff --git a/ape-server/modules/conf/javascript.conf b/ape-server/modules/conf/javascript.conf new file mode 100755 index 0000000..53c0b39 --- /dev/null +++ b/ape-server/modules/conf/javascript.conf @@ -0,0 +1 @@ +scripts_path = ../scripts/ diff --git a/ape-server/modules/conf/proxy.conf b/ape-server/modules/conf/proxy.conf new file mode 100755 index 0000000..3324c97 --- /dev/null +++ b/ape-server/modules/conf/proxy.conf @@ -0,0 +1,2 @@ +#allowed_hosts = irc.freenode.org:6667, irc.freenode.org:6668, google.com +allowed_hosts = any \ No newline at end of file diff --git a/ape-server/modules/deps/mysac/COPYING b/ape-server/modules/deps/mysac/COPYING new file mode 100755 index 0000000..53073b0 --- /dev/null +++ b/ape-server/modules/deps/mysac/COPYING @@ -0,0 +1,143 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + diff --git a/ape-server/modules/deps/mysac/Makefile b/ape-server/modules/deps/mysac/Makefile new file mode 100755 index 0000000..48f9835 --- /dev/null +++ b/ape-server/modules/deps/mysac/Makefile @@ -0,0 +1,76 @@ +# +# Copyright (c) 2009 Thierry FOURNIER +# +# This file is part of MySAC. +# +# MySAC 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 3 of the License +# +# MySAC 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 MySAC. If not, see . +# + +# Mysql lib directory +# exemple: /mysql-5.1.41/libmysql_r/.libs +MYSQL_LIB := /usr/lib + +# Mysql include directory +# exemple: /mysql-5.1.41/include +MYSQL_INC := /usr/include/mysql + +# get build version from the git tree in the form "lasttag-changes", +# and use "VERSION" file if unknown. +BUILDVER := $(shell ./mysac_ver) + +CFLAGS = -DBUILDVER=$(BUILDVER) -I$(MYSQL_INC) -O0 -g -Wall -fPIC +LDFLAGS = -g -L$(MYSQL_LIB) -lmysqlclient_r + +OBJS = mysac.o mysac_net.o mysac_decode_field.o mysac_decode_row.o mysac_encode_values.o mysac_errors.o + +build: make.deps + $(MAKE) lib + +pack: + rm -rf /tmp/mysac-$(BUILDVER) >/dev/null 2>&1; \ + git clone . /tmp/mysac-$(BUILDVER) && \ + echo "$(BUILDVER)" > VERSION; \ + cp VERSION /tmp/mysac-$(BUILDVER); \ + tar --exclude .git -C /tmp/ -vzcf mysac-$(BUILDVER).tar.gz mysac-$(BUILDVER) && \ + rm -rf /tmp/mysac-$(BUILDVER) >/dev/null 2>&1; \ + +lib: libmysac.a +#libmysac.so + +libmysac.so: libmysac.a + $(LD) -o libmysac.so -shared -soname libmysac.so.0.0 libmysac.a + +libmysac.a: $(OBJS) + $(AR) -rcv libmysac.a $(OBJS) + +make.deps: *.c *.h + for src in *.c; do \ + DEPS="$$(sed -e 's/^#include[ ]"\(.*\)"/\1/; t; d;' $$src | xargs echo)"; \ + echo "$${src//.c/.o}: $$src $$DEPS"; \ + done > make.deps + +exemple: libmysac.a + $(MAKE) -C exemple CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" + +clean: + rm -rf make.deps libmysac.so libmysac.a main.o man html $(OBJS) + +doc: + doxygen mysac.doxygen + +api: + echo " " > header_file + rm -rf apidoc >/dev/null 2>&1 + doxygen mysac-api.doxygen + +include make.deps diff --git a/ape-server/modules/deps/mysac/README b/ape-server/modules/deps/mysac/README new file mode 100755 index 0000000..23c198c --- /dev/null +++ b/ape-server/modules/deps/mysac/README @@ -0,0 +1,21 @@ +MySAC: MySQL Simple Asynchonous Client + -- - - - + +Reference site: +--------------- + +http://www.arpalert.org/index.php?page=main + + +Build: +------ + +Just type "make". + + +List: +----- + +you can send questions at: mysac-list@arpalert.org +Subscribe to the list: mysac-list+subscribe@arpalert.org + diff --git a/ape-server/modules/deps/mysac/VERSION b/ape-server/modules/deps/mysac/VERSION new file mode 100755 index 0000000..0267903 --- /dev/null +++ b/ape-server/modules/deps/mysac/VERSION @@ -0,0 +1 @@ +0.4b diff --git a/ape-server/modules/deps/mysac/mysac.c b/ape-server/modules/deps/mysac/mysac.c new file mode 100755 index 0000000..2df14e1 --- /dev/null +++ b/ape-server/modules/deps/mysac/mysac.c @@ -0,0 +1,1448 @@ +/* + * Copyright (c) 2009 Thierry FOURNIER + * + * This file is part of MySAC. + * + * MySAC 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 3 of the License + * + * MySAC 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 MySAC. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "mysac_decode_field.h" +#include "mysac_encode_values.h" +#include "mysac_decode_row.h" +#include "mysac.h" +#include "mysac_net.h" +#include "mysac_utils.h" + +enum my_response_t { + MYSAC_RET_EOF = 1000, + MYSAC_RET_OK, + MYSAC_RET_ERROR, + MYSAC_RET_DATA +}; + +const char *mysac_type[] = { + [MYSQL_TYPE_DECIMAL] = "MYSQL_TYPE_DECIMAL", + [MYSQL_TYPE_TINY] = "MYSQL_TYPE_TINY", + [MYSQL_TYPE_SHORT] = "MYSQL_TYPE_SHORT", + [MYSQL_TYPE_LONG] = "MYSQL_TYPE_LONG", + [MYSQL_TYPE_FLOAT] = "MYSQL_TYPE_FLOAT", + [MYSQL_TYPE_DOUBLE] = "MYSQL_TYPE_DOUBLE", + [MYSQL_TYPE_NULL] = "MYSQL_TYPE_NULL", + [MYSQL_TYPE_TIMESTAMP] = "MYSQL_TYPE_TIMESTAMP", + [MYSQL_TYPE_LONGLONG] = "MYSQL_TYPE_LONGLONG", + [MYSQL_TYPE_INT24] = "MYSQL_TYPE_INT24", + [MYSQL_TYPE_DATE] = "MYSQL_TYPE_DATE", + [MYSQL_TYPE_TIME] = "MYSQL_TYPE_TIME", + [MYSQL_TYPE_DATETIME] = "MYSQL_TYPE_DATETIME", + [MYSQL_TYPE_YEAR] = "MYSQL_TYPE_YEAR", + [MYSQL_TYPE_NEWDATE] = "MYSQL_TYPE_NEWDATE", + [MYSQL_TYPE_VARCHAR] = "MYSQL_TYPE_VARCHAR", + [MYSQL_TYPE_BIT] = "MYSQL_TYPE_BIT", + [MYSQL_TYPE_NEWDECIMAL] = "MYSQL_TYPE_NEWDECIMAL", + [MYSQL_TYPE_ENUM] = "MYSQL_TYPE_ENUM", + [MYSQL_TYPE_SET] = "MYSQL_TYPE_SET", + [MYSQL_TYPE_TINY_BLOB] = "MYSQL_TYPE_TINY_BLOB", + [MYSQL_TYPE_MEDIUM_BLOB] = "MYSQL_TYPE_MEDIUM_BLOB", + [MYSQL_TYPE_LONG_BLOB] = "MYSQL_TYPE_LONG_BLOB", + [MYSQL_TYPE_BLOB] = "MYSQL_TYPE_BLOB", + [MYSQL_TYPE_VAR_STRING] = "MYSQL_TYPE_VAR_STRING", + [MYSQL_TYPE_STRING] = "MYSQL_TYPE_STRING", + [MYSQL_TYPE_GEOMETRY] = "MYSQL_TYPE_GEOMETRY" +}; + +enum read_state { + RDST_INIT = 0, + RDST_READ_LEN, + RDST_READ_DATA, + RDST_DECODE_DATA +}; + +static int my_response(MYSAC *m) { + int i; + int err; + int errcode; + char *read; + unsigned long len; + unsigned long rlen; + char nul; + + switch ((enum read_state)m->readst) { + + case RDST_INIT: + m->len = 0; + m->readst = RDST_READ_LEN; + + /* read length */ + case RDST_READ_LEN: + /* check for avalaible size in buffer */ + if (m->read_len < 4) { + m->errorcode = MYERR_BUFFER_OVERSIZE; + return MYSAC_RET_ERROR; + } + err = mysac_read(m->fd, m->read + m->len, + 4 - m->len, &errcode); + if (err == -1) { + m->errorcode = errcode; + return errcode; + } + + m->len += err; + if (m->len < 4) { + m->errorcode = MYERR_WANT_READ; + return MYERR_WANT_READ; + } + + /* decode */ + m->packet_length = uint3korr(&m->read[0]); + + /* packet number */ + m->packet_number = m->read[3]; + + /* update read state */ + m->readst = RDST_READ_DATA; + m->len = 0; + + /* read data */ + case RDST_READ_DATA: + /* check for avalaible size in buffer */ + if (m->read_len < m->packet_length) { + m->errorcode = MYERR_BUFFER_OVERSIZE; + return MYSAC_RET_ERROR; + } + err = mysac_read(m->fd, m->read + m->len, + m->packet_length - m->len, &errcode); + if (err == -1) + return errcode; + + m->len += err; + if (m->len < m->packet_length) { + m->errorcode = MYERR_WANT_READ; + return MYERR_WANT_READ; + } + m->read_len -= m->packet_length; + + /* re-init eof */ + m->readst = RDST_DECODE_DATA; + m->eof = 1; + + /* decode data */ + case RDST_DECODE_DATA: + + /* error */ + if ((unsigned char)m->read[0] == 255) { + + /* defined mysql error */ + if (m->packet_length > 3) { + + /* read error code */ + // TODO: voir quoi foutre de ca plus tard + // m->errorcode = uint2korr(&m->read[1]); + + /* read mysal code and message */ + for (i=3; i<3+5; i++) + m->read[i] = m->read[i+1]; + m->read[8] = ' '; + m->mysql_error = &m->read[3]; + m->read[m->packet_length] = '\0'; + m->errorcode = MYERR_MYSQL_ERROR; + } + + /* unknown error */ + else + m->errorcode = MYERR_UNKNOWN_ERROR; + + return MYSAC_RET_ERROR; + } + + /* EOF marker: marque la fin d'une serie + (la fin des headers dans une requete) */ + else if ((unsigned char)m->read[0] == 254) { + m->warnings = uint2korr(&m->read[1]); + m->status = uint2korr(&m->read[3]); + m->eof = 1; + return MYSAC_RET_EOF; + } + + /* success */ + else if ((unsigned char)m->read[0] == 0) { + + read = &m->read[1]; + rlen = m->packet_length - 1; + + /* affected rows */ + len = my_lcb(read, &m->affected_rows, &nul, rlen); + rlen -= len; + read += len; + /* m->affected_rows = uint2korr(&m->read[1]); */ + + /* insert id */ + len = my_lcb(read, &m->insert_id, &nul, rlen); + rlen -= len; + read += len; + + /* server status */ + m->status = uint2korr(read); + read += 2; + + /* server warnings */ + m->warnings = uint2korr(read); + + return MYSAC_RET_OK; + } + + /* read response ... + * + * Result Set Packet 1-250 (first byte of Length-Coded Binary) + * Field Packet 1-250 ("") + * Row Data Packet 1-250 ("") + */ + else + return MYSAC_RET_DATA; + + default: + m->errorcode = MYERR_UNEXPECT_R_STATE; + return MYSAC_RET_ERROR; + } + + m->errorcode = MYERR_PACKET_CORRUPT; + return MYSAC_RET_ERROR; +} + +void mysac_init(MYSAC *mysac, char *buffer, unsigned int buffsize) { + + /* init */ + memset(mysac, 0, sizeof(MYSAC)); + mysac->qst = MYSAC_START; + mysac->buf = buffer; + mysac->bufsize = buffsize; +} + +MYSAC *mysac_new(int buffsize) { + MYSAC *m; + char *buf; + + /* struct memory */ + m = malloc(sizeof(MYSAC)); + if (m == NULL) + return NULL; + + /* buff memory */ + buf = malloc(buffsize); + if (buf == NULL) { + free(m); + return NULL; + } + + /* init */ + memset(m, 0, sizeof(MYSAC)); + m->free_it = 1; + m->qst = MYSAC_START; + m->buf = buf; + m->bufsize = buffsize; + + return m; +} + +void mysac_setup(MYSAC *mysac, const char *my_addr, const char *user, + const char *passwd, const char *db, + unsigned long client_flag) { + mysac->addr = my_addr; + mysac->login = user; + mysac->password = passwd; + mysac->database = db; + mysac->flags = client_flag; + mysac->call_it = mysac_connect; +} + +int mysac_connect(MYSAC *mysac) { + int err; + int errcode; + int i; + int len; + + switch (mysac->qst) { + + /*********************************************** + network connexion + ***********************************************/ + case MYSAC_START: + err = mysac_socket_connect(mysac->addr, &mysac->fd); + if (err != 0) { + mysac->qst = MYSAC_START; + mysac->errorcode = err; + return err; + } + mysac->qst = MYSAC_CONN_CHECK; + return MYERR_WANT_READ; + + /*********************************************** + check network connexion + ***********************************************/ + case MYSAC_CONN_CHECK: + err = mysac_socket_connect_check(mysac->fd); + if (err != 0) { + close(mysac->fd); + mysac->qst = MYSAC_START; + mysac->errorcode = err; + return err; + } + mysac->qst = MYSAC_READ_GREATINGS; + mysac->len = 0; + mysac->readst = 0; + mysac->read = mysac->buf; + mysac->read_len = mysac->bufsize; + + /*********************************************** + read greatings + ***********************************************/ + case MYSAC_READ_GREATINGS: + + err = my_response(mysac); + + if (err == MYERR_WANT_READ) + return MYERR_WANT_READ; + + /* error */ + else if (err == MYSAC_RET_ERROR) + return mysac->errorcode; + + /* ok */ + else if (err != MYSAC_RET_DATA) { + mysac->errorcode = MYERR_PROTOCOL_ERROR; + return mysac->errorcode; + } + + /* decode greatings */ + i = 0; + + /* protocol */ + mysac->protocol = mysac->buf[i]; + i++; + + /* version */ + mysac->version = &mysac->buf[i]; + + /* search \0 */ + while (mysac->buf[i] != 0) + i++; + i++; + + /* thread id */ + mysac->threadid = uint4korr(&mysac->buf[i]); + + /* first part of salt */ + strncpy(mysac->salt, &mysac->buf[i+4], SCRAMBLE_LENGTH_323); + i += 4 + SCRAMBLE_LENGTH_323 + 1; + + /* options */ + mysac->options = uint2korr(&mysac->buf[i]); + + /* charset */ + mysac->charset = mysac->buf[i+2]; + + /* server status */ + mysac->status = uint2korr(&mysac->buf[i+3]); + + /* salt part 2 */ + strncpy(mysac->salt + SCRAMBLE_LENGTH_323, &mysac->buf[i+5+13], + SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323); + mysac->salt[SCRAMBLE_LENGTH] = '\0'; + + /* checks */ + if (mysac->protocol != PROTOCOL_VERSION) + return CR_VERSION_ERROR; + + /******************************** + prepare auth packet + ********************************/ + + /* set m->buf number */ + mysac->packet_number++; + mysac->buf[3] = mysac->packet_number; + + /* set options */ + if (mysac->options & CLIENT_LONG_PASSWORD) + mysac->flags |= CLIENT_LONG_PASSWORD; + mysac->flags |= CLIENT_LONG_FLAG | + CLIENT_PROTOCOL_41 | + CLIENT_SECURE_CONNECTION; + to_my_2(mysac->flags, &mysac->buf[4]); + + /* set extended options */ + to_my_2(0, &mysac->buf[6]); + + /* max m->bufs */ + to_my_4(0x40000000, &mysac->buf[8]); + + /* charset */ + /* 8: swedish */ + mysac->buf[12] = 8; + + /* 24 unused */ + memset(&mysac->buf[13], 0, 24); + + /* username */ + strcpy(&mysac->buf[36], mysac->login); + i = 36 + strlen(mysac->login) + 1; + + /* password CLIENT_SECURE_CONNECTION */ + if (mysac->options & CLIENT_SECURE_CONNECTION) { + + /* the password hash len */ + mysac->buf[i] = SCRAMBLE_LENGTH; + i++; + scramble(&mysac->buf[i], mysac->salt, mysac->password); + i += SCRAMBLE_LENGTH; + } + + /* password ! CLIENT_SECURE_CONNECTION */ + else { + scramble_323(&mysac->buf[i], mysac->salt, mysac->password); + i += SCRAMBLE_LENGTH_323 + 1; + } + + /* Add database if needed */ + if ((mysac->options & CLIENT_CONNECT_WITH_DB) && + (mysac->database != NULL)) { + /* TODO : debordement de buffer */ + len = strlen(mysac->database); + memcpy(&mysac->buf[i], mysac->database, len); + i += len; + mysac->buf[i] = '\0'; + } + + /* len */ + to_my_3(i-4, &mysac->buf[0]); + mysac->len = i; + mysac->send = mysac->buf; + mysac->qst = MYSAC_SEND_AUTH_1; + + /*********************************************** + send paquet + ***********************************************/ + case MYSAC_SEND_AUTH_1: + err = mysac_write(mysac->fd, mysac->send, mysac->len, &errcode); + + if (err == -1) + return errcode; + + mysac->len -= err; + mysac->send += err; + if (mysac->len > 0) + return MYERR_WANT_WRITE; + + mysac->qst = MYSAC_RECV_AUTH_1; + mysac->readst = 0; + mysac->read = mysac->buf; + mysac->read_len = mysac->bufsize; + + /*********************************************** + read response 1 + ***********************************************/ + case_MYSAC_RECV_AUTH_1: + case MYSAC_RECV_AUTH_1: + /* + MYSAC_RET_EOF, + MYSAC_RET_OK, + MYSAC_RET_ERROR, + MYSAC_RET_DATA + */ + err = my_response(mysac); + + if (err == MYERR_WANT_READ) + return MYERR_WANT_READ; + + /* error */ + if (err == MYSAC_RET_ERROR) + return mysac->errorcode; + + /* ok */ + else if (err == MYSAC_RET_OK) + return 0; + + /* + By sending this very specific reply server asks us to send scrambled + password in old format. + */ + else if (mysac->packet_length == 1 && err == MYSAC_RET_EOF && + mysac->options & CLIENT_SECURE_CONNECTION) { + /* continue special paquet after conditions */ + } + + /* protocol error */ + else { + mysac->errorcode = MYERR_PROTOCOL_ERROR; + return mysac->errorcode; + } + + /* send scrambled password in old format */ + + /* set packet number */ + mysac->packet_number++; + mysac->buf[3] = mysac->packet_number; + + /* send scrambled password in old format. */ + scramble_323(&mysac->buf[4], mysac->salt, mysac->password); + mysac->buf[4+SCRAMBLE_LENGTH_323] = '\0'; + + /* len */ + to_my_3(SCRAMBLE_LENGTH_323+1, &mysac->buf[0]); + mysac->qst = MYSAC_SEND_AUTH_2; + mysac->len = SCRAMBLE_LENGTH_323 + 1 + 4; + mysac->send = mysac->buf; + + /* send scrambled password in old format */ + case MYSAC_SEND_AUTH_2: + err = mysac_write(mysac->fd, mysac->send, mysac->len, &errcode); + + if (err == -1) + return errcode; + + mysac->len -= err; + mysac->send += err; + if (mysac->len > 0) + return MYERR_WANT_WRITE; + + mysac->qst = MYSAC_RECV_AUTH_1; + mysac->readst = 0; + mysac->read = mysac->buf; + mysac->read_len = mysac->bufsize; + goto case_MYSAC_RECV_AUTH_1; + + case MYSAC_SEND_QUERY: + case MYSAC_RECV_QUERY_COLNUM: + case MYSAC_RECV_QUERY_COLDESC1: + case MYSAC_RECV_QUERY_COLDESC2: + case MYSAC_RECV_QUERY_EOF1: + case MYSAC_RECV_QUERY_EOF2: + case MYSAC_RECV_QUERY_DATA: + case MYSAC_SEND_INIT_DB: + case MYSAC_RECV_INIT_DB: + case MYSAC_SEND_STMT_QUERY: + case MYSAC_RECV_STMT_QUERY: + case MYSAC_SEND_STMT_EXECUTE: + case MYSAC_RECV_STMT_EXECUTE: + case MYSAC_READ_NUM: + case MYSAC_READ_HEADER: + case MYSAC_READ_LINE: + mysac->errorcode = MYERR_BAD_STATE; + return MYERR_BAD_STATE; + + } + + return 0; +} + +int mysac_set_database(MYSAC *mysac, const char *database) { + int i; + + /* set packet number */ + mysac->buf[3] = 0; + + /* set mysql command */ + mysac->buf[4] = COM_INIT_DB; + + /* build sql query */ + i = strlen(database); + memcpy(&mysac->buf[5], database, i); + + /* len */ + to_my_3(i + 1, &mysac->buf[0]); + + /* send params */ + mysac->send = mysac->buf; + mysac->len = i + 5; + mysac->qst = MYSAC_SEND_INIT_DB; + mysac->call_it = mysac_send_database; + + return 0; +} + +int mysac_send_database(MYSAC *mysac) { + int err; + int errcode; + + switch (mysac->qst) { + + /********************************************************** + * + * send query on network + * + **********************************************************/ + case MYSAC_SEND_INIT_DB: + err = mysac_write(mysac->fd, mysac->send, mysac->len, &errcode); + + if (err == -1) + return errcode; + + mysac->len -= err; + mysac->send += err; + if (mysac->len > 0) + return MYERR_WANT_WRITE; + mysac->qst = MYSAC_RECV_INIT_DB; + mysac->readst = 0; + mysac->read = mysac->buf; + + /********************************************************** + * + * receive + * + **********************************************************/ + case MYSAC_RECV_INIT_DB: + err = my_response(mysac); + + if (err == MYERR_WANT_READ) + return MYERR_WANT_READ; + + /* error */ + if (err == MYSAC_RET_ERROR) + return mysac->errorcode; + + /* protocol error */ + else if (err == MYSAC_RET_OK) + return 0; + + else { + mysac->errorcode = MYERR_PROTOCOL_ERROR; + return mysac->errorcode; + } + + case MYSAC_START: + case MYSAC_CONN_CHECK: + case MYSAC_READ_GREATINGS: + case MYSAC_SEND_AUTH_1: + case MYSAC_RECV_AUTH_1: + case MYSAC_SEND_AUTH_2: + case MYSAC_SEND_QUERY: + case MYSAC_RECV_QUERY_COLNUM: + case MYSAC_RECV_QUERY_COLDESC1: + case MYSAC_RECV_QUERY_COLDESC2: + case MYSAC_RECV_QUERY_EOF1: + case MYSAC_RECV_QUERY_EOF2: + case MYSAC_RECV_QUERY_DATA: + case MYSAC_SEND_STMT_QUERY: + case MYSAC_RECV_STMT_QUERY: + case MYSAC_SEND_STMT_EXECUTE: + case MYSAC_RECV_STMT_EXECUTE: + case MYSAC_READ_NUM: + case MYSAC_READ_HEADER: + case MYSAC_READ_LINE: + mysac->errorcode = MYERR_BAD_STATE; + return MYERR_BAD_STATE; + + } + + mysac->errorcode = MYERR_BAD_STATE; + return MYERR_BAD_STATE; +} + +int mysac_b_set_stmt_prepare(MYSAC *mysac, unsigned long *stmt_id, + const char *request, int len) { + + /* set packet number */ + mysac->buf[3] = 0; + + /* set mysql command */ + mysac->buf[4] = COM_STMT_PREPARE; + + /* check len */ + if (mysac->bufsize - 5 < len) + return -1; + + /* build sql query */ + memcpy(&mysac->buf[5], request, len); + + /* l */ + to_my_3(len + 1, &mysac->buf[0]); + + /* send params */ + mysac->send = mysac->buf; + mysac->len = len + 5; + mysac->qst = MYSAC_SEND_STMT_QUERY; + mysac->call_it = mysac_send_stmt_prepare; + mysac->stmt_id = stmt_id; + + return 0; +} + +int mysac_s_set_stmt_prepare(MYSAC *mysac, unsigned long *stmt_id, + const char *request) { + return mysac_b_set_stmt_prepare(mysac, stmt_id, request, strlen(request)); +} + +int mysac_v_set_stmt_prepare(MYSAC *mysac, unsigned long *stmt_id, + const char *fmt, va_list ap) { + int len; + + /* set packet number */ + mysac->buf[3] = 0; + + /* set mysql command */ + mysac->buf[4] = COM_STMT_PREPARE; + + /* build sql query */ + len = vsnprintf(&mysac->buf[5], mysac->bufsize - 5, fmt, ap); + if (len >= mysac->bufsize - 5) + return -1; + + /* len */ + to_my_3(len + 1, &mysac->buf[0]); + + /* send params */ + mysac->send = mysac->buf; + mysac->len = len + 5; + mysac->qst = MYSAC_SEND_STMT_QUERY; + mysac->call_it = mysac_send_stmt_prepare; + mysac->stmt_id = stmt_id; + + return 0; +} + +int mysac_set_stmt_prepare(MYSAC *mysac, unsigned long *stmt_id, + const char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + return mysac_v_set_stmt_prepare(mysac, stmt_id, fmt, ap); +} + +int mysac_send_stmt_prepare(MYSAC *mysac) { + int err; + int errcode; + + switch (mysac->qst) { + + /********************************************************** + * + * send query on network + * + **********************************************************/ + case MYSAC_SEND_STMT_QUERY: + err = mysac_write(mysac->fd, mysac->send, mysac->len, &errcode); + + if (err == -1) + return errcode; + + mysac->len -= err; + mysac->send += err; + if (mysac->len > 0) + return MYERR_WANT_WRITE; + mysac->qst = MYSAC_RECV_STMT_QUERY; + mysac->readst = 0; + mysac->read = mysac->buf; + + /********************************************************** + * + * receive + * + **********************************************************/ + case MYSAC_RECV_STMT_QUERY: + err = my_response(mysac); + + if (err == MYERR_WANT_READ) + return MYERR_WANT_READ; + + /* error */ + if (err == MYSAC_RET_ERROR) + return mysac->errorcode; + + /* protocol error */ + if (err != MYSAC_RET_OK) { + mysac->errorcode = MYERR_PROTOCOL_ERROR; + return mysac->errorcode; + } + + /* 0: don't care */ + + /* 1-4: get statement id */ + *mysac->stmt_id = uint4korr(&mysac->buf[1]); + + /* 5-6: get nb of columns */ + mysac->nb_cols = uint2korr(&mysac->buf[5]); + + /* 7-8: Number of placeholders in the statement */ + mysac->nb_plhold = uint2korr(&mysac->buf[7]); + + /* 9-.. don't care ! */ + + mysac->qst = MYSAC_RECV_QUERY_COLDESC1; + + /********************************************************** + * + * receive place holder description + * + **********************************************************/ + case_MYSAC_RECV_QUERY_COLDESC1: + mysac->readst = 0; + mysac->read = mysac->buf; + + case MYSAC_RECV_QUERY_COLDESC1: + + err = my_response(mysac); + + if (err == MYERR_WANT_READ) + return MYERR_WANT_READ; + + /* error */ + if (err == MYSAC_RET_ERROR) + return mysac->errorcode; + + /* protocol error */ + else if (err != MYSAC_RET_DATA) { + mysac->errorcode = MYERR_PROTOCOL_ERROR; + return mysac->errorcode; + } + + /* XXX for a moment, dont decode columns + * names and types + */ + mysac->nb_plhold--; + if (mysac->nb_plhold != 0) + goto case_MYSAC_RECV_QUERY_COLDESC1; + + mysac->readst = 0; + mysac->qst = MYSAC_RECV_QUERY_EOF1; + mysac->read = mysac->buf; + + /********************************************************** + * + * receive EOF + * + **********************************************************/ + case MYSAC_RECV_QUERY_EOF1: + err = my_response(mysac); + + if (err == MYERR_WANT_READ) + return MYERR_WANT_READ; + + /* error */ + if (err == MYSAC_RET_ERROR) + return mysac->errorcode; + + /* protocol error */ + else if (err != MYSAC_RET_EOF) { + mysac->errorcode = MYERR_PROTOCOL_ERROR; + return mysac->errorcode; + } + + mysac->qst = MYSAC_RECV_QUERY_COLDESC2; + + /********************************************************** + * + * receive column description + * + **********************************************************/ + case_MYSAC_RECV_QUERY_COLDESC2: + mysac->readst = 0; + mysac->read = mysac->buf; + + case MYSAC_RECV_QUERY_COLDESC2: + + err = my_response(mysac); + + if (err == MYERR_WANT_READ) + return MYERR_WANT_READ; + + /* error */ + if (err == MYSAC_RET_ERROR) + return mysac->errorcode; + + /* protocol error */ + else if (err != MYSAC_RET_DATA) { + mysac->errorcode = MYERR_PROTOCOL_ERROR; + return mysac->errorcode; + } + + /* XXX for a moment, dont decode columns + * names and types + */ + mysac->nb_cols--; + if (mysac->nb_cols != 0) + goto case_MYSAC_RECV_QUERY_COLDESC2; + + mysac->readst = 0; + mysac->qst = MYSAC_RECV_QUERY_EOF2; + mysac->read = mysac->buf; + + /********************************************************** + * + * receive EOF + * + **********************************************************/ + case MYSAC_RECV_QUERY_EOF2: + err = my_response(mysac); + + if (err == MYERR_WANT_READ) + return MYERR_WANT_READ; + + /* error */ + if (err == MYSAC_RET_ERROR) + return mysac->errorcode; + + /* protocol error */ + else if (err != MYSAC_RET_EOF) { + mysac->errorcode = MYERR_PROTOCOL_ERROR; + return mysac->errorcode; + } + + return 0; + + case MYSAC_START: + case MYSAC_CONN_CHECK: + case MYSAC_READ_GREATINGS: + case MYSAC_SEND_AUTH_1: + case MYSAC_RECV_AUTH_1: + case MYSAC_SEND_AUTH_2: + case MYSAC_SEND_QUERY: + case MYSAC_RECV_QUERY_COLNUM: + case MYSAC_RECV_QUERY_DATA: + case MYSAC_SEND_INIT_DB: + case MYSAC_RECV_INIT_DB: + case MYSAC_SEND_STMT_EXECUTE: + case MYSAC_RECV_STMT_EXECUTE: + case MYSAC_READ_NUM: + case MYSAC_READ_HEADER: + case MYSAC_READ_LINE: + mysac->errorcode = MYERR_BAD_STATE; + return MYERR_BAD_STATE; + } + + mysac->errorcode = MYERR_BAD_STATE; + return MYERR_BAD_STATE; +} + + + +int mysac_set_stmt_execute(MYSAC *mysac, MYSAC_RES *res, unsigned long stmt_id, + MYSAC_BIND *values, int nb) { + int i; + int nb_bf; + int desc_off; + int vals_off; + int len = 3 + 1 + 1 + 4 + 1 + 4; + int ret; + + /* check len */ + if (mysac->bufsize < len) { + mysac->errorcode = MYERR_BUFFER_TOO_SMALL; + mysac->len = 0; + return -1; + } + + /* 3 : set packet number */ + mysac->buf[3] = 0; + + /* 4 : set mysql command */ + mysac->buf[4] = COM_STMT_EXECUTE; + + /* 5-8 : build sql query */ + to_my_4(stmt_id, &mysac->buf[5]); + + /* 9 : flags (unused) */ + mysac->buf[9] = 0; + + /* 10-13 : iterations (unused) */ + to_my_4(1, &mysac->buf[10]); + + /* number of bytes for the NULL values bitfield */ + nb_bf = ( nb / 8 ) + 1; + desc_off = len + nb_bf + 1; + vals_off = desc_off + ( nb * 2 ); + + /* check len */ + if (mysac->bufsize < vals_off) { + mysac->errorcode = MYERR_BUFFER_TOO_SMALL; + mysac->len = 0; + return -1; + } + + /* init bitfield: set 0 */ + memset(&mysac->buf[len], 0, nb_bf); + + /* build NULL bitfield and values type */ + for (i=0; ibuf[len + (i << 3)] |= 1 << (i & 0xf); + + /*********************** + * + * Value type + * + ***********************/ + mysac->buf[desc_off + ( i * 2 )] = values[i].type; + mysac->buf[desc_off + ( i * 2 ) + 1] = 0x00; /* ???? */ + + /*********************** + * + * set values data + * + ***********************/ + ret = mysac_encode_value(&values[i], &mysac->buf[vals_off], + mysac->bufsize - vals_off); + if (ret < 0) { + mysac->errorcode = MYERR_BUFFER_TOO_SMALL; + mysac->len = 0; + return -1; + } + vals_off += ret; + } + + /* 01 byte ??? */ + mysac->buf[len + nb_bf] = 0x01; + + /* 0-2 : len + * 4 = packet_len + packet_id + */ + to_my_3(vals_off - 4, &mysac->buf[0]); + + /* send params */ + mysac->res = res; + mysac->send = mysac->buf; + mysac->len = vals_off; + mysac->qst = MYSAC_SEND_QUERY; + mysac->call_it = mysac_send_stmt_execute; + + return 0; +} + +inline +int mysac_b_set_query(MYSAC *mysac, MYSAC_RES *res, const char *query, int len) { + + /* set packet number */ + mysac->buf[3] = 0; + + /* set mysql command */ + mysac->buf[4] = COM_QUERY; + + /* build sql query */ + if (mysac->bufsize - 5 < len) { + mysac->errorcode = MYERR_BUFFER_TOO_SMALL; + mysac->len = 0; + return -1; + } + memcpy(&mysac->buf[5], query, len); + + /* len */ + to_my_3(len + 1, &mysac->buf[0]); + + /* send params */ + mysac->res = res; + mysac->send = mysac->buf; + mysac->len = len + 5; + mysac->qst = MYSAC_SEND_QUERY; + mysac->call_it = mysac_send_query; + + return 0; +} + +int mysac_s_set_query(MYSAC *mysac, MYSAC_RES *res, const char *query) { + return mysac_b_set_query(mysac, res, query, strlen(query)); +} + +inline +int mysac_v_set_query(MYSAC *mysac, MYSAC_RES *res, const char *fmt, va_list ap) { + int len; + + /* set packet number */ + mysac->buf[3] = 0; + + /* set mysql command */ + mysac->buf[4] = COM_QUERY; + + /* build sql query */ + len = vsnprintf(&mysac->buf[5], mysac->bufsize - 5, fmt, ap); + if (len >= mysac->bufsize - 5) { + mysac->errorcode = MYERR_BUFFER_TOO_SMALL; + mysac->len = 0; + return -1; + } + + /* len */ + to_my_3(len + 1, &mysac->buf[0]); + + /* send params */ + mysac->res = res; + mysac->send = mysac->buf; + mysac->len = len + 5; + mysac->qst = MYSAC_SEND_QUERY; + mysac->call_it = mysac_send_query; + + return 0; +} + +int mysac_set_query(MYSAC *mysac, MYSAC_RES *res, const char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + return mysac_v_set_query(mysac, res, fmt, ap); +} + +int mysac_send_query(MYSAC *mysac) { + int err; + int errcode; + int i; + int len; + MYSAC_RES *res; + + res = mysac->res; + + switch (mysac->qst) { + + /********************************************************** + * + * send query on network + * + **********************************************************/ + case MYSAC_SEND_QUERY: + err = mysac_write(mysac->fd, mysac->send, mysac->len, &errcode); + + if (err == -1) + return errcode; + + mysac->len -= err; + mysac->send += err; + if (mysac->len > 0) + return MYERR_WANT_WRITE; + mysac->qst = MYSAC_RECV_QUERY_COLNUM; + mysac->readst = 0; + + /********************************************************** + * + * receive + * + **********************************************************/ + + /* prepare struct + + +---------------+-----------------+ + | MYSQL_FIELD[] | char[] | + | resp->nb_cols | all fields name | + +---------------+-----------------+ + + */ + + res->nb_lines = 0; + res->cols = (MYSQL_FIELD *)(res->buffer + sizeof(MYSAC_RES)); + INIT_LIST_HEAD(&res->data); + mysac->read = res->buffer; + mysac->read_len = res->buffer_len; + + case MYSAC_RECV_QUERY_COLNUM: + err = my_response(mysac); + + if (err == MYERR_WANT_READ) + return MYERR_WANT_READ; + + /* error */ + if (err == MYSAC_RET_ERROR) + return mysac->errorcode; + + /* ok ( for insert or no return data command ) */ + if (err == MYSAC_RET_OK) + return 0; + + /* protocol error */ + if (err != MYSAC_RET_DATA) { + mysac->errorcode = MYERR_PROTOCOL_ERROR; + return mysac->errorcode; + } + + /* get nb col TODO: pas sur que ce soit un byte */ + res->nb_cols = mysac->read[0]; + mysac->read_id = 0; + mysac->qst = MYSAC_RECV_QUERY_COLDESC1; + + /* prepare cols space */ + + /* check for avalaible size in buffer */ + if (mysac->read_len < sizeof(MYSQL_FIELD) * res->nb_cols) { + mysac->errorcode = MYERR_BUFFER_OVERSIZE; + return mysac->errorcode; + } + res->cols = (MYSQL_FIELD *)mysac->read; + mysac->read += sizeof(MYSQL_FIELD) * mysac->res->nb_cols; + mysac->read_len -= sizeof(MYSQL_FIELD) * mysac->res->nb_cols; + + /********************************************************** + * + * receive column description + * + **********************************************************/ + + case_MYSAC_RECV_QUERY_COLDESC1: + mysac->readst = 0; + + case MYSAC_RECV_QUERY_COLDESC1: + + err = my_response(mysac); + + if (err == MYERR_WANT_READ) + return MYERR_WANT_READ; + + /* error */ + if (err == MYSAC_RET_ERROR) + return mysac->errorcode; + + /* protocol error */ + else if (err != MYSAC_RET_DATA) { + mysac->errorcode = MYERR_PROTOCOL_ERROR; + return mysac->errorcode; + } + + /* decode mysql packet with field desc, use packet buffer for storing + mysql data (field name) */ +#if 0 + for (i=0; ipacket_length; i++) { + fprintf(stderr, "%02x ", (unsigned char)mysac->read[i]); + } + fprintf(stderr, "\n"); +#endif + len = mysac_decode_field(mysac->read, mysac->packet_length, + &res->cols[mysac->read_id]); + + if (len < 0) { + mysac->errorcode = len * -1; + return mysac->errorcode; + } + mysac->read += len; + mysac->read_len += mysac->packet_length - len; + + mysac->read_id++; + if (mysac->read_id < res->nb_cols) + goto case_MYSAC_RECV_QUERY_COLDESC1; + + mysac->readst = 0; + mysac->qst = MYSAC_RECV_QUERY_EOF1; + + /********************************************************** + * + * receive EOF + * + **********************************************************/ + case MYSAC_RECV_QUERY_EOF1: + err = my_response(mysac); + + if (err == MYERR_WANT_READ) + return MYERR_WANT_READ; + + /* error */ + if (err == MYSAC_RET_ERROR) + return mysac->errorcode; + + /* protocol error */ + else if (err != MYSAC_RET_EOF) { + mysac->errorcode = MYERR_PROTOCOL_ERROR; + return mysac->errorcode; + } + + mysac->qst = MYSAC_RECV_QUERY_DATA; + + /********************************************************** + * + * read data lines + * + **********************************************************/ + case_MYSAC_RECV_QUERY_DATA: + + /* + +-------------------+----------------+-----------------+----------------+ + | struct mysac_rows | MYSAC_ROW[] | unsigned long[] | struct tm[] | + | | mysac->nb_cols | mysac->nb_cols | mysac->nb_time | + +-------------------+----------------+-----------------+----------------+ + */ + + /* check for avalaible size in buffer */ + if (mysac->read_len < sizeof(MYSAC_ROWS) + ( res->nb_cols * ( + sizeof(MYSAC_ROW) + sizeof(unsigned long) ) ) ) { + mysac->errorcode = MYERR_BUFFER_OVERSIZE; + return mysac->errorcode; + } + mysac->read_len -= sizeof(MYSAC_ROWS) + ( res->nb_cols * ( + sizeof(MYSAC_ROW) + sizeof(unsigned long) ) ); + + /* reserve space for MYSAC_ROWS and add it into chained list */ + res->cr = (MYSAC_ROWS *)mysac->read; + list_add_tail(&res->cr->link, &res->data); + mysac->read += sizeof(MYSAC_ROWS); + + /* space for each field definition into row */ + res->cr->data = (MYSAC_ROW *)mysac->read; + mysac->read += sizeof(MYSAC_ROW) * res->nb_cols; + + /* space for length table */ + res->cr->lengths = (unsigned long *)mysac->read; + mysac->read += sizeof(unsigned long) * res->nb_cols; + + /* struct tm */ + for (i=0; ires->nb_cols; i++) { + switch(mysac->res->cols[i].type) { + + /* date type */ + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_YEAR: + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_DATE: + if (mysac->read_len < sizeof(struct tm)) { + mysac->errorcode = MYERR_BUFFER_OVERSIZE; + return mysac->errorcode; + } + mysac->res->cr->data[i].tm = (struct tm *)mysac->read; + mysac->read += sizeof(struct tm); + mysac->read_len -= sizeof(struct tm); + memset(mysac->res->cr->data[i].tm, 0, sizeof(struct tm)); + break; + + /* do nothing for other types */ + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_TINY: + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_FLOAT: + case MYSQL_TYPE_DOUBLE: + case MYSQL_TYPE_NULL: + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_NEWDATE: + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_BIT: + case MYSQL_TYPE_NEWDECIMAL: + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_SET: + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_GEOMETRY: + break; + } + } + + /* set state at 0 */ + mysac->readst = 0; + + case MYSAC_RECV_QUERY_DATA: + err = my_response(mysac); + + if (err == MYERR_WANT_READ) + return MYERR_WANT_READ; + + /* error */ + else if (err == MYSAC_RET_ERROR) + return mysac->errorcode; + + /* EOF */ + else if (err == MYSAC_RET_EOF) { + list_del(&mysac->res->cr->link); + mysac->res->cr = NULL; + return 0; + } + + /* read data in string type */ + else if (err == MYSAC_RET_DATA) { +#if 0 + for (i=0; ipacket_length; i+=20) { + int j; + + for(j=i;jread[j]); + + for(j=i;jread[j])) + fprintf(stderr, "%c", (unsigned char)mysac->read[j]); + else + fprintf(stderr, "."); + + fprintf(stderr, "\n"); + } + fprintf(stderr, "\n\n"); +#endif + len = mysac_decode_string_row(mysac->read, mysac->packet_length, + res, res->cr); + if (len < 0) { + mysac->errorcode = len * -1; + return mysac->errorcode; + } + mysac->read += len; + mysac->read_len += mysac->packet_length - len; + } + + /* read data in binary type */ + else if (err == MYSAC_RET_OK) { +#if 0 + for (i=0; ipacket_length; i++) { + fprintf(stderr, "%02x ", (unsigned char)mysac->read[i]); + } + fprintf(stderr, "\n"); +#endif + len = mysac_decode_binary_row(mysac->read, mysac->packet_length, + res, res->cr); + if (len == -1) { + mysac->errorcode = MYERR_BINFIELD_CORRUPT; + return mysac->errorcode; + } + mysac->read += len; + mysac->read_len += mysac->packet_length - len; + } + + /* protocol error */ + else { + mysac->errorcode = MYERR_PROTOCOL_ERROR; + return mysac->errorcode; + } + + /* next line */ + mysac->res->nb_lines++; + goto case_MYSAC_RECV_QUERY_DATA; + + case MYSAC_START: + case MYSAC_CONN_CHECK: + case MYSAC_READ_GREATINGS: + case MYSAC_SEND_AUTH_1: + case MYSAC_RECV_AUTH_1: + case MYSAC_SEND_AUTH_2: + case MYSAC_SEND_INIT_DB: + case MYSAC_RECV_INIT_DB: + case MYSAC_SEND_STMT_QUERY: + case MYSAC_RECV_STMT_QUERY: + case MYSAC_SEND_STMT_EXECUTE: + case MYSAC_RECV_STMT_EXECUTE: + case MYSAC_READ_NUM: + case MYSAC_READ_HEADER: + case MYSAC_READ_LINE: + case MYSAC_RECV_QUERY_COLDESC2: + case MYSAC_RECV_QUERY_EOF2: + mysac->errorcode = MYERR_BAD_STATE; + return MYERR_BAD_STATE; + } + + mysac->errorcode = MYERR_BAD_STATE; + return MYERR_BAD_STATE; +} + diff --git a/ape-server/modules/deps/mysac/mysac.doxygen b/ape-server/modules/deps/mysac/mysac.doxygen new file mode 100755 index 0000000..14a21b4 --- /dev/null +++ b/ape-server/modules/deps/mysac/mysac.doxygen @@ -0,0 +1,1252 @@ +# Doxyfile 1.5.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = mysac + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = 0 + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = html/ + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian, +# Italian, Japanese, Japanese-en (Japanese with English messages), Korean, +# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian, +# Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# This tag can be used to specify the encoding used in the generated output. +# The encoding is not always determined by the language that is chosen, +# but also whether or not the output is meant for Windows or non-Windows users. +# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES +# forces the Windows encoding (this is the default for the Windows binary), +# whereas setting the tag to NO uses a Unix-style encoding (the default for +# all platforms other than Windows). + +USE_WINDOWS_ENCODING = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explicit @brief command for a brief description. + +JAVADOC_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for Java. +# For instance, namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to +# include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = YES + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from the +# version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = YES + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = mysac.h mysac_decode_field.h mysac_decode_row.h mysac_net.h mysac_utils.h + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = NO + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. Otherwise they will link to the documentstion. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = . + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = YES + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = ../man/ + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = YES + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = YES + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = YES + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will +# generate a call dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then doxygen will +# generate a caller dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable caller graphs for selected +# functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that a graph may be further truncated if the graph's +# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH +# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), +# the graph is not depth-constrained. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, which results in a white background. +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/ape-server/modules/deps/mysac/mysac.h b/ape-server/modules/deps/mysac/mysac.h new file mode 100755 index 0000000..c4e1eee --- /dev/null +++ b/ape-server/modules/deps/mysac/mysac.h @@ -0,0 +1,920 @@ +/* + * Copyright (c) 2009 Thierry FOURNIER + * + * This file is part of MySAC. + * + * MySAC 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 3 of the License + * + * MySAC 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 MySAC. If not, see . + */ + +/** @file */ + +#ifndef __MYSAC_H__ +#define __MYSAC_H__ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* def imported from: linux-2.6.24/include/linux/stddef.h */ +#define mysac_offset_of(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +/** + * container_of - cast a member of a structure out to the containing structure + * + * def imported from: linux-2.6.24/include/linux/kernel.h + * + * @param ptr the pointer to the member. + * @param type the type of the container struct this is embedded in. + * @param member the name of the member within the struct. + * + */ +#define mysac_container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - mysac_offset_of(type,member) );}) + +/** + * Simple doubly linked list implementation. + * + * def imported from: linux-2.6.24/include/linux/list.h + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ +struct mysac_list_head { + struct mysac_list_head *next, *prev; +}; + +/** + * list_entry - get the struct for this entry + * + * def imported from: linux-2.6.24/include/linux/list.h + * + * @param ptr: the &struct list_head pointer. + * @param type: the type of the struct this is embedded in. + * @param member: the name of the list_struct within the struct. + */ +#define mysac_list_entry(ptr, type, member) \ + mysac_container_of(ptr, type, member) + +/** + * list_first_entry - get the first element from a list + * + * def imported from: linux-2.6.24/include/linux/list.h + * @param ptr the list head to take the element from. + * @param type the type of the struct this is embedded in. + * @param member the name of the list_struct within the struct. + * + * Note, that list is expected to be not empty. + */ +#define mysac_list_first_entry(ptr, type, member) \ + mysac_list_entry((ptr)->next, type, member) + +#define mysac_list_next_entry(ptr, type, member) \ + mysac_list_first_entry(ptr, type, member); + +enum my_query_st { + MYSAC_START, + + MYSAC_CONN_CHECK, + MYSAC_READ_GREATINGS, + MYSAC_SEND_AUTH_1, + MYSAC_RECV_AUTH_1, + MYSAC_SEND_AUTH_2, + + MYSAC_SEND_QUERY, + MYSAC_RECV_QUERY_COLNUM, + MYSAC_RECV_QUERY_COLDESC1, + MYSAC_RECV_QUERY_COLDESC2, + MYSAC_RECV_QUERY_EOF1, + MYSAC_RECV_QUERY_EOF2, + MYSAC_RECV_QUERY_DATA, + + MYSAC_SEND_INIT_DB, + MYSAC_RECV_INIT_DB, + + MYSAC_SEND_STMT_QUERY, + MYSAC_RECV_STMT_QUERY, + + MYSAC_SEND_STMT_EXECUTE, + MYSAC_RECV_STMT_EXECUTE, + + MYSAC_READ_NUM, + MYSAC_READ_HEADER, + MYSAC_READ_LINE +}; + +#define MYSAC_COL_MAX_LEN 50 +#define MYSAC_COL_MAX_NUN 100 + +/* errors */ +enum { + MYERR_PROTOCOL_ERROR = 1, + MYERR_BUFFER_OVERSIZE, + MYERR_PACKET_CORRUPT, + MYERR_WANT_READ, + MYERR_WANT_WRITE, + MYERR_UNKNOWN_ERROR, + MYERR_MYSQL_ERROR, + MYERR_SERVER_LOST, + MYERR_BAD_PORT, + MYERR_BAD_STATE, + MYERR_RESOLV_HOST, + MYERR_SYSTEM, + MYERR_CANT_CONNECT, + MYERR_BUFFER_TOO_SMALL, + MYERR_UNEXPECT_R_STATE, + MYERR_STRFIELD_CORRUPT, + MYERR_BINFIELD_CORRUPT, + MYERR_BAD_LCB, + MYERR_LEN_OVER_BUFFER, + MYERR_CONVLONG, + MYERR_CONVLONGLONG, + MYERR_CONVFLOAT, + MYERR_CONVDOUBLE, + MYERR_CONVTIME, + MYERR_CONVTIMESTAMP, + MYERR_CONVDATE +}; + +extern const char *mysac_type[]; +extern const char *mysac_errors[]; + +/** + * This is union containing all c type matching mysql types + */ +typedef union { + signed char stiny; /* MYSQL_TYPE_TINY TINYINT */ + unsigned char utiny; /* MYSQL_TYPE_TINY TINYINT */ + unsigned char mbool; /* MYSQL_TYPE_TINY TINYINT */ + short ssmall; /* MYSQL_TYPE_SHORT SMALLINT */ + unsigned short small; /* MYSQL_TYPE_SHORT SMALLINT */ + int sint; /* MYSQL_TYPE_LONG INT */ + unsigned int uint; /* MYSQL_TYPE_LONG INT */ + long long sbigint; /* MYSQL_TYPE_LONGLONG BIGINT */ + unsigned long long ubigint; /* MYSQL_TYPE_LONGLONG BIGINT */ + float mfloat; /* MYSQL_TYPE_FLOAT FLOAT */ + double mdouble; /* MYSQL_TYPE_DOUBLE DOUBLE */ + struct timeval tv; /* MYSQL_TYPE_TIME TIME */ + struct tm *tm; /* MYSQL_TYPE_DATE DATE + MYSQL_TYPE_DATETIME DATETIME + MYSQL_TYPE_TIMESTAMP TIMESTAMP */ + char* string; /* MYSQL_TYPE_STRING TEXT,CHAR,VARCHAR */ + char* blob; /* MYSQL_TYPE_BLOB BLOB,BINARY,VARBINARY */ + void *ptr; /* generic pointer */ +} MYSAC_ROW; + +/** + * This is chained element. contain pointer to each elements of one row + */ +typedef struct { + struct mysac_list_head link; + unsigned long *lengths; + MYSAC_ROW *data; +} MYSAC_ROWS; + +/** + * This contain the complete result of one request + */ +typedef struct { + char *buffer; + int buffer_len; + int nb_cols; + int nb_lines; + int nb_time; + MYSQL_FIELD *cols; + struct mysac_list_head data; + MYSAC_ROWS *cr; +} MYSAC_RES; + +/** + * This contain list of values for statement binding + */ +typedef struct mysac_bind { + enum enum_field_types type; + void *value; + int value_len; + char is_null; +} MYSAC_BIND; + +/** + * This contain the necessary for one mysql connection + */ +typedef struct mysac { + int len; + char *read; + int read_len; + char *send; + int readst; + + unsigned int packet_length; + unsigned int packet_number; + + /* mysac */ + char free_it; + int fd; + int (*call_it)(/* MYSAC * */ struct mysac *); + + /*defconnect */ + unsigned int protocol; + char *version; + unsigned int threadid; + char salt[SCRAMBLE_LENGTH + 1]; + unsigned int options; + unsigned int charset; + unsigned int status; + unsigned long affected_rows; + unsigned int warnings; + unsigned int errorcode; + unsigned long insert_id; + char *mysql_code; + char *mysql_error; + char eof; + + /* user */ + const char *addr; + const char *login; + const char *password; + const char *database; + const char *query; + unsigned int flags; + + /* query */ + enum my_query_st qst; + int read_id; + MYSAC_RES *res; + unsigned long *stmt_id; + + /* the buffer */ + unsigned int bufsize; + char *buf; + + /* special stmt */ + int nb_cols; /* number of columns in response */ + int nb_plhold; /* number of placeholders in request */ +} MYSAC; + +/** + * Allocates and initializes a MYSQL object. + * If mysql is a NULL pointer, the function allocates, initializes, and + * returns a new object. Otherwise, the object is initialized and the address + * of the object is returned. If mysql_init() allocates a new object, it is + * freed when mysql_close() is called to close the connection. + * + * @param buffsize is the size of the buffer + * + * @return An initialized MYSAC* handle. NULL if there was insufficient memory + * to allocate a new object. + */ +MYSAC *mysac_new(int buffsize); + +/** + * Initializes a MYSQL object. + * If mysql is a NULL pointer, the function allocates, initializes, and + * returns a new object. Otherwise, the object is initialized and the address + * of the object is returned. If mysql_init() allocates a new object, it is + * freed when mysql_close() is called to close the connection. + * + * @param mysac Should be the address of an existing MYSAC structure. + * @param buffer is ptr on the pre-allocated buffer + * @param buffsize is the size of the buffer + */ +void mysac_init(MYSAC *mysac, char *buffer, unsigned int buffsize); + +/** + * mysac_connect() attempts to establish a connection to a MySQL database engine + * running on host. mysql_real_connect() must complete successfully before you + * can execute any other API functions that require a valid MYSQL connection + * handle structure. + * + * @param mysac The first parameter should be the address of an existing MYSQL + * structure. Before calling mysql_real_connect() you must call + * mysql_init() to initialize the MYSQL structure. You can change a lot + * of connect options with the mysql_options() call. + * + * @param my_addr like ":" ":", "socket_unix_file" or + * NULL. If NULL, bind is set to socket 0 + * + * @param user The user parameter contains the user's MySQL login ID. If user + * is NULL or the empty string "", the current user is assumed. + * + * @param passwd The passwd parameter contains the password for user. If passwd + * is NULL, only entries in the user table for the user that have a blank + * (empty) password field are checked for a match. + * + * @param db is the database name. If db is not NULL, the connection sets the + * default database to this value. + * + * @param client_flag The value of client_flag is usually 0, but can be set to a + * combination of the following flags to enable certain features: + * + * Flag Name Flag Description + * CLIENT_COMPRESS Use compression protocol. + * CLIENT_FOUND_ROWS Return the number of found (matched) rows, + * not the number of changed rows. + * CLIENT_IGNORE_SPACE Allow spaces after function names. Makes all + * functions names reserved words. + */ +void mysac_setup(MYSAC *mysac, const char *my_addr, const char *user, + const char *passwd, const char *db, + unsigned long client_flag); + +/** + * Run network connexion and mysql authentication + * + * @param mysac Should be the address of an existing MYSQL structure. + * + * @return + * MYERR_WANT_READ : want read socket + * MYERR_WANT_WRITE : want write socket + * CR_CONN_HOST_ERROR : Failed to connect to the MySQL server. + * CR_CONNECTION_ERROR : Failed to connect to the local MySQL server. + * CR_IPSOCK_ERROR : Failed to create an IP socket. + * CR_OUT_OF_MEMORY : Out of memory. + * CR_SOCKET_CREATE_ERROR : Failed to create a Unix socket. + * CR_UNKNOWN_HOST : Failed to find the IP address for the hostname. + * CR_VERSION_ERROR : A protocol mismatch resulted from attempting to + * connect to a server with a client library that + * uses a different protocol version. + * CR_SERVER_LOST : If connect_timeout > 0 and it took longer than + * connect_timeout seconds to connect to the server + * or if the server died while executing the + * init-command. + */ +int mysac_connect(MYSAC *mysac); + +/** + * Closes a previously opened connection. mysql_close() also deallocates the + * connection handle pointed to by mysql if the handle was allocated + * automatically by mysql_init() or mysql_connect(). + * + * @param mysac Should be the address of an existing MYSQL structure. + */ +static inline +void mysac_close(MYSAC *mysac) { + if (mysac->free_it == 1) + free(mysac); +} + +/** + * This function return the mysql filedescriptor used for connection + * to the mysql server + * + * @param mysac Should be the address of an existing MYSAC structure. + * + * @return mysql filedescriptor + */ +static inline +int mysac_get_fd(MYSAC *mysac) { + return mysac->fd; +} + +/** + * this function call the io function associated with the current + * command. (mysac_send_database, mysac_send_query and mysac_connect) + * + * @param mysac Should be the address of an existing MYSAC structure. + * + * @return 0 is ok, or all errors associated with functions + * mysac_send_database, mysac_send_query and mysac_connect or + * MYERR_BAD_STATE : the function does nothing to do (is an error) + */ +static inline +int mysac_io(MYSAC *mysac) { + if (mysac == NULL || mysac->call_it == NULL) + return MYERR_BAD_STATE; + return mysac->call_it(mysac); +} + +/** + * Build use database message + * + * @param mysac Should be the address of an existing MYSQL structure. + * @param database is the database name + */ +int mysac_set_database(MYSAC *mysac, const char *database); + +/** + * This send use database command + * + * @param mysac Should be the address of an existing MYSQL structure. + * + * @return + * 0 => ok + * MYSAC_WANT_READ + * MYSAC_WANT_WRITE + * ... + */ +int mysac_send_database(MYSAC *mysac); + +/** + * Initialize MYSAC_RES structur + * This function can not allocate memory, just use your buffer. + * + * @param buffer this buffer must contain all the sql response. + * this size is: + * sizeof(MYSAC_RES) + + * ( sizeof(MYSQL_FIELD) * nb_field ) + + * ( different fields names ) + * + * and for each row: + * sizeof(MYSAC_ROWS) + + * ( sizeof(MYSAC_ROW) * nb_field ) + + * ( sizeof(unsigned long) * nb_field ) + + * ( sizeof(struct tm) for differents date fields of the request ) + + * ( differents strings returned by the request ) + + * + * @param len is the len of the buffer + * + * @return MYSAC_RES. this function cannot be fail + */ +static inline +MYSAC_RES *mysac_init_res(char *buffer, int len) { + MYSAC_RES *res; + + /* check minimu length */ + if (len < sizeof(MYSAC_RES)) + return NULL; + + res = (MYSAC_RES *)buffer; + res->nb_cols = 0; + res->nb_lines = 0; + res->buffer = buffer + sizeof(MYSAC_RES); + res->buffer_len = len - sizeof(MYSAC_RES); + + return res; +} + +/** + * Initialize query + * + * @param mysac Should be the address of an existing MYSAC structur. + * @param res Should be the address of an existing MYSAC_RES structur. + * @param fmt is the output format with the printf style + * + * @return 0: ok, -1 nok + */ +int mysac_set_query(MYSAC *mysac, MYSAC_RES *res, const char *fmt, ...); + +/** + * Initialize query + * + * @param mysac Should be the address of an existing MYSAC structur. + * @param res Should be the address of an existing MYSAC_RES structur. + * @param fmt is the output format with the printf style + * @param ap is the argument list on format vprintf + * + * @return 0: ok, -1 nok + */ +int mysac_v_set_query(MYSAC *mysac, MYSAC_RES *res, const char *fmt, va_list ap); + +/** + * Initialize query + * + * @param mysac Should be the address of an existing MYSAC structur. + * @param res Should be the address of an existing MYSAC_RES structur. + * @param query is a string (terminated by \0) containing the query + * + * @return 0: ok, -1 nok + */ +int mysac_s_set_query(MYSAC *mysac, MYSAC_RES *res, const char *query); + +/** + * Initialize query + * + * @param mysac Should be the address of an existing MYSAC structur. + * @param res Should be the address of an existing MYSAC_RES structur. + * @param query is a string containing the query + * @param len is the len of the query + * + * @return 0: ok, -1 nok + */ +int mysac_b_set_query(MYSAC *mysac, MYSAC_RES *res, const char *query, int len); + +/** + * This function return the mysql response pointer + * + * @param mysac Should be the address of an existing MYSAC structure. + * + * @return mysql response pointer + */ +static inline +MYSAC_RES *mysac_get_res(MYSAC *mysac) { + return mysac->res; +} + +/** + * Send sql query command + * + * @param mysac Should be the address of an existing MYSAC structur. + * + * @return + * 0 => ok + * MYSAC_WANT_READ + * MYSAC_WANT_WRITE + * ... + */ +int mysac_send_query(MYSAC *mysac); + +/** + * Prepare statement + * + * @param mysac Should be the address of an existing MYSAC structur. + * @param stmt_id is the receiver of the statement id + * @param fmt is the output format with the printf style + * + * @return 0: ok, -1 nok + */ +int mysac_set_stmt_prepare(MYSAC *mysac, unsigned long *stmt_id, const char *fmt, ...); + +/** + * Prepare statement + * + * @param mysac Should be the address of an existing MYSAC structur. + * @param stmt_id is the receiver of the statement id + * @param fmt is the output format with the printf style + * @param ap is the argument list on format vprintf + * + * @return 0: ok, -1 nok + */ +int mysac_v_set_stmt_prepare(MYSAC *mysac, unsigned long *stmt_id, const char *fmt, va_list ap); + +/** + * Prepare statement + * + * @param mysac Should be the address of an existing MYSAC structur. + * @param stmt_id is the receiver of the statement id + * @param query is a string (terminated by \0) containing the query + * + * @return 0: ok, -1 nok + */ +int mysac_s_set_stmt_prepare(MYSAC *mysac, unsigned long *stmt_id, const char *request); + +/** + * Prepare statement + * + * @param mysac Should be the address of an existing MYSAC structur. + * @param stmt_id is the receiver of the statement id + * @param request is a string containing the query + * @param len is the len of the query + * + * @return 0: ok, -1 nok + */ +int mysac_b_set_stmt_prepare(MYSAC *mysac, unsigned long *stmt_id, const char *request, int len); + +/** + * Send sql query command + * + * @param mysac Should be the address of an existing MYSAC structur. + * @param stmt_id is pointer for storing the statement id + * + * @return + * 0 => ok + * MYSAC_WANT_READ + * MYSAC_WANT_WRITE + * ... + */ +int mysac_send_stmt_prepare(MYSAC *mysac); + +/** + * Execute statement + * + * @param mysac Should be the address of an existing MYSAC structur. + * @param res Should be the address of an existing MYSAC_RES structur. + * @param stmt_id the statement id + * @param values is array of values send for the request + * @param nb is number of values + * + * @return 0: ok, -1 nok + */ +int mysac_set_stmt_execute(MYSAC *mysac, MYSAC_RES *res, unsigned long stmt_id, + MYSAC_BIND *values, int nb); + +/** + * send stmt execute command + * + * @param mysac Should be the address of an existing MYSQL structure. + * + * @return + */ +static inline +int mysac_send_stmt_execute(MYSAC *mysac) { + return mysac_send_query(mysac); +} + +/** + * After executing a statement with mysql_query() returns the number of rows + * changed (for UPDATE), deleted (for DELETE), orinserted (for INSERT). For + * SELECT statements, mysql_affected_rows() works like mysql_num_rows(). + * + * @param mysac Should be the address of an existing MYSQL structure. + * + * @return An integer greater than zero indicates the number of rows affected + * or retrieved. Zero indicates that no records were updated for an + * UPDATE statement, no rows matched the WHERE clause in the query or + * that no query has yet been executed. -1 indicates that the query + * returned an error or that, for a SELECT query, mysql_affected_rows() + * was called prior to calling mysql_store_result(). Because + * mysql_affected_rows() returns an unsigned value, you can check for -1 + * by comparing the return value to (my_ulonglong)-1 (or to + * (my_ulonglong)~0, which is equivalent). + */ +int mysac_affected_rows(MYSAC *mysac); + +/** + * Returns the number of columns for the most recent query on the connection. + * + * @param res Should be the address of an existing MYSAC_RES structure. + * + * @return number of columns + */ +static inline +unsigned int mysac_field_count(MYSAC_RES *res) { + return res->nb_cols; +} + +/** + * Returns the number of rows in the result set. + * + * mysql_num_rows() is intended for use with statements that return a result + * set, such as SELECT. For statements such as INSERT, UPDATE, or DELETE, the + * number of affected rows can be obtained with mysql_affected_rows(). + * + * @param res Should be the address of an existing MYSAC_RES structure. + * + * @return The number of rows in the result set. + */ +static inline +unsigned long mysac_num_rows(MYSAC_RES *res) { + return res->nb_lines; +} + +/** + * Retrieves the next row of a result set. mysql_fetch_row() returns NULL when + * there are no more rows to retrieve or if an error occurred. + * + * The number of values in the row is given by mysql_num_fields(result). + * + * The lengths of the field values in the row may be obtained by calling + * mysql_fetch_lengths(). Empty fields and fields containing NULL both have + * length 0; you can distinguish these by checking the pointer for the field + * value. If the pointer is NULL, the field is NULL; otherwise, the field is + * empty. + * + * @param res Should be the address of an existing MYSAC_RES structure. + * + * @return A MYSAC_ROW structure for the next row. NULL if there are no more + * rows to retrieve or if an error occurred. + */ +static inline +MYSAC_ROW *mysac_fetch_row(MYSAC_RES *res) { + if (res->cr == NULL) + res->cr = mysac_list_first_entry(&res->data, MYSAC_ROWS, link); + else + res->cr = mysac_list_next_entry(&res->cr->link, MYSAC_ROWS, link); + if (&res->data == &res->cr->link) { + res->cr = NULL; + return NULL; + } + return res->cr->data; +} + +/** + * Set pointer on the first row, you can exec mysac_fetch_row, return it the + * first row; + */ +static inline +void mysac_first_row(MYSAC_RES *res) { + res->cr = NULL; +} + +/** + * Get current row, dont touch row ptr + */ +static inline +MYSAC_ROW *mysac_cur_row(MYSAC_RES *res) { + return res->cr->data; +} + +/** + * Returns the value generated for an AUTO_INCREMENT column by the previous + * INSERT or UPDATE statement. Use this function after you have performed an + * INSERT statement into a table that contains an AUTO_INCREMENT field + * + * http://dev.mysql.com/doc/refman/5.0/en/mysql-insert-id.html + * + * @param m Should be the address of an existing MYSQL structure. + * + * @return the value generated for an AUTO_INCREMENT column + */ +static inline +unsigned long mysac_insert_id(MYSAC *m) { + return m->insert_id; +} + + + +#if 0 +mysql_fetch_fields() /*Returns an array of all field structures*/ +mysql_fetch_field() /*Returns the type of the next table field*/ +mysql_fetch_lengths() /*Returns the lengths of all columns in the current row*/ +#endif + +/** + * Changes the user and causes the database specified by db to become the + * default (current) database on the connection specified by mysql. In + * subsequent queries, this database is the default for table references that + * do not include an explicit database specifier. + * + * mysql_change_user() fails if the connected user cannot be authenticated or + * doesn't have permission to use the database. In this case, the user and + * database are not changed + * + * This command resets the state as if one had done a new connect. It always + * performs a ROLLBACK of any active transactions, closes and drops all + * temporary tables, and unlocks all locked tables. Session system variables + * are reset to the values of the corresponding global system variables. + * Prepared statements are released and HANDLER variables are closed. Locks + * acquired with GET_LOCK() are released. These effects occur even if the user + * didn't change. + * + * @param mysac Should be the address of an existing MYSQL structure. + * + * @param user The user parameter contains the user's MySQL login ID. If user + * is NULL or the empty string "", the current user is assumed. + * + * @param passwd The passwd parameter contains the password for user. If passwd + * is NULL, only entries in the user table for the user that have a blank + * (empty) password field are checked for a match. + * + * @param db The db parameter may be set to NULL if you don't want to have a + * default database. + * + * @return + * CR_COMMANDS_OUT_OF_SYNC : Commands were executed in an improper order. + * CR_SERVER_GONE_ERROR : The MySQL server has gone away. + * CR_SERVER_LOST : The connection to the server was lost during + * the query. + * CR_UNKNOWN_ERROR : An unknown error occurred. + * ER_UNKNOWN_COM_ERROR : The MySQL server doesn't implement this + * command (probably an old server). + * ER_ACCESS_DENIED_ERROR : The user or password was wrong. + * ER_BAD_DB_ERROR : The database didn't exist. + * ER_DBACCESS_DENIED_ERROR : The user did not have access rights to the + * database. + * ER_WRONG_DB_NAME : The database name was too long. + */ +int mysac_change_user(MYSAC *mysac, const char *user, const char *passwd, + const char *db); + +/** + * Returns the default character set name for the current connection. + * + * @param mysac Should be the address of an existing MYSQL structure. + * + * @return The default character set name + */ +//const char *mysac_character_set_name(MYSAC *mysac); + +/** + * For the connection specified by mysql, mysql_errno() returns the error code + * for the most recently invoked API function that can succeed or fail. A return + * value of zero means that no error occurred. Client error message numbers are + * listed in the MySQL errmsg.h header file. Server error message numbers are + * listed in mysqld_error.h. Errors also are listed at Appendix B, Errors, Error + * Codes, and Common Problems. + * + * @param mysac Should be the address of an existing MYSQL structure. + */ +static inline +unsigned int mysac_errno(MYSAC *mysac) { + return mysac->errorcode; +} + +/** + * For the connection specified by mysql, mysql_error() returns a null- + * terminated string containing the error message for the most recently invoked + * API function that failed. If a function didn't fail, the return value of + * mysql_error() may be the previous error or an empty string to indicate no + * error. + * + * @param mysac Should be the address of an existing MYSQL structure. + */ +static inline +const char *mysac_error(MYSAC *mysac) { + return mysac_errors[mysac->errorcode]; +} + +/** + * For the connection specified by mysql, mysql_error() returns a null- + * terminated string containing the error message for the most recently invoked + * API function that failed. If a function didn't fail, the return value of + * mysql_error() may be the previous error or an empty string to indicate no + * error. + * + * @param mysac Should be the address of an existing MYSQL structure. + */ +static inline +const char *mysac_advance_error(MYSAC *mysac) { + if (mysac->errorcode == MYERR_MYSQL_ERROR) + return mysac->mysql_error; + else if (mysac->errorcode == MYERR_SYSTEM) + return strerror(errno); + else + return mysac_errors[mysac->errorcode]; +} + +/* +1607 ulong STDCALL +1608 mysac_escape_string(MYSQL *mysql, char *to,const char *from, +1609 ulong length) +1610 { +1611 if (mysql->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES) +1612 return escape_quotes_for_mysql(mysql->charset, to, 0, from, length); +1613 return escape_string_for_mysql(mysql->charset, to, 0, from, length); +1614 } +*/ + +#if 0 +mysql_affected_rows() /*Returns the number of rows changed/deleted/inserted by the last UPDATE, DELETE, or INSERT query*/ +mysql_autocommit() /*Toggles autocommit mode on/off*/ +mysql_commit() /*Commits the transaction*/ +mysql_create_db() /*Creates a database (this function is deprecated; use the SQL statement CREATE DATABASE instead)*/ +mysql_data_seek() /*Seeks to an arbitrary row number in a query result set*/ +mysql_debug() /*Does a DBUG_PUSH with the given string*/ +mysql_drop_db() /*Drops a database (this function is deprecated; use the SQL statement DROP DATABASE instead)*/ +mysql_dump_debug_info() /*Makes the server write debug information to the log*/ +mysql_escape_string() /*Escapes special characters in a string for use in an SQL statement*/ +mysql_fetch_field_direct() /*Returns the type of a table field, given a field number*/ +mysql_field_seek() /*Puts the column cursor on a specified column*/ +mysql_field_tell() /*Returns the position of the field cursor used for the last mysql_fetch_field()*/ +mysql_free_result() /*Frees memory used by a result set*/ +mysql_get_character_set_info() /*Return information about default character set*/ +mysql_get_client_info() /*Returns client version information as a string*/ +mysql_get_client_version() /*Returns client version information as an integer*/ +mysql_get_host_info() /*Returns a string describing the connection*/ +mysql_get_proto_info() /*Returns the protocol version used by the connection*/ +mysql_get_server_info() /*Returns the server version number*/ +mysql_get_server_version() /*Returns version number of server as an integer*/ +mysql_get_ssl_cipher() /*Return current SSL cipher*/ +mysql_hex_string() /*Encode string in hexadecimal format*/ +mysql_info() /*Returns information about the most recently executed query*/ +mysql_init() /*Gets or initializes a MYSQL structure*/ +mysql_kill() /*Kills a given thread*/ +mysql_library_end() /*Finalize the MySQL C API library*/ +mysql_library_init() /*Initialize the MySQL C API library*/ +mysql_list_dbs() /*Returns database names matching a simple regular expression*/ +mysql_list_fields() /*Returns field names matching a simple regular expression*/ +mysql_list_processes() /*Returns a list of the current server threads*/ +mysql_list_tables() /*Returns table names matching a simple regular expression*/ +mysql_more_results() /*Checks whether any more results exist*/ +mysql_next_result() /*Returns/initiates the next result in multiple-statement executions*/ +mysql_num_fields() /*Returns the number of columns in a result set*/ +mysql_num_rows() /*Returns the number of rows in a result set*/ +mysql_options() /*Sets connect options for mysql_real_connect()*/ +mysql_ping() /*Checks whether the connection to the server is working, reconnecting as necessary*/ +mysql_query() /*Executes an SQL query specified as a null-terminated string*/ +mysql_real_connect() /*Connects to a MySQL server*/ +mysql_real_escape_string() /*Escapes special characters in a string for use in an SQL statement, taking into account the current character set of the connection*/ +mysql_real_query() /*Executes an SQL query specified as a counted string*/ +mysql_refresh() /*Flush or reset tables and caches*/ +mysql_reload() /*Tells the server to reload the grant tables*/ +mysql_rollback() /*Rolls back the transaction*/ +mysql_row_seek() /*Seeks to a row offset in a result set, using value returned from mysql_row_tell()*/ +mysql_row_tell() /*Returns the row cursor position*/ +mysql_select_db() /*Selects a database*/ +mysql_server_end() /*Finalize the MySQL C API library*/ +mysql_server_init() /*Initialize the MySQL C API library*/ +mysql_set_character_set() /*Set default character set for current connection*/ +mysql_set_local_infile_default() /*Set the LOAD DATA LOCAL INFILE handler callbacks to their default values*/ +mysql_set_local_infile_handler() /*Install application-specific LOAD DATA LOCAL INFILE handler callbacks*/ +mysql_set_server_option() /*Sets an option for the connection (like multi-statements)*/ +mysql_sqlstate() /*Returns the SQLSTATE error code for the last error*/ +mysql_shutdown() /*Shuts down the database server*/ +mysql_ssl_set() /*Prepare to establish SSL connection to server*/ +mysql_stat() /*Returns the server status as a string*/ +mysql_store_result() /*Retrieves a complete result set to the client*/ +mysql_thread_id() /*Returns the current thread ID*/ +mysql_use_result() /*Initiates a row-by-row result set retrieval*/ +mysql_warning_count() /*Returns the warning count for the previous SQL statement*/ +#endif + + +#endif diff --git a/ape-server/modules/deps/mysac/mysac_decode_field.c b/ape-server/modules/deps/mysac/mysac_decode_field.c new file mode 100755 index 0000000..fa28ce1 --- /dev/null +++ b/ape-server/modules/deps/mysac/mysac_decode_field.c @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2009 Thierry FOURNIER + * + * This file is part of MySAC. + * + * MySAC 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 3 of the License + * + * MySAC 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 MySAC. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "mysac_utils.h" +#include "mysac.h" + +int mysac_decode_field(char *buf, int len, MYSQL_FIELD *col) { + int i; + unsigned long size; + char nul; + char *wh; + int tmp_len; + + /* + VERSION 4.0 + Bytes Name + ----- ---- + n (Length Coded String) table + n (Length Coded String) name + 4 (Length Coded Binary) length + 2 (Length Coded Binary) type + 2 (Length Coded Binary) flags + 1 decimals + n (Length Coded Binary) default + + -> VERSION 4.1 + Bytes Name + ----- ---- + n (Length Coded String) catalog + n (Length Coded String) db + n (Length Coded String) table + n (Length Coded String) org_table + n (Length Coded String) name + n (Length Coded String) org_name + 1 (filler) + 2 charsetnr + 4 length + 1 type + 2 flags + 1 decimals + 2 (filler), always 0x00 + n (Length Coded Binary) default + */ + + wh = buf; + + i = 0; + + /* n (Length Coded String) catalog */ + tmp_len = my_lcb(&buf[i], &size, &nul, len-i); + if (tmp_len == -1) + return -MYERR_BAD_LCB; + i += tmp_len; + if (i + size > len) + return -MYERR_LEN_OVER_BUFFER; + col->catalog_length = size; + memmove(wh, &buf[i], size); + col->catalog = wh; + col->catalog[size] = '\0'; + wh += size + 1; + i += size; + + /* n (Length Coded String) db */ + tmp_len = my_lcb(&buf[i], &size, &nul, len-i); + if (tmp_len == -1) + return -MYERR_BAD_LCB; + i += tmp_len; + if (i + size > len) + return -MYERR_LEN_OVER_BUFFER; + col->db_length = size; + memmove(wh, &buf[i], size); + col->db = wh; + col->db[size] = '\0'; + wh += size + 1; + i += size; + + /* n (Length Coded String) table */ + tmp_len = my_lcb(&buf[i], &size, &nul, len-i); + if (tmp_len == -1) + return -MYERR_BAD_LCB; + i += tmp_len; + if (i + size > len) + return -MYERR_LEN_OVER_BUFFER; + col->table_length = size; + memmove(wh, &buf[i], size); + col->table = wh; + col->table[size] = '\0'; + wh += size + 1; + i += size; + + /* n (Length Coded String) org_table */ + tmp_len = my_lcb(&buf[i], &size, &nul, len-i); + if (tmp_len == -1) + return -MYERR_BAD_LCB; + i += tmp_len; + if (i + size > len) + return -MYERR_LEN_OVER_BUFFER; + col->org_table_length = size; + memmove(wh, &buf[i], size); + col->org_table = wh; + col->org_table[size] = '\0'; + wh += size + 1; + i += size; + + /* n (Length Coded String) name */ + tmp_len = my_lcb(&buf[i], &size, &nul, len-i); + if (tmp_len == -1) + return -MYERR_BAD_LCB; + i += tmp_len; + if (i + size > len) + return -MYERR_LEN_OVER_BUFFER; + col->name_length = size; + memmove(wh, &buf[i], size); + col->name = wh; + col->name[size] = '\0'; + wh += size + 1; + i += size; + + /* n (Length Coded String) org_name */ + tmp_len = my_lcb(&buf[i], &size, &nul, len-i); + if (tmp_len == -1) + return -MYERR_BAD_LCB; + i += tmp_len; + if (i + size > len) + return -MYERR_LEN_OVER_BUFFER; + col->org_name_length = size; + memmove(wh, &buf[i], size); + col->org_name = wh; + col->org_name[size] = '\0'; + wh += size + 1; + i += size; + + /* check len */ + if (i + 13 > len) + return -MYERR_LEN_OVER_BUFFER; + + /* (filler) */ + i += 1; + + /* charset */ + col->charsetnr = uint2korr(&buf[i]); + i += 2; + + /* length */ + col->length = uint4korr(&buf[i]); + i += 4; + + /* type */ + col->type = (unsigned char)buf[i]; + i += 1; + + /* flags */ + col->flags = uint3korr(&buf[i]); + i += 2; + + /* decimals */ + col->decimals = buf[i]; + i += 1; + + /* filler */ + i += 2; + + /* default - a priori facultatif */ + if (len-i > 0) { + tmp_len = my_lcb(&buf[i], &size, &nul, len-i); + if (tmp_len == -1) + return -MYERR_BAD_LCB; + i += tmp_len; + if (i + size > len) + return -MYERR_LEN_OVER_BUFFER; + col->def_length = size; + memmove(wh, &buf[i], size); + col->def = wh; + col->def[size] = '\0'; + wh += size + 1; + i += size; + } + else { + col->def = NULL; + col->def_length = 0; + } + + + /* set write pointer */ + return wh - buf; +} diff --git a/ape-server/modules/deps/mysac/mysac_decode_field.h b/ape-server/modules/deps/mysac/mysac_decode_field.h new file mode 100755 index 0000000..1aa6701 --- /dev/null +++ b/ape-server/modules/deps/mysac/mysac_decode_field.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2009 Thierry FOURNIER + * + * This file is part of MySAC. + * + * MySAC 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 3 of the License + * + * MySAC 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 MySAC. If not, see . + */ + +/** @file */ + +#ifndef __MYSAC_DECODE_PAQUET__ +#define __MYSAC_DECODE_PAQUET__ + +#include "mysac.h" + +/** + * mysac_decode_field decode field header packet + * + * @param buf id the buffer containing packet + * the strings stored into col, are allocated into + * this buffer + * @param len is then length of the packet + * @param col is valid col struct space for storing pointers + * and values + * + * @return the len of the buffer used for storing data or + * -1 if the packet is corrupted + */ +int mysac_decode_field(char *buf, int len, MYSQL_FIELD *col); + +#endif diff --git a/ape-server/modules/deps/mysac/mysac_decode_row.c b/ape-server/modules/deps/mysac/mysac_decode_row.c new file mode 100755 index 0000000..548d7aa --- /dev/null +++ b/ape-server/modules/deps/mysac/mysac_decode_row.c @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2009 Thierry FOURNIER + * + * This file is part of MySAC. + * + * MySAC 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 3 of the License + * + * MySAC 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 MySAC. If not, see . + */ + +/* the order of theses headers and defines + * is important */ +#include +#undef _ISOC99_SOURCE +#define _ISOC99_SOURCE +#include +#include +#include +#include +#include +#include + +#include "mysac.h" +#include "mysac_utils.h" + + + + +/************************************************** + + read data in binary type + +**************************************************/ +int mysac_decode_binary_row(char *buf, int packet_len, + MYSAC_RES *res, MYSAC_ROWS *row) { + int j; + int i; + char nul; + unsigned long len; + int tmp_len; + char *wh; + char _null_ptr[16]; + char *null_ptr; + unsigned char bit; + + wh = buf; + null_ptr = _null_ptr; + bit = 4; /* first 2 bits are reserved */ + + /* first bit is unused */ + i = 1; + + /* skip null bits */ + tmp_len = ( (res->nb_cols + 9) / 8 ); + if (i + tmp_len > packet_len) + return -1; + + memcpy(_null_ptr, &buf[i], tmp_len); + i += tmp_len; + + for (j = 0; j < res->nb_cols; j++) { + + /* + We should set both row_ptr and is_null to be able to see + nulls in mysql_stmt_fetch_column. This is because is_null may point + to user data which can be overwritten between mysql_stmt_fetch and + mysql_stmt_fetch_column, and in this case nullness of column will be + lost. See mysql_stmt_fetch_column for details. + */ + if ( (*null_ptr & bit) != 0 ) { + /* do nothing */ + } + + else { + switch (res->cols[j].type) { + + /* read null */ + case MYSQL_TYPE_NULL: + row->data[j].blob = NULL; + + /* read blob */ + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + /* decimal ? maybe for very big num ... crypto key ? */ + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + /* .... */ + case MYSQL_TYPE_BIT: + /* read text */ + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_VARCHAR: + /* read date */ + case MYSQL_TYPE_NEWDATE: + tmp_len = my_lcb(&buf[i], &len, &nul, packet_len-i); + if (tmp_len == -1) + return -1; + i += tmp_len; + if (i + len > packet_len) + return -1; + if (nul == 1) + row->data[j].blob = NULL; + else { + memmove(wh, &buf[i], len); + row->data[j].blob = wh; + row->data[j].blob[len] = '\0'; + i += len; + wh += len + 1; + } + row->lengths[j] = len; + break; + + case MYSQL_TYPE_TINY: + if (i > packet_len - 1) + return -1; + row->data[j].stiny = buf[i]; + i++; + break; + + case MYSQL_TYPE_SHORT: + if (i > packet_len - 2) + return -1; + row->data[j].ssmall = sint2korr(&buf[i]); + i += 2; + break; + + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_LONG: + if (i > packet_len - 4) + return -1; + row->data[j].sint = sint4korr(&buf[i]); + i += 4; + break; + + case MYSQL_TYPE_LONGLONG: + if (i > packet_len - 8) + return -1; + row->data[j].sbigint = sint8korr(&buf[i]); + i += 8; + break; + + case MYSQL_TYPE_FLOAT: + if (i > packet_len - 4) + return -1; + float4get(row->data[j].mfloat, &buf[i]); + i += 4; + break; + + case MYSQL_TYPE_DOUBLE: + if (i > packet_len - 8) + return -1; + float8get(row->data[j].mdouble, &buf[i]); + i += 8; + break; + + /* libmysql/libmysql.c:3370 + * static void read_binary_time(MYSQL_TIME *tm, uchar **pos) */ + case MYSQL_TYPE_TIME: + tmp_len = my_lcb(&buf[i], &len, &nul, packet_len-i); + if (tmp_len == -1) + return -1; + i += tmp_len; + if (i + len > packet_len) + return -1; + if (nul == 1) + row->data[j].blob = NULL; + + if (len > 0) { + row->data[j].tv.tv_sec = + ( uint4korr(&buf[i+1]) * 86400 ) + + ( buf[i+5] * 3600 ) + + ( buf[i+6] * 60 ) + + buf[i+7]; + if (buf[i] != 0) + row->data[j].tv.tv_sec = - row->data[j].tv.tv_sec; + if (len > 8) + row->data[j].tv.tv_usec = uint4korr(&buf[i+8]); + else + row->data[j].tv.tv_usec = 0; + } + i += len; + break; + + case MYSQL_TYPE_YEAR: + row->data[j].tm->tm_year = uint2korr(&buf[i]) - 1900; + row->data[j].tm->tm_mday = 1; + i += 2; + break; + + /* libmysql/libmysql.c:3400 + * static void read_binary_datetime(MYSQL_TIME *tm, uchar **pos) */ + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_DATETIME: + tmp_len = my_lcb(&buf[i], &len, &nul, packet_len-i); + if (tmp_len == -1) + return -1; + i += tmp_len; + if (i + len > packet_len) + return -1; + if (nul == 1) + row->data[j].blob = NULL; + + row->data[j].tm->tm_year = uint2korr(&buf[i+0]) - 1900; + row->data[j].tm->tm_mon = buf[i+2] - 1; + row->data[j].tm->tm_mday = buf[i+3]; + if (len > 4) { + row->data[j].tm->tm_hour = buf[i+4]; + row->data[j].tm->tm_min = buf[i+5]; + row->data[j].tm->tm_sec = buf[i+6]; + } + if (len > 7) { + /* les microsecondes ... */ + } + i += len; + break; + + /* libmysql/libmysql.c:3430 + * static void read_binary_date(MYSQL_TIME *tm, uchar **pos) */ + case MYSQL_TYPE_DATE: + tmp_len = my_lcb(&buf[i], &len, &nul, packet_len-i); + if (tmp_len == -1) + return -1; + i += tmp_len; + if (i + len > packet_len) + return -1; + if (nul == 1) + row->data[j].blob = NULL; + + row->data[j].tm->tm_year = uint2korr(&buf[i+0]) - 1900; + row->data[j].tm->tm_mon = buf[i+2] - 1; + row->data[j].tm->tm_mday = buf[i+3]; + i += len; + break; + + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_SET: + case MYSQL_TYPE_GEOMETRY: + break; + } + } + + /* To next bit */ + bit <<= 1; + + /* To next byte */ + if ( (bit & 255) == 0 ) { + bit = 1; + null_ptr++; + } + } + return wh - buf; +} + +/************************************************** + + read data in string type + +**************************************************/ +int mysac_decode_string_row(char *buf, int packet_len, + MYSAC_RES *res, MYSAC_ROWS *row) { + int i, j; + int tmp_len; + unsigned long len; + char nul; + char *wh; + char mem; + char *error; + + i = 0; + wh = buf; + + for (j = 0; j < res->nb_cols; j++) { + + tmp_len = my_lcb(&buf[i], &len, &nul, packet_len-i); + if (tmp_len == -1) + return -MYERR_BAD_LCB; + + i += tmp_len; + + if (i + len > packet_len) + return -MYERR_LEN_OVER_BUFFER; + + if (nul == 1) { + row->data[j].blob = NULL; + continue; + } + + memmove(wh, &buf[i], len); + row->data[j].blob = wh; + row->data[j].blob[len] = '\0'; + wh += len + 1; + row->lengths[j] = len; + + /* next packet */ + i += len; + } + + return wh - buf; +} + diff --git a/ape-server/modules/deps/mysac/mysac_decode_row.h b/ape-server/modules/deps/mysac/mysac_decode_row.h new file mode 100755 index 0000000..d59d56b --- /dev/null +++ b/ape-server/modules/deps/mysac/mysac_decode_row.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2009 Thierry FOURNIER + * + * This file is part of MySAC. + * + * MySAC 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 3 of the License + * + * MySAC 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 MySAC. If not, see . + */ + +/** @file */ + +#ifndef __MYSAC_DECODE_ROW_H__ +#define __MYSAC_DECODE_ROW_H__ + +#include "mysac.h" + +/** + * This decode mysql row binary format packet + * + * @param buf is the buffer containing packet the strings stored into col, are + * allocated into this buffer + * @param len is the length of the packet + * @param res is valid MYSAC_RES + * @param row is valid col struct space for storing pointers and values + * + * @return the len of the buffer used for storing data or + * -1 if the packet is corrupted + */ +int mysac_decode_binary_row(char *buf, int len, MYSAC_RES *res, MYSAC_ROWS *row); + +/** + * This decode mysql row string format packet + * + * @param buf is the buffer containing packet the strings stored into col, are + * allocated into this buffer + * @param len is the length of the packet + * @param res is valid MYSAC_RES + * @param row is valid col struct space for storing pointers and values + * + * @return the len of the buffer used for storing data or + * -1 if the packet is corrupted + */ +int mysac_decode_string_row(char *buf, int len, MYSAC_RES *res, MYSAC_ROWS *row); + +#endif diff --git a/ape-server/modules/deps/mysac/mysac_encode_values.c b/ape-server/modules/deps/mysac/mysac_encode_values.c new file mode 100755 index 0000000..63693e8 --- /dev/null +++ b/ape-server/modules/deps/mysac/mysac_encode_values.c @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2009 Thierry FOURNIER + * + * This file is part of MySAC. + * + * MySAC 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 3 of the License + * + * MySAC 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 MySAC. If not, see . + */ + +/* the order of theses headers and defines + * is important */ +#include +//#undef _ISOC99_SOURCE +//#define _ISOC99_SOURCE +//#include +//#include +//#include +//#include +//#include +//#include + +#include "mysac.h" +#include "mysac_utils.h" + + + + +/************************************************** + + read data in binary type + +**************************************************/ +int mysac_encode_value(MYSAC_BIND *val, char *out, int len) { + /* + int j; + int i; + char nul; + unsigned long len; + int tmp_len; + char *wh; + char _null_ptr[16]; + char *null_ptr; + unsigned char bit; + */ + int l; +// int data_len; + struct timeval *tv; + struct tm *tm; + unsigned int v; + char sign; + + switch (val->type) { + + /* read null */ + case MYSQL_TYPE_NULL: + l = 0; + break; + + /* read blob */ + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + /* decimal ? maybe for very big num ... crypto key ? */ + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + /* .... */ + case MYSQL_TYPE_BIT: + /* read text */ + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_VARCHAR: + /* read date */ + case MYSQL_TYPE_NEWDATE: + + l = set_my_lcb(val->value_len, 0, out, len); + if (l < 0) + return -1; + + len -= l; + + if (len < val->value_len) + return -1; + + memcpy(&out[l], val->value, val->value_len); + l += val->value_len; + break; + + case MYSQL_TYPE_TINY: + if (len < 1) + return -1; + l = 1; + out[0] = *(int *)val->value; + break; + + case MYSQL_TYPE_SHORT: + if (len < 2) + return -1; + l = 2; + to_my_2(*(int *)val->value, out); + break; + + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_LONG: + if (len < 4) + return -1; + l = 4; + to_my_4(*(int *)val->value, out); + break; + + case MYSQL_TYPE_LONGLONG: + if (len < 8) + return -1; + l = 8; + to_my_8(*(long long int *)val->value, out); + break; + + case MYSQL_TYPE_FLOAT: + if (len < 4) + return -1; + l = 4; + /* TODO: cast a revoir */ + float4store((*(int *)val->value), out); + break; + + case MYSQL_TYPE_DOUBLE: + if (len < 8) + return -1; + l = 8; + /* TODO: cast a revoir */ + float8store(*(int *)val->value, out); + break; + + /* libmysql/libmysql.c:3370 + * static void read_binary_time(MYSQL_TIME *tm, uchar **pos) + * + * n*lcb 4*j 1*h 1*m 1*s 1*sig 4*usec soit 12 bytes + */ + case MYSQL_TYPE_TIME: + + l = set_my_lcb(12, 0, out, len); + if (l < 0) + return -1; + + len -= l; + + if (len < 12) + return -1; + + tv = (struct timeval *)val->value; + if (tv->tv_sec < 0) { + tv->tv_sec = - tv->tv_sec; + sign = 1; + } + else + sign = 0; + + /* nb days */ + to_my_4(tv->tv_sec / 86400, &out[l]); + l += 4; + + /* remainder in secs */ + v = tv->tv_sec % 86400; + + /* nb hours */ + out[l] = v / 3600; + l++; + + /* remainder in secs */ + v = v % 3600; + + /* nb mins */ + out[l] = v / 60; + l++; + + /* secs */ + out[l] = v % 60; + l++; + + /* signe */ + out[l] = sign; + l++; + + /* u secs */ + to_my_4(tv->tv_usec, &out[l]); + l += 4; + + break; + + case MYSQL_TYPE_YEAR: + if (len < 2) + return -1; + + tm = (struct tm *)val->value; + to_my_2(tm->tm_year + 1900, out); + l = 2; + break; + + /* libmysql/libmysql.c:3400 + * static void read_binary_datetime(MYSQL_TIME *tm, uchar **pos) + * + * 1*lcb 2*year 1*mon 1*day 1*hour 1*min 1*sec + */ + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_DATETIME: + + l = set_my_lcb(7, 0, out, len); + if (l < 0) + return -1; + + len -= l; + + if (len < 7) + return -1; + + tm = (struct tm *)val->value; + + to_my_2(tm->tm_year + 1900, &out[l]); + l += 2; + + out[l] = tm->tm_mon + 1; + l++; + + out[l] = tm->tm_mday; + l++; + + out[l] = tm->tm_hour; + l++; + + out[l] = tm->tm_min; + l++; + + out[l] = tm->tm_sec; + l++; + + break; + + /* libmysql/libmysql.c:3430 + * static void read_binary_date(MYSQL_TIME *tm, uchar **pos) + * + * 1*lcb 2*year 1*mon 1*day + */ + case MYSQL_TYPE_DATE: + + l = set_my_lcb(4, 0, out, len); + if (l < 0) + return -1; + + len -= l; + + if (len < 4) + return -1; + + tm = (struct tm *)val->value; + + to_my_2(tm->tm_year + 1900, &out[l]); + l += 2; + + out[l] = tm->tm_mon + 1; + l++; + + out[l] = tm->tm_mday; + l++; + + break; + + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_SET: + case MYSQL_TYPE_GEOMETRY: + /* TODO: a faire */ + break; + } + + return l; +} + diff --git a/ape-server/modules/deps/mysac/mysac_encode_values.h b/ape-server/modules/deps/mysac/mysac_encode_values.h new file mode 100755 index 0000000..9c18c93 --- /dev/null +++ b/ape-server/modules/deps/mysac/mysac_encode_values.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2009 Thierry FOURNIER + * + * This file is part of MySAC. + * + * MySAC 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 3 of the License + * + * MySAC 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 MySAC. If not, see . + */ +#ifndef __MYSAC_ENCODE_VALUES_H__ +#define __MYSAC_ENCODE_VALUES_H__ + +int mysac_encode_value(MYSAC_BIND *val, char *out, int len); + +#endif diff --git a/ape-server/modules/deps/mysac/mysac_errors.c b/ape-server/modules/deps/mysac/mysac_errors.c new file mode 100755 index 0000000..90236cc --- /dev/null +++ b/ape-server/modules/deps/mysac/mysac_errors.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2009 Thierry FOURNIER + * + * This file is part of MySAC. + * + * MySAC 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 3 of the License + * + * MySAC 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 MySAC. If not, see . + */ + +#include "mysac.h" + +const char *mysac_errors[] = { + [0] = "no error", + [MYERR_PROTOCOL_ERROR] = "mysql protocol error", + [MYERR_BUFFER_OVERSIZE] = "buffer oversize", + [MYERR_PACKET_CORRUPT] = "packet corrupted", + [MYERR_WANT_READ] = "mysac need to read data on socket", + [MYERR_WANT_WRITE] = "mysac need to write data on socket", + [MYERR_UNKNOWN_ERROR] = "unknown error", + [MYERR_MYSQL_ERROR] = "mysql server return an error", + [MYERR_SERVER_LOST] = "server network connexion is break", + [MYERR_BAD_PORT] = "bad port number", + [MYERR_BAD_STATE] = "unexpected internal error: bad state", + [MYERR_RESOLV_HOST] = "can not resolve host name", + [MYERR_SYSTEM] = "system error (see errno)", + [MYERR_CANT_CONNECT] = "can not connect to host", + [MYERR_BUFFER_TOO_SMALL] = "the buffer can not contain request", + [MYERR_UNEXPECT_R_STATE] = "Unexpected state when reading data", + [MYERR_STRFIELD_CORRUPT] = "Mysql string mode field corrupt", + [MYERR_BINFIELD_CORRUPT] = "Mysql binary mode field corrupt", + [MYERR_BAD_LCB] = "Mysql protocol bad length coded binary", + [MYERR_LEN_OVER_BUFFER] = "Mysql protocol give len over the packet size", + [MYERR_CONVLONG] = "Error in string to long int type conversion", + [MYERR_CONVLONGLONG] = "Error in string to long long int type conversion", + [MYERR_CONVFLOAT] = "Error in string to float type conversion", + [MYERR_CONVDOUBLE] = "Error in string to double type conversion", + [MYERR_CONVTIME] = "Error in time string to time conversion", + [MYERR_CONVTIMESTAMP] = "Error in timestamp string to time conversion", + [MYERR_CONVDATE] = "Error in date string to time conversion" +}; + diff --git a/ape-server/modules/deps/mysac/mysac_net.c b/ape-server/modules/deps/mysac/mysac_net.c new file mode 100755 index 0000000..03cba93 --- /dev/null +++ b/ape-server/modules/deps/mysac/mysac_net.c @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2009 Thierry FOURNIER + * + * This file is part of MySAC. + * + * MySAC 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 3 of the License + * + * MySAC 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 MySAC. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mysac.h" + +int mysac_socket_connect(const char *socket_name, int *fd) { + int ret_code; + int listen_socket; + int conf_socket_type = -1; + struct sockaddr_storage conf_adress; + int one = 1; + char *error; + int already_binded = 0; + int i; + char *conf_addr; + int conf_port; + char path[1024]; + + memset(&conf_adress, 0, sizeof(struct sockaddr_storage)); + + // ----------------------------------- + // detect address type + // ----------------------------------- + + // copy data + strncpy(path, socket_name, 1024); + + // on cherche le separateur ':' + for(i=strlen(path)-1; i>0; i--) + if (path[i]==':') + break; + + // si on l'à trouvé, on sépare l'indicateur de reseau du port + if (path[i]==':') { + path[i] = '\0'; + conf_addr = path; + conf_port = strtol(&path[i+1], &error, 10); + if (*error != '\0') + return MYERR_BAD_PORT; + } + + // si on ne trouve pas de separateur, ben c'est + // que c'est une socket unxi + else + conf_socket_type = AF_UNIX; + + // AF_UNIX detected create pipe and buils structur + if ( conf_socket_type == AF_UNIX ) { + ((struct sockaddr_un *)&conf_adress)->sun_family = AF_UNIX; + strncpy(((struct sockaddr_un *)&conf_adress)->sun_path, path, + sizeof(((struct sockaddr_un *)&conf_adress)->sun_path) - 1); + + goto end_of_building_address; + } + + // AF_INET detected, builds address structur + ret_code = inet_pton(AF_INET, conf_addr, + &((struct sockaddr_in *)&conf_adress)->sin_addr.s_addr); + if (ret_code > 0) { + conf_socket_type = AF_INET; + ((struct sockaddr_in *)&conf_adress)->sin_port = htons(conf_port); + ((struct sockaddr_in *)&conf_adress)->sin_family = AF_INET; + goto end_of_building_address; + } + + // AF_INET6 detected, builds address structur + ret_code = inet_pton(AF_INET6, conf_addr, + &((struct sockaddr_in6 *)&conf_adress)->sin6_addr.s6_addr); + if (ret_code > 0) { + conf_socket_type = AF_INET6; + ((struct sockaddr_in6 *)&conf_adress)->sin6_port = htons(conf_port); + ((struct sockaddr_in6 *)&conf_adress)->sin6_family = AF_INET6; + goto end_of_building_address; + } + + // adress format error + return MYERR_RESOLV_HOST; + + end_of_building_address: + + // open socket for AF_UNIX + if (conf_socket_type == AF_UNIX) { + listen_socket = socket(AF_UNIX, SOCK_STREAM, 0); + if (listen_socket < 0) + return MYERR_SYSTEM; + } + + // open socket for network protocol + else { + listen_socket = socket(conf_socket_type, SOCK_STREAM, IPPROTO_TCP); + if (listen_socket < 0) + return MYERR_SYSTEM; + } + + // set non block opt + ret_code = fcntl(listen_socket, F_SETFL, O_NONBLOCK); + if (ret_code < 0) { + close(listen_socket); + return MYERR_SYSTEM; + } + + // tcp no delay + if (conf_socket_type == AF_INET6 || conf_socket_type == AF_INET ) { + ret_code = setsockopt(listen_socket, IPPROTO_TCP, TCP_NODELAY, + (char *)&one, sizeof(one)); + if (ret_code < 0) { + close(listen_socket); + return MYERR_SYSTEM; + } + } + + // reuse addr + if (conf_socket_type == AF_INET6 || conf_socket_type == AF_INET ) { + ret_code = setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, + (char *)&one, sizeof(one)); + if (ret_code < 0) { + close(listen_socket); + return MYERR_SYSTEM; + } + } + + // bind address + if (already_binded == 0) { + switch (conf_socket_type) { + case AF_INET: + ret_code = connect(listen_socket, + (struct sockaddr *)&conf_adress, + sizeof(struct sockaddr_in)); + break; + + case AF_INET6: + ret_code = connect(listen_socket, + (struct sockaddr *)&conf_adress, + sizeof(struct sockaddr_in6)); + break; + + case AF_UNIX: + ret_code = connect(listen_socket, + (struct sockaddr *)&conf_adress, + sizeof(struct sockaddr_un)); + break; + } + if (ret_code < 0 && errno != EINPROGRESS){ + close(listen_socket); + return MYERR_SYSTEM; + } + } + + /* return filedescriptor */ + *fd = listen_socket; + return 0; +} + +int mysac_socket_connect_check(int fd) { + int ret; + int code; + size_t len = sizeof(int); + + ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &code, &len); + if (ret != 0) { + return MYERR_SYSTEM; + } + if (code != 0) { + return MYERR_CANT_CONNECT; + } + return 0; +} + +ssize_t mysac_read(int fd, void *buf, size_t count, int *err) { + ssize_t len; + + len = read(fd, buf, count); + + if (len == 0) { + *err = MYERR_SERVER_LOST; + return -1; + } + + if (len == -1) { + if (errno == EAGAIN) + *err = MYERR_WANT_READ; + else + *err = MYERR_SERVER_LOST; + return -1; + } + + *err = 0; + return len; +} + +ssize_t mysac_write(int fd, const void *buf, size_t len, int *err) { + ssize_t ret; + + ret = write(fd, buf, len); + + if (ret == -1) { + if (errno == EAGAIN) + *err = MYERR_WANT_WRITE; + else + *err = MYERR_SERVER_LOST; + return -1; + } + + *err = 0; + return ret; +} + diff --git a/ape-server/modules/deps/mysac/mysac_net.h b/ape-server/modules/deps/mysac/mysac_net.h new file mode 100755 index 0000000..17bd053 --- /dev/null +++ b/ape-server/modules/deps/mysac/mysac_net.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2009 Thierry FOURNIER + * + * This file is part of MySAC. + * + * MySAC 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 3 of the License + * + * MySAC 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 MySAC. If not, see . + */ + +#ifndef __MYSAC_NET_H__ +#define __MYSAC_NET_H__ + +int mysac_socket_connect(const char *socket_name, int *fd); +int mysac_socket_connect_check(int fd); +ssize_t mysac_read(int fd, void *buf, size_t count, int *err); +ssize_t mysac_write(int fd, const void *buf, size_t len, int *err); + +#endif diff --git a/ape-server/modules/deps/mysac/mysac_utils.h b/ape-server/modules/deps/mysac/mysac_utils.h new file mode 100755 index 0000000..78dcd44 --- /dev/null +++ b/ape-server/modules/deps/mysac/mysac_utils.h @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2009 Thierry FOURNIER + * + * This file is part of MySAC. + * + * MySAC 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 3 of the License + * + * MySAC 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 MySAC. If not, see . + */ + +#ifndef __MYSAC_UTILS_H__ +#define __MYSAC_UTILS_H__ + +#include + +#include "mysac.h" + +/* definitions imported from linux-2.6.24/include/linux/list.h */ + +static inline void INIT_LIST_HEAD(struct mysac_list_head *list) { + list->next = list; + list->prev = list; +} + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct mysac_list_head *new, + struct mysac_list_head *prev, + struct mysac_list_head *next) { + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct mysac_list_head *new, + struct mysac_list_head *head) { + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct mysac_list_head * prev, + struct mysac_list_head * next) { + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty() on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void list_del(struct mysac_list_head *entry) { + __list_del(entry->prev, entry->next); +} + + + +static inline void to_my_2(int value, char *m) { + m[1] = value >> 8; + m[0] = value; +} +static inline void to_my_3(int value, char *m) { + m[2] = value >> 16; + m[1] = value >> 8; + m[0] = value; +} +static inline void to_my_4(int value, char *m) { + m[3] = value >> 24; + m[2] = value >> 16; + m[1] = value >> 8; + m[0] = value; +} +static inline void to_my_8(long long value, char *m) { + m[7] = value >> 56; + m[6] = value >> 48; + m[5] = value >> 40; + m[4] = value >> 32; + m[3] = value >> 24; + m[2] = value >> 16; + m[1] = value >> 8; + m[0] = value; +} + +/* length coded binary + 0-250 0 = value of first byte + 251 0 column value = NULL + only appropriate in a Row Data Packet + 252 2 = value of following 16-bit word + 253 3 = value of following 24-bit word + 254 8 = value of following 64-bit word + + fichier mysql: source mysql: sql/pack.c +*/ +static inline int my_lcb_ll(char *m, unsigned long long *r, char *nul, int len) { + if (len < 1) + return -1; + switch ((unsigned char)m[0]) { + + case 251: /* fb : 1 octet */ + *r = 0; + *nul=1; + return 1; + + case 252: /* fc : 2 octets */ + if (len < 3) + return -1; + *r = uint2korr(&m[1]); + *nul=0; + return 3; + + case 253: /* fd : 3 octets */ + if (len < 4) + return -1; + *r = uint3korr(&m[1]); + *nul=0; + return 4; + + case 254: /* fe */ + if (len < 9) + return -1; + *r = uint8korr(&m[1]); + *nul=0; + return 9; + + default: + *r = (unsigned char)m[0]; + *nul=0; + return 1; + } +} + +static inline int my_lcb(char *m, unsigned long *r, char *nul, int len) { + unsigned long long val; + int retcode; + retcode = my_lcb_ll(m, &val, nul, len); + *r = val; + return retcode; +} + +/* length coded binary + 0-250 0 = value of first byte + 251 0 column value = NULL + only appropriate in a Row Data Packet + 252 2 = value of following 16-bit word + 253 3 = value of following 24-bit word + 254 8 = value of following 64-bit word + + fichier mysql: source mysql: sql/pack.c +*/ +static inline int set_my_lcb(unsigned long long len, int null, char *out, int out_len) { + + /* set null */ + if (null == 1) { + if (out_len < 1) + return -1; + out[0] = 251; + return 1; + } + + /* set value 0-250 */ + if (len >= 0 && len <= 250) { + if (out_len < 1) + return -1; + out[0] = len; + return 1; + } + + /* set 2 bytes value 251-65535 */ + if (len >= 251 && len < 0xffff) { + if (out_len < 3) + return -1; + out[0] = 252; + to_my_2(len, &out[1]); + return 3; + } + + /* set 3 bytes value 65536-16777215 */ + if (len > 0xffff && len <= 0xffffff) { + if (out_len < 4) + return -1; + out[0] = 253; + to_my_3(len, &out[1]); + return 4; + } + + /* set 4 bytes value 65536-4294967295 */ + if (len > 0xffffff && len <= 0xffffffff) { + if (out_len < 5) + return -1; + out[0] = 254; + to_my_4(len, &out[1]); + return 5; + } + + /* set 8 bytes value 4294967296-18446744073709551615 */ + if (len > 0xffffffff && len <= 0xffffffffffffffffULL) { + if (out_len < 9) + return -1; + out[0] = 254; + to_my_4(len, &out[1]); + return 9; + } + + /* error */ + return -1; +} + +#endif diff --git a/ape-server/modules/deps/mysac/mysac_ver b/ape-server/modules/deps/mysac/mysac_ver new file mode 100755 index 0000000..df4ee52 --- /dev/null +++ b/ape-server/modules/deps/mysac/mysac_ver @@ -0,0 +1,19 @@ +#!/bin/bash + +# test if git is ok +git >/dev/null 2>&1 +if test $? -ge 127; then + cat VERSION + exit 0 +fi + +# test if this archive is dev with git +if test ! -d .git; then + cat VERSION + exit 0 +fi + +# get dev version +ref=`(git describe --tags) 2>/dev/null` +ref=${ref%-g*} +echo "${ref#v}" diff --git a/ape-server/modules/global_plugins.h b/ape-server/modules/global_plugins.h new file mode 100755 index 0000000..c3f8826 --- /dev/null +++ b/ape-server/modules/global_plugins.h @@ -0,0 +1,22 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009 Anthony Catel + + This file is part of ACE Server. + ACE 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. + + ACE 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 ACE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* global_plugins.h */ + + diff --git a/ape-server/modules/lib/README b/ape-server/modules/lib/README new file mode 100755 index 0000000..b8892bc --- /dev/null +++ b/ape-server/modules/lib/README @@ -0,0 +1 @@ +All the .so placed here will be loaded. diff --git a/ape-server/modules/libape-spidermonkey.c b/ape-server/modules/libape-spidermonkey.c new file mode 100644 index 0000000..33a1fbc --- /dev/null +++ b/ape-server/modules/libape-spidermonkey.c @@ -0,0 +1,3193 @@ +/* + Copyright (C) 2009 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* Javascript plugins support using spidermonkey API */ +/* HOWTO : http://www.ape-project.org/wiki/index.php/How_to_build_a_serverside_JS_module */ + +#define XP_UNIX + +#include "../src/configure.h" +#ifdef _USE_MYSQL +#include +#endif +#include +#include +#include +#include "plugins.h" +#include "global_plugins.h" + +#define MODULE_NAME "spidermonkey" + +/* Return the global SpiderMonkey Runtime instance e.g. ASMR->runtime */ +#define ASMR ((ape_sm_runtime *)get_property(g_ape->properties, "sm_runtime")->val) +#define ASMC ((JSContext *)get_property(g_ape->properties, "sm_context")->val) + +#define APEUSER_TO_JSOBJ(apeuser) \ + (JSObject *)get_property(apeuser->properties, "jsobj")->val +#define APESUBUSER_TO_JSOBJ(apeuser) \ + (JSObject *)get_property(apeuser->properties, "jsobj")->val +#define APECHAN_TO_JSOBJ(apechan) \ + (JSObject *)get_property(apechan->properties, "jsobj")->val + +#define APE_JS_EVENT(cb, argc, argv) ape_fire_callback(cb, argc, argv, g_ape) +static int ape_fire_cmd(const char *name, JSObject *obj, JSObject *cb, callbackp *callbacki, acetables *g_ape); + +static JSObject *ape_json_to_jsobj(JSContext *cx, json_item *head, JSObject *root); +/* JSNative macro prototype */ +#define APE_JS_NATIVE(func_name) \ + static JSBool func_name(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) \ + {\ + ape_sm_compiled *asc; \ + acetables *g_ape; \ + asc = JS_GetContextPrivate(cx); \ + g_ape = asc->g_ape;\ + +#define APE_JS_NATIVE_END(func_name) } + +#ifdef _USE_MYSQL +static void apemysql_finalize(JSContext *cx, JSObject *jsmysql); +#endif + +static JSBool ape_sm_stub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return JS_TRUE; +} + +typedef enum { + APE_EVENT, + APE_CMD, + APE_HOOK, + APE_BADCMD +} ape_sm_callback_t; + +typedef struct _ape_sm_callback ape_sm_callback; +struct _ape_sm_callback +{ + char *callbackname; + jsval func; + ape_sm_callback_t type; + JSContext *cx; + struct _ape_sm_callback *next; +}; +static int ape_fire_hook(ape_sm_callback *cbk, JSObject *obj, JSObject *cb, callbackp *callbacki, acetables *g_ape); + +typedef struct _ape_sm_compiled ape_sm_compiled; +struct _ape_sm_compiled { + char *filename; + + JSScript *bytecode; + JSContext *cx; + JSObject *global; + JSObject *scriptObj; + + acetables *g_ape; + + struct { + ape_sm_callback *head; + ape_sm_callback *foot; + } callbacks; + + struct _ape_sm_compiled *next; +}; + +typedef struct _ape_sm_runtime ape_sm_runtime; +struct _ape_sm_runtime { + JSRuntime *runtime; + + ape_sm_compiled *scripts; +}; + +struct _ape_sock_callbacks { + + JSObject *server_obj; + ape_sm_compiled *asc; + short int state; + void *private; +}; + +struct _ape_sock_js_obj { + ape_socket *client; + JSObject *client_obj; +}; + +#ifdef _USE_MYSQL +struct _ape_mysql_queue { + struct _ape_mysql_queue *next; + MYSAC_RES *res; + char *query; + unsigned int query_len; + jsval callback; +}; +#endif + +typedef enum { + SQL_READY_FOR_QUERY, + SQL_NEED_QUEUE +} ape_mysql_state_t; + +#ifdef _USE_MYSQL +struct _ape_mysql_data { + MYSAC *my; + void (*on_success)(struct _ape_mysql_data *, int); + JSObject *jsmysql; + JSContext *cx; + char *db; + void *data; + jsval callback; + ape_mysql_state_t state; + + struct { + struct _ape_mysql_queue *head; + struct _ape_mysql_queue *foot; + } queue; +}; + +static void mysac_query_success(struct _ape_mysql_data *myhandle, int code); +static struct _ape_mysql_queue *apemysql_push_queue(struct _ape_mysql_data *myhandle, char *query, unsigned int query_len, jsval callback); +static void apemysql_shift_queue(struct _ape_mysql_data *myhandle); +#endif +//static JSBool sockserver_addproperty(JSContext *cx, JSObject *obj, jsval idval, jsval *vp); + +static ace_plugin_infos infos_module = { + "Javascript embeded", // Module Name + "0.01", // Module Version + "Anthony Catel", // Module Author + "javascript.conf" // Config file +}; + +static JSClass apesocket_class = { + "apesocket", JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +/* Standard javascript object */ +static JSClass global_class = { + "global", JSCLASS_GLOBAL_FLAGS, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, + JSCLASS_NO_OPTIONAL_MEMBERS + +}; + +/* The main Ape Object (global) */ +static JSClass ape_class = { + "Ape", JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +static JSClass b64_class = { + "base64", JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +static JSClass sha1_class = { + "sha1", JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +static JSClass socketserver_class = { + "sockServer", JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +static JSClass socketclient_class = { + "sockClient", JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +static JSClass raw_class = { + "raw", JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +static JSClass user_class = { + "user", JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +static JSClass subuser_class = { + "subuser", JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +static JSClass channel_class = { + "channel", JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +static JSClass pipe_class = { + "pipe", JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +#ifdef _USE_MYSQL +static JSClass mysql_class = { + "MySQL", JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, apemysql_finalize, + JSCLASS_NO_OPTIONAL_MEMBERS +}; +#endif + +static JSClass cmdresponse_class = { + "cmdresponse", JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + + +APE_JS_NATIVE(apesocket_write) +//{ + JSString *string; + JSBool burn = JS_FALSE; + struct _ape_sock_callbacks *cb = JS_GetPrivate(cx, obj); + ape_socket *client; + + if (cb == NULL) { + return JS_TRUE; + } + + client = ((struct _ape_sock_js_obj *)cb->private)->client; + + if (client == NULL || !JS_ConvertArguments(cx, argc, argv, "S/b", &string, &burn)) { + return JS_TRUE; + } + + sendbin(client->fd, JS_GetStringBytes(string), JS_GetStringLength(string), (burn == JS_TRUE ? 1 : 0), g_ape); + + return JS_TRUE; +} + +APE_JS_NATIVE(apesocketclient_write) +//{ + JSString *string; + JSBool burn = JS_FALSE; + ape_socket *client = JS_GetPrivate(cx, obj); + + if (client == NULL) { + return JS_TRUE; + } + + if (!JS_ConvertArguments(cx, argc, argv, "S/b", &string)) { + return JS_TRUE; + } + + sendbin(client->fd, JS_GetStringBytes(string), JS_GetStringLength(string), (burn == JS_TRUE ? 1 : 0), g_ape); + + return JS_TRUE; +} + +static JSBool apesocketclient_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + ape_socket *client = JS_GetPrivate(cx, obj); + + if (client == NULL) { + return JS_TRUE; + } + + shutdown(client->fd, 2); + + return JS_TRUE; +} + +APE_JS_NATIVE(apesocket_close) +//{ + ape_socket *client; + JSBool safe = JS_FALSE; + + struct _ape_sock_callbacks *cb = JS_GetPrivate(cx, obj); + + if (cb == NULL || !cb->state) { + return JS_TRUE; + } + + client = ((struct _ape_sock_js_obj *)cb->private)->client; + + if (client == NULL) { + return JS_TRUE; + } + + if (!JS_ConvertArguments(cx, argc, argv, "/b", &safe)) { + return JS_TRUE; + } + + cb->state = 0; + + if (safe == JS_TRUE) { + shutdown(client->fd, 2); + } else { + safe_shutdown(client->fd, g_ape); + } + return JS_TRUE; +} + +APE_JS_NATIVE(apesocketserver_close) +//{ + ape_socket *server; + + server = JS_GetPrivate(cx, obj); + + if (server == NULL) { + return JS_TRUE; + } + + shutdown(server->fd, 2); + + return JS_TRUE; +} + +static json_item *jsobj_to_ape_json(JSContext *cx, JSObject *json_obj) +{ + unsigned int i, length = 0, isarray = 0; + jsval propname; + JSIdArray *enumjson = NULL; + json_item *ape_json = NULL; + + /* TODO Fixme : If array has no contigus values, they cannot be retrived, use JS_NewPropertyIterator, JS_NextProperty */ + + if (JS_IsArrayObject(cx, json_obj) == JS_TRUE) { + isarray = 1; + JS_GetArrayLength(cx, json_obj, &length); + if (length) { + //ape_json = json_new_array(); + } + ape_json = json_new_array(); + } else { + enumjson = JS_Enumerate(cx, json_obj); + if ((length = enumjson->length)) { + //ape_json = json_new_object(); + } + ape_json = json_new_object(); + } + + for (i = 0; i < length; i++) { + jsval vp; + JSString *key = NULL; + JSString *value = NULL; + json_item *val_obj = NULL; + + if (!isarray) { + JS_IdToValue(cx, enumjson->vector[i], &propname); + key = JS_ValueToString(cx, propname); + JS_GetProperty(cx, json_obj, JS_GetStringBytes(key), &vp); + } else { + JS_GetElement(cx, json_obj, i, &vp); + } + + switch(JS_TypeOfValue(cx, vp)) { + case JSTYPE_VOID: + + break; + case JSTYPE_OBJECT: + + /* hmm "null" is an empty object */ + if (JSVAL_TO_OBJECT(vp) == NULL) { + if (!isarray) { + json_set_property_null(ape_json, JS_GetStringBytes(key), JS_GetStringLength(key)); + } else { + json_set_element_null(ape_json); + } + break; + } + if ((val_obj = jsobj_to_ape_json(cx, JSVAL_TO_OBJECT(vp))) != NULL) { + if (!isarray) { + json_set_property_objN(ape_json, JS_GetStringBytes(key), JS_GetStringLength(key), val_obj); + } else { + json_set_element_obj(ape_json, val_obj); + } + } + break; + case JSTYPE_FUNCTION: + + break; + case JSTYPE_STRING: + + value = JSVAL_TO_STRING(vp); + + if (!isarray) { + json_set_property_strN(ape_json, JS_GetStringBytes(key), JS_GetStringLength(key), JS_GetStringBytes(value), JS_GetStringLength(value)); + } else { + json_set_element_strN(ape_json, JS_GetStringBytes(value), JS_GetStringLength(value)); + } + + break; + case JSTYPE_NUMBER: + { + jsdouble dp; + JS_ValueToNumber(cx, vp, &dp); + + if (!isarray) { + /* json_set_property_intN(ape_json, JS_GetStringBytes(key), JS_GetStringLength(key), (long int)dp); */ + json_set_property_floatN(ape_json, JS_GetStringBytes(key), JS_GetStringLength(key), dp); + } else { + /* json_set_element_int(ape_json, (long int)dp); */ + json_set_element_float(ape_json, dp); + } + } + break; + case JSTYPE_BOOLEAN: + if (!isarray) { + json_set_property_boolean(ape_json, JS_GetStringBytes(key), JS_GetStringLength(key), (vp == JSVAL_TRUE)); + } else { + json_set_element_boolean(ape_json, (vp == JSVAL_TRUE)); + } + break; + default: + + break; + } + } + if (!isarray) { + JS_DestroyIdArray(cx, enumjson); + } + + return ape_json; +} + + +APE_JS_NATIVE(apepipe_sm_get_property) +//{ + const char *property; + transpipe *pipe = JS_GetPrivate(cx, obj); + + if (pipe == NULL) { + return JS_TRUE; + } + + if (!JS_ConvertArguments(cx, 1, argv, "s", &property)) { + return JS_TRUE; + } + if (strcmp(property, "pubid") == 0) { + *rval = STRING_TO_JSVAL(JS_NewStringCopyN(cx, pipe->pubid, 32)); + } else { + extend *getprop = get_property(pipe->properties, property); + if (getprop != NULL) { + if (getprop->type == EXTEND_STR) { + *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, getprop->val)); + } else if (getprop->type == EXTEND_JSON) { + JSObject *propobj = ape_json_to_jsobj(cx, ((json_item *)getprop->val)->jchild.child, NULL); + *rval = OBJECT_TO_JSVAL(propobj); + } + } + } + + return JS_TRUE; +} + +APE_JS_NATIVE(apepipe_sm_get_parent) +//{ + transpipe *pipe = JS_GetPrivate(cx, obj); + + if (pipe == NULL) { + return JS_TRUE; + } + + switch(pipe->type) { + case USER_PIPE: + *rval = OBJECT_TO_JSVAL(APEUSER_TO_JSOBJ(((USERS*)pipe->pipe))); + case CHANNEL_PIPE: + *rval = OBJECT_TO_JSVAL(APECHAN_TO_JSOBJ(((CHANNEL*)pipe->pipe))); + default: + break; + } + + return JS_TRUE; +} + +APE_JS_NATIVE(apepipe_sm_set_property) +//{ + char *key; + transpipe *pipe = JS_GetPrivate(cx, obj); + int typextend = EXTEND_STR; + void *valuextend = NULL; + + if (pipe == NULL) { + return JS_TRUE; + } + + if (argc != 2) { + return JS_TRUE; + } + + if (!JS_ConvertArguments(cx, 1, argv, "s", &key)) { + return JS_TRUE; + } + + if (JSVAL_IS_OBJECT(argv[1])) { /* Convert to APE JSON Object */ + json_item *ji; + + if ((ji = jsobj_to_ape_json(cx, JSVAL_TO_OBJECT(argv[1]))) != NULL) { + typextend = EXTEND_JSON; + valuextend = ji; + } + } else { /* Convert to string */ + typextend = EXTEND_STR; + valuextend = JS_GetStringBytes(JS_ValueToString(cx, argv[1])); /* No needs to be gc-rooted while there is no JSAPI Call after that */ + } + + switch(pipe->type) { + case USER_PIPE: + /* Set property on directly on the user (not on the pipe) */ + add_property(&((USERS *)(pipe->pipe))->properties, key, valuextend, typextend, EXTEND_ISPUBLIC); + break; + case CHANNEL_PIPE: + /* Set property on directly on the channel (not on the pipe) */ + add_property(&((CHANNEL *)(pipe->pipe))->properties, key, valuextend, typextend, EXTEND_ISPUBLIC); + break; + case CUSTOM_PIPE: + add_property(&pipe->properties, key, valuextend, typextend, EXTEND_ISPUBLIC); + default: + break; + } + + return JS_TRUE; +} + + +APE_JS_NATIVE(apepipe_sm_to_object) +//{ + transpipe *spipe; + json_item *pipe_object; + JSObject *js_pipe_object; + + if ((spipe = JS_GetPrivate(cx, obj)) == NULL) { + return JS_TRUE; + } + + pipe_object = get_json_object_pipe(spipe); + + js_pipe_object = ape_json_to_jsobj(cx, pipe_object->jchild.child, NULL); + + *rval = OBJECT_TO_JSVAL(js_pipe_object); + + return JS_TRUE; + +} + +APE_JS_NATIVE(apepipe_sm_destroy) +//{ + transpipe *pipe = JS_GetPrivate(cx, obj); + + if (pipe == NULL) { + return JS_TRUE; + } + + JS_SetPrivate(cx, obj, (void *)NULL); + destroy_pipe(pipe, g_ape); + + return JS_TRUE; +} + +static JSBool sm_send_raw(JSContext *cx, transpipe *to_pipe, int chl, uintN argc, jsval *argv, acetables *g_ape) +{ + RAW *newraw; + const char *raw; + JSObject *json_obj = NULL, *options = NULL; + json_item *jstr; + jsval vp; + + if (to_pipe == NULL) { + return JS_TRUE; + } + + if (!JS_ConvertArguments(cx, 3, argv, "so/o", &raw, &json_obj, &options) || json_obj == NULL) { + return JS_TRUE; + } + + jstr = jsobj_to_ape_json(cx, json_obj); + + if (options != NULL && JS_GetProperty(cx, options, "from", &vp) && JSVAL_IS_OBJECT(vp) && JS_InstanceOf(cx, JSVAL_TO_OBJECT(vp), &pipe_class, 0) == JS_TRUE) { + JSObject *js_pipe = JSVAL_TO_OBJECT(vp); + transpipe *from_pipe = JS_GetPrivate(cx, js_pipe); + + if (from_pipe != NULL && from_pipe->type == USER_PIPE) { + json_set_property_objN(jstr, "from", 4, get_json_object_pipe(from_pipe)); + + if (to_pipe->type == USER_PIPE) { + json_set_property_objN(jstr, "pipe", 4, get_json_object_pipe(from_pipe)); + } else if (to_pipe->type == CHANNEL_PIPE) { + json_item *jcopy = json_item_copy(jstr, NULL); + if (((CHANNEL*)to_pipe->pipe)->head != NULL && ((CHANNEL*)to_pipe->pipe)->head->next != NULL) { + + json_set_property_objN(jstr, "pipe", 4, get_json_object_pipe(to_pipe)); + + newraw = forge_raw(raw, jstr); + post_raw_channel_restricted(newraw, to_pipe->pipe, from_pipe->pipe, g_ape); + } + if (options != NULL && JS_GetProperty(cx, options, "restrict", &vp) && JSVAL_IS_OBJECT(vp) && JS_InstanceOf(cx, JSVAL_TO_OBJECT(vp), &subuser_class, 0) == JS_TRUE) { + JSObject *subjs = JSVAL_TO_OBJECT(vp); + subuser *sub = JS_GetPrivate(cx, subjs); + if (sub != NULL && ((USERS *)from_pipe->pipe)->nsub > 1) { + json_set_property_objN(jcopy, "pipe", 4, get_json_object_pipe(to_pipe)); + newraw = forge_raw(raw, jcopy); + post_raw_restricted(newraw, from_pipe->pipe, sub, g_ape); + } else { + free_json_item(jcopy); + } + + } else { + free_json_item(jcopy); + } + return JS_TRUE; + } + } else if (from_pipe != NULL && from_pipe->type == CUSTOM_PIPE) { + json_set_property_objN(jstr, "pipe", 4, get_json_object_pipe(from_pipe)); + } + } + + /* in the case of sendResponse */ + /* TODO : May be borken if to_pipe->type == CHANNNEL and from == USER */ + if (chl) { + json_set_property_intN(jstr, "chl", 3, chl); + } + + newraw = forge_raw(raw, jstr); + + if (to_pipe->type == CHANNEL_PIPE) { + if (options != NULL && JS_GetProperty(cx, options, "restrict", &vp) && JSVAL_IS_OBJECT(vp) && JS_InstanceOf(cx, JSVAL_TO_OBJECT(vp), &user_class, 0) == JS_TRUE) { + JSObject *userjs = JSVAL_TO_OBJECT(vp); + USERS *user = JS_GetPrivate(cx, userjs); + + if (user == NULL) { + free_raw(newraw); + + return JS_TRUE; + } + + post_raw_channel_restricted(newraw, to_pipe->pipe, user, g_ape); + + return JS_TRUE; + } + post_raw_channel(newraw, to_pipe->pipe, g_ape); + } else { + if (options != NULL && JS_GetProperty(cx, options, "restrict", &vp) && JSVAL_IS_OBJECT(vp) && JS_InstanceOf(cx, JSVAL_TO_OBJECT(vp), &subuser_class, 0) == JS_TRUE) { + JSObject *subjs = JSVAL_TO_OBJECT(vp); + subuser *sub = JS_GetPrivate(cx, subjs); + + if (sub == NULL || ((USERS *)to_pipe->pipe)->nsub < 2 || to_pipe->pipe != sub->user) { + free_raw(newraw); + return JS_TRUE; + } + + post_raw_restricted(newraw, to_pipe->pipe, sub, g_ape); + + return JS_TRUE; + + } + post_raw(newraw, to_pipe->pipe, g_ape); + } + + return JS_TRUE; +} + +APE_JS_NATIVE(apepipe_sm_send_raw) +//{ + transpipe *to_pipe = JS_GetPrivate(cx, obj); + + if (to_pipe == NULL) { + return JS_TRUE; + } + + return sm_send_raw(cx, to_pipe, 0, argc, argv, g_ape); + +} + +APE_JS_NATIVE(apepipe_sm_send_response) +//{ + jsval user, chl, pipe; + JS_GetProperty(cx, obj, "user", &user); + + if (user == JSVAL_VOID || JS_InstanceOf(cx, JSVAL_TO_OBJECT(user), &user_class, 0) == JS_FALSE) { + return JS_TRUE; + } + + JS_GetProperty(cx, JSVAL_TO_OBJECT(user), "pipe", &pipe); + + if (pipe == JSVAL_VOID || JS_InstanceOf(cx, JSVAL_TO_OBJECT(pipe), &pipe_class, 0) == JS_FALSE) { + return JS_TRUE; + } + + JS_GetProperty(cx, obj, "chl", &chl); + + if (chl == JSVAL_VOID || !JSVAL_IS_NUMBER(chl)) { + return JS_TRUE; + } + + /* TODO : Fixme JSVAL_TO_INT => double */ + return sm_send_raw(cx, JS_GetPrivate(cx, JSVAL_TO_OBJECT(pipe)), JSVAL_TO_INT(chl), argc, argv, g_ape); +} + +APE_JS_NATIVE(apechannel_sm_get_property) +//{ + const char *property; + CHANNEL *chan = JS_GetPrivate(cx, obj); + + if (chan == NULL) { + return JS_TRUE; + } + + if (!JS_ConvertArguments(cx, 1, argv, "s", &property)) { + return JS_TRUE; + } + + if (strcmp(property, "pubid") == 0) { + *rval = STRING_TO_JSVAL(JS_NewStringCopyN(cx, chan->pipe->pubid, 32)); + } else if (strcmp(property, "name") == 0) { + *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, chan->name)); + } else { + extend *getprop = get_property(chan->properties, property); + if (getprop != NULL) { + if (getprop->type == EXTEND_STR) { + *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, getprop->val)); + } else if (getprop->type == EXTEND_JSON) { + JSObject *propobj = ape_json_to_jsobj(cx, ((json_item *)getprop->val)->jchild.child, NULL); + *rval = OBJECT_TO_JSVAL(propobj); + } + } + } + + return JS_TRUE; +} + +APE_JS_NATIVE(apechannel_sm_set_property) +//{ + char *key; + JSString *property; + + CHANNEL *chan = JS_GetPrivate(cx, obj); + + if (chan == NULL) { + return JS_TRUE; + } + + if (!JS_ConvertArguments(cx, 2, argv, "s", &key)) { + return JS_TRUE; + } + + if (JSVAL_IS_OBJECT(argv[1])) { /* Convert to APE JSON Object */ + json_item *ji; + + if ((ji = jsobj_to_ape_json(cx, JSVAL_TO_OBJECT(argv[1]))) != NULL) { + add_property(&chan->properties, key, ji, EXTEND_JSON, EXTEND_ISPUBLIC); + } + } else { /* Convert to string */ + property = JS_ValueToString(cx, argv[1]); /* No needs to be gc-rooted while there is no JSAPI Call after that */ + add_property(&chan->properties, key, JS_GetStringBytes(property), EXTEND_STR, EXTEND_ISPUBLIC); + } + + + return JS_TRUE; +} + +APE_JS_NATIVE(apechannel_sm_isinteractive) +//{ + CHANNEL *chan = JS_GetPrivate(cx, obj); + + if (chan == NULL) { + return JS_TRUE; + } + + *rval = (!(chan->flags & CHANNEL_NONINTERACTIVE) ? JSVAL_TRUE : JSVAL_FALSE); + + return JS_TRUE; +} + + +APE_JS_NATIVE(apeuser_sm_get_property) +//{ + const char *property; + USERS *user = JS_GetPrivate(cx, obj); + + if (user == NULL) { + return JS_TRUE; + } + + if (!JS_ConvertArguments(cx, 1, argv, "s", &property)) { + return JS_TRUE; + } + + if (strcmp(property, "sessid") == 0) { + *rval = STRING_TO_JSVAL(JS_NewStringCopyN(cx, user->sessid, 32)); + } else if (strcmp(property, "pubid") == 0) { + *rval = STRING_TO_JSVAL(JS_NewStringCopyN(cx, user->pipe->pubid, 32)); + } else if (strcmp(property, "ip") == 0) { + *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, user->ip)); + } else { + extend *getprop = get_property(user->properties, property); + if (getprop != NULL) { + if (getprop->type == EXTEND_STR) { + *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, getprop->val)); + } else if (getprop->type == EXTEND_JSON) { + JSObject *propobj = ape_json_to_jsobj(cx, ((json_item *)getprop->val)->jchild.child, NULL); + *rval = OBJECT_TO_JSVAL(propobj); + } + } + } + + return JS_TRUE; +} + +APE_JS_NATIVE(apeuser_sm_quit) +//{ + USERS *user = JS_GetPrivate(cx, obj); + + if (user == NULL) { + return JS_TRUE; + } + + deluser(user, g_ape); + + return JS_TRUE; +} + +APE_JS_NATIVE(apeuser_sm_join) +//{ + CHANNEL *chan; + char *chan_name; + JSObject *chan_obj; + USERS *user = JS_GetPrivate(cx, obj); + + *rval = JSVAL_FALSE; + + if (user == NULL) { + return JS_TRUE; + } + + if (JSVAL_IS_STRING(argv[0])) { + JS_ConvertArguments(cx, 1, argv, "s", &chan_name); + if ((chan = getchan(chan_name, g_ape)) == NULL) { + return JS_TRUE; + } + } else if (JSVAL_IS_OBJECT(argv[0])) { + JS_ConvertArguments(cx, 1, argv, "o", &chan_obj); + if (!JS_InstanceOf(cx, chan_obj, &channel_class, 0) || (chan = JS_GetPrivate(cx, chan_obj)) == NULL) { + return JS_TRUE; + } + } else { + return JS_TRUE; + } + + join(user, chan, g_ape); + + *rval = JSVAL_TRUE; + return JS_TRUE; +} + +APE_JS_NATIVE(apeuser_sm_left) +//{ + CHANNEL *chan; + char *chan_name; + JSObject *chan_obj; + USERS *user = JS_GetPrivate(cx, obj); + + *rval = JSVAL_FALSE; + + if (user == NULL) { + return JS_TRUE; + } + + if (JSVAL_IS_STRING(argv[0])) { + JS_ConvertArguments(cx, 1, argv, "s", &chan_name); + if ((chan = getchan(chan_name, g_ape)) == NULL) { + return JS_TRUE; + } + } else if (JSVAL_IS_OBJECT(argv[0])) { + JS_ConvertArguments(cx, 1, argv, "o", &chan_obj); + if (!JS_InstanceOf(cx, chan_obj, &channel_class, 0) || (chan = JS_GetPrivate(cx, chan_obj)) == NULL) { + return JS_TRUE; + } + } else { + return JS_TRUE; + } + + left(user, chan, g_ape); + + *rval = JSVAL_TRUE; + return JS_TRUE; +} + +APE_JS_NATIVE(apeuser_sm_set_property) +//{ + char *key; + JSString *property; + USERS *user = JS_GetPrivate(cx, obj); + + if (user == NULL) { + return JS_TRUE; + } + + if (argc != 2) { + return JS_TRUE; + } + + if (!JS_ConvertArguments(cx, 1, argv, "s", &key)) { + return JS_TRUE; + } + + if (JSVAL_IS_OBJECT(argv[1])) { /* Convert to APE JSON Object */ + json_item *ji; + + if ((ji = jsobj_to_ape_json(cx, JSVAL_TO_OBJECT(argv[1]))) != NULL) { + add_property(&user->properties, key, ji, EXTEND_JSON, EXTEND_ISPUBLIC); + } + } else { /* Convert to string */ + property = JS_ValueToString(cx, argv[1]); /* No needs to be gc-rooted while there is no JSAPI Call after that */ + add_property(&user->properties, key, JS_GetStringBytes(property), EXTEND_STR, EXTEND_ISPUBLIC); + } + + return JS_TRUE; +} + +#ifdef _USE_MYSQL +APE_JS_NATIVE(apemysql_sm_errorstring) +//{ + struct _ape_mysql_data *myhandle; + + if ((myhandle = JS_GetPrivate(cx, obj)) == NULL) { + return JS_TRUE; + } + + *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, mysac_advance_error(myhandle->my))); + + return JS_TRUE; +} + +APE_JS_NATIVE(apemysql_sm_insert_id) +//{ + struct _ape_mysql_data *myhandle; + + if ((myhandle = JS_GetPrivate(cx, obj)) == NULL) { + return JS_TRUE; + } + + *rval = INT_TO_JSVAL(mysac_insert_id(myhandle->my)); + + return JS_TRUE; +} + +APE_JS_NATIVE(apemysql_escape) +//{ + + char *input_c, *escaped; + unsigned long int len; + + if (!JS_ConvertArguments(cx, 1, argv, "s", &input_c)) { + return JS_TRUE; + } + + len = strlen(input_c); + escaped = xmalloc(sizeof(char) * (len*2+1)); + + len = mysql_escape_string(escaped, input_c, len); + + *rval = STRING_TO_JSVAL(JS_NewStringCopyN(cx, escaped, len)); + + free(escaped); + + return JS_TRUE; +} + +APE_JS_NATIVE(apemysql_sm_query) +//{ + JSString *query; + struct _ape_mysql_data *myhandle; + jsval callback; + + if ((myhandle = JS_GetPrivate(cx, obj)) == NULL) { + return JS_TRUE; + } + + if (!JS_ConvertArguments(cx, 1, argv, "S", &query)) { + return JS_TRUE; + } + if (!JS_ConvertValue(cx, argv[1], JSTYPE_FUNCTION, &callback)) { + return JS_TRUE; + } + + apemysql_push_queue(myhandle, xstrdup(JS_GetStringBytes(query)), JS_GetStringLength(query), callback); + + return JS_TRUE; +} +#endif + +static JSFunctionSpec apesocket_funcs[] = { + JS_FS("write", apesocket_write, 1, 0, 0), + JS_FS("close", apesocket_close, 0, 0, 0), + JS_FS_END +}; + +static JSFunctionSpec apesocketserver_funcs[] = { + JS_FS("close", apesocketserver_close, 0, 0, 0), + JS_FS_END +}; + +static JSFunctionSpec apesocketclient_funcs[] = { + JS_FS("write", apesocketclient_write, 1, 0, 0), + JS_FS("close", apesocketclient_close, 0, 0, 0), + JS_FS_END +}; + +static JSFunctionSpec apesocket_client_funcs[] = { + JS_FS("onAccept", ape_sm_stub, 0, 0, 0), + JS_FS("onRead", ape_sm_stub, 0, 0, 0), + JS_FS("onDisconnect", ape_sm_stub, 0, 0, 0), + JS_FS("onConnect", ape_sm_stub, 0, 0, 0), + JS_FS_END +}; + +static JSFunctionSpec apeuser_funcs[] = { + JS_FS("getProperty", apeuser_sm_get_property, 1, 0, 0), + JS_FS("setProperty", apeuser_sm_set_property, 2, 0, 0), + JS_FS("join", apeuser_sm_join, 1, 0, 0), + JS_FS("left", apeuser_sm_left, 1, 0, 0), + JS_FS("quit", apeuser_sm_quit, 0, 0, 0), + JS_FS_END +}; + +static JSFunctionSpec apechannel_funcs[] = { + JS_FS("getProperty", apechannel_sm_get_property, 1, 0, 0), + JS_FS("setProperty", apechannel_sm_set_property, 2, 0, 0), + JS_FS("isInteractive", apechannel_sm_isinteractive, 1, 0, 0), + JS_FS_END +}; + +static JSFunctionSpec apepipe_funcs[] = { + JS_FS("sendRaw", apepipe_sm_send_raw, 3, 0, 0), + JS_FS("toObject", apepipe_sm_to_object, 0, 0, 0), + JS_FS("getProperty", apepipe_sm_get_property, 1, 0, 0), + JS_FS("setProperty", apepipe_sm_set_property, 2, 0, 0), + JS_FS("getParent", apepipe_sm_get_parent, 0, 0, 0), + JS_FS("onSend", ape_sm_stub, 0, 0, 0), + JS_FS_END +}; + +#ifdef _USE_MYSQL +static JSFunctionSpec apemysql_funcs[] = { + JS_FS("onConnect", ape_sm_stub, 0, 0, 0), + JS_FS("onError", ape_sm_stub, 0, 0, 0), + JS_FS("errorString", apemysql_sm_errorstring, 0, 0, 0), + JS_FS("query", apemysql_sm_query, 2, 0, 0), + JS_FS("getInsertId", apemysql_sm_insert_id, 0, 0, 0), + JS_FS_END +}; + +static JSFunctionSpec apemysql_funcs_static[] = { + JS_FS("escape", apemysql_escape, 1, 0, 0), + JS_FS_END +}; +#endif + + +static JSFunctionSpec cmdresponse_funcs[] = { + JS_FS("sendResponse", apepipe_sm_send_response, 3, 0, 0), + JS_FS_END +}; + +static JSFunctionSpec apepipecustom_funcs[] = { + JS_FS("destroy", apepipe_sm_destroy, 0, 0, 0), + JS_FS_END +}; + +static JSObject *sm_ape_socket_to_jsobj(JSContext *cx, ape_socket *client) +{ + /*if (client->data != NULL) { + return (JSObject *)client->data; + } else {*/ + + JSObject *obj = JS_NewObject(cx, &apesocket_class, NULL, NULL); + + JS_AddRoot(cx, &obj); + + JS_DefineFunctions(cx, obj, apesocketclient_funcs); + JS_SetPrivate(cx, obj, client); + + JS_RemoveRoot(cx, &obj); + + client->data = obj; + return obj; +// } +} + +static void sm_sock_onaccept(ape_socket *client, acetables *g_ape) +{ + jsval rval; + if (client->attach != NULL) { + struct _ape_sock_callbacks *cb = ((struct _ape_sock_callbacks *)client->attach); + + struct _ape_sock_callbacks *cbcopy; + struct _ape_sock_js_obj *sock_obj = xmalloc(sizeof(*sock_obj)); + + sock_obj->client = client; + + JSObject *obj; + jsval params[1]; + + cbcopy = xmalloc(sizeof(struct _ape_sock_callbacks)); + cbcopy->private = sock_obj; + cbcopy->asc = cb->asc; + cbcopy->server_obj = cb->server_obj; + cbcopy->state = 1; + + client->attach = cbcopy; + + //JS_SetContextThread(cb->asc->cx); + //JS_BeginRequest(cb->asc->cx); + + obj = JS_NewObject(cb->asc->cx, &apesocket_class, NULL, NULL); + sock_obj->client_obj = obj; + JS_AddRoot(cb->asc->cx, &sock_obj->client_obj); + + JS_SetPrivate(cb->asc->cx, obj, cbcopy); + JS_DefineFunctions(cb->asc->cx, obj, apesocket_funcs); + params[0] = OBJECT_TO_JSVAL(sock_obj->client_obj); + + //JS_AddRoot(cb->asc->cx, ¶ms[0]); + + JS_CallFunctionName(cb->asc->cx, cb->server_obj, "onAccept", 1, params, &rval); + + //JS_RemoveRoot(cb->asc->cx, ¶ms[0]); + + //JS_EndRequest(cb->asc->cx); + //JS_ClearContextThread(cb->asc->cx); + + } +} + +static void sm_sock_ondisconnect(ape_socket *client, acetables *g_ape) +{ + jsval rval; + + if (client->attach != NULL) { + struct _ape_sock_callbacks *cb = ((struct _ape_sock_callbacks *)client->attach); + JSObject *client_obj = ((struct _ape_sock_js_obj *)cb->private)->client_obj; + + //JS_SetContextThread(cb->asc->cx); + //JS_BeginRequest(cb->asc->cx); + + if (client_obj != NULL) { + jsval params[1]; + params[0] = OBJECT_TO_JSVAL(client_obj); + + JS_CallFunctionName(cb->asc->cx, cb->server_obj, "onDisconnect", 1, params, &rval); + JS_SetPrivate(cb->asc->cx, client_obj, (void *)NULL); + JS_RemoveRoot(cb->asc->cx, &((struct _ape_sock_js_obj *)cb->private)->client_obj); + } else { + JS_CallFunctionName(cb->asc->cx, cb->server_obj, "onDisconnect", 0, NULL, &rval); + JS_SetPrivate(cb->asc->cx, cb->server_obj, (void *)NULL); + JS_RemoveRoot(cb->asc->cx, &cb->server_obj); + } + + free(cb->private); + free(cb); + + //JS_EndRequest(cb->asc->cx); + //JS_ClearContextThread(cb->asc->cx); + + } +} + +static void sm_sock_onread_lf(ape_socket *client, char *data, acetables *g_ape) +{ + jsval rval; + if (client->attach != NULL) { + struct _ape_sock_callbacks *cb = ((struct _ape_sock_callbacks *)client->attach); + JSObject *client_obj = ((struct _ape_sock_js_obj *)cb->private)->client_obj; + + if (!cb->state) { + return; + } + //JS_SetContextThread(cb->asc->cx); + //JS_BeginRequest(cb->asc->cx); + + if (client_obj != NULL) { + jsval params[2]; + params[0] = OBJECT_TO_JSVAL(client_obj); + params[1] = STRING_TO_JSVAL(JS_NewStringCopyZ(cb->asc->cx, data)); + + JS_CallFunctionName(cb->asc->cx, cb->server_obj, "onRead", 2, params, &rval); + } else { + jsval params[1]; + params[0] = STRING_TO_JSVAL(JS_NewStringCopyZ(cb->asc->cx, data)); + + JS_CallFunctionName(cb->asc->cx, cb->server_obj, "onRead", 1, params, &rval); + + } + JS_MaybeGC(cb->asc->cx); + //JS_EndRequest(cb->asc->cx); + //JS_ClearContextThread(cb->asc->cx); + + } +} + +static void sm_sock_onconnect(ape_socket *client, acetables *g_ape) +{ + jsval rval; + + if (client->attach != NULL) { + struct _ape_sock_callbacks *cb = ((struct _ape_sock_callbacks *)client->attach); + ((struct _ape_sock_js_obj *)cb->private)->client = client; + //JS_SetContextThread(cb->asc->cx); + //JS_BeginRequest(cb->asc->cx); + JS_CallFunctionName(cb->asc->cx, cb->server_obj, "onConnect", 0, NULL, &rval); + //JS_EndRequest(cb->asc->cx); + //JS_ClearContextThread(cb->asc->cx); + + } +} + +static void sm_sock_onread(ape_socket *client, ape_buffer *buf, size_t offset, acetables *g_ape) +{ + jsval rval; + if (client->attach != NULL) { + struct _ape_sock_callbacks *cb = ((struct _ape_sock_callbacks *)client->attach); + JSObject *client_obj = ((struct _ape_sock_js_obj *)cb->private)->client_obj; + + if (!cb->state) { + return; + } + //JS_SetContextThread(cb->asc->cx); + //JS_BeginRequest(cb->asc->cx); + + if (client_obj != NULL) { + jsval params[2]; + params[0] = OBJECT_TO_JSVAL(client_obj); + params[1] = STRING_TO_JSVAL(JS_NewStringCopyN(cb->asc->cx, buf->data, buf->length)); + JS_CallFunctionName(cb->asc->cx, cb->server_obj, "onRead", 2, params, &rval); + } else { + jsval params[1]; + + params[0] = STRING_TO_JSVAL(JS_NewStringCopyN(cb->asc->cx, buf->data, buf->length)); + + JS_CallFunctionName(cb->asc->cx, cb->server_obj, "onRead", 1, params, &rval); + + } + JS_MaybeGC(cb->asc->cx); + //JS_EndRequest(cb->asc->cx); + //JS_ClearContextThread(cb->asc->cx); + buf->length = 0; + } +} + +/* Reporting error from JS compilation (parse error, etc...) */ +static void reportError(JSContext *cx, const char *message, JSErrorReport *report) +{ + fprintf(stderr, "%s:%u:%s\n", + report->filename ? report->filename : "", + (unsigned int) report->lineno, + message); +} + +static JSObject *ape_json_to_jsobj(JSContext *cx, json_item *head, JSObject *root) +{ + JS_EnterLocalRootScope(cx); + while (head != NULL) { + if (head->jchild.child == NULL && head->key.val != NULL) { + jsval jval; + + if (root == NULL) { + root = JS_NewObject(cx, NULL, NULL, NULL); + } + + if (head->jval.vu.str.value != NULL) { + jval = STRING_TO_JSVAL(JS_NewStringCopyN(cx, head->jval.vu.str.value, head->jval.vu.str.length)); + } else { + jsdouble dp = (head->jval.vu.integer_value ? head->jval.vu.integer_value : head->jval.vu.float_value); + JS_NewNumberValue(cx, dp, &jval); + } + JS_SetProperty(cx, root, head->key.val, &jval); + } else if (head->key.val == NULL && head->jchild.child == NULL) { + jsuint rval; + jsval jval; + + if (root == NULL) { + root = JS_NewArrayObject(cx, 0, NULL); + } + + if (head->jval.vu.str.value != NULL) { + jval = STRING_TO_JSVAL(JS_NewStringCopyN(cx, head->jval.vu.str.value, head->jval.vu.str.length)); + } else { + jsdouble dp = (head->jval.vu.integer_value ? head->jval.vu.integer_value : head->jval.vu.float_value); + JS_NewNumberValue(cx, dp, &jval); + } + + if (JS_GetArrayLength(cx, root, &rval)) { + JS_SetElement(cx, root, rval, &jval); + } + } else if (head->jchild.child != NULL) { + JSObject *cobj = NULL; + + switch(head->jchild.type) { + case JSON_C_T_OBJ: + cobj = JS_NewObject(cx, NULL, NULL, NULL); + break; + case JSON_C_T_ARR: + cobj = JS_NewArrayObject(cx, 0, NULL); + break; + default: + break; + } + + ape_json_to_jsobj(cx, head->jchild.child, cobj); + + + if (head->key.val != NULL) { + jsval jval; + + if (root == NULL) { + root = JS_NewObject(cx, NULL, NULL, NULL); + } + + jval = OBJECT_TO_JSVAL(cobj); + JS_SetProperty(cx, root, head->key.val, &jval); + } else { + jsval jval; + jsuint rval; + + if (root == NULL) { + root = JS_NewArrayObject(cx, 0, NULL); + } + + jval = OBJECT_TO_JSVAL(cobj); + + if (JS_GetArrayLength(cx, root, &rval)) { + JS_SetElement(cx, root, rval, &jval); + } + } + + + } + head = head->next; + } + + JS_LeaveLocalRootScope(cx); + return root; +} + +static void ape_sm_pipe_on_send_wrapper(transpipe *pipe, USERS *user, json_item *jstr, acetables *g_ape) +{ + JSObject *obj; + jsval params[2], rval; + JSContext *cx = get_property(pipe->properties, "cx")->val; + + obj = ape_json_to_jsobj(cx, jstr->jchild.child, NULL); + + params[0] = OBJECT_TO_JSVAL(APEUSER_TO_JSOBJ(user)); + params[1] = OBJECT_TO_JSVAL(obj); + + JS_CallFunctionName(cx, pipe->data, "onSend", 2, params, &rval); + + JS_RemoveRoot(cx, &obj); +} + +/* Dispatch CMD to the corresponding javascript callback */ +static unsigned int ape_sm_cmd_wrapper(callbackp *callbacki) +{ + acetables *g_ape = callbacki->g_ape; + ape_sm_compiled *asc = ASMR->scripts; + struct _http_header_line *hlines; + + json_item *head = callbacki->param; + JSContext *cx = ASMC; + + JSObject *obj; // param object + JSObject *cb; // cmd object + JSObject *hl; // http object + + if (asc == NULL) { + return (RETURN_NOTHING); + } + + //JS_SetContextThread(cx); + //JS_BeginRequest(cx); + jsval jval; + + obj = ape_json_to_jsobj(cx, head, NULL); + JS_AddRoot(cx, &obj); + + cb = JS_NewObject(cx, &cmdresponse_class, NULL, NULL); + JS_AddRoot(cx, &cb); + JS_DefineFunctions(cx, cb, cmdresponse_funcs); + + jval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, callbacki->host)); + /* infos.host */ + JS_SetProperty(cx, cb, "host", &jval); + + hl = JS_DefineObject(cx, cb, "http", NULL, NULL, 0); + + for (hlines = callbacki->hlines; hlines != NULL; hlines = hlines->next) { + s_tolower(hlines->key.val, hlines->key.len); + jval = STRING_TO_JSVAL(JS_NewStringCopyN(cx, hlines->value.val, hlines->value.len)); + JS_SetProperty(cx, hl, hlines->key.val, &jval); + } + + jval = OBJECT_TO_JSVAL(sm_ape_socket_to_jsobj(cx, callbacki->client)); + JS_SetProperty(cx, cb, "client", &jval); + + jval = INT_TO_JSVAL(callbacki->chl); + /* infos.chl */ + JS_SetProperty(cx, cb, "chl", &jval); + + jval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, callbacki->ip)); + JS_SetProperty(cx, cb, "ip", &jval); + + if (callbacki->call_user != NULL) { + jval = OBJECT_TO_JSVAL(APEUSER_TO_JSOBJ(callbacki->call_user)); + /* infos.user */ + JS_SetProperty(cx, cb, "user", &jval); + + if (callbacki->call_subuser != NULL) { + jval = OBJECT_TO_JSVAL(APESUBUSER_TO_JSOBJ(callbacki->call_subuser)); + /* infos.subuser */ + JS_SetProperty(cx, cb, "subuser", &jval); + } + } + + JS_RemoveRoot(cx, &obj); + JS_RemoveRoot(cx, &cb); + + //JS_EndRequest(cx); + //JS_ClearContextThread(cx); + + if (callbacki->data == NULL) { + return ape_fire_cmd(callbacki->cmd, obj, cb, callbacki, callbacki->g_ape); + } else { + return ape_fire_hook(callbacki->data, obj, cb, callbacki, callbacki->g_ape); + } + + return (RETURN_NOTHING); +} + +APE_JS_NATIVE(ape_sm_register_bad_cmd) +//{ + ape_sm_callback *ascb; + + ascb = JS_malloc(cx, sizeof(*ascb)); + + if (!JS_ConvertValue(cx, argv[0], JSTYPE_FUNCTION, &ascb->func)) { + JS_free(cx, ascb); + return JS_TRUE; + } + JS_AddRoot(cx, &ascb->func); + + /* TODO : Effacer si déjà existant (RemoveRoot & co) */ + ascb->next = NULL; + ascb->type = APE_BADCMD; + ascb->cx = cx; + ascb->callbackname = NULL; + + if (asc->callbacks.head == NULL) { + asc->callbacks.head = ascb; + asc->callbacks.foot = ascb; + } else { + asc->callbacks.foot->next = ascb; + asc->callbacks.foot = ascb; + } + + register_bad_cmd(ape_sm_cmd_wrapper, ascb, g_ape); + + return JS_TRUE; + +} + +APE_JS_NATIVE(ape_sm_register_cmd) +//{ + const char *cmd; + JSBool needsessid; + + ape_sm_callback *ascb; + + *rval = JSVAL_NULL; + + if (argc != 3) { + return JS_TRUE; + } + + if (!JS_ConvertArguments(cx, 2, argv, "sb", &cmd, &needsessid)) { + return JS_TRUE; + } + + ascb = JS_malloc(cx, sizeof(*ascb)); + + if (!JS_ConvertValue(cx, argv[2], JSTYPE_FUNCTION, &ascb->func)) { + JS_free(cx, ascb); + return JS_TRUE; + } + JS_AddRoot(cx, &ascb->func); + + /* TODO : Effacer si déjà existant (RemoveRoot & co) */ + ascb->next = NULL; + ascb->type = APE_CMD; + ascb->cx = cx; + ascb->callbackname = xstrdup(cmd); + + if (asc->callbacks.head == NULL) { + asc->callbacks.head = ascb; + asc->callbacks.foot = ascb; + } else { + asc->callbacks.foot->next = ascb; + asc->callbacks.foot = ascb; + } + + register_cmd(cmd, ape_sm_cmd_wrapper, (needsessid == JS_TRUE ? NEED_SESSID : NEED_NOTHING), g_ape); + + return JS_TRUE; +} + +APE_JS_NATIVE(ape_sm_hook_cmd) +//{ + const char *cmd; + + ape_sm_callback *ascb; + + *rval = JSVAL_NULL; + + if (argc != 2) { + return JS_TRUE; + } + + if (!JS_ConvertArguments(cx, 1, argv, "s", &cmd)) { + return JS_TRUE; + } + + ascb = JS_malloc(cx, sizeof(*ascb)); + + if (!JS_ConvertValue(cx, argv[1], JSTYPE_FUNCTION, &ascb->func)) { + JS_free(cx, ascb); + return JS_TRUE; + } + + if (!register_hook_cmd(cmd, ape_sm_cmd_wrapper, ascb, g_ape)) { + /* CMD doesn't exist */ + JS_free(cx, ascb); + return JS_TRUE; + } + JS_AddRoot(cx, &ascb->func); + + /* TODO : Effacer si déjà existant (RemoveRoot & co) */ + ascb->next = NULL; + ascb->type = APE_HOOK; + ascb->cx = cx; + ascb->callbackname = xstrdup(cmd); + + if (asc->callbacks.head == NULL) { + asc->callbacks.head = ascb; + asc->callbacks.foot = ascb; + } else { + asc->callbacks.foot->next = ascb; + asc->callbacks.foot = ascb; + } + + return JS_TRUE; +} + +APE_JS_NATIVE(ape_sm_include) +//{ + const char *file; + JSScript *bytecode; + jsval frval; + char rpath[512]; + + if (argc != 1) { + return JS_TRUE; + } + + if (!JS_ConvertArguments(cx, 1, argv, "s", &file)) { + return JS_TRUE; + } + + memset(rpath, '\0', sizeof(rpath)); + strncpy(rpath, READ_CONF("scripts_path"), 255); + strncat(rpath, file, 255); + + if (!g_ape->is_daemon) { + printf("[JS] Loading script %s...\n", rpath); + } + + bytecode = JS_CompileFile(cx, JS_GetGlobalObject(cx), rpath); + + if (bytecode == NULL) { + if (!g_ape->is_daemon) { + printf("[JS] Failed loading script %s\n", rpath); + } + return JS_TRUE; + } + + /* Adding to the root (prevent the script to be GC collected) */ +// JS_AddNamedRoot(cx, &scriptObj, file); + + JS_ExecuteScript(cx, JS_GetGlobalObject(cx), bytecode, &frval); + + return JS_TRUE; +} + +APE_JS_NATIVE(ape_sm_b64_encode) +//{ + JSString *string; + char *b64; + + if (argc != 1) { + return JS_TRUE; + } + + if (!JS_ConvertArguments(cx, 1, argv, "S", &string)) { + return JS_TRUE; + } + + b64 = base64_encode(JS_GetStringBytes(string), JS_GetStringLength(string)); + + *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, b64)); + + free(b64); + + return JS_TRUE; + +} + +APE_JS_NATIVE(ape_sm_b64_decode) +//{ + JSString *string; + char *b64; + int length, len; + + if (argc != 1) { + return JS_TRUE; + } + + if (!JS_ConvertArguments(cx, 1, argv, "S", &string)) { + return JS_TRUE; + } + + length = JS_GetStringLength(string); + + b64 = xmalloc(length+1); + len = base64_decode(b64, JS_GetStringBytes(string), length+1); + + if (len != -1) { + *rval = STRING_TO_JSVAL(JS_NewStringCopyN(cx, b64, len)); + } + + free(b64); + + return JS_TRUE; +} + + +APE_JS_NATIVE(ape_sm_sha1_bin) +//{ + JSString *string, *hmac = NULL; + unsigned char digest[20]; + + if (!JS_ConvertArguments(cx, argc, argv, "S/S", &string, &hmac)) { + return JS_TRUE; + } + + if (hmac == NULL) { + sha1_csum((unsigned char *)JS_GetStringBytes(string), JS_GetStringLength(string), digest); + } else { + sha1_hmac((unsigned char *)JS_GetStringBytes(hmac), JS_GetStringLength(hmac), (unsigned char *)JS_GetStringBytes(string), JS_GetStringLength(string), digest); + } + + *rval = STRING_TO_JSVAL(JS_NewStringCopyN(cx, (char *)digest, 20)); + + return JS_TRUE; +} + +APE_JS_NATIVE(ape_sm_sha1_str) +//{ + JSString *string, *hmac = NULL; + unsigned char digest[20]; + char output[40]; + unsigned int i; + + if (!JS_ConvertArguments(cx, argc, argv, "S/S", &string, &hmac)) { + return JS_TRUE; + } + + if (hmac == NULL) { + sha1_csum((unsigned char *)JS_GetStringBytes(string), JS_GetStringLength(string), digest); + } else { + sha1_hmac((unsigned char *)JS_GetStringBytes(hmac), JS_GetStringLength(hmac), (unsigned char *)JS_GetStringBytes(string), JS_GetStringLength(string), digest); + } + + for (i = 0; i < 20; i++) { + sprintf(output + (i*2), "%02x", digest[i]); + } + + *rval = STRING_TO_JSVAL(JS_NewStringCopyN(cx, output, 40)); + + return JS_TRUE; +} + +APE_JS_NATIVE(ape_sm_mkchan) +//{ + char *chan_name; + CHANNEL *new_chan; + + if (!JS_ConvertArguments(cx, 1, argv, "s", &chan_name) || getchan(chan_name, g_ape) != NULL) { + return JS_TRUE; + } + + if ((new_chan = mkchan(chan_name, 0, g_ape)) != NULL) { + *rval = OBJECT_TO_JSVAL(APECHAN_TO_JSOBJ(new_chan)); + } + + return JS_TRUE; +} + +APE_JS_NATIVE(ape_sm_rmchan) +//{ + char *chan_name; + CHANNEL *chan; + JSObject *chan_obj; + + if (JSVAL_IS_STRING(argv[0])) { + JS_ConvertArguments(cx, 1, argv, "s", &chan_name); + if ((chan = getchan(chan_name, g_ape)) == NULL) { + return JS_TRUE; + } + } else if (JSVAL_IS_OBJECT(argv[0])) { + JS_ConvertArguments(cx, 1, argv, "o", &chan_obj); + if (!JS_InstanceOf(cx, chan_obj, &channel_class, 0) || (chan = JS_GetPrivate(cx, chan_obj)) == NULL) { + return JS_TRUE; + } + } else { + return JS_TRUE; + } + + rmchan(chan, g_ape); + + return JS_TRUE; +} + +APE_JS_NATIVE(ape_sm_adduser) +//{ + JSObject *user; + USERS *u; + RAW *newraw; + json_item *jstr = NULL; + + if (!JS_ConvertArguments(cx, 1, argv, "o", &user)) { + return JS_TRUE; + } + if (JS_InstanceOf(cx, user, &user_class, 0) == JS_FALSE) { + return JS_TRUE; + } + + if ((u = JS_GetPrivate(cx, user)) == NULL) { + return JS_TRUE; + } + + adduser(NULL, NULL, NULL, u, g_ape); + + subuser_restor(u->subuser, g_ape); + + jstr = json_new_object(); + json_set_property_strN(jstr, "sessid", 6, u->sessid, 32); + + newraw = forge_raw(RAW_LOGIN, jstr); + newraw->priority = RAW_PRI_HI; + + post_raw(newraw, u, g_ape); + + if (u->cmdqueue != NULL) { + unsigned int ret; + json_item *queue; + struct _cmd_process pc = {NULL, u, u->subuser, u->subuser->client, NULL, NULL, 0}; + + for (queue = u->cmdqueue; queue != NULL; queue = queue->next) { + if ((ret = process_cmd(queue, &pc, NULL, g_ape)) != -1) { + if (ret == CONNECT_SHUTDOWN) { + shutdown(u->subuser->client->fd, 2); + } + break; + } + } + u->cmdqueue = NULL; + free_json_item(u->cmdqueue); + } + + return JS_TRUE; + +} + +APE_JS_NATIVE(ape_sm_addEvent) +//{ + const char *event; + + ape_sm_callback *ascb; + + *rval = JSVAL_NULL; + + if (argc != 2) { + return JS_TRUE; + } + + if (!JS_ConvertArguments(cx, 1, argv, "s", &event)) { + return JS_TRUE; + } + + ascb = JS_malloc(cx, sizeof(*ascb)); + + if (!JS_ConvertValue(cx, argv[1], JSTYPE_FUNCTION, &ascb->func)) { + JS_free(cx, ascb); + return JS_TRUE; + } + JS_AddRoot(cx, &ascb->func); + + ascb->next = NULL; + ascb->type = APE_EVENT; + ascb->cx = cx; + ascb->callbackname = xstrdup(event); + + if (asc->callbacks.head == NULL) { + asc->callbacks.head = ascb; + asc->callbacks.foot = ascb; + } else { + asc->callbacks.foot->next = ascb; + asc->callbacks.foot = ascb; + } + + return JS_TRUE; +} + +static JSObject *get_pipe_object(const char *pubid, transpipe *pipe, JSContext *cx, acetables *g_ape) +{ + JSObject *jspipe; + + if ((pipe != NULL) || ((pipe = get_pipe(pubid, g_ape)) != NULL)) { + if ((jspipe = pipe->data) == NULL) { + jspipe = JS_NewObject(cx, &pipe_class, get_property(g_ape->properties, "pipe_proto")->val, NULL); + + if (jspipe == NULL) { + return NULL; + } + + JS_AddRoot(cx, &jspipe); + //JS_DefineFunctions(cx, jspipe, apepipe_funcs); + JS_SetPrivate(cx, jspipe, pipe); + JS_RemoveRoot(cx, &jspipe); + + pipe->data = jspipe; + } + + return jspipe; + } + + return NULL; +} + +APE_JS_NATIVE(ape_sm_get_user_by_pubid) +//{ + char *pubid; + USERS *user; + + *rval = JSVAL_NULL; + + if (!JS_ConvertArguments(cx, 1, argv, "s", &pubid)) { + return JS_TRUE; + } + + if ((user = seek_user_simple(pubid, g_ape)) != NULL) { + *rval = OBJECT_TO_JSVAL(APEUSER_TO_JSOBJ(user)); + } + + return JS_TRUE; +} + +APE_JS_NATIVE(ape_sm_get_channel_by_pubid) +//{ + char *pubid; + CHANNEL *chan; + + *rval = JSVAL_NULL; + + if (!JS_ConvertArguments(cx, 1, argv, "s", &pubid)) { + return JS_TRUE; + } + + if ((chan = getchanbypubid(pubid, g_ape)) != NULL) { + *rval = OBJECT_TO_JSVAL(APECHAN_TO_JSOBJ(chan)); + } + + return JS_TRUE; +} + +APE_JS_NATIVE(ape_sm_get_pipe) +//{ + char *pubid; + JSObject *jspipe; + + *rval = JSVAL_NULL; + + if (!JS_ConvertArguments(cx, 1, argv, "s", &pubid)) { + return JS_TRUE; + } + + if ((jspipe = get_pipe_object(pubid, NULL, cx, g_ape)) != NULL) { + *rval = OBJECT_TO_JSVAL(jspipe); + } + return JS_TRUE; +} + +APE_JS_NATIVE(ape_sm_get_channel_by_name) +//{ + char *name; + CHANNEL *chan; + + *rval = JSVAL_NULL; + + if (!JS_ConvertArguments(cx, 1, argv, "s", &name)) { + return JS_TRUE; + } + if ((chan = getchan(name, g_ape)) != NULL) { + *rval = OBJECT_TO_JSVAL(APECHAN_TO_JSOBJ(chan)); + + return JS_TRUE; + } + + return JS_TRUE; +} + +APE_JS_NATIVE(ape_sm_config) +//{ + char *file, *key, *value; + *rval = JSVAL_NULL; + plug_config *config; + char conf_file[1024]; + + if (!JS_ConvertArguments(cx, 2, argv, "ss", &file, &key)) { + return JS_TRUE; + } + sprintf(conf_file, "%s%s", CONFIG_VAL(Config, modules_conf, g_ape->srv), file); + + config = plugin_parse_conf(conf_file); + value = plugin_get_conf(config, key); + + *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, value)); + + return JS_TRUE; +} + +APE_JS_NATIVE(ape_sm_mainconfig) +//{ + char *key, *section, *value; + *rval = JSVAL_NULL; + + if (!JS_ConvertArguments(cx, 2, argv, "ss", §ion, &key)) { + return JS_TRUE; + } + + value = ape_config_get_key(ape_config_get_section(g_ape->srv, section), key); + + *rval = (value != NULL ? STRING_TO_JSVAL(JS_NewStringCopyZ(cx, value)) : JSVAL_FALSE); + + return JS_TRUE; +} + +struct _ape_sm_timer +{ + JSContext *cx; + JSObject *global; + jsval func; + + uintN argc; + jsval *argv; + + int cleared; + struct _ticks_callback *timer; +}; + +static void ape_sm_timer_wrapper(struct _ape_sm_timer *params, int *last) +{ + jsval rval; + + //JS_SetContextThread(params->cx); + //JS_BeginRequest(params->cx); + if (!params->cleared) { + JS_CallFunctionValue(params->cx, params->global, params->func, params->argc, params->argv, &rval); + } + if (params->cleared) { /* JS_CallFunctionValue can set params->Cleared to true */ + ape_sm_compiled *asc; + asc = JS_GetContextPrivate(params->cx); + + if (!*last) { + *last = 1; + } + } + if (*last) { + JS_RemoveRoot(params->cx, ¶ms->func); + + if (params->argv != NULL) { + free(params->argv); + } + free(params); + } + //JS_EndRequest(params->cx); + //JS_ClearContextThread(params->cx); +} + +APE_JS_NATIVE(ape_sm_set_timeout) +//{ + struct _ape_sm_timer *params; + struct _ticks_callback *timer; + int ms, i; + + params = JS_malloc(cx, sizeof(*params)); + + if (params == NULL) { + return JS_FALSE; + } + + params->cx = cx; + params->global = obj; + params->argc = argc-2; + params->cleared = 0; + params->timer = NULL; + + params->argv = (argc-2 ? JS_malloc(cx, sizeof(*params->argv) * argc-2) : NULL); + + if (!JS_ConvertValue(cx, argv[0], JSTYPE_FUNCTION, ¶ms->func)) { + return JS_TRUE; + } + + if (!JS_ConvertArguments(cx, 1, &argv[1], "i", &ms)) { + return JS_TRUE; + } + + JS_AddRoot(cx, ¶ms->func); + + for (i = 0; i < argc-2; i++) { + params->argv[i] = argv[i+2]; + } + + timer = add_timeout(ms, ape_sm_timer_wrapper, params, g_ape); + timer->protect = 0; + params->timer = timer; + + *rval = INT_TO_JSVAL(timer->identifier); + + return JS_TRUE; + +} + +APE_JS_NATIVE(ape_sm_set_interval) +//{ + struct _ape_sm_timer *params; + struct _ticks_callback *timer; + int ms, i; + + params = JS_malloc(cx, sizeof(*params)); + + if (params == NULL) { + return JS_FALSE; + } + + params->cx = cx; + params->global = asc->global; + params->argc = argc-2; + params->cleared = 0; + params->timer = NULL; + + params->argv = (argc-2 ? JS_malloc(cx, sizeof(*params->argv) * argc-2) : NULL); + + if (!JS_ConvertValue(cx, argv[0], JSTYPE_FUNCTION, ¶ms->func)) { + return JS_TRUE; + } + + if (!JS_ConvertArguments(cx, 1, &argv[1], "i", &ms)) { + return JS_TRUE; + } + + JS_AddRoot(cx, ¶ms->func); + + for (i = 0; i < argc-2; i++) { + params->argv[i] = argv[i+2]; + } + + timer = add_periodical(ms, 0, ape_sm_timer_wrapper, params, g_ape); + timer->protect = 0; + params->timer = timer; + + *rval = INT_TO_JSVAL(timer->identifier); + + return JS_TRUE; +} + +APE_JS_NATIVE(ape_sm_clear_timeout) +//{ + unsigned int identifier; + struct _ape_sm_timer *params; + struct _ticks_callback *timer; + + if (!JS_ConvertArguments(cx, 1, argv, "i", &identifier)) { + return JS_TRUE; + } + + if ((timer = get_timer_identifier(identifier, g_ape)) != NULL && !timer->protect) { + params = timer->params; + params->cleared = 1; + } + + return JS_TRUE; +} + +APE_JS_NATIVE(ape_sm_echo) +//{ + JSString *string; + *rval = JSVAL_NULL; + + if (!JS_ConvertArguments(cx, 1, argv, "S", &string)) { + return JS_TRUE; + } + + if (!g_ape->is_daemon) { + fwrite(JS_GetStringBytes(string), 1, JS_GetStringLength(string), stdout); + fwrite("\n", 1, 1, stdout); + } else { + ape_log(APE_INFO, __FILE__, __LINE__, g_ape, + "JavaScript : %s", JS_GetStringBytes(string)); + } + + return JS_TRUE; +} + +APE_JS_NATIVE(ape_sm_raw_constructor) +//{ + char *rawname; + + if (!JS_ConvertArguments(cx, 1, argv, "s", &rawname)) { + return JS_TRUE; + } + + JS_SetPrivate(cx, obj, rawname); + + return JS_TRUE; +} + + +APE_JS_NATIVE(ape_sm_sockclient_constructor) +//{ + int port; + char *ip; + JSObject *options = NULL; + ape_socket *pattern; + jsval vp; + struct _ape_sock_callbacks *cbcopy; + struct _ape_sock_js_obj *sock_obj; + if (!JS_ConvertArguments(cx, argc, argv, "is/o", &port, &ip, &options)) { + return JS_TRUE; + } + + sock_obj = xmalloc(sizeof(*sock_obj)); + sock_obj->client_obj = NULL; + + cbcopy = xmalloc(sizeof(struct _ape_sock_callbacks)); + + cbcopy->private = sock_obj; + cbcopy->asc = asc; + cbcopy->server_obj = obj; + cbcopy->state = 1; + + JS_AddRoot(cx, &cbcopy->server_obj); + + pattern = xmalloc(sizeof(*pattern)); + pattern->callbacks.on_connect = sm_sock_onconnect; + pattern->callbacks.on_disconnect = sm_sock_ondisconnect; + if (options != NULL && JS_GetProperty(cx, options, "flushlf", &vp) && JSVAL_IS_BOOLEAN(vp) && vp == JSVAL_TRUE) { + pattern->callbacks.on_read_lf = sm_sock_onread_lf; + pattern->callbacks.on_read = NULL; + } else { + /* use the classic read callback */ + pattern->callbacks.on_read = sm_sock_onread; + pattern->callbacks.on_read_lf = NULL; + } + pattern->attach = cbcopy; + JS_SetPrivate(cx, obj, cbcopy); + + ape_connect_name(ip, port, pattern, g_ape); + +/* JS_DefineFunctions(cx, obj, apesocket_client_funcs); + JS_DefineFunctions(cx, obj, apesocket_funcs);*/ + + return JS_TRUE; +} + +APE_JS_NATIVE(ape_sm_pipe_constructor) +//{ + transpipe *pipe; + //JSObject *link; + + /* Add link to a Root ? */ + pipe = init_pipe(NULL, CUSTOM_PIPE, g_ape); + pipe->on_send = ape_sm_pipe_on_send_wrapper; + pipe->data = obj; + + add_property(&pipe->properties, "cx", cx, EXTEND_POINTER, EXTEND_ISPRIVATE); + + JS_SetPrivate(cx, obj, pipe); + + /* TODO : This private data must be removed is the pipe is destroyed */ + + + return JS_TRUE; +} + +#ifdef _USE_MYSQL +static void ape_mysql_handle_io(struct _ape_mysql_data *myhandle, acetables *g_ape) +{ + int ret; + + if (myhandle->my->call_it == NULL) { + return; + } + ret = mysac_io(myhandle->my); + + switch(ret) { + case MYERR_WANT_WRITE: + case MYERR_WANT_READ: + break; + default: + myhandle->my->call_it = NULL; /* prevent any extra IO call */ + + if (myhandle->on_success != NULL) { + myhandle->on_success(myhandle, ret); + } + + break; + } +} + +static void ape_mysql_io_read(ape_socket *client, ape_buffer *buf, size_t offset, acetables *g_ape) +{ + struct _ape_mysql_data *myhandle = client->data; + + ape_mysql_handle_io(myhandle, g_ape); +} + +static void ape_mysql_io_write(ape_socket *client, acetables *g_ape) +{ + struct _ape_mysql_data *myhandle = client->data; + + ape_mysql_handle_io(myhandle, g_ape); +} + +static void mysac_setdb_success(struct _ape_mysql_data *myhandle, int code) +{ + jsval rval; + if (myhandle->jsmysql == NULL) { + return; + } + + if (!code) { + myhandle->state = SQL_READY_FOR_QUERY; + apemysql_shift_queue(myhandle); + + JS_CallFunctionName(myhandle->cx, myhandle->jsmysql, "onConnect", 0, NULL, &rval); + } else { + jsval params[1]; + params[0] = INT_TO_JSVAL(code); + + JS_CallFunctionName(myhandle->cx, myhandle->jsmysql, "onError", 1, params, &rval); + + myhandle->jsmysql = NULL; + myhandle->on_success = NULL; + + /* TODO : Supress queue */ + + free(myhandle->db); + } +} + +static void mysac_connect_success(struct _ape_mysql_data *myhandle, int code) +{ + jsval rval; + if (myhandle->jsmysql == NULL) { + return; + } + + if (!code) { + myhandle->on_success = mysac_setdb_success; + + mysac_set_database(myhandle->my, myhandle->db); + mysac_send_database(myhandle->my); + + } else { + jsval params[1]; + params[0] = INT_TO_JSVAL(code); + + JS_CallFunctionName(myhandle->cx, myhandle->jsmysql, "onError", 1, params, &rval); + + /* TODO : Supress queue */ + + myhandle->jsmysql = NULL; + myhandle->on_success = NULL; + + free(myhandle->db); + } +} + +static void mysac_query_success(struct _ape_mysql_data *myhandle, int code) +{ + struct _ape_mysql_queue *queue = myhandle->data; + jsval params[2], rval; + myhandle->state = SQL_READY_FOR_QUERY; + myhandle->on_success = NULL; + + if (!code) { + MYSAC_ROW *row; + MYSAC_RES *myres = queue->res; + + unsigned int nfield = mysac_field_count(myres), nrow = mysac_num_rows(myres), pos = 0; + JSObject *res = JS_NewArrayObject(myhandle->cx, nrow, NULL); /* First param [{},{},{},] */ + + JS_AddRoot(myhandle->cx, &res); + + while (nrow && (row = mysac_fetch_row(myres)) != NULL) { + unsigned int i; + jsval currentval; + JSObject *elem = JS_NewObject(myhandle->cx, NULL, NULL, NULL); + JS_AddRoot(myhandle->cx, &elem); + currentval = OBJECT_TO_JSVAL(elem); + JS_SetElement(myhandle->cx, res, pos, ¤tval); + JS_RemoveRoot(myhandle->cx, &elem); + for (i = 0; i < nfield; i++) { + int fieldlen, valuelen; + char *field, *val; + jsval jval; + + valuelen = ((MYSAC_RES *)myres)->cr->lengths[i]; + fieldlen = ((MYSAC_RES *)myres)->cols[i].name_length; + + field = ((MYSAC_RES *)myres)->cols[i].name; + val = row[i].blob; + + jval = STRING_TO_JSVAL(JS_NewStringCopyN(myhandle->cx, val, valuelen)); + JS_SetProperty(myhandle->cx, elem, field, &jval); + } + pos++; + } + params[0] = OBJECT_TO_JSVAL(res); + params[1] = JSVAL_FALSE; + + JS_RemoveRoot(myhandle->cx, &res); + JS_CallFunctionValue(myhandle->cx, myhandle->jsmysql, queue->callback, 2, params, &rval); + } else { + params[0] = JSVAL_FALSE; + params[1] = INT_TO_JSVAL(code); + + JS_CallFunctionValue(myhandle->cx, myhandle->jsmysql, queue->callback, 2, params, &rval); + } + JS_RemoveRoot(myhandle->cx, &queue->callback); + + free(queue->query); + free(queue->res); + free(queue); + + apemysql_shift_queue(myhandle); +} + +static void apemysql_finalize(JSContext *cx, JSObject *jsmysql) +{ + struct _ape_mysql_data *myhandle; + + if ((myhandle = JS_GetPrivate(cx, jsmysql)) != NULL) { + myhandle->jsmysql = NULL; + /* Todo: shutdown mysql */ + } +} + +static void apemysql_shift_queue(struct _ape_mysql_data *myhandle) +{ + struct _ape_mysql_queue *queue; + int ret; + + if (myhandle->queue.head == NULL || myhandle->state != SQL_READY_FOR_QUERY) { + return; + } + + queue = myhandle->queue.head; + myhandle->state = SQL_NEED_QUEUE; + myhandle->data = queue; + + if ((myhandle->queue.head = queue->next) == NULL) { + myhandle->queue.foot = NULL; + } + mysac_b_set_query(myhandle->my, queue->res, queue->query, queue->query_len); + + switch((ret = mysac_send_query(myhandle->my))) { + case MYERR_WANT_WRITE: + case MYERR_WANT_READ: + break; + default: + myhandle->on_success = NULL; + myhandle->my->call_it = NULL; + mysac_query_success(myhandle, ret); + return; + } + + myhandle->on_success = mysac_query_success; + +} + +static struct _ape_mysql_queue *apemysql_push_queue(struct _ape_mysql_data *myhandle, char *query, unsigned int query_len, jsval callback) +{ + struct _ape_mysql_queue *nqueue; + int basemem = (1024*1024); + MYSAC_RES *res; + char *res_buf = xmalloc(sizeof(char) * basemem); + + res = mysac_init_res(res_buf, basemem); + + nqueue = xmalloc(sizeof(*nqueue)); + + nqueue->next = NULL; + nqueue->query = query; + nqueue->query_len = query_len; + nqueue->callback = callback; + nqueue->res = res; + + if (myhandle->queue.foot == NULL) { + myhandle->queue.head = nqueue; + } else { + myhandle->queue.foot->next = nqueue; + } + myhandle->queue.foot = nqueue; + + JS_AddRoot(myhandle->cx, &nqueue->callback); + + if (myhandle->queue.head->next == NULL && myhandle->state == SQL_READY_FOR_QUERY) { + + apemysql_shift_queue(myhandle); + return NULL; + } + return nqueue; +} + +APE_JS_NATIVE(ape_sm_mysql_constructor) +//{ + char *host, *login, *pass, *db; + + MYSAC *my; + int fd; + struct _ape_mysql_data *myhandle; + + if (!JS_ConvertArguments(cx, argc, argv, "ssss", &host, &login, &pass, &db)) { + return JS_TRUE; + } + + myhandle = xmalloc(sizeof(*myhandle)); + + my = mysac_new(1024*1024); + mysac_setup(my, host, login, pass, db, 0); + mysac_connect(my); + + myhandle->my = my; + myhandle->jsmysql = obj; + myhandle->cx = cx; + myhandle->db = xstrdup(db); + myhandle->data = NULL; + myhandle->callback = JSVAL_NULL; + myhandle->state = SQL_NEED_QUEUE; + myhandle->queue.head = NULL; + myhandle->queue.foot = NULL; + + JS_SetPrivate(cx, obj, myhandle); + + fd = mysac_get_fd(my); + + prepare_ape_socket (fd, g_ape); + + g_ape->co[fd]->fd = fd; + g_ape->co[fd]->stream_type = STREAM_DELEGATE; + + g_ape->co[fd]->callbacks.on_read = ape_mysql_io_read; + g_ape->co[fd]->callbacks.on_write = ape_mysql_io_write; + g_ape->co[fd]->data = myhandle; + + events_add(g_ape->events, fd, EVENT_READ|EVENT_WRITE); + + //myhandle->to_call = mysac_connect; + myhandle->on_success = mysac_connect_success; + + return JS_TRUE; +} +#endif + +APE_JS_NATIVE(ape_sm_sockserver_constructor) +//{ + int port; + char *ip; + JSObject *options = NULL; + ape_socket *server; + jsval vp; + + if (!JS_ConvertArguments(cx, argc, argv, "is/o", &port, &ip, &options)) { + return JS_TRUE; + } + + server = ape_listen(port, ip, g_ape); + + if (server == NULL) { + *rval = JSVAL_FALSE; + return JS_TRUE; + } + + server->attach = xmalloc(sizeof(struct _ape_sock_callbacks)); + + ((struct _ape_sock_callbacks *)server->attach)->asc = asc; + ((struct _ape_sock_callbacks *)server->attach)->private = NULL; + ((struct _ape_sock_callbacks *)server->attach)->server_obj = obj; + ((struct _ape_sock_callbacks *)server->attach)->state = 1; + + JS_AddRoot(cx, &((struct _ape_sock_callbacks *)server->attach)->server_obj); + + /* check if flushlf is set to true in the optional object */ + if (options != NULL && JS_GetProperty(cx, options, "flushlf", &vp) && JSVAL_IS_BOOLEAN(vp) && vp == JSVAL_TRUE) { + server->callbacks.on_read_lf = sm_sock_onread_lf; + } else { + /* use the classic read callback */ + server->callbacks.on_read = sm_sock_onread; + } + + server->callbacks.on_accept = sm_sock_onaccept; + server->callbacks.on_disconnect = sm_sock_ondisconnect; + + /* store the ape_socket server in the js object */ + JS_SetPrivate(cx, obj, server); + + JS_DefineFunctions(cx, obj, apesocket_client_funcs); + + return JS_TRUE; +} + +APE_JS_NATIVE(ape_sm_xorize) +//{ + JSString *s1, *s2; + char *ps1, *ps2, *final; + int i, len; + + if (!JS_ConvertArguments(cx, 2, argv, "SS", &s1, &s2)) { + return JS_TRUE; + } + + ps1 = JS_GetStringBytes(s1); + ps2 = JS_GetStringBytes(s2); + len = JS_GetStringLength(s1); + + if (JS_GetStringLength(s2) < len) { + return JS_TRUE; + } + + final = xmalloc(sizeof(char) * len); + + for (i = 0; i < len; i++) { + final[i] = ps1[i] ^ ps2[i]; + } + + *rval = STRING_TO_JSVAL(JS_NewStringCopyN(cx, final, len)); + + free(final); + + return JS_TRUE; +} + +static JSFunctionSpec ape_funcs[] = { + JS_FS("addEvent", ape_sm_addEvent, 2, 0, 0), /* Ape.addEvent('name', function() { }); */ + JS_FS("registerCmd", ape_sm_register_cmd, 3, 0, 0), + JS_FS("registerHookBadCmd", ape_sm_register_bad_cmd, 1, 0, 0), + JS_FS("registerHookCmd", ape_sm_hook_cmd, 2, 0, 0), + JS_FS("log", ape_sm_echo, 1, 0, 0),/* Ape.echo('stdout\n'); */ + JS_FS("getPipe", ape_sm_get_pipe, 1, 0, 0), + JS_FS("getChannelByName", ape_sm_get_channel_by_name, 1, 0, 0), + JS_FS("getUserByPubid", ape_sm_get_user_by_pubid, 1, 0, 0), + JS_FS("getChannelByPubid", ape_sm_get_channel_by_pubid, 1, 0, 0), + JS_FS("config", ape_sm_config, 2, 0, 0), + JS_FS("mainConfig", ape_sm_mainconfig, 2, 0, 0), + JS_FS("setTimeout", ape_sm_set_timeout, 2, 0, 0), + JS_FS("setInterval", ape_sm_set_interval, 2, 0, 0), + JS_FS("clearTimeout", ape_sm_clear_timeout, 1, 0, 0), + JS_FS("clearInterval", ape_sm_clear_timeout, 1, 0, 0), + JS_FS("xorize", ape_sm_xorize, 2, 0, 0), + JS_FS("addUser", ape_sm_adduser, 1, 0, 0), + JS_FS("mkChan", ape_sm_mkchan, 1, 0, 0), + JS_FS("rmChan", ape_sm_rmchan, 1, 0, 0), + JS_FS_END +}; + +static JSFunctionSpec global_funcs[] = { + JS_FS("include", ape_sm_include, 1, 0, 0), + JS_FS_END +}; + +static JSFunctionSpec b64_funcs[] = { + JS_FS("encode", ape_sm_b64_encode, 1, 0, 0), + JS_FS("decode", ape_sm_b64_decode, 1, 0, 0), + JS_FS_END +}; + +static JSFunctionSpec sha1_funcs[] = { + JS_FS("str", ape_sm_sha1_str, 1, 0, 0), + JS_FS("bin", ape_sm_sha1_bin, 1, 0, 0), + JS_FS_END +}; + + +static void ape_sm_define_ape(ape_sm_compiled *asc, JSContext *gcx, acetables *g_ape) +{ + JSObject *obj, *b64, *sha1, *sockclient, *sockserver, *custompipe, *user, *channel, *pipe, *subuser; + #ifdef _USE_MYSQL + JSObject *jsmysql; + #endif + + obj = JS_DefineObject(asc->cx, asc->global, "Ape", &ape_class, NULL, 0); + b64 = JS_DefineObject(asc->cx, obj, "base64", &b64_class, NULL, 0); + sha1 = JS_DefineObject(asc->cx, obj, "sha1", &sha1_class, NULL, 0); + user = JS_DefineObject(gcx, obj, "user", &user_class, NULL, 0); + subuser = JS_DefineObject(gcx, obj, "subuser", &subuser_class, NULL, 0); + channel = JS_DefineObject(gcx, obj, "channel", &channel_class, NULL, 0); + pipe = JS_DefineObject(gcx, obj, "pipe", &pipe_class, NULL, 0); + + JS_DefineFunctions(gcx, user, apeuser_funcs); + JS_DefineFunctions(gcx, channel, apechannel_funcs); + JS_DefineFunctions(gcx, pipe, apepipe_funcs); + + add_property(&g_ape->properties, "user_proto", user, EXTEND_POINTER, EXTEND_ISPRIVATE); + add_property(&g_ape->properties, "subuser_proto", subuser, EXTEND_POINTER, EXTEND_ISPRIVATE); + add_property(&g_ape->properties, "channel_proto", channel, EXTEND_POINTER, EXTEND_ISPRIVATE); + add_property(&g_ape->properties, "pipe_proto", pipe, EXTEND_POINTER, EXTEND_ISPRIVATE); + + JS_DefineFunctions(asc->cx, obj, ape_funcs); + JS_DefineFunctions(asc->cx, asc->global, global_funcs); + JS_DefineFunctions(asc->cx, b64, b64_funcs); + JS_DefineFunctions(asc->cx, sha1, sha1_funcs); + + custompipe = JS_InitClass(asc->cx, obj, NULL, &pipe_class, ape_sm_pipe_constructor, 0, NULL, NULL, NULL, NULL); + sockserver = JS_InitClass(asc->cx, obj, NULL, &socketserver_class, ape_sm_sockserver_constructor, 2, NULL, NULL, NULL, NULL); + sockclient = JS_InitClass(asc->cx, obj, NULL, &socketclient_class, ape_sm_sockclient_constructor, 2, NULL, NULL, NULL, NULL); + #ifdef _USE_MYSQL + jsmysql = JS_InitClass(asc->cx, obj, NULL, &mysql_class, ape_sm_mysql_constructor, 2, NULL, NULL, NULL, apemysql_funcs_static); + #endif + JS_InitClass(asc->cx, obj, NULL, &raw_class, ape_sm_raw_constructor, 1, NULL, NULL, NULL, NULL); /* Not used */ + + JS_DefineFunctions(asc->cx, sockclient, apesocket_client_funcs); + JS_DefineFunctions(asc->cx, sockclient, apesocket_funcs); + + JS_DefineFunctions(asc->cx, sockserver, apesocket_client_funcs); + JS_DefineFunctions(asc->cx, sockserver, apesocketserver_funcs); + + JS_DefineFunctions(asc->cx, custompipe, apepipe_funcs); + JS_DefineFunctions(asc->cx, custompipe, apepipecustom_funcs); + + #ifdef _USE_MYSQL + JS_DefineFunctions(asc->cx, jsmysql, apemysql_funcs); + #endif + + JS_SetContextPrivate(asc->cx, asc); +} + +static int process_cmd_return(JSContext *cx, jsval rval, callbackp *callbacki, acetables *g_ape) +{ + JSObject *ret_opt = NULL; + + if (JSVAL_IS_INT(rval) && JSVAL_TO_INT(rval) == 0) { + return RETURN_BAD_PARAMS; + } else if (JSVAL_IS_INT(rval) && JSVAL_TO_INT(rval) == -1) { + return RETURN_NOTHING; + } else if (JSVAL_IS_INT(rval) && JSVAL_TO_INT(rval) == -2) { + return RETURN_HANG; + } else if (JSVAL_IS_OBJECT(rval)) { + jsval vp[2]; + ret_opt = JSVAL_TO_OBJECT(rval); + if (JS_IsArrayObject(cx, ret_opt) == JS_FALSE) { + jsval rawname, data; + + JS_GetProperty(cx, ret_opt, "name", &rawname); + JS_GetProperty(cx, ret_opt, "data", &data); + + if (rawname != JSVAL_VOID && JSVAL_IS_STRING(rawname) && data != JSVAL_VOID && JSVAL_IS_OBJECT(data)) { + json_item *rawdata = NULL; + + if ((rawdata = jsobj_to_ape_json(cx, JSVAL_TO_OBJECT(data))) != NULL) { + RAW *newraw = forge_raw(JS_GetStringBytes(JSVAL_TO_STRING(rawname)), rawdata); + + send_raw_inline(callbacki->client, callbacki->transport, newraw, g_ape); + + return RETURN_NULL; + } + } + } else { + unsigned int length = 0; + JS_GetArrayLength(cx, ret_opt, &length); + if (length == 2 && JS_GetElement(cx, ret_opt, 0, &vp[0]) && JS_GetElement(cx, ret_opt, 1, &vp[1]) && vp[0] != JSVAL_VOID && vp[1] != JSVAL_VOID) { + if (JSVAL_IS_STRING(vp[1])) { + RAW *newraw; + JSString *code = JS_ValueToString(cx, vp[0]); + json_item *jlist = json_new_object(); + + if (callbacki->chl) { + json_set_property_intN(jlist, "chl", 3, callbacki->chl); + } + + json_set_property_strZ(jlist, "code", JS_GetStringBytes(code)); + json_set_property_strZ(jlist, "value", JS_GetStringBytes(JSVAL_TO_STRING(vp[1]))); + + newraw = forge_raw(RAW_ERR, jlist); + + if (callbacki->call_user != NULL) { + post_raw_sub(newraw, callbacki->call_subuser, g_ape); + } else { + send_raw_inline(callbacki->client, callbacki->transport, newraw, g_ape); + return RETURN_CONTINUE; + } + return RETURN_HANG; + } + } + } + } + return RETURN_CONTINUE; +} + +static int ape_fire_cmd(const char *name, JSObject *obj, JSObject *cb, callbackp *callbacki, acetables *g_ape) +{ + ape_sm_compiled *asc = ASMR->scripts; + jsval params[2]; + + if (asc == NULL) { + return RETURN_CONTINUE; + } + params[0] = OBJECT_TO_JSVAL(obj); + params[1] = OBJECT_TO_JSVAL(cb); + + while (asc != NULL) { + ape_sm_callback *cbk; + + for (cbk = asc->callbacks.head; cbk != NULL; cbk = cbk->next) { + if ((cbk->type == APE_CMD && strcasecmp(name, cbk->callbackname) == 0)) { + jsval rval; + + if (JS_CallFunctionValue(cbk->cx, JS_GetGlobalObject(cbk->cx), cbk->func, 2, params, &rval) == JS_FALSE) { + return (cbk->type == APE_CMD ? RETURN_BAD_PARAMS : RETURN_BAD_CMD); + } + + return process_cmd_return(cbk->cx, rval, callbacki, g_ape); + } + } + asc = asc->next; + } + return RETURN_CONTINUE; +} + +static int ape_fire_hook(ape_sm_callback *cbk, JSObject *obj, JSObject *cb, callbackp *callbacki, acetables *g_ape) +{ + ape_sm_compiled *asc = ASMR->scripts; + jsval params[3]; + jsval rval; + int flagret; + + if (asc == NULL) { + return RETURN_CONTINUE; + } + + params[0] = OBJECT_TO_JSVAL(obj); + params[1] = OBJECT_TO_JSVAL(cb); + if (cbk->type == APE_BADCMD) { + params[2] = STRING_TO_JSVAL(JS_NewStringCopyZ(cbk->cx, callbacki->cmd)); + } + + if (JS_CallFunctionValue(cbk->cx, cb, (cbk->func), (cbk->type == APE_BADCMD ? 3 : 2), params, &rval) == JS_FALSE) { + return (cbk->type != APE_BADCMD ? RETURN_BAD_PARAMS : RETURN_BAD_CMD); + } + + flagret = process_cmd_return(cbk->cx, rval, callbacki, g_ape); + + return (cbk->type == APE_BADCMD && flagret == RETURN_CONTINUE ? RETURN_BAD_CMD : flagret); +} + +static void ape_fire_callback(const char *name, uintN argc, jsval *argv, acetables *g_ape) +{ + ape_sm_compiled *asc = ASMR->scripts; + + if (asc == NULL) { + return; + } + + while (asc != NULL) { + ape_sm_callback *cb; + + for (cb = asc->callbacks.head; cb != NULL; cb = cb->next) { + + if (cb->type == APE_EVENT && strcasecmp(name, cb->callbackname) == 0) { + jsval rval; + + //JS_SetContextThread(asc->cx); + //JS_BeginRequest(asc->cx); + + JS_CallFunctionValue(asc->cx, asc->global, (cb->func), argc, argv, &rval); + + //JS_EndRequest(asc->cx); + //JS_ClearContextThread(asc->cx); + } + } + + asc = asc->next; + } + +} + +static void init_module(acetables *g_ape) // Called when module is loaded +{ + JSRuntime *rt; + JSContext *gcx; + + ape_sm_runtime *asr; + jsval rval; + int i; + char rpath[512]; + + glob_t globbuf; + + rt = JS_NewRuntime(128L * 1024L * 1024L); + + if (rt == NULL) { + printf("[ERR] Not enougth memory\n"); + exit(0); + } + asr = xmalloc(sizeof(*asr)); + asr->runtime = rt; + asr->scripts = NULL; + + /* Setup a global context to store shared object */ + gcx = JS_NewContext(rt, 8192); + JS_SetOptions(gcx, JSOPTION_VAROBJFIX | JSOPTION_JIT); + JS_SetVersion(gcx, JSVERSION_LATEST); + JS_SetErrorReporter(gcx, reportError); + JS_InitStandardClasses(gcx, JS_NewObject(gcx, &global_class, NULL, NULL)); + + add_property(&g_ape->properties, "sm_context", gcx, EXTEND_POINTER, EXTEND_ISPRIVATE); + add_property(&g_ape->properties, "sm_runtime", asr, EXTEND_POINTER, EXTEND_ISPRIVATE); + + memset(rpath, '\0', sizeof(rpath)); + strncpy(rpath, READ_CONF("scripts_path"), 256); + strcat(rpath, "/*.ape.js"); + + glob(rpath, 0, NULL, &globbuf); + + for (i = 0; i < globbuf.gl_pathc; i++) { + ape_sm_compiled *asc = xmalloc(sizeof(*asc)); + + asc->filename = (void *)xstrdup(globbuf.gl_pathv[i]); + + asc->cx = JS_NewContext(rt, 8192); + //JS_SetGCZeal(asc->cx, 2); + + if (asc->cx == NULL) { + free(asc->filename); + free(asc); + continue; + } + + //JS_SetContextThread(asc->cx); + //JS_BeginRequest(asc->cx); + + JS_SetOptions(asc->cx, JSOPTION_VAROBJFIX | JSOPTION_JIT); + JS_SetVersion(asc->cx, JSVERSION_LATEST); + JS_SetErrorReporter(asc->cx, reportError); + + asc->global = JS_NewObject(asc->cx, &global_class, NULL, NULL); + + JS_InitStandardClasses(asc->cx, asc->global); + + /* define the Ape Object */ + ape_sm_define_ape(asc, gcx, g_ape); + + asc->bytecode = JS_CompileFile(asc->cx, asc->global, asc->filename); + + if (asc->bytecode != NULL) { + asc->scriptObj = JS_NewScriptObject(asc->cx, asc->bytecode); + + /* Adding to the root (prevent the script to be GC collected) */ + JS_AddNamedRoot(asc->cx, &asc->scriptObj, asc->filename); + + /* put the Ape table on the script structure */ + asc->g_ape = g_ape; + + asc->callbacks.head = NULL; + asc->callbacks.foot = NULL; + + /* Run the script */ + JS_ExecuteScript(asc->cx, asc->global, asc->bytecode, &rval); + + } + //JS_EndRequest(asc->cx); + //JS_ClearContextThread(asc->cx); + + if (asc->bytecode == NULL) { + /* cleaning memory */ + } else { + asc->next = asr->scripts; + asr->scripts = asc; + } + } + globfree(&globbuf); + + APE_JS_EVENT("init", 0, NULL); + +} + +static void free_module(acetables *g_ape) // Called when module is unloaded +{ + // TODO free other allocated objects + + ape_sm_compiled *asc = ASMR->scripts; + ape_sm_compiled *prev_asc; + + while (asc != NULL) { + free(asc->filename); + JS_DestroyContext(asc->cx); + prev_asc = asc; + asc = asc->next; + free(prev_asc); + } + + JS_DestroyContext(ASMC); + JS_DestroyRuntime(ASMR->runtime); + + free(ASMR); +} + +static USERS *ape_cb_add_user(USERS *allocated, acetables *g_ape) +{ + jsval params[1]; + + USERS *u = adduser(NULL, NULL, NULL, allocated, g_ape); + + if (u != NULL) { + + params[0] = OBJECT_TO_JSVAL(APEUSER_TO_JSOBJ(u)); + APE_JS_EVENT("adduser", 1, params); + } + + return u; +} + +static USERS *ape_cb_allocateuser(ape_socket *client, const char *host, const char *ip, acetables *g_ape) +{ + JSObject *user; + extend *jsobj; + JSContext *gcx = ASMC; + jsval pipe; + + USERS *u = adduser(client, host, ip, NULL, g_ape); + + if (u != NULL) { + user = JS_NewObject(gcx, &user_class, get_property(g_ape->properties, "user_proto")->val, NULL); + + /* Store the JSObject into a private properties of the user */ + jsobj = add_property(&u->properties, "jsobj", user, EXTEND_POINTER, EXTEND_ISPRIVATE); + JS_AddRoot(gcx, &jsobj->val); /* add user object to the gc root */ + + JS_SetPrivate(gcx, user, u); + + pipe = OBJECT_TO_JSVAL(get_pipe_object(NULL, u->pipe, gcx, g_ape)); + JS_SetProperty(gcx, user, "pipe", &pipe); + } + + return u; +} + +static void ape_cb_del_user(USERS *user, int istmp, acetables *g_ape) +{ + jsval params[1]; + extend *jsobj; + JSObject *pipe; + JSContext *gcx = ASMC; + + if (!istmp) { + params[0] = OBJECT_TO_JSVAL(APEUSER_TO_JSOBJ(user)); + APE_JS_EVENT("deluser", 1, params); + } + + pipe = user->pipe->data; + JS_SetPrivate(gcx, pipe, (void *)NULL); + + jsobj = get_property(user->properties, "jsobj"); + + JS_SetPrivate(gcx, jsobj->val, (void *)NULL); + JS_RemoveRoot(gcx, &jsobj->val); + + deluser(user, g_ape); +} + +static CHANNEL *ape_cb_mkchan(char *name, int flags, acetables *g_ape) +{ + JSObject *js_channel; + extend *jsobj; + jsval params[1], pipe; + JSContext *gcx = ASMC; + CHANNEL *chan; + + if ((chan = mkchan(name, flags, g_ape)) == NULL) { + return NULL; + } + + //JS_SetContextThread(gcx); + //JS_BeginRequest(gcx); + + js_channel = JS_NewObject(gcx, &channel_class, get_property(g_ape->properties, "channel_proto")->val, NULL); + + if (js_channel == NULL) { + return NULL; + } + + jsobj = add_property(&chan->properties, "jsobj", js_channel, EXTEND_POINTER, EXTEND_ISPRIVATE); + JS_AddRoot(gcx, &jsobj->val); + + pipe = OBJECT_TO_JSVAL(get_pipe_object(NULL, chan->pipe, gcx, g_ape)); + JS_SetProperty(gcx, js_channel, "pipe", &pipe); + JS_SetPrivate(gcx, js_channel, chan); + + //JS_EndRequest(gcx); + //JS_ClearContextThread(gcx); + + params[0] = OBJECT_TO_JSVAL(js_channel); + APE_JS_EVENT("mkchan", 1, params); + + return chan; +} + +static void ape_cb_rmchan(CHANNEL *chan, acetables *g_ape) +{ + extend *jsobj; + jsval params[1]; + JSObject *pipe; + JSContext *gcx = ASMC; + + params[0] = OBJECT_TO_JSVAL(APECHAN_TO_JSOBJ(chan)); + + APE_JS_EVENT("rmchan", 1, params); + + jsobj = get_property(chan->properties, "jsobj"); + + JS_SetPrivate(gcx, jsobj->val, (void *)NULL); + JS_RemoveRoot(gcx, &jsobj->val); + + pipe = chan->pipe->data; + JS_SetPrivate(gcx, pipe, (void *)NULL); + + rmchan(chan, g_ape); +} + +static void ape_cb_join(USERS *user, CHANNEL *chan, acetables *g_ape) +{ + jsval params[2]; + + params[0] = OBJECT_TO_JSVAL(APEUSER_TO_JSOBJ(user)); + params[1] = OBJECT_TO_JSVAL(APECHAN_TO_JSOBJ(chan)); + + APE_JS_EVENT("beforeJoin", 2, params); + + join(user, chan, g_ape); + + APE_JS_EVENT("afterJoin", 2, params); + APE_JS_EVENT("join", 2, params); +} + +static void ape_cb_left(USERS *user, CHANNEL *chan, acetables *g_ape) +{ + jsval params[2]; + + params[0] = OBJECT_TO_JSVAL(APEUSER_TO_JSOBJ(user)); + params[1] = OBJECT_TO_JSVAL(APECHAN_TO_JSOBJ(chan)); + + APE_JS_EVENT("left", 2, params); + + left(user, chan, g_ape); + +} + +static void ape_cb_addsubuser(subuser *sub, acetables *g_ape) +{ + JSObject *subjs; + extend *jsobj; + JSContext *gcx = ASMC; + + subjs = JS_NewObject(gcx, &subuser_class, get_property(g_ape->properties, "subuser_proto")->val, NULL); + + if (subjs == NULL) { + return; + } + + jsobj = add_property(&sub->properties, "jsobj", subjs, EXTEND_POINTER, EXTEND_ISPRIVATE); + JS_AddRoot(gcx, &jsobj->val); + + JS_SetPrivate(gcx, subjs, sub); + +} + +static void ape_cb_delsubuser(subuser *sub, acetables *g_ape) +{ + extend *jsobj; + JSContext *gcx = ASMC; + + jsobj = get_property(sub->properties, "jsobj"); + + JS_SetPrivate(gcx, jsobj->val, (void *)NULL); + JS_RemoveRoot(gcx, &jsobj->val); + +} + +static ace_callbacks callbacks = { + ape_cb_add_user, /* Called when new user is added */ + ape_cb_del_user, /* Called when a user is disconnected */ + ape_cb_mkchan, /* Called when new chan is created */ + ape_cb_rmchan, /* Called when a chan is deleted */ + ape_cb_join, /* Called when a user join a channel */ + ape_cb_left, /* Called when a user leave a channel */ + NULL, + NULL, + ape_cb_allocateuser, + ape_cb_addsubuser, + ape_cb_delsubuser +}; + +APE_INIT_PLUGIN(MODULE_NAME, init_module, free_module, callbacks) + diff --git a/ape-server/modules/plugins.h b/ape-server/modules/plugins.h new file mode 100755 index 0000000..c495621 --- /dev/null +++ b/ape-server/modules/plugins.h @@ -0,0 +1,96 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* ace.h */ + + +#ifndef _MODULES_APE_H +#define _MODULES_APE_H +#if __cplusplus + extern "C" { +#endif + +#include "../src/main.h" +#include "../src/channel.h" +#include "../src/cmd.h" +#include "../src/extend.h" +#include "../src/users.h" +#include "../src/utils.h" +#include "../src/plugins.h" +#include "../src/ticks.h" +#include "../src/pipe.h" +#include "../src/raw.h" +#include "../src/base64.h" +#include "../src/sha1.h" +#include "../src/events.h" +#include "../src/log.h" +#include "../src/config.h" + +#include + +#define READ_CONF(key) plugin_get_conf(infos_module.conf, key) + +typedef struct _ace_callbacks ace_callbacks; + +struct _ace_callbacks +{ + USERS *(*c_adduser)(USERS *, acetables *); + void (*c_deluser)(USERS *, int istmp, acetables *); + CHANNEL *(*c_mkchan)(char *, int, acetables *); + void (*c_rmchan)(CHANNEL *, acetables *); + void (*c_join)(USERS *, CHANNEL *, acetables *); + void (*c_left)(USERS *, CHANNEL *, acetables *); + void (*c_tickuser)(subuser *, acetables *); + void (*c_post_raw_sub)(RAW *, subuser *, acetables *); + USERS *(*c_allocateuser)(ape_socket *, const char *, const char *, acetables *); + void (*c_addsubuser)(subuser *, acetables *); + void (*c_delsubuser)(subuser *, acetables *); +}; + +typedef struct _plug_config plug_config; +struct _plug_config +{ + char *key; + char *value; + struct _plug_config *next; +}; + +#if __cplusplus +#define APE_PLUGIN_ENTRY_POINT extern "C" +#else +#define APE_PLUGIN_ENTRY_POINT +#endif + +#define APE_INIT_PLUGIN(modname, initfunc, freefunc, modcallbacks) \ + APE_PLUGIN_ENTRY_POINT void ape_module_init(ace_plugins *module) \ + { \ + infos_module.conf = NULL; \ + module->cb = &modcallbacks; \ + module->infos = &infos_module; \ + module->loader = initfunc; \ + module->unloader = freefunc; \ + module->modulename = modname; \ + } + +#if __cplusplus + } +#endif + +#endif + diff --git a/ape-server/scripts/commands/inlinepush.js b/ape-server/scripts/commands/inlinepush.js new file mode 100755 index 0000000..cc29498 --- /dev/null +++ b/ape-server/scripts/commands/inlinepush.js @@ -0,0 +1,18 @@ +Ape.registerCmd("inlinepush", false, function(params, infos) { + if (params.password == Ape.config("inlinepush.conf", "password")) { + + if ($defined(params.channel) && $defined(params.data) && $defined(params.raw)) { + var chan = Ape.getChannelByName(params.channel); + if (!$defined(chan)) return ["401", "UNKNOWN_CHANNEL"]; + + chan.pipe.sendRaw(params.raw, params.data); + + return {"name":"pushed","data":{"value":"ok"}}; + } else { + return 0; + } + } else { + return ["400", "BAD_PASSWORD"]; + } + +}) \ No newline at end of file diff --git a/ape-server/scripts/commands/proxy.js b/ape-server/scripts/commands/proxy.js new file mode 100755 index 0000000..49385ce --- /dev/null +++ b/ape-server/scripts/commands/proxy.js @@ -0,0 +1,81 @@ +Ape.registerCmd("PROXY_CONNECT", true, function(params, infos) { + if (!$defined(params.host) || !$defined(params.port)) { + return 0; + } + var allowed = Ape.config('proxy.conf', 'allowed_hosts'); + + if (allowed != 'any') { + var hosts = allowed.split(','); + var isallowed = false; + + for (var i = 0; i < hosts.length; i++) { + var parts = hosts[i].trim().split(':'); + if (parts[0] == params.host) { + if (parts.length == 2 && parts[1] != params.port) continue; + isallowed = true; + break; + } + } + if (!isallowed) return [900, "NOT_ALLOWED"]; + } + + var socket = new Ape.sockClient(params.port, params.host); + socket.chl = infos.chl; + /* TODO : Add socket to the user */ + + socket.onConnect = function() { + /* "this" refer to socket object */ + /* Create a new pipe (with a pubid) */ + var pipe = new Ape.pipe(); + + infos.user.proxys.set(pipe.getProperty('pubid'), pipe); + + /* Set some private properties */ + pipe.link = socket; + pipe.nouser = false; + this.pipe = pipe; + + /* Called when an user send a "SEND" command on this pipe */ + pipe.onSend = function(user, params) { + /* "this" refer to the pipe object */ + this.link.write(Ape.base64.decode(unescape(params.msg))); + } + + pipe.onDettach = function() { + this.link.close(); + } + + /* Send a PROXY_EVENT raw to the user and attach the pipe */ + infos.user.pipe.sendRaw("PROXY_EVENT", {"event": "connect", "chl": this.chl}, {from: this.pipe}); + } + + socket.onRead = function(data) { + infos.user.pipe.sendRaw("PROXY_EVENT", {"event": "read", "data": Ape.base64.encode(data)}, {from: this.pipe}); + } + + socket.onDisconnect = function(data) { + if ($defined(this.pipe)) { + if (!this.pipe.nouser) { /* User is not available anymore */ + infos.user.pipe.sendRaw("PROXY_EVENT", {"event": "disconnect"}, {from: this.pipe}); + infos.user.proxys.erase(this.pipe.getProperty('pubid')); + } + /* Destroy the pipe */ + this.pipe.destroy(); + } + } + + return 1; +}); + +Ape.addEvent("deluser", function(user) { + user.proxys.each(function(val) { + val.nouser = true; + val.onDettach(); + }); +}); + +Ape.addEvent("adduser", function(user) { + user.proxys = new $H; +}) + + diff --git a/ape-server/scripts/examples/example1.js b/ape-server/scripts/examples/example1.js new file mode 100755 index 0000000..5884082 --- /dev/null +++ b/ape-server/scripts/examples/example1.js @@ -0,0 +1,59 @@ + +Ape.addEvent("init", function() { + + /* Fired when a new user is connecting */ + Ape.addEvent("adduser", function(user) { + user.foo = "bar"; // Private property + user.setProperty('foo', 'bar'); // Public property (send to ther users) + }); + + /* Fired when a user is disconnecting */ + Ape.addEvent("deluser", function(user) { + Ape.log("Del user ("+ user.foo +") : " + user.getProperty('foo')); + }); + + /* Fired just after the user join a channel */ + Ape.addEvent("afterJoin", function(user, channel) { + Ape.log("JOIN !" + channel.getProperty('name')); + }); + + /* Fired just before the user join a channel */ + Ape.addEvent("beforeJoin", function(user, channel) { + Ape.log("Before..."); + }) + + /* Fired when a channel is created */ + Ape.addEvent("mkchan", function(channel) { + Ape.log("new channel " + channel.getProperty('name')); + }); + + + /* Register a new command (false : sessid not needed) */ + Ape.registerCmd("webhook", false, function(params, infos) { + var data = {params:params, infos:infos}; + + /* make a post request */ + Ape.HTTPRequest('http://www.rabol.fr/bordel/post.php', data); + + }); + + /* Register a CMD that require user to be loged (sessid needed) */ + Ape.registerCmd("foocmd", true, function(params, infos) { + /* Send a row to the given pipe (pubid) */ + Ape.getPipe(params.pipe).sendRaw("foo", {"key":"val"}, { + from: infos.user.pipe /* (optional) User is attached to the raw */ + }); + }); + + /* Hook an existing command (for instance to add params and fire a BAD_PARAMS) */ + Ape.registerHookCmd("foocmd", function(params, infos) { + if (!$defined(params.pipe)) { + return 0; // BAD_PARAMS + } else { + return 1; + } + }); + +}); + + diff --git a/ape-server/scripts/examples/ircserver.js b/ape-server/scripts/examples/ircserver.js new file mode 100755 index 0000000..dfa3884 --- /dev/null +++ b/ape-server/scripts/examples/ircserver.js @@ -0,0 +1,236 @@ + +var IRCApeclient = new Class({ + socket_listener: false, + sessid: false, + chl: 0, + client: false, + + initialize: function(nickname, client) { + this.socket_listener = new Ape.sockClient('6969', '127.0.0.1', {flushlf: true}); + this.client = client; + + this.socket_listener.onConnect = function() { + this.sendCmd('CONNECT', {name: nickname}, this.socket_listener); + }.bind(this); + + this.socket_listener.onRead = function(data) { + data = data.trim(); + if (data[0] == '[') { + var jsobj = eval(data); + jsobj.each(this.parseRaw.bind(this)); + } + }.bind(this); + + this.socket_listener.onDisconnect = function() { + // Ape.log('Listener disco'); + } + + Ape.setInterval(function(obj){ + obj.sendCmd('CHECK', {'null':'null'}); + }, 20000, this); + }, + + sendCmd: function(cmdname, obj, sock) { + + var mainobj = {'cmd':cmdname, 'chl':this.chl, params:obj}; + if (this.sessid) mainobj.sessid = this.sessid; + + this.chl++; + + var jsonobj = JSON.stringify([mainobj]); + + if (!sock) { + var control = new Ape.sockClient('6969', '127.0.0.1', {flushlf: true}); + Ape.log('12.12.12.1'); + Ape.log("SEND CMD " + control); + + control.onConnect = function() { + this.write('GET /1/?'+jsonobj+' HTTP/1.1\n'); + this.write('Host: 0\n\n'); + } + + control.onRead = function(data) { + data = data.trim(); + if (data[0] == '[') { + var jsobj = eval(data); + jsobj.each(this.parseRaw.bind(this)); + } + }.bind(this); + + control.onDisconnect = function() { + // Ape.log('Controler disco'); + } + } else { + sock.write('GET /1/?'+jsonobj+' HTTP/1.1\n'); + sock.write('Host: 0\n\n'); + } + }, + + parseRaw: function(obj) { + // Ape.log('Recu : ' + obj.raw); + if ($defined(this['raw' + obj.raw])) this['raw' + obj.raw](obj.data); + }, + + rawLOGIN: function(data) { + this.sessid = data.sessid; + this.client.send('NOTICE APE :*** Your sessid ' + this.sessid); + }, + + rawCHANNEL: function(data) { + /* + :para_!n=para@local.weelya.com JOIN :#ape-project-fr + :wolfe.freenode.net 332 para_ #ape-project-fr :APE roxorise + :wolfe.freenode.net 333 para_ #ape-project-fr Lowan 1244582828 + :wolfe.freenode.net 353 para_ @ #ape-project-fr :para_ jchavarria_work Korri1 paraboul jchavarria korri efyx Fy- Lowan + :wolfe.freenode.net 366 para_ #ape-project-fr :End of /NAMES list. + */ + var users = []; + data.users.each(function(user) { + users.push(user.properties.name); + }) + this.client.send(':'+this.client.nickname+'!n=ape@weelya.ca.rox.grave JOIN :#'+data.pipe.properties.name); + this.client.serverRaw('332', 'Tes sur un channel APE mec', '#'+data.pipe.properties.name); + this.client.serverRaw('353', users.join(' '), '@ #'+data.pipe.properties.name); + this.client.serverRaw('366', 'End of /NAMES list.', '#'+data.pipe.properties.name); + }, + + rawJOIN: function(data) { + this.client.send(':'+data.user.properties.name+'!n=ape@weelya.ca.rox.grave JOIN :#'+data.pipe.properties.name); + }, + + rawERR: function(data) { + Ape.log('ERREUR : ' + data.value); + }, + + rawDATA: function(data) { + //:efyx_!n=efyx@lap34-1-82-225-185-146.fbx.proxad.net PRIVMSG #ape-project :-test for paraboul + this.client.send(':'+data.from.properties.name+'!n=ape@weelya.ca.rox.grave PRIVMSG #'+data.pipe.properties.name+' :-'+decodeURIComponent(data.msg)); + } + +}); + + +var IRCClient = new Class({ + socket: false, + nickname: false, + connected: false, + serverobj: false, + apeclient: false, + + initialize: function(socket, serverobj) { + this.socket = socket; + this.serverobj = serverobj; + + }, + + serverRaw: function(number, msg, more) { + if (!this.connected) return false; + this.send(":"+this.serverobj.servername+" "+number+" "+this.nickname+" "+(more ? more : '')+" :"+msg); + }, + + send: function(data) { + //Ape.log(data); + this.socket.write(data + "\n"); + }, + + setNick: function(nick) { + this.nickname = nick; + + if (!this.connected) { + + this.apeclient = new IRCApeclient(nick, this); + this.connected = true; + this.serverRaw('001', 'Welcome to the APE IRC Network ' + nick); + this.serverRaw('002', 'Your host is '+this.serverobj.servername+'['+this.serverobj.servername+'/'+this.serverobj.port+'], running version APE Server Beta 3'); + this.serverRaw('375', '- APE Message of the Day - '); + this.serverRaw('372', '- _ ____ _____ ___ ____ ____ '); + this.serverRaw('372', '- / \\ | _ \\| ____| |_ _| _ \\ / ___|'); + this.serverRaw('372', '- / _ \\ | |_) | _| | || |_) | | '); + this.serverRaw('372', '- / ___ \\| __/| |___ | || _ <| |___ '); + this.serverRaw('372', '- /_/ \\_\\_| |_____| |___|_| \\_\\\\____|'); + this.serverRaw('376', 'End of /MOTD command.'); + this.serverRaw('290', 'IDENTIFY-MSG'); + + } + }, + + joinChan: function(chan) { + this.apeclient.sendCmd('JOIN', {channels:chan}); + }, + + privMsg: function(pipe, data) { + if (pipe[0] == '#') { + var pubid = Ape.getChannelByName(pipe.substr(1)).getProperty('pubid'); + this.apeclient.sendCmd('SEND', {pipe:pubid, msg:data}); + } + + } + +}) + + +var IRC = new Class({ + port: 6667, + servername: 'apeirc.ape-project.org', + + initialize: function () { + var irc = new Ape.sockServer('6667', '0.0.0.0', {flushlf: true}); + + irc.onAccept = function(client) { + this.onAccept(client); + }.bind(this); + + irc.onRead = function(client, data) { + this.onRead(client.obj, data.trim()); + }.bind(this); + + irc.onDisconnect = function(client) { + this.onDisconnect(client); + }.bind(this); + + }, + + onAccept: function(client) { + client.obj = new IRCClient(client, this); + + client.obj.send("NOTICE AUTH :*** Looking up your hostname..."); + client.obj.send("NOTICE AUTH :*** Checking ident"); + client.obj.send("NOTICE AUTH :*** No identd (auth) response"); + client.obj.send("NOTICE AUTH :*** Found your hostname"); + + }, + + onRead: function(clientobj, data) { + Ape.log(data); + var raw = data.split(' '); + if ($defined(this['cmd' + raw[0]])) { + var cmd = raw[0]; + raw.shift(); + this['cmd' + cmd](clientobj, raw.join(' ')); + } + }, + + onDisconnect: function(client) { + + }, + + cmdNICK: function(clientobj, nick) { + clientobj.setNick(nick); + }, + + cmdJOIN: function(clientobj, chan) { + clientobj.joinChan(chan.substr(1)); + }, + + cmdPRIVMSG: function(clientobj, data) { + var msg = data.split(':'); + var pipe = data.split(' '); + msg.shift(); + msg = encodeURIComponent(msg.join(':')); + clientobj.privMsg(pipe[0], msg); + } + +}); + +Ape.log('Starting IRC server...'); +new IRC(); diff --git a/ape-server/scripts/examples/move.js b/ape-server/scripts/examples/move.js new file mode 100755 index 0000000..e3bacec --- /dev/null +++ b/ape-server/scripts/examples/move.js @@ -0,0 +1,16 @@ +Ape.registerCmd('setpos', true, function(params, infos) { + if ((!$defined(params.x) || !$defined(params.y)) && (!isFinite(params.x) || !isFinite(params.y))) return 0; + + infos.user.setProperty('x', params.x); + infos.user.setProperty('y', params.y); + + var chan = Ape.getChannelByPubid(params.pipe); + + if (chan) { + chan.pipe.sendRaw('positions', {'x': params.x, 'y': params.y}, {'from': infos.user.pipe}); + } else { + return ['109', 'UNKNOWN_PIPE']; + } + + return 1; +}); diff --git a/ape-server/scripts/examples/nickname.js b/ape-server/scripts/examples/nickname.js new file mode 100755 index 0000000..93bcd47 --- /dev/null +++ b/ape-server/scripts/examples/nickname.js @@ -0,0 +1,20 @@ +var userlist = new $H; + +Ape.registerHookCmd("connect", function(params, cmd) { + if (!$defined(params) || !$defined(params.name)) return 0; + if (userlist.has(params.name.toLowerCase())) return ["007", "NICK_USED"]; + if (params.name.length > 16 || params.name.test('[^a-zA-Z0-9]', 'i')) return ["006", "BAD_NICK"]; + + + cmd.user.setProperty('name', params.name); + + return 1; +}); + +Ape.addEvent('adduser', function(user) { + userlist.set(user.getProperty('name').toLowerCase(), true); +}); + +Ape.addEvent('deluser', function(user) { + userlist.erase(user.getProperty('name').toLowerCase()); +}); diff --git a/ape-server/scripts/examples/server.js b/ape-server/scripts/examples/server.js new file mode 100755 index 0000000..2a1c160 --- /dev/null +++ b/ape-server/scripts/examples/server.js @@ -0,0 +1,28 @@ +/* Listen on port 6970 (multicast) and high performences server */ +/* Check ./framework/proxy.js for client API */ + +var socket = new Ape.sockServer(6970, "0.0.0.0", { + flushlf: true /* onRead event is fired only when a \n is received (and splitted around it) e.g. foo\nbar\n will call onRead two times with "foo" and "bar" */ +}); + +/* fired when a client is connecting */ +socket.onAccept = function(client) { + Ape.log("New client"); + client.write("Hello world\n"); + client.foo = "bar"; // Properties are persistants + //client.close(); +} + +/* fired when a client send data */ +socket.onRead = function(client, data) { + Ape.log("Data from client : " + data); +} + +/* fired when a client has disconnected */ +socket.onDisconnect = function(client) { + Ape.log("A client has disconnected"); + +} + +Ape.log("Listen on port " + port + '...'); + diff --git a/ape-server/scripts/framework/Http.js b/ape-server/scripts/framework/Http.js new file mode 100644 index 0000000..47b8290 --- /dev/null +++ b/ape-server/scripts/framework/Http.js @@ -0,0 +1,187 @@ +/* Copyright (C) 2009 Weelya & Gasquez Florian */ + +/* +Exemple 1: + // URL to call + var request = new Http('http://www.google.fr/'); + request.getContent(function(result) { + Ape.log(result); + }); + +Example 2: + var request = new Http('http://twitter.com:80/statuses/update.json'); + request.set('method', 'POST'); + + // GET or POST data + request.writeData('status', 'Hello!'); + + // HTTP Auth + request.set('auth', 'user:password'); + + request.getContent(function (result) { + Ape.log(result); + }); + +Example 3: + // URL to call + var request = new Http('http://www.google.fr/'); + request.finish(function(result) { + // {status:Integer, headers:Array, body:String} + }); +*/ + +var Http = new Class({ + method: 'GET', + headers: [], + body: [], + url: null, + + initialize: function(url, port) { + this.url = url; + this.port = port || 80; + + this.parseURL(); + }, + + parseURL: function() { + var result = this.url.match("^.*?://(.*?)(:([0-9]+))?((/.*)|)$"); + this.host = result[1]; + this.port = result[3] || 80; + this.query = result[4]; + }, + + set: function(key, value) { + if (key == 'auth') { + this.auth = 'Basic ' + Ape.base64.encode(value); + this.setHeader('Authorization', this.auth); + } else { + this[key] = value; + } + }, + + setHeader: function(key, value) { + this.headers[key] = value; + }, + + setHeaders: function(object) { + this.headers = object; + }, + + write: function(data) { + this.body.push(data); + }, + + writeData: function(key, value) { + var tmpData = {}; + tmpData[key] = value; + this.write(Hash.toQueryString(tmpData)); + }, + + writeObject: function(data) { + this.write(Hash.toQueryString(data)); + }, + + getContentSize: function() { + return this.response.length-this.responseHeadersLength-4; + }, + + connect: function() { + if (this.method == 'POST') { + this.setHeader('Content-length', this.body.join('&').length); + this.setHeader('Content-Type', 'application/x-www-form-urlencoded'); + } + + this.setHeader('User-Agent', 'APE JS Client'); + this.setHeader('Accept', '*/*'); + + this.socket = new Ape.sockClient(this.port, this.host, { flushlf: false }); + + this.sockConnect(); + this.sockRead(); + }, + + sockConnect: function() { + this.socket.onConnect = function() { + if (this.body.length != 0 && this.method == 'GET') { + var getData = ''; + + if (this.method == 'GET') { + getData = '?' + this.body.join('&'); + } + } + + var toWrite = this.method + " " + this.query + " HTTP/1.0\r\nHost: " + this.host + "\r\n"; + + for (var i in this.headers) { + if (this.headers.hasOwnProperty(i)) { + toWrite += i + ': ' + this.headers[i] + "\r\n"; + } + } + + this.socket.write(toWrite + "\r\n"); + this.socket.write(this.body.join('&')); + }.bind(this); + }, + + sockRead: function() { + this.response = ''; + this.socket.onRead = function(data) { + this.response += data; + if (this.response.contains("\r\n\r\n")) { + if (!$defined(this.responseHeaders)) { + var tmp = this.response.split("\r\n\r\n"); + this.responseHeadersLength = tmp[0].length; + tmp = tmp[0].split("\r\n"); + this.responseHeaders = {}; + this.responseCode = tmp[0].split(" "); + this.responseCode = this.responseCode[1].toInt(); + + for (var i = 1; i < tmp.length; i++) { + var tmpHeaders = tmp[i].split(": "); + this.responseHeaders[tmpHeaders[0]] = tmpHeaders[1]; + } + } else { + if ($defined(this.responseHeaders['Content-Length']) && this.getContentSize() >= this.responseHeaders['Content-Length']) { + this.socket.close(); + } + if ($defined(this.responseHeaders['Location'])) { + socket.close(); + } + } + } + }.bind(this); + }, + + read: function(callback) { + this.socket.onDisconnect = function(callback) { + this.response = this.response.split("\r\n\r\n"); + this.response.shift(); + this.response = this.response.join(); + this.httpResponse = {status:this.responseCode, headers:this.responseHeaders, body:this.response}; + + if ($defined(this.responseHeaders)) { + if ($defined(this.responseHeaders['Location'])) { + var newRequest = new Http(this.responseHeaders['Location']); + newRequest.setHeaders(this.headers); + newRequest.set('method', this.method); + newRequest.write(this.body.join('&')); + newRequest.finish(callback); + } else { + callback.run(this.httpResponse); + } + } + }.bind(this, callback); + }, + + finish: function(callback) { + this.connect(); + this.read(callback); + }, + + getContent: function (callback) { + this.connect(); + this.read(function(result) { + callback.run(result['body']); + }); + }, +}); \ No newline at end of file diff --git a/ape-server/scripts/framework/http_auth.js b/ape-server/scripts/framework/http_auth.js new file mode 100755 index 0000000..302438e --- /dev/null +++ b/ape-server/scripts/framework/http_auth.js @@ -0,0 +1,23 @@ +(function(){ + function http_auth(url, params, callback) + { + var request = new Http(url); + request.set('method', 'POST'); + request.writeObject(params); + request.getContent(function(result) { + var ret = {}; + try { ret = JSON.parse(result); } catch(e){}; + + callback(ret); + }); + } + + Ape.registerHookCmd("CONNECT", function(params, cmd) { + http_auth("http://127.0.0.1/index.php", params, function(result) { + if (result == 1) Ape.addUser(cmd.user); + else cmd.sendResponse("FAIL", {"you":"fail"}); + }); + + return -1; + }); +})(); diff --git a/ape-server/scripts/framework/mootools.js b/ape-server/scripts/framework/mootools.js new file mode 100755 index 0000000..e6b2561 --- /dev/null +++ b/ape-server/scripts/framework/mootools.js @@ -0,0 +1,1096 @@ +/* +--- + +script: Core.js + +description: The core of MooTools, contains all the base functions and the Native and Hash implementations. Required by all the other scripts. + +license: MIT-style license. + +copyright: Copyright (c) 2006-2008 [Valerio Proietti](http://mad4milk.net/). + +authors: The MooTools production team (http://mootools.net/developers/) + +inspiration: +- Class implementation inspired by [Base.js](http://dean.edwards.name/weblog/2006/03/base/) Copyright (c) 2006 Dean Edwards, [GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php) +- Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php) + +provides: [Mootools, Native, Hash.base, Array.each, $util] + +... +*/ + +setTimeout = Ape.setTimeout; +setInterval = Ape.setInterval; + +var MooTools = { + 'version': '1.2.4', + 'build': '0d9113241a90b9cd5643b926795852a2026710d4' +}; + +var Native = function(options){ + options = options || {}; + var name = options.name; + var legacy = options.legacy; + var protect = options.protect; + var methods = options.implement; + var generics = options.generics; + var initialize = options.initialize; + var afterImplement = options.afterImplement || function(){}; + var object = initialize || legacy; + generics = generics !== false; + + object.constructor = Native; + object.$family = {name: 'native'}; + if (legacy && initialize) object.prototype = legacy.prototype; + object.prototype.constructor = object; + + if (name){ + var family = name.toLowerCase(); + object.prototype.$family = {name: family}; + Native.typize(object, family); + } + + var add = function(obj, name, method, force){ + if (!protect || force || !obj.prototype[name]) obj.prototype[name] = method; + if (generics) Native.genericize(obj, name, protect); + afterImplement.call(obj, name, method); + return obj; + }; + + object.alias = function(a1, a2, a3){ + if (typeof a1 == 'string'){ + var pa1 = this.prototype[a1]; + if ((a1 = pa1)) return add(this, a2, a1, a3); + } + for (var a in a1) this.alias(a, a1[a], a2); + return this; + }; + + object.implement = function(a1, a2, a3){ + if (typeof a1 == 'string') return add(this, a1, a2, a3); + for (var p in a1) add(this, p, a1[p], a2); + return this; + }; + + if (methods) object.implement(methods); + + return object; +}; + +Native.genericize = function(object, property, check){ + if ((!check || !object[property]) && typeof object.prototype[property] == 'function') object[property] = function(){ + var args = Array.prototype.slice.call(arguments); + return object.prototype[property].apply(args.shift(), args); + }; +}; + +Native.implement = function(objects, properties){ + for (var i = 0, l = objects.length; i < l; i++) objects[i].implement(properties); +}; + +Native.typize = function(object, family){ + if (!object.type) object.type = function(item){ + return ($type(item) === family); + }; +}; + +(function(){ + var natives = {'Array': Array, 'Date': Date, 'Function': Function, 'Number': Number, 'RegExp': RegExp, 'String': String}; + for (var n in natives) new Native({name: n, initialize: natives[n], protect: true}); + + var types = {'boolean': Boolean, 'native': Native, 'object': Object}; + for (var t in types) Native.typize(types[t], t); + + var generics = { + 'Array': ["concat", "indexOf", "join", "lastIndexOf", "pop", "push", "reverse", "shift", "slice", "sort", "splice", "toString", "unshift", "valueOf"], + 'String': ["charAt", "charCodeAt", "concat", "indexOf", "lastIndexOf", "match", "replace", "search", "slice", "split", "substr", "substring", "toLowerCase", "toUpperCase", "valueOf"] + }; + for (var g in generics){ + for (var i = generics[g].length; i--;) Native.genericize(natives[g], generics[g][i], true); + } +})(); + +var Hash = new Native({ + + name: 'Hash', + + initialize: function(object){ + if ($type(object) == 'hash') object = $unlink(object.getClean()); + for (var key in object) this[key] = object[key]; + return this; + } + +}); + +Hash.implement({ + + forEach: function(fn, bind){ + for (var key in this){ + if (this.hasOwnProperty(key)) fn.call(bind, this[key], key, this); + } + }, + + getClean: function(){ + var clean = {}; + for (var key in this){ + if (this.hasOwnProperty(key)) clean[key] = this[key]; + } + return clean; + }, + + getLength: function(){ + var length = 0; + for (var key in this){ + if (this.hasOwnProperty(key)) length++; + } + return length; + } + +}); + +Hash.alias('forEach', 'each'); + +Array.implement({ + + forEach: function(fn, bind){ + for (var i = 0, l = this.length; i < l; i++) fn.call(bind, this[i], i, this); + } + +}); + +Array.alias('forEach', 'each'); + +function $A(iterable){ + if (iterable.item){ + var l = iterable.length, array = new Array(l); + while (l--) array[l] = iterable[l]; + return array; + } + return Array.prototype.slice.call(iterable); +}; + +function $arguments(i){ + return function(){ + return arguments[i]; + }; +}; + +function $chk(obj){ + return !!(obj || obj === 0); +}; + +function $clear(timer){ + clearTimeout(timer); + clearInterval(timer); + return null; +}; + +function $defined(obj){ + return (obj != undefined); +}; + +function $each(iterable, fn, bind){ + var type = $type(iterable); + ((type == 'arguments' || type == 'collection' || type == 'array') ? Array : Hash).each(iterable, fn, bind); +}; + +function $empty(){}; + +function $extend(original, extended){ + for (var key in (extended || {})) original[key] = extended[key]; + return original; +}; + +function $H(object){ + return new Hash(object); +}; + +function $lambda(value){ + return ($type(value) == 'function') ? value : function(){ + return value; + }; +}; + +function $merge(){ + var args = Array.slice(arguments); + args.unshift({}); + return $mixin.apply(null, args); +}; + +function $mixin(mix){ + for (var i = 1, l = arguments.length; i < l; i++){ + var object = arguments[i]; + if ($type(object) != 'object') continue; + for (var key in object){ + var op = object[key], mp = mix[key]; + mix[key] = (mp && $type(op) == 'object' && $type(mp) == 'object') ? $mixin(mp, op) : $unlink(op); + } + } + return mix; +}; + +function $pick(){ + for (var i = 0, l = arguments.length; i < l; i++){ + if (arguments[i] != undefined) return arguments[i]; + } + return null; +}; + +function $random(min, max){ + return Math.floor(Math.random() * (max - min + 1) + min); +}; + +function $splat(obj){ + var type = $type(obj); + return (type) ? ((type != 'array' && type != 'arguments') ? [obj] : obj) : []; +}; + +var $time = Date.now || function(){ + return +new Date; +}; + +function $try(){ + for (var i = 0, l = arguments.length; i < l; i++){ + try { + return arguments[i](); + } catch(e){} + } + return null; +}; + +function $type(obj){ + if (obj == undefined) return false; + if (obj.$family) return (obj.$family.name == 'number' && !isFinite(obj)) ? false : obj.$family.name; + if (obj.nodeName){ + switch (obj.nodeType){ + case 1: return 'element'; + case 3: return (/\S/).test(obj.nodeValue) ? 'textnode' : 'whitespace'; + } + } else if (typeof obj.length == 'number'){ + if (obj.callee) return 'arguments'; + else if (obj.item) return 'collection'; + } + return typeof obj; +}; + +function $unlink(object){ + var unlinked; + switch ($type(object)){ + case 'object': + unlinked = {}; + for (var p in object) unlinked[p] = $unlink(object[p]); + break; + case 'hash': + unlinked = new Hash(object); + break; + case 'array': + unlinked = []; + for (var i = 0, l = object.length; i < l; i++) unlinked[i] = $unlink(object[i]); + break; + default: return object; + } + return unlinked; +}; + + +/* +--- + +script: Array.js + +description: Contains Array Prototypes like each, contains, and erase. + +license: MIT-style license. + +requires: +- /$util +- /Array.each + +provides: [Array] + +... +*/ + +Array.implement({ + + every: function(fn, bind){ + for (var i = 0, l = this.length; i < l; i++){ + if (!fn.call(bind, this[i], i, this)) return false; + } + return true; + }, + + filter: function(fn, bind){ + var results = []; + for (var i = 0, l = this.length; i < l; i++){ + if (fn.call(bind, this[i], i, this)) results.push(this[i]); + } + return results; + }, + + clean: function(){ + return this.filter($defined); + }, + + indexOf: function(item, from){ + var len = this.length; + for (var i = (from < 0) ? Math.max(0, len + from) : from || 0; i < len; i++){ + if (this[i] === item) return i; + } + return -1; + }, + + map: function(fn, bind){ + var results = []; + for (var i = 0, l = this.length; i < l; i++) results[i] = fn.call(bind, this[i], i, this); + return results; + }, + + some: function(fn, bind){ + for (var i = 0, l = this.length; i < l; i++){ + if (fn.call(bind, this[i], i, this)) return true; + } + return false; + }, + + associate: function(keys){ + var obj = {}, length = Math.min(this.length, keys.length); + for (var i = 0; i < length; i++) obj[keys[i]] = this[i]; + return obj; + }, + + link: function(object){ + var result = {}; + for (var i = 0, l = this.length; i < l; i++){ + for (var key in object){ + if (object[key](this[i])){ + result[key] = this[i]; + delete object[key]; + break; + } + } + } + return result; + }, + + contains: function(item, from){ + return this.indexOf(item, from) != -1; + }, + + extend: function(array){ + for (var i = 0, j = array.length; i < j; i++) this.push(array[i]); + return this; + }, + + getLast: function(){ + return (this.length) ? this[this.length - 1] : null; + }, + + getRandom: function(){ + return (this.length) ? this[$random(0, this.length - 1)] : null; + }, + + include: function(item){ + if (!this.contains(item)) this.push(item); + return this; + }, + + combine: function(array){ + for (var i = 0, l = array.length; i < l; i++) this.include(array[i]); + return this; + }, + + erase: function(item){ + for (var i = this.length; i--; i){ + if (this[i] === item) this.splice(i, 1); + } + return this; + }, + + empty: function(){ + this.length = 0; + return this; + }, + + flatten: function(){ + var array = []; + for (var i = 0, l = this.length; i < l; i++){ + var type = $type(this[i]); + if (!type) continue; + array = array.concat((type == 'array' || type == 'collection' || type == 'arguments') ? Array.flatten(this[i]) : this[i]); + } + return array; + }, + + hexToRgb: function(array){ + if (this.length != 3) return null; + var rgb = this.map(function(value){ + if (value.length == 1) value += value; + return value.toInt(16); + }); + return (array) ? rgb : 'rgb(' + rgb + ')'; + }, + + rgbToHex: function(array){ + if (this.length < 3) return null; + if (this.length == 4 && this[3] == 0 && !array) return 'transparent'; + var hex = []; + for (var i = 0; i < 3; i++){ + var bit = (this[i] - 0).toString(16); + hex.push((bit.length == 1) ? '0' + bit : bit); + } + return (array) ? hex : '#' + hex.join(''); + } + +}); + + +/* +--- + +script: Function.js + +description: Contains Function Prototypes like create, bind, pass, and delay. + +license: MIT-style license. + +requires: +- /Native +- /$util + +provides: [Function] + +... +*/ + +Function.implement({ + + extend: function(properties){ + for (var property in properties) this[property] = properties[property]; + return this; + }, + + create: function(options){ + var self = this; + options = options || {}; + return function(event){ + var args = options.arguments; + args = (args != undefined) ? $splat(args) : Array.slice(arguments, (options.event) ? 1 : 0); + if (options.event) args = [event || window.event].extend(args); + var returns = function(){ + return self.apply(options.bind || null, args); + }; + if (options.delay) return setTimeout(returns, options.delay); + if (options.periodical) return setInterval(returns, options.periodical); + if (options.attempt) return $try(returns); + return returns(); + }; + }, + + run: function(args, bind){ + return this.apply(bind, $splat(args)); + }, + + pass: function(args, bind){ + return this.create({bind: bind, arguments: args}); + }, + + bind: function(bind, args){ + return this.create({bind: bind, arguments: args}); + }, + + bindWithEvent: function(bind, args){ + return this.create({bind: bind, arguments: args, event: true}); + }, + + attempt: function(args, bind){ + return this.create({bind: bind, arguments: args, attempt: true})(); + }, + + delay: function(delay, bind, args){ + return this.create({bind: bind, arguments: args, delay: delay})(); + }, + + periodical: function(periodical, bind, args){ + return this.create({bind: bind, arguments: args, periodical: periodical})(); + } + +}); + + +/* +--- + +script: Number.js + +description: Contains Number Prototypes like limit, round, times, and ceil. + +license: MIT-style license. + +requires: +- /Native +- /$util + +provides: [Number] + +... +*/ + +Number.implement({ + + limit: function(min, max){ + return Math.min(max, Math.max(min, this)); + }, + + round: function(precision){ + precision = Math.pow(10, precision || 0); + return Math.round(this * precision) / precision; + }, + + times: function(fn, bind){ + for (var i = 0; i < this; i++) fn.call(bind, i, this); + }, + + toFloat: function(){ + return parseFloat(this); + }, + + toInt: function(base){ + return parseInt(this, base || 10); + } + +}); + +Number.alias('times', 'each'); + +(function(math){ + var methods = {}; + math.each(function(name){ + if (!Number[name]) methods[name] = function(){ + return Math[name].apply(null, [this].concat($A(arguments))); + }; + }); + Number.implement(methods); +})(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']); + + +/* +--- + +script: String.js + +description: Contains String Prototypes like camelCase, capitalize, test, and toInt. + +license: MIT-style license. + +requires: +- /Native + +provides: [String] + +... +*/ + +String.implement({ + + test: function(regex, params){ + return ((typeof regex == 'string') ? new RegExp(regex, params) : regex).test(this); + }, + + contains: function(string, separator){ + return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : this.indexOf(string) > -1; + }, + + trim: function(){ + return this.replace(/^\s+|\s+$/g, ''); + }, + + clean: function(){ + return this.replace(/\s+/g, ' ').trim(); + }, + + camelCase: function(){ + return this.replace(/-\D/g, function(match){ + return match.charAt(1).toUpperCase(); + }); + }, + + hyphenate: function(){ + return this.replace(/[A-Z]/g, function(match){ + return ('-' + match.charAt(0).toLowerCase()); + }); + }, + + capitalize: function(){ + return this.replace(/\b[a-z]/g, function(match){ + return match.toUpperCase(); + }); + }, + + escapeRegExp: function(){ + return this.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1'); + }, + + toInt: function(base){ + return parseInt(this, base || 10); + }, + + toFloat: function(){ + return parseFloat(this); + }, + + hexToRgb: function(array){ + var hex = this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/); + return (hex) ? hex.slice(1).hexToRgb(array) : null; + }, + + rgbToHex: function(array){ + var rgb = this.match(/\d{1,3}/g); + return (rgb) ? rgb.rgbToHex(array) : null; + }, + + stripScripts: function(option){ + var scripts = ''; + var text = this.replace(/]*>([\s\S]*?)<\/script>/gi, function(){ + scripts += arguments[1] + '\n'; + return ''; + }); + if (option === true) $exec(scripts); + else if ($type(option) == 'function') option(scripts, text); + return text; + }, + + substitute: function(object, regexp){ + return this.replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){ + if (match.charAt(0) == '\\') return match.slice(1); + return (object[name] != undefined) ? object[name] : ''; + }); + } + +}); + + +/* +--- + +script: Hash.js + +description: Contains Hash Prototypes. Provides a means for overcoming the JavaScript practical impossibility of extending native Objects. + +license: MIT-style license. + +requires: +- /Hash.base + +provides: [Hash] + +... +*/ + +Hash.implement({ + + has: Object.prototype.hasOwnProperty, + + keyOf: function(value){ + for (var key in this){ + if (this.hasOwnProperty(key) && this[key] === value) return key; + } + return null; + }, + + hasValue: function(value){ + return (Hash.keyOf(this, value) !== null); + }, + + extend: function(properties){ + Hash.each(properties || {}, function(value, key){ + Hash.set(this, key, value); + }, this); + return this; + }, + + combine: function(properties){ + Hash.each(properties || {}, function(value, key){ + Hash.include(this, key, value); + }, this); + return this; + }, + + erase: function(key){ + if (this.hasOwnProperty(key)) delete this[key]; + return this; + }, + + get: function(key){ + return (this.hasOwnProperty(key)) ? this[key] : null; + }, + + set: function(key, value){ + if (!this[key] || this.hasOwnProperty(key)) this[key] = value; + return this; + }, + + empty: function(){ + Hash.each(this, function(value, key){ + delete this[key]; + }, this); + return this; + }, + + include: function(key, value){ + if (this[key] == undefined) this[key] = value; + return this; + }, + + map: function(fn, bind){ + var results = new Hash; + Hash.each(this, function(value, key){ + results.set(key, fn.call(bind, value, key, this)); + }, this); + return results; + }, + + filter: function(fn, bind){ + var results = new Hash; + Hash.each(this, function(value, key){ + if (fn.call(bind, value, key, this)) results.set(key, value); + }, this); + return results; + }, + + every: function(fn, bind){ + for (var key in this){ + if (this.hasOwnProperty(key) && !fn.call(bind, this[key], key)) return false; + } + return true; + }, + + some: function(fn, bind){ + for (var key in this){ + if (this.hasOwnProperty(key) && fn.call(bind, this[key], key)) return true; + } + return false; + }, + + getKeys: function(){ + var keys = []; + Hash.each(this, function(value, key){ + keys.push(key); + }); + return keys; + }, + + getValues: function(){ + var values = []; + Hash.each(this, function(value){ + values.push(value); + }); + return values; + }, + + toQueryString: function(base){ + var queryString = []; + Hash.each(this, function(value, key){ + if (base) key = base + '[' + key + ']'; + var result; + switch ($type(value)){ + case 'object': result = Hash.toQueryString(value, key); break; + case 'array': + var qs = {}; + value.each(function(val, i){ + qs[i] = val; + }); + result = Hash.toQueryString(qs, key); + break; + default: result = key + '=' + encodeURIComponent(value); + } + if (value != undefined) queryString.push(result); + }); + + return queryString.join('&'); + } + +}); + +Hash.alias({keyOf: 'indexOf', hasValue: 'contains'}); + + +/* +--- + +script: Class.js + +description: Contains the Class Function for easily creating, extending, and implementing reusable Classes. + +license: MIT-style license. + +requires: +- /$util +- /Native +- /Array +- /String +- /Function +- /Number +- /Hash + +provides: [Class] + +... +*/ + +function Class(params){ + + if (params instanceof Function) params = {initialize: params}; + + var newClass = function(){ + Object.reset(this); + if (newClass._prototyping) return this; + this._current = $empty; + var value = (this.initialize) ? this.initialize.apply(this, arguments) : this; + delete this._current; delete this.caller; + return value; + }.extend(this); + + newClass.implement(params); + + newClass.constructor = Class; + newClass.prototype.constructor = newClass; + + return newClass; + +}; + +Function.prototype.protect = function(){ + this._protected = true; + return this; +}; + +Object.reset = function(object, key){ + + if (key == null){ + for (var p in object) Object.reset(object, p); + return object; + } + + delete object[key]; + + switch ($type(object[key])){ + case 'object': + var F = function(){}; + F.prototype = object[key]; + var i = new F; + object[key] = Object.reset(i); + break; + case 'array': object[key] = $unlink(object[key]); break; + } + + return object; + +}; + +new Native({name: 'Class', initialize: Class}).extend({ + + instantiate: function(F){ + F._prototyping = true; + var proto = new F; + delete F._prototyping; + return proto; + }, + + wrap: function(self, key, method){ + if (method._origin) method = method._origin; + + return function(){ + if (method._protected && this._current == null) throw new Error('The method "' + key + '" cannot be called.'); + var caller = this.caller, current = this._current; + this.caller = current; this._current = arguments.callee; + var result = method.apply(this, arguments); + this._current = current; this.caller = caller; + return result; + }.extend({_owner: self, _origin: method, _name: key}); + + } + +}); + +Class.implement({ + + implement: function(key, value){ + + if ($type(key) == 'object'){ + for (var p in key) this.implement(p, key[p]); + return this; + } + + var mutator = Class.Mutators[key]; + + if (mutator){ + value = mutator.call(this, value); + if (value == null) return this; + } + + var proto = this.prototype; + + switch ($type(value)){ + + case 'function': + if (value._hidden) return this; + proto[key] = Class.wrap(this, key, value); + break; + + case 'object': + var previous = proto[key]; + if ($type(previous) == 'object') $mixin(previous, value); + else proto[key] = $unlink(value); + break; + + case 'array': + proto[key] = $unlink(value); + break; + + default: proto[key] = value; + + } + + return this; + + } + +}); + +Class.Mutators = { + + Extends: function(parent){ + + this.parent = parent; + this.prototype = Class.instantiate(parent); + + this.implement('parent', function(){ + var name = this.caller._name, previous = this.caller._owner.parent.prototype[name]; + if (!previous) throw new Error('The method "' + name + '" has no parent.'); + return previous.apply(this, arguments); + }.protect()); + + }, + + Implements: function(items){ + $splat(items).each(function(item){ + if (item instanceof Function) item = Class.instantiate(item); + this.implement(item); + }, this); + + } + +}; + + +/* +--- + +script: Class.Extras.js + +description: Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks. + +license: MIT-style license. + +requires: +- /Class + +provides: [Chain, Events, Options] + +... +*/ + +var Chain = new Class({ + + $chain: [], + + chain: function(){ + this.$chain.extend(Array.flatten(arguments)); + return this; + }, + + callChain: function(){ + return (this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false; + }, + + clearChain: function(){ + this.$chain.empty(); + return this; + } + +}); + +var Events = new Class({ + + $events: {}, + + addEvent: function(type, fn, internal){ + type = Events.removeOn(type); + if (fn != $empty){ + this.$events[type] = this.$events[type] || []; + this.$events[type].include(fn); + if (internal) fn.internal = true; + } + return this; + }, + + addEvents: function(events){ + for (var type in events) this.addEvent(type, events[type]); + return this; + }, + + fireEvent: function(type, args, delay){ + type = Events.removeOn(type); + if (!this.$events || !this.$events[type]) return this; + this.$events[type].each(function(fn){ + fn.create({'bind': this, 'delay': delay, 'arguments': args})(); + }, this); + return this; + }, + + removeEvent: function(type, fn){ + type = Events.removeOn(type); + if (!this.$events[type]) return this; + if (!fn.internal) this.$events[type].erase(fn); + return this; + }, + + removeEvents: function(events){ + var type; + if ($type(events) == 'object'){ + for (type in events) this.removeEvent(type, events[type]); + return this; + } + if (events) events = Events.removeOn(events); + for (type in this.$events){ + if (events && events != type) continue; + var fns = this.$events[type]; + for (var i = fns.length; i--; i) this.removeEvent(type, fns[i]); + } + return this; + } + +}); + +Events.removeOn = function(string){ + return string.replace(/^on([A-Z])/, function(full, first){ + return first.toLowerCase(); + }); +}; + +var Options = new Class({ + + setOptions: function(){ + this.options = $merge.run([this.options].extend(arguments)); + if (!this.addEvent) return this; + for (var option in this.options){ + if ($type(this.options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue; + this.addEvent(option, this.options[option]); + delete this.options[option]; + } + return this; + } + +}); diff --git a/ape-server/scripts/framework/userslist.js b/ape-server/scripts/framework/userslist.js new file mode 100755 index 0000000..8e4356d --- /dev/null +++ b/ape-server/scripts/framework/userslist.js @@ -0,0 +1,15 @@ +Ape.addEvent("mkchan", function(channel) { + channel.userslist = new $H; +}); + +Ape.addEvent("afterJoin", function(user, channel) { + channel.userslist.set(user.getProperty('pubid'), user); +}); + +Ape.addEvent("left", function(user, channel) { + channel.userslist.erase(user.getProperty('pubid')); +}); + +Ape.registerCmd("list", false, function(params, infos) { + +}); diff --git a/ape-server/scripts/main.ape.js b/ape-server/scripts/main.ape.js new file mode 100755 index 0000000..98bb88b --- /dev/null +++ b/ape-server/scripts/main.ape.js @@ -0,0 +1,14 @@ +Ape.addEvent("init", function() { + include("framework/mootools.js"); + include("framework/Http.js"); + include("framework/userslist.js"); + include("utils/utils.js"); + include("commands/proxy.js"); + include("commands/inlinepush.js"); + include("examples/nickname.js"); + include("examples/move.js"); + include("utils/checkTool.js"); //Just needed for the APE JSF diagnostic tool, once APE is installed you can remove it + //include("examples/ircserver.js"); + //include("framework/http_auth.js"); +}); + diff --git a/ape-server/scripts/utils/checkTool.js b/ape-server/scripts/utils/checkTool.js new file mode 100755 index 0000000..fc5e554 --- /dev/null +++ b/ape-server/scripts/utils/checkTool.js @@ -0,0 +1,5 @@ +Ape.registerCmd('setup', false, function(params, infos) { + var domain = Ape.mainConfig('Server', 'domain'); + if (domain == 'auto') domain = params.domain; + return {"name": "setupResponse", "data": {"domain": domain}}; +}); diff --git a/ape-server/scripts/utils/utils.js b/ape-server/scripts/utils/utils.js new file mode 100755 index 0000000..21926b5 --- /dev/null +++ b/ape-server/scripts/utils/utils.js @@ -0,0 +1,9 @@ +var ape_http_request = Ape.HTTPRequest; + +Ape.HTTPRequest = function(url, data) { + switch ($type(data)){ + case 'object': case 'hash': data = Hash.toQueryString(data); + } + + ape_http_request(url, data); +}; diff --git a/ape-server/src/base64.c b/ape-server/src/base64.c new file mode 100644 index 0000000..d8dcfc6 --- /dev/null +++ b/ape-server/src/base64.c @@ -0,0 +1,88 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* base64.c */ +/* taken from ffmpeg */ + +#include +#include "utils.h" + +static uint8_t map2[] = +{ + 0x3e, 0xff, 0xff, 0xff, 0x3f, 0x34, 0x35, 0x36, + 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1a, 0x1b, + 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, + 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33 + }; + +int base64_decode(unsigned char* out, const char *in, int out_length) +{ + int i, v; + unsigned char *dst = out; + + v = 0; + for (i = 0; in[i] && in[i] != '='; i++) { + unsigned int index= in[i]-43; + if (index>=(sizeof(map2)/sizeof(map2[0])) || map2[index] == 0xff) + return -1; + v = (v << 6) + map2[index]; + if (i & 3) { + if (dst - out < out_length) { + *dst++ = v >> (6 - 2 * (i & 3)); + } + } + } + + return (dst - out); +} + +char *base64_encode(unsigned char * src, int len) +{ + static const char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + char *ret, *dst; + unsigned i_bits = 0; + int i_shift = 0; + int bytes_remaining = len; + + ret = dst = xmalloc(len * 4 / 3 + 12); + + if (len) { + while (bytes_remaining) { + i_bits = (i_bits << 8) + *src++; + bytes_remaining--; + i_shift += 8; + + do { + *dst++ = b64[(i_bits << 6 >> i_shift) & 0x3f]; + i_shift -= 6; + } while (i_shift > 6 || (bytes_remaining == 0 && i_shift > 0)); + } + while ((dst - ret) & 3) + *dst++ = '='; + } + *dst = '\0'; + return ret; +} + diff --git a/ape-server/src/base64.h b/ape-server/src/base64.h new file mode 100755 index 0000000..20c9a67 --- /dev/null +++ b/ape-server/src/base64.h @@ -0,0 +1,29 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* base64.h */ + +#ifndef _IBASE64_H +#define _IBASE64_H + +int base64_decode(char * out, const char *in, int out_length); +char *base64_encode(char * src, int len); + +#endif + diff --git a/ape-server/src/channel.c b/ape-server/src/channel.c new file mode 100755 index 0000000..cd4076a --- /dev/null +++ b/ape-server/src/channel.c @@ -0,0 +1,559 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* channel.c */ + +#include "channel.h" +#include "hash.h" +#include "utils.h" +#include "extend.h" +#include "json.h" +#include "raw.h" +#include "plugins.h" + +unsigned int isvalidchan(char *name) +{ + char *pName; + if (strlen(name) > MAX_CHAN_LEN) { + return 0; + } + + for (pName = (*name == '*' ? &name[1] : name ); *pName; pName++) { + *pName = tolower(*pName); + if (*pName == '_' || *pName == '|' || *pName == ':' || *pName == '.') { + continue; + } + if (!isalnum(*pName) || ispunct(*pName)) { + return 0; + } + } + return 1; +} + +CHANNEL *mkchan(char *chan, int flags, acetables *g_ape) +{ + CHANNEL *new_chan = NULL; + + FIRE_EVENT(mkchan, new_chan, chan, flags, g_ape); + + if (!isvalidchan(chan)) { + return NULL; + } + + new_chan = (CHANNEL *) xmalloc(sizeof(*new_chan)); + + memcpy(new_chan->name, chan, strlen(chan)+1); + + new_chan->head = NULL; + new_chan->banned = NULL; + new_chan->properties = NULL; + new_chan->flags = flags | (*new_chan->name == '*' ? CHANNEL_NONINTERACTIVE : 0); + + //memcpy(new_chan->topic, topic, strlen(topic)+1); + + new_chan->pipe = init_pipe(new_chan, CHANNEL_PIPE, g_ape); + + hashtbl_append(g_ape->hLusers, chan, (void *)new_chan); + + /* just to test */ + //proxy_attach(proxy_init("olol", "localhost", 1337, g_ape), new_chan->pipe->pubid, 0, g_ape); + + return new_chan; + +} + +CHANNEL *getchan(const char *chan, acetables *g_ape) +{ + if (strlen(chan) > MAX_CHAN_LEN) { + return NULL; + } + return (CHANNEL *)hashtbl_seek(g_ape->hLusers, chan); +} + +CHANNEL *getchanbypubid(const char *pubid, acetables *g_ape) +{ + transpipe *gpipe; + + gpipe = get_pipe(pubid, g_ape); + + if (gpipe == NULL || gpipe->type != CHANNEL_PIPE) { + return NULL; + } + + return gpipe->pipe; + +} + +void rmchan(CHANNEL *chan, acetables *g_ape) +{ + if (chan->head != NULL) { + struct userslist *head = chan->head; + chan->flags |= CHANNEL_NONINTERACTIVE; /* Force to be non interactive (don't send LEFT raw) */ + chan->flags &= ~CHANNEL_AUTODESTROY; /* Don't destroy it */ + + while(head != NULL) { + struct userslist *thead = head->next; + left(head->userinfo, chan, g_ape); + head = thead; + } + } + + FIRE_EVENT_NULL(rmchan, chan, g_ape); + + rmallban(chan); + + hashtbl_erase(g_ape->hLusers, chan->name); + + clear_properties(&chan->properties); + + destroy_pipe(chan->pipe, g_ape); + + free(chan); + chan = NULL; +} + +void join(USERS *user, CHANNEL *chan, acetables *g_ape) +{ + userslist *list, *ulist; + RAW *newraw; + json_item *jlist; + CHANLIST *chanl; + + FIRE_EVENT_NULL(join, user, chan, g_ape); + + if (isonchannel(user, chan)) { + return; + } + + jlist = json_new_object(); + + list = xmalloc(sizeof(*list)); // TODO is it free ? + list->userinfo = user; + list->level = 1; + list->next = chan->head; + + chan->head = list; + + chanl = xmalloc(sizeof(*chanl)); // TODO is it free ? + chanl->chaninfo = chan; + chanl->next = user->chan_foot; + + user->chan_foot = chanl; + + if (!(chan->flags & CHANNEL_NONINTERACTIVE)) { + + json_item *user_list; + json_item *uinfo; + + user_list = json_new_array(); + + if (list->next != NULL) { + + uinfo = json_new_object(); + + json_set_property_objN(uinfo, "user", 4, get_json_object_user(user)); + json_set_property_objN(uinfo, "pipe", 4, get_json_object_channel(chan)); + + newraw = forge_raw(RAW_JOIN, uinfo); + post_raw_channel_restricted(newraw, chan, user, g_ape); + } + + ulist = chan->head; + while (ulist != NULL) { + + json_item *juser = get_json_object_user(ulist->userinfo); + + if (ulist->userinfo != user) { + //make_link(user, ulist->userinfo); + } + + json_set_property_intN(juser, "level", 5, ulist->level); + + json_set_element_obj(user_list, juser); + + ulist = ulist->next; + } + json_set_property_objN(jlist, "users", 5, user_list); + } + + json_set_property_objN(jlist, "pipe", 4, get_json_object_channel(chan)); + + newraw = forge_raw(RAW_CHANNEL, jlist); + post_raw(newraw, user, g_ape); + + #if 0 + if (user->flags & FLG_AUTOOP) { + setlevel(NULL, user, chan, 3); + } + #endif + +} + +void left_all(USERS *user, acetables *g_ape) +{ + CHANLIST *list, *tList; + + if (user == NULL) { + return; + } + + list = user->chan_foot; + + while (list != NULL) { + tList = list->next; + + left(user, list->chaninfo, g_ape); + + list = tList; + } +} + +void left(USERS *user, CHANNEL *chan, acetables *g_ape) // Vider la liste chainée de l'user +{ + userslist *list, *prev; + + CHANLIST *clist, *ctmp; + RAW *newraw; + json_item *jlist; + + FIRE_EVENT_NULL(left, user, chan, g_ape); + + if (!isonchannel(user, chan)) { + return; + } + + list = chan->head; + prev = NULL; + + clist = user->chan_foot; + ctmp = NULL; + + while (clist != NULL) { + if (clist->chaninfo == chan) { + if (ctmp != NULL) { + ctmp->next = clist->next; + } else { + user->chan_foot = clist->next; + } + free(clist); + break; + } + ctmp = clist; + clist = clist->next; + } + + while (list != NULL && list->userinfo != NULL) { + if (list->userinfo == user) { + jlist = json_new_object(); + + json_set_property_objN(jlist, "user", 4, get_json_object_user(user)); + json_set_property_objN(jlist, "pipe", 4, get_json_object_channel(chan)); + + newraw = forge_raw(RAW_LEFT, jlist); + post_raw(newraw, user, g_ape); + + if (prev != NULL) { + prev->next = list->next; + } else { + chan->head = list->next; + } + free(list); + list = NULL; + if (chan->head != NULL && !(chan->flags & CHANNEL_NONINTERACTIVE)) { + jlist = json_new_object(); + + json_set_property_objN(jlist, "user", 4, get_json_object_user(user)); + json_set_property_objN(jlist, "pipe", 4, get_json_object_channel(chan)); + + newraw = forge_raw(RAW_LEFT, jlist); + post_raw_channel(newraw, chan, g_ape); + } else if (chan->head == NULL && chan->flags & CHANNEL_AUTODESTROY) { + rmchan(chan, g_ape); + } + break; + } + prev = list; + list = list->next; + } + +} + +userslist *getlist(const char *chan, acetables *g_ape) +{ + CHANNEL *lchan; + + if (strlen(chan) > MAX_CHAN_LEN) { + return NULL; + } + if ((lchan = (CHANNEL *)hashtbl_seek(g_ape->hLusers, chan)) == NULL) { + return NULL; + } + return lchan->head; +} + +/* get user info to a specific channel (i.e. level) */ +userslist *getuchan(USERS *user, CHANNEL *chan) +{ + userslist *list; + + if (user == NULL || chan == NULL) { + return 0; + } + list = chan->head; + + while (list != NULL) { + if (list->userinfo == user) { + return list; + } + list = list->next; + } + return NULL; +} + +// TODO : Rewrite this f***g ugly function +unsigned int setlevel(USERS *user_actif, USERS *user_passif, CHANNEL *chan, unsigned int lvl, acetables *g_ape) +{ + RAW *newraw; + userslist *user_passif_chan, *user_actif_chan; + json_item *jlist; + + user_passif_chan = getuchan(user_passif, chan); + + if (user_actif != NULL) { + user_actif_chan = getuchan(user_actif, chan); + + if (user_passif_chan == NULL || user_actif_chan == NULL || ((user_actif_chan->level < lvl || user_actif_chan->level < user_passif_chan->level) && !(user_actif->flags & FLG_AUTOOP)) || lvl < 1 || lvl > 32) { + send_error(user_actif, "SETLEVEL_ERROR", "110", g_ape); + + return 0; + } + + user_passif_chan->level = lvl; + + if (!(chan->flags & CHANNEL_NONINTERACTIVE)) { + jlist = json_new_object(); + + json_set_property_objN(jlist, "ope", 3, get_json_object_user(user_passif)); + json_set_property_objN(jlist, "oper", 4, get_json_object_user(user_actif)); + json_set_property_objN(jlist, "pipe", 4, get_json_object_channel(chan)); + json_set_property_intN(jlist, "level", 5, lvl); + + newraw = forge_raw(RAW_SETLEVEL, jlist); + post_raw_channel(newraw, chan, g_ape); + } + return 1; + } else if (user_passif_chan != NULL && lvl > 0 && lvl < 32) { + user_passif_chan->level = lvl; + + if (!(chan->flags & CHANNEL_NONINTERACTIVE)) { + jlist = json_new_object(); + + json_set_property_objN(jlist, "ope", 3, get_json_object_user(user_passif)); + json_set_property_objN(jlist, "oper", 4, get_json_object_user(NULL)); + json_set_property_objN(jlist, "pipe", 4, get_json_object_channel(chan)); + json_set_property_intN(jlist, "level", 5, lvl); + + newraw = forge_raw(RAW_SETLEVEL, jlist); + post_raw_channel(newraw, chan, g_ape); + + } + return 1; + } + return 0; +} + +/*unsigned int settopic(USERS *user, CHANNEL *chan, const char *topic, acetables *g_ape) +{ + RAW *newraw; + userslist *list; + json_item *jlist; + + list = getuchan(user, chan); + + if (list == NULL || list->level < 3 || strlen(topic)+1 > MAX_TOPIC_LEN) { + + send_error(user, "SETTOPIC_ERROR", "111", g_ape); + + } else { + memcpy(chan->topic, topic, strlen(topic)+1); + + jlist = json_new_object(); + json_set_property_objN(jlist, "user", 4, get_json_object_user(user)); + json_set_property_objN(jlist, "pipe", 4, get_json_object_channel(chan)); + + newraw = forge_raw(RAW_SETTOPIC, jlist); + post_raw_channel(newraw, chan, g_ape); + + return 1; + } + return 0; +}*/ + +void ban(CHANNEL *chan, USERS *banner, const char *ip, char *reason, unsigned int expire, acetables *g_ape) // Ban IP +{ + userslist *uTmp, *tUtmp; + RAW *newraw; + json_item *jlist; + BANNED *blist, *bTmp; + + unsigned int isban = 0; + + long int nextime = (expire * 60)+time(NULL); // NOW ! + + if (chan == NULL) { + return; + } + + uTmp = chan->head; + bTmp = chan->banned; + + while (uTmp != NULL) { + + if (strcmp(ip, uTmp->userinfo->ip) == 0) { // We find somebody with the same IP + jlist = json_new_object(); + + json_set_property_strZ(jlist, "reason", reason); + json_set_property_objN(jlist, "banner", 6, get_json_object_user(banner)); + json_set_property_objN(jlist, "pipe", 4, get_json_object_channel(chan)); + + newraw = forge_raw(RAW_BAN, jlist); + + post_raw(newraw, uTmp->userinfo, g_ape); + + if (isban == 0) { + blist = xmalloc(sizeof(*blist)); + + memset(blist->reason, 0, 256); + strncpy(blist->ip, ip, 16); + strncpy(blist->reason, reason, 255); + blist->expire = nextime; + blist->next = bTmp; + chan->banned = blist; + isban = 1; + } + tUtmp = uTmp->next; + left(uTmp->userinfo, chan, g_ape); // if the user is the last : "chan" is free (rmchan()) + uTmp = tUtmp; + continue; + } + uTmp = uTmp->next; + } + +} + +BANNED *getban(CHANNEL *chan, const char *ip) +{ + BANNED *blist, *bTmp, *bWait; + + blist = chan->banned; + bTmp = NULL; + + while (blist != NULL) { + if (blist->expire < time(NULL)) { + bWait = blist->next; + free(blist); + blist = bWait; + + if (bTmp == NULL) { + chan->banned = blist; + } else { + bTmp->next = blist; + } + continue; + } else if (strcmp(blist->ip, ip) == 0) { + return blist; + } + bTmp = blist; + blist = blist->next; + } + + return NULL; +} + +void rmban(CHANNEL *chan, const char *ip) +{ + BANNED *blist, *bTmp, *bWait; + + blist = chan->banned; + bTmp = NULL; + + while (blist != NULL) { + if (blist->expire < time(NULL) || strcmp(blist->ip, ip) == 0) { + bWait = blist->next; + free(blist); + blist = bWait; + + if (bTmp == NULL) { + chan->banned = blist; + } else { + bTmp->next = blist; + } + continue; + } + + bTmp = blist; + blist = blist->next; + } +} + +void rmallban(CHANNEL *chan) +{ + BANNED *blist, *bTmp; + + blist = chan->banned; + + while (blist != NULL) { + bTmp = blist->next; + free(blist); + blist = bTmp; + } + chan->banned = NULL; +} + +json_item *get_json_object_channel(CHANNEL *chan) +{ + json_item *jstr = json_new_object(); + json_set_property_strN(jstr, "casttype", 8, "multi", 5); + json_set_property_strN(jstr, "pubid", 5, chan->pipe->pubid, 32); + + json_item *jprop = json_new_object(); + json_set_property_strZ(jprop, "name", chan->name); + + extend *eTmp = chan->properties; + + while (eTmp != NULL) { + if (eTmp->visibility == EXTEND_ISPUBLIC) { + if (eTmp->type == EXTEND_JSON) { + json_item *jcopy = json_item_copy(eTmp->val, NULL); + + json_set_property_objZ(jprop, eTmp->key, jcopy); + } else { + json_set_property_strZ(jprop, eTmp->key, eTmp->val); + } + } + + eTmp = eTmp->next; + } + json_set_property_objN(jstr, "properties", 10, jprop); + + //} + + return jstr; +} + diff --git a/ape-server/src/channel.h b/ape-server/src/channel.h new file mode 100755 index 0000000..8dac15e --- /dev/null +++ b/ape-server/src/channel.h @@ -0,0 +1,90 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* channel.h */ + +#ifndef _CHANNEL_H +#define _CHANNEL_H + +#include "main.h" +#include "pipe.h" +#include "users.h" +#include "extend.h" +#include "json.h" + +#define MAX_TOPIC_LEN 128 +#define DEFAULT_TOPIC "Chat%20powered%20by%20AJAX%20Push%20Engine\0" + +#define CHANNEL_NONINTERACTIVE 0x01 +#define CHANNEL_AUTODESTROY 0x02 + +typedef struct CHANNEL +{ + //char topic[MAX_TOPIC_LEN+1]; + + struct _transpipe *pipe; + struct userslist *head; + + struct BANNED *banned; + + extend *properties; + + int flags; + char name[MAX_CHAN_LEN+1]; + +} CHANNEL; + +typedef struct BANNED +{ + char ip[16]; + char reason[257]; + + long int expire; + + struct BANNED *next; +} BANNED; + +CHANNEL *mkchan(char *chan, int flags, acetables *g_ape); +CHANNEL *getchan(const char *chan, acetables *g_ape); + +BANNED *getban(CHANNEL *chan, const char *ip); +CHANNEL *getchanbypubid(const char *pubid, acetables *g_ape); +int mkallchan(acetables *g_ape); + +void rmchan(CHANNEL *chan, acetables *g_ape); + +void join(struct USERS *user, CHANNEL *chan, acetables *g_ape); +void left(struct USERS *user, CHANNEL *chan, acetables *g_ape); +void left_all(struct USERS *user, acetables *g_ape); + +void ban(CHANNEL *chan, struct USERS *banner, const char *ip, char *reason, unsigned int expire, acetables *g_ape); +void rmban(CHANNEL *chan, const char *ip); +void rmallban(CHANNEL *chan); + +struct userslist *getlist(const char *chan, acetables *g_ape); +struct userslist *getuchan(struct USERS *user, CHANNEL *chan); + +unsigned int setlevel(struct USERS *user_actif, struct USERS *user_passif, CHANNEL *chan, unsigned int lvl, acetables *g_ape); +//unsigned int settopic(struct USERS *user, CHANNEL *chan, const char *topic, acetables *g_ape); +unsigned int isvalidchan(char *name); + +json_item *get_json_object_channel(CHANNEL *chan); + +#endif + diff --git a/ape-server/src/cmd.c b/ape-server/src/cmd.c new file mode 100755 index 0000000..6992364 --- /dev/null +++ b/ape-server/src/cmd.c @@ -0,0 +1,648 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* cmd.c */ + +#include "cmd.h" +#include "json.h" +#include "config.h" +#include "utils.h" +#include "proxy.h" +#include "raw.h" +#include "transports.h" + +void do_register(acetables *g_ape) +{ + register_cmd("CONNECT", cmd_connect, NEED_NOTHING, g_ape); + register_cmd("SCRIPT", cmd_script, NEED_NOTHING, g_ape); + + register_cmd("CHECK", cmd_check, NEED_SESSID, g_ape); + register_cmd("SEND", cmd_send, NEED_SESSID, g_ape); + + register_cmd("QUIT", cmd_quit, NEED_SESSID, g_ape); + register_cmd("JOIN", cmd_join, NEED_SESSID, g_ape); + register_cmd("LEFT", cmd_left, NEED_SESSID, g_ape); + register_cmd("SESSION", cmd_session, NEED_SESSID, g_ape); +} + +void register_cmd(const char *cmd, unsigned int (*func)(callbackp *), unsigned int need, acetables *g_ape) +{ + callback *new_cmd, *old_cmd; + + new_cmd = (callback *) xmalloc(sizeof(*new_cmd)); + + new_cmd->func = func; + new_cmd->need = need; + + /* Unregister old cmd if exists */ + if ((old_cmd = (callback *)hashtbl_seek(g_ape->hCallback, cmd)) != NULL) { + hashtbl_erase(g_ape->hCallback, cmd); + } + + hashtbl_append(g_ape->hCallback, cmd, (void *)new_cmd); + +} + +void register_bad_cmd(unsigned int (*func)(callbackp *), void *data, acetables *g_ape) +{ + callback_hook *new_cmd; + + new_cmd = xmalloc(sizeof(*new_cmd)); + + new_cmd->func = func; + new_cmd->next = g_ape->bad_cmd_callbacks; + new_cmd->data = data; + + g_ape->bad_cmd_callbacks = new_cmd; + +} + +int register_hook_cmd(const char *cmd, unsigned int (*func)(callbackp *), void *data, acetables *g_ape) +{ + callback_hook *hook; + + if (hashtbl_seek(g_ape->hCallback, cmd) == NULL) { + return 0; + } + + hook = xmalloc(sizeof(*hook)); + hook->cmd = xstrdup(cmd); + hook->func = func; + hook->data = data; + hook->next = NULL; + + if (g_ape->cmd_hook.head == NULL) { + g_ape->cmd_hook.head = hook; + g_ape->cmd_hook.foot = hook; + } else { + g_ape->cmd_hook.foot->next = hook; + g_ape->cmd_hook.foot = hook; + } + + return 1; +} + +int call_cmd_hook(const char *cmd, callbackp *cp, acetables *g_ape) +{ + callback_hook *hook; + + for (hook = g_ape->cmd_hook.head; hook != NULL; hook = hook->next) { + unsigned int ret; + + cp->data = hook->data; + if (strcasecmp(hook->cmd, cmd) == 0 && (ret = hook->func(cp)) != RETURN_CONTINUE) { + return ret; + } + } + cp->data = NULL; + return RETURN_CONTINUE; +} + +void unregister_cmd(const char *cmd, acetables *g_ape) +{ + hashtbl_erase(g_ape->hCallback, cmd); +} + +static unsigned int handle_bad_cmd(callbackp *callbacki) +{ + callback_hook *hook_bad; + int flagret; + + for (hook_bad = callbacki->g_ape->bad_cmd_callbacks; hook_bad != NULL; hook_bad = hook_bad->next) { + callbacki->data = hook_bad->data; + if ((flagret = hook_bad->func(callbacki)) != RETURN_BAD_CMD) { + return flagret; + } else { + ; + } + } + callbacki->data = NULL; + + return RETURN_BAD_CMD; +} + +int process_cmd(json_item *ijson, struct _cmd_process *pc, subuser **iuser, acetables *g_ape) +{ + callback *cmdback, tmpback = {handle_bad_cmd, NEED_NOTHING}; + json_item *rjson = json_lookup(ijson->jchild.child, "cmd"), *jchl; + subuser *sub = pc->sub; + unsigned int flag; + unsigned short int attach = 1; + + if (rjson != NULL && rjson->jval.vu.str.value != NULL) { + callbackp cp; + cp.client = NULL; + cp.cmd = rjson->jval.vu.str.value; + cp.data = NULL; + cp.hlines = NULL; + + json_item *jsid; + + if ((cmdback = (callback *)hashtbl_seek(g_ape->hCallback, rjson->jval.vu.str.value)) == NULL) { + cmdback = &tmpback; + } + + if ((pc->guser == NULL && (jsid = json_lookup(ijson->jchild.child, "sessid")) != NULL && jsid->jval.vu.str.value != NULL)) { + pc->guser = seek_user_id(jsid->jval.vu.str.value, g_ape); + } + + if (cmdback->need != NEED_NOTHING || pc->guser != NULL) { // We process the connection like a "NEED_SESSID" if the user provide its key + + if (pc->guser == NULL) { + + RAW *newraw; + json_item *jlist = json_new_object(); + + json_set_property_strZ(jlist, "code", "004"); + json_set_property_strZ(jlist, "value", "BAD_SESSID"); + + newraw = forge_raw(RAW_ERR, jlist); + + send_raw_inline(pc->client, pc->transport, newraw, g_ape); + + return (CONNECT_SHUTDOWN); + } else if (sub == NULL) { + + sub = getsubuser(pc->guser, pc->host); + if (sub != NULL && sub->client->fd != pc->client->fd && sub->state == ALIVE) { + /* The user open a new connection while he already has one openned */ + struct _transport_open_same_host_p retval = transport_open_same_host(sub, pc->client, pc->guser->transport); + + if (retval.client_close != NULL) { + RAW *newraw; + json_item *jlist = json_new_object(); + + json_set_property_strZ(jlist, "value", "null"); + + newraw = forge_raw("CLOSE", jlist); + + send_raw_inline((retval.client_close->fd == pc->client->fd ? pc->client : sub->client), pc->transport, newraw, g_ape); + + shutdown(retval.client_close->fd, 2); + } + sub->client = cp.client = retval.client_listener; + sub->state = retval.substate; + attach = retval.attach; + + } else if (sub == NULL) { + sub = addsubuser(pc->client, pc->host, pc->guser, g_ape); + if (sub != NULL) { + subuser_restor(sub, g_ape); + } + } else if (sub != NULL) { + sub->client = pc->client; + } + pc->guser->idle = (long int)time(NULL); // update user idle + + sub->idle = pc->guser->idle; // Update subuser idle + + } + + } + + if (pc->guser != NULL && sub != NULL && (jchl = json_lookup(ijson->jchild.child, "chl")) != NULL && jchl->jval.vu.integer_value > sub->current_chl) { + sub->current_chl = jchl->jval.vu.integer_value; + } else if (pc->guser != NULL && sub != NULL) { + /* if a bad challenge is detected, we are stoping walking on cmds */ + send_error(pc->guser, "BAD_CHL", "250", g_ape); + + sub->state = ALIVE; + + return (CONNECT_KEEPALIVE); + } + + cp.param = json_lookup(ijson->jchild.child, "params"); + cp.client = (cp.client != NULL ? cp.client : pc->client); + cp.call_user = pc->guser; + cp.call_subuser = sub; + cp.g_ape = g_ape; + cp.host = pc->host; + cp.ip = pc->ip; + cp.chl = (sub != NULL ? sub->current_chl : 0); + cp.transport = pc->transport; + cp.hlines = pc->hlines; + + /* Little hack to access user object on connect hook callback (preallocate an user) */ + if (strncasecmp(cp.cmd, "CONNECT", 7) == 0 && cp.cmd[7] == '\0') { + pc->guser = cp.call_user = adduser(cp.client, cp.host, cp.ip, NULL, g_ape); + pc->guser->transport = pc->transport; + sub = cp.call_subuser = cp.call_user->subuser; + } + + if ((flag = call_cmd_hook(cp.cmd, &cp, g_ape)) == RETURN_CONTINUE) { + flag = cmdback->func(&cp); + } + + if (flag & RETURN_NULL) { + pc->guser = NULL; + } else if (flag & RETURN_BAD_PARAMS) { + RAW *newraw; + json_item *jlist = json_new_object(); + + if (cp.chl) { + json_set_property_intN(jlist, "chl", 3, cp.chl); + } + json_set_property_strZ(jlist, "code", "001"); + json_set_property_strZ(jlist, "value", "BAD_PARAMS"); + + newraw = forge_raw(RAW_ERR, jlist); + + if (cp.call_user != NULL) { + //cp.call_user->istmp = 0; + if (sub == NULL) { + sub = getsubuser(pc->guser, pc->host); + } + post_raw_sub(newraw, sub, g_ape); + } else { + send_raw_inline(pc->client, pc->transport, newraw, g_ape); + } + + //guser = NULL; + } else if (flag & RETURN_BAD_CMD) { + RAW *newraw; + json_item *jlist = json_new_object(); + + if (cp.chl) { + json_set_property_intN(jlist, "chl", 3, cp.chl); + } + json_set_property_strZ(jlist, "code", "003"); + json_set_property_strZ(jlist, "value", "BAD_CMD"); + + newraw = forge_raw(RAW_ERR, jlist); + + if (cp.call_user != NULL) { + if (sub == NULL) { + sub = getsubuser(pc->guser, pc->host); + } + post_raw_sub(newraw, sub, g_ape); + } else { + send_raw_inline(pc->client, pc->transport, newraw, g_ape); + } + } + + if (pc->guser != NULL) { + if (sub == NULL) { + sub = getsubuser(pc->guser, pc->host); + } + if (iuser != NULL) { + *iuser = (attach ? sub : NULL); + } + /* If tmpfd is set, we do not have any reasons to change its state */ + sub->state = ALIVE; + + if (flag & RETURN_HANG || flag & RETURN_BAD_PARAMS) { + return (CONNECT_KEEPALIVE); + } + + } else if (flag & RETURN_HANG) { + /* Doesn't need sessid */ + return (CONNECT_KEEPALIVE); + } else { + return (CONNECT_SHUTDOWN); + } + } else { + + RAW *newraw; + json_item *jlist = json_new_object(); + + json_set_property_strZ(jlist, "code", "003"); + json_set_property_strZ(jlist, "value", "NO_CMD"); + + newraw = forge_raw(RAW_ERR, jlist); + + send_raw_inline(pc->client, pc->transport, newraw, g_ape); + //printf("Cant find %s\n", rjson->jval.vu.str.value); + return (CONNECT_SHUTDOWN); + } + + return -1; +} + + +unsigned int checkcmd(clientget *cget, transport_t transport, subuser **iuser, acetables *g_ape) +{ + struct _cmd_process pc = {cget->hlines, NULL, NULL, cget->client, cget->host, cget->ip_get, transport}; + + json_item *ijson, *ojson; + + unsigned int ret; + + ijson = ojson = init_json_parser(cget->get); + if (ijson == NULL || ijson->jchild.child == NULL) { + RAW *newraw; + json_item *jlist = json_new_object(); + + json_set_property_strZ(jlist, "code", "005"); + json_set_property_strZ(jlist, "value", "BAD_JSON"); + + newraw = forge_raw(RAW_ERR, jlist); + + send_raw_inline(cget->client, transport, newraw, g_ape); + } else { + for (ijson = ijson->jchild.child; ijson != NULL; ijson = ijson->next) { + + if (pc.guser != NULL && pc.guser->istmp) { /* if "CONNECT" was delayed, push other cmd to the queue and stop execution */ + pc.guser->cmdqueue = json_item_copy(ijson, NULL); + break; + } + if ((ret = process_cmd(ijson, &pc, iuser, g_ape)) != -1) { + free_json_item(ojson); + return ret; + } + if (*iuser != NULL) { + pc.sub = *iuser; + } + } + free_json_item(ojson); + + return (CONNECT_KEEPALIVE); + } + + return (CONNECT_SHUTDOWN); +} + + +unsigned int cmd_connect(callbackp *callbacki) +{ + USERS *nuser; + RAW *newraw; + json_item *jstr = NULL; + + nuser = adduser(NULL, NULL, NULL, callbacki->call_user, callbacki->g_ape); + + callbacki->call_user = nuser; + + subuser_restor(getsubuser(callbacki->call_user, callbacki->host), callbacki->g_ape); + + jstr = json_new_object(); + json_set_property_strN(jstr, "sessid", 6, nuser->sessid, 32); + + newraw = forge_raw(RAW_LOGIN, jstr); + newraw->priority = RAW_PRI_HI; + + post_raw(newraw, nuser, callbacki->g_ape); + + return (RETURN_NOTHING); +} + +unsigned int cmd_script(callbackp *callbacki) +{ + char *domain = CONFIG_VAL(Server, domain, callbacki->g_ape->srv); + char *script = NULL; + int alloc = 0; + APE_PARAMS_INIT(); + + if (domain == NULL) { + send_error(callbacki->call_user, "NO_DOMAIN", "201", callbacki->g_ape); + } else { + char *autodom; + if (strcmp(domain, "auto") == 0 && (autodom = JSTR(domain)) != NULL) { + domain = autodom; + #if 0 + /* http://geekandpoke.typepad.com/.a/6a00d8341d3df553ef0120a6d65b8a970b-pi */ + + struct _http_header_line *hlines; + + for (hlines = callbacki->client->http.hlines; hlines != NULL; hlines = hlines->next) { + if (strcasecmp(hlines->key.val, "host") == 0) { + char *loc; + char *newdom = xmalloc(sizeof(char) * (hlines->value.len + 1)); + memset(newdom, '\0', hlines->value.len + 1); + if ((loc = strrchr(hlines->value.val, '.')) != NULL) { + int i, pos = 0; + + for (i = 0; i < hlines->value.len; i++, pos++) { + newdom[pos] = hlines->value.val[i]; + if (newdom[pos] == ':') { + newdom[pos] = '\0'; + break; + } + if (hlines->value.val[i] == '.' && &hlines->value.val[i] < loc) { + pos = -1; + } + } + newdom[pos] = '\0'; + domain = newdom; + alloc = 1; + } + } + } + #endif + } + sendf(callbacki->client->fd, callbacki->g_ape, "%s\n\n\t\n", HEADER_DEFAULT, domain); + + if (alloc) { + free(domain); + } + + JFOREACH(scripts, script) { + sendf(callbacki->client->fd, callbacki->g_ape, "\t\n", script); + } + sendbin(callbacki->client->fd, "\n\n\n", 30, 0, callbacki->g_ape); + } + + return (RETURN_NOTHING); +} + +unsigned int cmd_join(callbackp *callbacki) +{ + CHANNEL *jchan; + RAW *newraw; + json_item *jlist = NULL; + BANNED *blist; + char *chan_name = NULL; + + APE_PARAMS_INIT(); + + JFOREACH(channels, chan_name) { + + if ((jchan = getchan(chan_name, callbacki->g_ape)) == NULL) { + jchan = mkchan(chan_name, CHANNEL_AUTODESTROY, callbacki->g_ape); + + if (jchan == NULL) { + send_error(callbacki->call_user, "CANT_JOIN_CHANNEL", "202", callbacki->g_ape); + + } else { + join(callbacki->call_user, jchan, callbacki->g_ape); + } + + } else if (isonchannel(callbacki->call_user, jchan)) { + + send_error(callbacki->call_user, "ALREADY_ON_CHANNEL", "100", callbacki->g_ape); + + } else { + blist = getban(jchan, callbacki->call_user->ip); + if (blist != NULL) { + + jlist = json_new_object(); + + json_set_property_strZ(jlist, "reason", blist->reason); + json_set_property_strZ(jlist, "error", "YOU_ARE_BANNED"); + + /* + TODO: Add Until + */ + newraw = forge_raw(RAW_ERR, jlist); + + post_raw(newraw, callbacki->call_user, callbacki->g_ape); + } else { + join(callbacki->call_user, jchan, callbacki->g_ape); + } + } + } JFOREACH_ELSE { + return (RETURN_BAD_PARAMS); + } + return (RETURN_NOTHING); +} + +unsigned int cmd_check(callbackp *callbacki) +{ + return (RETURN_NOTHING); +} + +unsigned int cmd_send(callbackp *callbacki) +{ + json_item *jlist = NULL; + char *msg, *pipe; + + APE_PARAMS_INIT(); + + if ((msg = JSTR(msg)) != NULL && (pipe = JSTR(pipe)) != NULL) { + jlist = json_new_object(); + + json_set_property_strZ(jlist, "msg", msg); + + post_to_pipe(jlist, RAW_DATA, pipe, callbacki->call_subuser, callbacki->g_ape); + + return (RETURN_NOTHING); + } + + return (RETURN_BAD_PARAMS); + +} +unsigned int cmd_quit(callbackp *callbacki) +{ + RAW *newraw; + json_item *jlist = json_new_object(); + + json_set_property_strZ(jlist, "value", "null"); + + newraw = forge_raw("QUIT", jlist); + + send_raw_inline(callbacki->client, callbacki->transport, newraw, callbacki->g_ape); + + deluser(callbacki->call_user, callbacki->g_ape); // After that callbacki->call_user is free'd + + return (RETURN_NULL); +} + + +unsigned int cmd_left(callbackp *callbacki) +{ + CHANNEL *chan; + char *chan_name; + + APE_PARAMS_INIT(); + + if ((chan_name = JSTR(channel)) != NULL) { + + if ((chan = getchan(chan_name, callbacki->g_ape)) == NULL) { + send_error(callbacki->call_user, "UNKNOWN_CHANNEL", "103", callbacki->g_ape); + + } else if (!isonchannel(callbacki->call_user, chan)) { + send_error(callbacki->call_user, "NOT_IN_CHANNEL", "104", callbacki->g_ape); + + } else { + + left(callbacki->call_user, chan, callbacki->g_ape); + } + + return (RETURN_NOTHING); + } + + return (RETURN_BAD_PARAMS); +} + +unsigned int cmd_session(callbackp *callbacki) +{ + char *key, *val, *action; + APE_PARAMS_INIT(); + + if ((action = JSTR(action)) != NULL) { + if (strcasecmp(action, "set") == 0) { + JFOREACH_K(values, key, val) { + if (set_session(callbacki->call_user, key, val, 0, callbacki->g_ape) == NULL) { + send_error(callbacki->call_user, "SESSION_ERROR", "203", callbacki->g_ape); + } + } JFOREACH_ELSE { + return (RETURN_BAD_PARAMS); + } + } else if (strcasecmp(action, "get") == 0) { + json_item *jlist = NULL, *jobj = json_new_object(); + RAW *newraw; + + //set_json("sessions", NULL, &jlist); + + JFOREACH(values, val) { + session *sTmp = get_session(callbacki->call_user, val); + json_set_property_strZ(jobj, val, (sTmp != NULL ? sTmp->val : "null")); + } JFOREACH_ELSE { + free_json_item(jlist); + return (RETURN_BAD_PARAMS); + } + jlist = json_new_object(); + json_set_property_objN(jlist, "sessions", 8, jobj); + json_set_property_intN(jlist, "chl", 3, callbacki->chl); + + newraw = forge_raw("SESSIONS", jlist); + newraw->priority = RAW_PRI_LO; + + post_raw_sub(newraw, callbacki->call_subuser, callbacki->g_ape); + + } else { + return (RETURN_BAD_PARAMS); + } + } else { + return (RETURN_BAD_PARAMS); + } + + return (RETURN_NOTHING); +} + +#if 0 +/* This is usefull to ask all subuser to update their sessions */ +unsigned int cmd_pong(callbackp *callbacki) +{ + if (strcmp(callbacki->param[2], callbacki->call_user->lastping) == 0) { + RAW *newraw; + + callbacki->call_user->lastping[0] = '\0'; + + json *jlist = NULL; + + set_json("value", callbacki->param[2], &jlist); + + newraw = forge_raw("UPDATE", jlist); + + post_raw_sub(newraw, getsubuser(callbacki->call_user, callbacki->host), callbacki->g_ape); + } + return (RETURN_NOTHING); +} + +#endif diff --git a/ape-server/src/cmd.h b/ape-server/src/cmd.h new file mode 100755 index 0000000..4e8ede1 --- /dev/null +++ b/ape-server/src/cmd.h @@ -0,0 +1,146 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* cmd.h */ + +#ifndef _CMD_H +#define _CMD_H + +#include "users.h" +#include "handle_http.h" +#include "sock.h" +#include "main.h" +#include "transports.h" + +/* + Non identifiable user's error. + E.g. Bad sessid, bad param + Note : time is not defined in this case +*/ +#define ERR_BAD_PARAM "[\n{\"raw\":\"ERR\",\"time\":null,\"data\":{\"code\":\"001\",\"value\":\"BAD_PARAM\"}}\n]\n" +#define ERR_BAD_CMD "[\n{\"raw\":\"ERR\",\"time\":null,\"data\":{\"code\":\"002\",\"value\":\"BAD_CMD\"}}\n]\n" +#define ERR_BAD_SESSID "[\n{\"raw\":\"ERR\",\"time\":null,\"data\":{\"code\":\"004\",\"value\":\"BAD_SESSID\"}}\n]\n" +#define ERR_BAD_JSON "[\n{\"raw\":\"ERR\",\"time\":null,\"data\":{\"code\":\"005\",\"value\":\"BAD_JSON\"}}\n]\n" +#define ERR_BAD_CHL "[\n{\"raw\":\"ERR\",\"time\":null,\"data\":{\"code\":\"005\",\"value\":\"BAD_CHL\"}}\n]\n" +#define ERR_CONNECT "[\n{\"raw\":\"ERR\",\"time\":null,\"data\":{\"code\":\"200\",\"value\":\"UNKNOWN_CONNECTION_ERROR\"}}\n]\n" + + + +#define SUCCESS_LOGIN "200" + + +#define MOTD_FILE "MOTD" + +unsigned int checkcmd(clientget *cget, transport_t transport, subuser **iuser, acetables *g_ape); + + +void do_register(acetables *g_ape); + +/* Flag returned by cmd callback */ + + +#define RETURN_LOGIN 0x01 +#define RETRUN_SESSID 0x02 +#define RETURN_NULL 0x04 +#define RETURN_UPDATE_IP 0x08 +#define RETURN_NOTHING 0x10 +#define RETURN_BAD_PARAMS 0x20 +#define RETURN_CONTINUE 0x40 +#define RETURN_BAD_CMD 0x80 +#define RETURN_HANG 0x100 + +typedef struct _callbackp callbackp; + +struct _callbackp +{ + ape_socket *client; + json_item *param; + struct _http_header_line *hlines; + + struct USERS *call_user; + + const char *ip; + const char *host; + const char *cmd; + void *data; + + subuser *call_subuser; + acetables *g_ape; + + transport_t transport; + int chl; +}; + + +typedef struct callback +{ + unsigned int (*func)(struct _callbackp *); /* Callback func */ + unsigned int need; /* Need SESSID ? */ +} callback; + +typedef struct _callback_hook +{ + const char *cmd; + void *data; + unsigned int (*func)(struct _callbackp *); + struct _callback_hook *next; +} callback_hook; + +enum { + NEED_NICK = 0, + NEED_SESSID, + NEED_NOTHING +}; + +struct _cmd_process { + struct _http_header_line *hlines; + USERS *guser; + subuser *sub; + ape_socket *client; + const char *host; + const char *ip; + transport_t transport; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////// +unsigned int cmd_connect(struct _callbackp *); +unsigned int cmd_check(struct _callbackp *); +unsigned int cmd_send(struct _callbackp *); +unsigned int cmd_quit(struct _callbackp *); +unsigned int cmd_setlevel(struct _callbackp *); +unsigned int cmd_settopic(struct _callbackp *); +unsigned int cmd_join(struct _callbackp *); +unsigned int cmd_left(struct _callbackp *); +unsigned int cmd_kick(struct _callbackp *); +unsigned int cmd_ban(struct _callbackp *); +unsigned int cmd_session(struct _callbackp *); +unsigned int cmd_pconnect(struct _callbackp *); +unsigned int cmd_script(struct _callbackp *); +unsigned int cmd_pong(struct _callbackp *); +unsigned int cmd_proxy_connect(struct _callbackp *); +unsigned int cmd_proxy_write(struct _callbackp *); +/////////////////////////////////////////////////////////////////////////////////////////////// +int process_cmd(json_item *ijson, struct _cmd_process *pc, subuser **iuser, acetables *g_ape); +void register_cmd(const char *cmd, unsigned int (*func)(callbackp *), unsigned int need, acetables *g_ape); +void unregister_cmd(const char *cmd, acetables *g_ape); +void register_bad_cmd(unsigned int (*func)(callbackp *), void *data, acetables *g_ape); +int register_hook_cmd(const char *cmd, unsigned int (*func)(callbackp *), void *data, acetables *g_ape); +int call_cmd_hook(const char *cmd, callbackp *cp, acetables *g_ape); +#endif + diff --git a/ape-server/src/config.c b/ape-server/src/config.c new file mode 100755 index 0000000..df169a9 --- /dev/null +++ b/ape-server/src/config.c @@ -0,0 +1,172 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* config.c */ + +#include +#include +#include + +#include "config.h" +#include "utils.h" + +apeconfig *ape_config_get_section(apeconfig *conf, const char *section) +{ + apeconfig *pConf = conf; + + while (pConf != NULL) { + + if (strcasecmp(pConf->section, section) == 0) { + + return pConf; + } + pConf = pConf->next; + } + + return NULL; +} + +char *ape_config_get_key(apeconfig *conf, const char *key) +{ + struct _apeconfig_def *def; + + if (conf == NULL) { + return NULL; + } + + def = conf->def; + + while (def != NULL) { + if (strcasecmp(def->key, key) == 0) { + return def->val; + } + def = def->next; + } + + return NULL; + +} + +apeconfig *ape_config_load(const char *filename) +{ + FILE *fp = fopen(filename, "r"); + apeconfig *conf = NULL; + char lines[2048], *tkn[3]; + + int open_brace = 0, curline = 0; + + if (fp == NULL) { + printf("NO (unable to open %s)\n", filename); + return NULL; + } + + + while(fgets(lines, 2048, fp)) { + int len; + int i; + curline++; + + if (*lines == '#' || *lines == '\r' || *lines == '\n' || *lines == '\0') { + continue; + } + len = strlen(lines); + + switch(open_brace) { + case 0: + for (i = 0; i < len; i++) { + if (lines[i] == '{') { + apeconfig *config; + + open_brace = 1; + lines[i] = '\0'; + + config = xmalloc(sizeof(*conf)); + + strncpy(config->section, trim(lines), 32); + + config->section[32] = '\0'; + config->def = NULL; + config->next = conf; + conf = config; + + break; + } + } + if (!open_brace) { + printf("[Error] Parse error in configuration file (out of brace) at line %i\n", curline); + return NULL; + } + break; + case 1: + for (i = 0; i < len; i++) { + if (lines[i] == '}') { + open_brace = 0; + break; + } + } + if (open_brace) { + int nTok = 0; + struct _apeconfig_def *def; + + nTok = explode('=', lines, tkn, 3); + + if (nTok > 1) { + printf("[Error] Parse error in configuration file (illegal equality \"=\") at line %i\n", curline); + return NULL; + } + def = xmalloc(sizeof(*def)); + if (nTok == 1) { + strncpy(def->key, trim(tkn[0]), 33); + def->key[32] = '\0'; + + def->val = xstrdup(trim(tkn[1])); + } else { + def->key[0] = '\0'; + def->val = xstrdup(trim(lines)); + } + + def->next = conf->def; + conf->def = def; + + } + break; + } + } + + return conf; +} + +void ape_config_free(apeconfig *conf) +{ + apeconfig *tmp_s; + apeconfig_def *tmp_d; + + while (conf != NULL) { + while (conf->def != NULL) { + tmp_d = conf->def->next; + free(conf->def->val); + free(conf->def); + conf->def = tmp_d; + } + tmp_s = conf->next; + free(conf); + conf = tmp_s; + } +} + diff --git a/ape-server/src/config.h b/ape-server/src/config.h new file mode 100755 index 0000000..46244e9 --- /dev/null +++ b/ape-server/src/config.h @@ -0,0 +1,51 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* config.h */ + +#ifndef _CONFIG_H +#define _CONFIG_H + + +#define APE_CONFIG_FILE "ape.conf" + + +typedef struct _apeconfig_def { + char *val; + struct _apeconfig_def *next; + char key[33]; +} apeconfig_def; + +typedef struct apeconfig { + apeconfig_def *def; + struct apeconfig *next; + char section[33]; +} apeconfig; + + +apeconfig *ape_config_load(const char *filename); +char *ape_config_get_key(apeconfig *conf, const char *key); +apeconfig *ape_config_get_section(apeconfig *conf, const char *section); +void ape_config_free(apeconfig *conf); + +#define CONFIG_VAL(section, key, srv) \ + (ape_config_get_key(ape_config_get_section(srv, #section), #key) == NULL ? "" : ape_config_get_key(ape_config_get_section(srv, #section), #key)) + +#endif + diff --git a/ape-server/src/dns.c b/ape-server/src/dns.c new file mode 100755 index 0000000..c26a888 --- /dev/null +++ b/ape-server/src/dns.c @@ -0,0 +1,205 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* dns.c */ + +#ifdef WINDOWS +#include +#include +#else +#include +#include +#include +#include +#include +#endif +#include +#include +#include +#include +#include +#include + +#include +#include "dns.h" +#include "main.h" +#include "events.h" +#include "utils.h" +#include "ticks.h" + + +static enum dns_class qcls = DNS_C_IN; + +static struct query *query_new(const char *name, const unsigned char *dn, enum dns_type qtyp) { + struct query *q = xmalloc(sizeof(*q)); + + unsigned l = dns_dnlen(dn); + unsigned char *cdn = xmalloc(l); + + memcpy(cdn, dn, l); + + q->name = xstrdup(name); + q->dn = cdn; + q->qtyp = qtyp; + + return q; +} + +static void ape_dns_io() +{ + dns_ioevent(NULL, 0); +} + +static void ape_dns_read(ape_socket *client, ape_buffer *buf, size_t offset, acetables *g_ape) +{ + ape_dns_io(); +} + +static void ape_dns_write(ape_socket *client, acetables *g_ape) +{ + ape_dns_io(); +} + +static void query_free(struct query *q) { + free(q->name); + free(q->dn); + free(q); +} + + +static void dnscb(struct dns_ctx *ctx, void *result, void *data) { + int r = dns_status(ctx); + struct query *q = data; + struct dns_parse p; + struct dns_rr rr; + unsigned nrr; + unsigned char dn[DNS_MAXDN]; + const unsigned char *pkt, *cur, *end; + + if (!result) { + return; + } + + pkt = result; end = pkt + r; cur = dns_payload(pkt); + dns_getdn(pkt, &cur, end, dn, sizeof(dn)); + dns_initparse(&p, NULL, pkt, cur, end); + p.dnsp_qcls = p.dnsp_qtyp = 0; + nrr = 0; + + while((r = dns_nextrr(&p, &rr)) > 0) { + if (!dns_dnequal(dn, rr.dnsrr_dn)) continue; + if ((qcls == DNS_C_ANY || qcls == rr.dnsrr_cls) && + (q->qtyp == DNS_T_ANY || q->qtyp == rr.dnsrr_typ)) + ++nrr; + else if (rr.dnsrr_typ == DNS_T_CNAME && !nrr) { + if (dns_getdn(pkt, &rr.dnsrr_dptr, end, + p.dnsp_dnbuf, sizeof(p.dnsp_dnbuf)) <= 0 || + rr.dnsrr_dptr != rr.dnsrr_dend) { + r = DNS_E_PROTOCOL; + break; + } + else { + dns_dntodn(p.dnsp_dnbuf, dn, sizeof(dn)); + } + } + } + if (!r && !nrr) + r = DNS_E_NODATA; + if (r < 0) { + free(result); + return; + } + dns_rewind(&p, NULL); + p.dnsp_qtyp = q->qtyp == DNS_T_ANY ? 0 : q->qtyp; + p.dnsp_qcls = qcls == DNS_C_ANY ? 0 : qcls; + while(dns_nextrr(&p, &rr)) { + const unsigned char *dptr = rr.dnsrr_dptr; + if (rr.dnsrr_dsz == 4) { + char *ip = xmalloc(sizeof(char) * 16); + sprintf(ip, "%d.%d.%d.%d", dptr[0], dptr[1], dptr[2], dptr[3]); + + q->callback(ip, q->data, q->g_ape); + break; + } + } + + free(result); + query_free(q); +} + + +void ape_gethostbyname(char *name, void (*callback)(char *, void *, acetables *), void *data, acetables *g_ape) +{ + + struct in_addr addr; + struct query *q; + unsigned char dn[DNS_MAXDN]; + int abs = 0; + enum dns_type l_qtyp = 0; + + if (dns_pton(AF_INET, name, &addr) > 0) { + /* We have an IP */ + callback(xstrdup(name), data, g_ape); + return; + } else if (!dns_ptodn(name, strlen(name), dn, sizeof(dn), &abs)) { + /* We have an invalid domain name */ + return; + } else { + l_qtyp = DNS_T_A; + } + + q = query_new(name, dn, l_qtyp); + + q->data = data; + q->callback = callback; + q->g_ape = g_ape; + + if (abs) { + abs = DNS_NOSRCH; + } + if (!dns_submit_dn(NULL, dn, qcls, l_qtyp, abs, 0, dnscb, q)) { + query_free(q); + return; + } + + dns_timeouts(NULL, -1, 0); +} + +static void ape_dns_timeout(void *params, int *last) +{ + dns_timeouts(NULL, -1, 0); +} + + +void ape_dns_init(acetables *g_ape) +{ + int sock = dns_init(NULL, 1); + + prepare_ape_socket(sock, g_ape); + + g_ape->co[sock]->fd = sock; + g_ape->co[sock]->stream_type = STREAM_DELEGATE; + + g_ape->co[sock]->callbacks.on_read = ape_dns_read; + g_ape->co[sock]->callbacks.on_write = ape_dns_write; + + events_add(g_ape->events, sock, EVENT_READ|EVENT_WRITE); + + add_periodical(50, 0, ape_dns_timeout, NULL, g_ape); +} diff --git a/ape-server/src/dns.h b/ape-server/src/dns.h new file mode 100755 index 0000000..4aa32fa --- /dev/null +++ b/ape-server/src/dns.h @@ -0,0 +1,42 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* http.h */ + +#ifndef _DNS_H +#define _DNS_H + +#include "main.h" +#include "sock.h" +#include + +struct query { + char *name; /* original query string */ + unsigned char *dn; /* the DN being looked up */ + void (*callback)(char *ip, void *data, acetables *g_ape); + void *data; + acetables *g_ape; + enum dns_type qtyp; /* type of the query */ +}; + +void ape_dns_init(); +void ape_gethostbyname(char *name, void (*callback)(char *, void *, acetables *), void *data, acetables *g_ape); + +#endif + diff --git a/ape-server/src/entry.c b/ape-server/src/entry.c new file mode 100755 index 0000000..dd3f97d --- /dev/null +++ b/ape-server/src/entry.c @@ -0,0 +1,343 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* entry.c */ + +#include "plugins.h" +#include "main.h" +#include "sock.h" + +#include "config.h" +#include "cmd.h" + +#include "channel.h" + +#include +#include +#include +#include +#include +#include "utils.h" +#include "ticks.h" +#include "proxy.h" +#include "events.h" +#include "transports.h" +#include "servers.h" +#include "dns.h" +#include "log.h" + +#include +#include + +#include + +static void signal_handler(int sign) +{ + server_is_running = 0; + +} + +static int inc_rlimit(int nofile) +{ + struct rlimit rl; + + rl.rlim_cur = nofile; + rl.rlim_max = nofile; + + return setrlimit(RLIMIT_NOFILE, &rl); +} + +static void ape_daemon(int pidfile, acetables *g_ape) +{ + + if (0 != fork()) { + exit(0); + } + if (-1 == setsid()) { + exit(0); + } + signal(SIGHUP, SIG_IGN); + + if (0 != fork()) { + exit(0); + } + + g_ape->is_daemon = 1; + + if (pidfile > 0) { + char pidstring[32]; + int len; + len = sprintf(pidstring, "%i", getpid()); + write(pidfile, pidstring, len); + close(pidfile); + } +} + + +int main(int argc, char **argv) +{ + apeconfig *srv; + + int random, im_r00t = 0, pidfd = 0, serverfd; + unsigned int getrandom = 0; + const char *pidfile = NULL; + char *confs_path = NULL; + + struct _fdevent fdev; + + char cfgfile[513] = APE_CONFIG_FILE; + + acetables *g_ape; + + if (argc > 1 && strcmp(argv[1], "--version") == 0) { + printf("\n AJAX Push Engine Server %s - (C) Anthony Catel \n http://www.ape-project.org/\n\n", _VERSION); + return 0; + } + if (argc > 1 && strcmp(argv[1], "--help") == 0) { + printf("\n AJAX Push Engine Server %s - (C) Anthony Catel \n http://www.ape-project.org/\n", _VERSION); + printf("\n usage: aped [options]\n\n"); + printf(" Options:\n --help : Display this help\n --version : Show version number\n --cfg : Load a specific config file (default is %s)\n\n", cfgfile); + return 0; + } else if (argc > 2 && strcmp(argv[1], "--cfg") == 0) { + memset(cfgfile, 0, 513); + strncpy(cfgfile, argv[2], 512); + confs_path = get_path(cfgfile); + } else if (argc > 1) { + printf("\n AJAX Push Engine Server %s - (C) Anthony Catel \n http://www.ape-project.org/\n\n", _VERSION); + printf(" Unknown parameters - check \"aped --help\"\n\n"); + return 0; + } + if (NULL == (srv = ape_config_load(cfgfile))) { + printf("\nExited...\n\n"); + exit(1); + } + + if (getuid() == 0) { + im_r00t = 1; + } + + signal(SIGINT, &signal_handler); + signal(SIGTERM, &signal_handler); + + if (VTICKS_RATE < 1) { + printf("[ERR] TICKS_RATE cant be less than 1\n"); + return 0; + } + + random = open("/dev/urandom", O_RDONLY); + if (!random) { + printf("Cannot open /dev/urandom... exiting\n"); + return 0; + } + read(random, &getrandom, 3); + srand(getrandom); + close(random); + + g_ape = xmalloc(sizeof(*g_ape)); + g_ape->basemem = 1; // set 1 for testing if growup works + g_ape->srv = srv; + g_ape->confs_path = confs_path; + g_ape->is_daemon = 0; + + ape_log_init(g_ape); + + fdev.handler = EVENT_UNKNOWN; + + #ifdef USE_EPOLL_HANDLER + fdev.handler = EVENT_EPOLL; + #endif + #ifdef USE_KQUEUE_HANDLER + fdev.handler = EVENT_KQUEUE; + #endif + + g_ape->co = xmalloc(sizeof(*g_ape->co) * g_ape->basemem); + memset(g_ape->co, 0, sizeof(*g_ape->co) * g_ape->basemem); + + g_ape->bad_cmd_callbacks = NULL; + g_ape->bufout = xmalloc(sizeof(struct _socks_bufout) * g_ape->basemem); + + g_ape->timers.timers = NULL; + g_ape->timers.ntimers = 0; + g_ape->events = &fdev; + if (events_init(g_ape, &g_ape->basemem) == -1) { + printf("Fatal error: APE compiled without an event handler... exiting\n"); + return 0; + }; + + serverfd = servers_init(g_ape); + + ape_log(APE_INFO, __FILE__, __LINE__, g_ape, + "APE starting up - pid : %i", getpid()); + + if (strcmp(CONFIG_VAL(Server, daemon, srv), "yes") == 0 && (pidfile = CONFIG_VAL(Server, pid_file, srv)) != NULL) { + if ((pidfd = open(pidfile, O_TRUNC | O_WRONLY | O_CREAT, 0655)) == -1) { + ape_log(APE_WARN, __FILE__, __LINE__, g_ape, + "Cant open pid file : %s", CONFIG_VAL(Server, pid_file, srv)); + } + } + + if (im_r00t) { + struct group *grp = NULL; + struct passwd *pwd = NULL; + + if (inc_rlimit(atoi(CONFIG_VAL(Server, rlimit_nofile, srv))) == -1) { + ape_log(APE_WARN, __FILE__, __LINE__, g_ape, + "Cannot set the max filedescriptos limit (setrlimit) %s", strerror(errno)); + } + + /* Set uid when uid section exists */ + if (ape_config_get_section(srv, "uid")) { + + /* Get the user information (uid section) */ + if ((pwd = getpwnam(CONFIG_VAL(uid, user, srv))) == NULL) { + ape_log(APE_ERR, __FILE__, __LINE__, g_ape, + "Can\'t find username %s", CONFIG_VAL(uid, user, srv)); + return -1; + } + if (pwd->pw_uid == 0) { + ape_log(APE_ERR, __FILE__, __LINE__, g_ape, + "%s uid can\'t be 0", CONFIG_VAL(uid, user, srv)); + return -1; + } + + /* Get the group information (uid section) */ + if ((grp = getgrnam(CONFIG_VAL(uid, group, srv))) == NULL) { + printf("[ERR] Can\'t find group %s\n", CONFIG_VAL(uid, group, srv)); + ape_log(APE_ERR, __FILE__, __LINE__, g_ape, + "Can\'t find group %s", CONFIG_VAL(uid, group, srv)); + return -1; + } + + if (grp->gr_gid == 0) { + ape_log(APE_ERR, __FILE__, __LINE__, g_ape, + "%s gid can\'t be 0", CONFIG_VAL(uid, group, srv)); + return -1; + } + + setgid(grp->gr_gid); + setgroups(0, NULL); + + initgroups(CONFIG_VAL(uid, user, srv), grp->gr_gid); + + setuid(pwd->pw_uid); + } + + } else { + printf("[WARN] You have to run \'aped\' as root to increase r_limit\n"); + ape_log(APE_WARN, __FILE__, __LINE__, g_ape, + "You have to run \'aped\' as root to increase r_limit"); + } + + if (strcmp(CONFIG_VAL(Server, daemon, srv), "yes") == 0) { + ape_log(APE_INFO, __FILE__, __LINE__, g_ape, + "Starting daemon"); + ape_daemon(pidfd, g_ape); + + events_reload(g_ape->events); + events_add(g_ape->events, serverfd, EVENT_READ); + } + + if (!g_ape->is_daemon) { + printf(" _ ___ ___ \n"); + printf(" /_\\ | _ \\ __|\n"); + printf(" / _ \\| _/ _| \n"); + printf("/_/ \\_\\_| |___|\nAJAX Push Engine\n\n"); + + printf("Bind on port %i\n\n", atoi(CONFIG_VAL(Server, port, srv))); + printf("Version : %s\n", _VERSION); + printf("Build : %s %s\n", __DATE__, __TIME__); + printf("Author : Weelya (contact@weelya.com)\n\n"); + } + signal(SIGPIPE, SIG_IGN); + + ape_dns_init(g_ape); + + g_ape->cmd_hook.head = NULL; + g_ape->cmd_hook.foot = NULL; + + g_ape->hLogin = hashtbl_init(); + g_ape->hSessid = hashtbl_init(); + + g_ape->hLusers = hashtbl_init(); + g_ape->hPubid = hashtbl_init(); + + g_ape->proxy.list = NULL; + g_ape->proxy.hosts = NULL; + + g_ape->hCallback = hashtbl_init(); + + g_ape->uHead = NULL; + + g_ape->nConnected = 0; + g_ape->plugins = NULL; + + g_ape->properties = NULL; + + add_ticked(check_timeout, g_ape); + + do_register(g_ape); + + transport_start(g_ape); + + findandloadplugin(g_ape); + + server_is_running = 1; + + /* Starting Up */ + sockroutine(g_ape); /* loop */ + /* Shutdown */ + + if (pidfile != NULL) { + unlink(pidfile); + } + + free(confs_path); + + timers_free(g_ape); + + events_free(g_ape); + + transport_free(g_ape); + + hashtbl_free(g_ape->hLogin); + hashtbl_free(g_ape->hSessid); + hashtbl_free(g_ape->hLusers); + hashtbl_free(g_ape->hPubid); + + hashtbl_free(g_ape->hCallback); + + free(g_ape->bufout); + + ape_config_free(srv); + + int i; + for (i = 0; i < g_ape->basemem; i++) { + if (g_ape->co[i] != NULL) { + free(g_ape->co[i]); + } + } + free(g_ape->co); + + free_all_plugins(g_ape); + + free(g_ape); + + return 0; +} + diff --git a/ape-server/src/event_epoll.c b/ape-server/src/event_epoll.c new file mode 100755 index 0000000..cf84557 --- /dev/null +++ b/ape-server/src/event_epoll.c @@ -0,0 +1,126 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* event_kqueue.c */ + +#include "events.h" +#include +#include + +#ifdef USE_EPOLL_HANDLER +static int event_epoll_add(struct _fdevent *ev, int fd, int bitadd) +{ + struct epoll_event kev; + + kev.events = EPOLLET | EPOLLPRI; + + if (bitadd & EVENT_READ) { + kev.events |= EPOLLIN; + } + + if (bitadd & EVENT_WRITE) { + kev.events |= EPOLLOUT; + } + + memset(&kev.data, 0, sizeof(kev.data)); + + kev.data.fd = fd; + + if (epoll_ctl(ev->epoll_fd, EPOLL_CTL_ADD, fd, &kev) == -1) { + return -1; + } + + return 1; +} + +static int event_epoll_poll(struct _fdevent *ev, int timeout_ms) +{ + int nfds; + + if ((nfds = epoll_wait(ev->epoll_fd, ev->events, *ev->basemem, timeout_ms)) == -1) { + return -1; + } + + return nfds; +} + +static int event_epoll_get_fd(struct _fdevent *ev, int i) +{ + return ev->events[i].data.fd; +} + +static void event_epoll_growup(struct _fdevent *ev) +{ + ev->events = xrealloc(ev->events, sizeof(struct epoll_event) * (*ev->basemem)); +} + +static int event_epoll_revent(struct _fdevent *ev, int i) +{ + int bitret = 0; + + if (ev->events[i].events & EPOLLIN) { + bitret = EVENT_READ; + } + if (ev->events[i].events & EPOLLOUT) { + bitret |= EVENT_WRITE; + } + + return bitret; +} + + +int event_epoll_reload(struct _fdevent *ev) +{ + int nfd; + if ((nfd = dup(ev->epoll_fd)) != -1) { + close(nfd); + close(ev->epoll_fd); + } + if ((ev->epoll_fd = epoll_create(1)) == -1) { + return 0; + } + + return 1; +} + +int event_epoll_init(struct _fdevent *ev) +{ + if ((ev->epoll_fd = epoll_create(1)) == -1) { + return 0; + } + + ev->events = xmalloc(sizeof(struct epoll_event) * (*ev->basemem)); + + ev->add = event_epoll_add; + ev->poll = event_epoll_poll; + ev->get_current_fd = event_epoll_get_fd; + ev->growup = event_epoll_growup; + ev->revent = event_epoll_revent; + ev->reload = event_epoll_reload; + + return 1; +} + +#else +int event_epoll_init(struct _fdevent *ev) +{ + return 0; +} +#endif + diff --git a/ape-server/src/event_kqueue.c b/ape-server/src/event_kqueue.c new file mode 100755 index 0000000..00f7da0 --- /dev/null +++ b/ape-server/src/event_kqueue.c @@ -0,0 +1,146 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* event_kqueue.c */ + +#include "events.h" +#include +#include + +#ifdef USE_KQUEUE_HANDLER +static int event_kqueue_add(struct _fdevent *ev, int fd, int bitadd) +{ + struct kevent kev; + struct timespec ts; + int filter = 0; + + memset(&kev, 0, sizeof(kev)); + + if (bitadd & EVENT_READ) { + filter = EVFILT_READ; + + ts.tv_sec = 0; + ts.tv_nsec = 0; + + EV_SET(&kev, fd, filter, EV_ADD|EV_CLEAR, 0, 0, NULL); + if (kevent(ev->kq_fd, &kev, 1, NULL, 0, &ts) == -1) { + return -1; + } + + } + + if (bitadd & EVENT_WRITE) { + filter = EVFILT_WRITE; + + memset(&kev, 0, sizeof(kev)); + + ts.tv_sec = 0; + ts.tv_nsec = 0; + + EV_SET(&kev, fd, filter, EV_ADD|EV_CLEAR, 0, 0, NULL); + if (kevent(ev->kq_fd, &kev, 1, NULL, 0, &ts) == -1) { + return -1; + } + + } + + return 1; +} + +static int event_kqueue_poll(struct _fdevent *ev, int timeout_ms) +{ + int nfds; + struct timespec ts; + + ts.tv_sec = timeout_ms / 1000; + ts.tv_nsec = (timeout_ms % 1000) * 1000000; + + if ((nfds = kevent(ev->kq_fd, NULL, 0, ev->events, *ev->basemem * 2, &ts)) == -1) { + return -1; + } + + return nfds; +} + +static int event_kqueue_get_fd(struct _fdevent *ev, int i) +{ + return ev->events[i].ident; +} + +static void event_kqueue_growup(struct _fdevent *ev) +{ + ev->events = xrealloc(ev->events, sizeof(struct kevent) * (*ev->basemem * 2)); +} + +static int event_kqueue_revent(struct _fdevent *ev, int i) +{ + int bitret = 0; + + if (ev->events[i].filter == EVFILT_READ) { + bitret = EVENT_READ; + } else if (ev->events[i].filter == EVFILT_WRITE) { + bitret = EVENT_WRITE; + } + + return bitret; +} + + +int event_kqueue_reload(struct _fdevent *ev) +{ + int nfd; + if ((nfd = dup(ev->kq_fd)) != -1) { + close(nfd); + close(ev->kq_fd); + } + + if ((ev->kq_fd = kqueue()) == -1) { + return 0; + } + + return 1; +} + +int event_kqueue_init(struct _fdevent *ev) +{ + if ((ev->kq_fd = kqueue()) == -1) { + return 0; + } + + ev->events = xmalloc(sizeof(struct kevent) * (*ev->basemem * 2)); + + memset(ev->events, 0, sizeof(struct kevent) * (*ev->basemem * 2)); + + ev->add = event_kqueue_add; + ev->poll = event_kqueue_poll; + ev->get_current_fd = event_kqueue_get_fd; + ev->growup = event_kqueue_growup; + ev->revent = event_kqueue_revent; + ev->reload = event_kqueue_reload; + + return 1; +} + +#else +int event_kqueue_init(struct _fdevent *ev) +{ + return 0; +} +#endif + diff --git a/ape-server/src/events.c b/ape-server/src/events.c new file mode 100755 index 0000000..35ffa5b --- /dev/null +++ b/ape-server/src/events.c @@ -0,0 +1,86 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* events.c */ + +#include "events.h" +#include "main.h" + +int events_init(acetables *g_ape, int *basemem) +{ + g_ape->events->basemem = basemem; + + switch(g_ape->events->handler) { + case EVENT_EPOLL: + return event_epoll_init(g_ape->events); + break; + case EVENT_KQUEUE: + return event_kqueue_init(g_ape->events); + default: + break; + } + + return -1; +} + +void events_free(acetables *g_ape) +{ + if (g_ape->events->handler != EVENT_UNKNOWN) { + free(g_ape->events->events); + } +} + +int events_add(struct _fdevent *ev, int fd, int bitadd) +{ + if (ev->add(ev, fd, bitadd) == -1) { + return -1; + } + return 1; +} + +int events_poll(struct _fdevent *ev, int timeout_ms) +{ + int nfds; + + if ((nfds = ev->poll(ev, timeout_ms)) == -1) { + return -1; + } + + return nfds; +} + +int events_get_current_fd(struct _fdevent *ev, int i) +{ + return ev->get_current_fd(ev, i); +} + +void events_growup(struct _fdevent *ev) +{ + ev->growup(ev); +} + +int events_revent(struct _fdevent *ev, int i) +{ + return ev->revent(ev, i); +} + +int events_reload(struct _fdevent *ev) +{ + return ev->reload(ev); +} diff --git a/ape-server/src/events.h b/ape-server/src/events.h new file mode 100755 index 0000000..f596ed9 --- /dev/null +++ b/ape-server/src/events.h @@ -0,0 +1,87 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* events.h */ + +#ifndef _EVENTS_H +#define _EVENTS_H + +#include "utils.h" +#include "main.h" + +#include "configure.h" + +#ifdef USE_KQUEUE_HANDLER +#include +#endif +#ifdef USE_EPOLL_HANDLER +#include +#endif + +/* Generics flags */ +#define EVENT_READ 0x01 +#define EVENT_WRITE 0x02 + +/* Events handler */ +typedef enum { + EVENT_UNKNOWN, + EVENT_EPOLL, /* Linux */ + EVENT_KQUEUE, /* BSD */ + EVENT_DEVPOLL, /* Solaris */ + EVENT_POLL, /* POSIX */ + EVENT_SELECT /* Generic (Windows) */ +} fdevent_handler_t; + +struct _fdevent { + /* Common values */ + int *basemem; + /* Interface */ + int (*add)(struct _fdevent *, int, int); + int (*poll)(struct _fdevent *, int); + int (*get_current_fd)(struct _fdevent *, int); + void (*growup)(struct _fdevent *); + int (*revent)(struct _fdevent *, int); + int (*reload)(struct _fdevent *); + + /* Specifics values */ + #ifdef USE_KQUEUE_HANDLER + struct kevent *events; + int kq_fd; + #endif + #ifdef USE_EPOLL_HANDLER + struct epoll_event *events; + int epoll_fd; + #endif + + fdevent_handler_t handler; +}; + +int events_init(acetables *g_ape, int *basemem); +void events_free(acetables *g_ape); +int events_add(struct _fdevent *ev, int fd, int bitadd); +int events_poll(struct _fdevent *ev, int timeout_ms); +int events_get_current_fd(struct _fdevent *ev, int i); +void events_growup(struct _fdevent *ev); +int events_revent(struct _fdevent *ev, int i); +int events_reload(struct _fdevent *ev); + +int event_kqueue_init(struct _fdevent *ev); +int event_epoll_init(struct _fdevent *ev); + +#endif diff --git a/ape-server/src/extend.c b/ape-server/src/extend.c new file mode 100755 index 0000000..9ad6a09 --- /dev/null +++ b/ape-server/src/extend.c @@ -0,0 +1,214 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* extend.c */ + +#include +#include +#include "extend.h" +#include "utils.h" +#include "json.h" + +/* + Add a property to an object (user, channel, proxy, acetables) + + + EXTEND_STR : allocate memory for val and free it when the property is deleted + EXTEND_JSON : put the given "json" object on the properties. This object is free'ed using json_free when the property is deleted + EXTEND_POINTER : add a private pointer as property (must be private. see EXTEND_PUBLIC) + + EXTEND_ISPUBLIC : The property is added to the json tree sent with get_json_object_* + EXTEND_ISPRIVATE : The property is not shown in get_json_object_* +*/ +extend *add_property(extend **entry, const char *key, void *val, EXTEND_TYPE etype, EXTEND_PUBLIC visibility) +{ + extend *new_property = NULL, *eTmp; + + if (strlen(key) > EXTEND_KEY_LENGTH) { + return NULL; + } + + /* Delete older property with this key (if any) */ + del_property(entry, key); + + eTmp = *entry; + + new_property = xmalloc(sizeof(*new_property)); + + /* The key cannot be longer than EXTEND_KEY_LENGTH */ + strcpy(new_property->key, key); + + switch(etype) { + case EXTEND_STR: + new_property->val = xstrdup(val); + break; + case EXTEND_POINTER: + default: + /* a pointer must be a private property */ + visibility = EXTEND_ISPRIVATE; + case EXTEND_JSON: + /* /!\ the JSON tree (val) is free'ed when this property is removed */ + new_property->val = val; + break; + } + + new_property->next = eTmp; + new_property->type = etype; + new_property->visibility = visibility; + + *entry = new_property; + + return new_property; + +} + + +extend *get_property(extend *entry, const char *key) +{ + while (entry != NULL) { + if (strcmp(entry->key, key) == 0) { + return entry; + } + entry = entry->next; + } + + return NULL; +} + +void *get_property_val(extend *entry, const char *key) +{ + extend *find; + + if ((find = get_property(entry, key)) != NULL) { + return find->val; + } + return NULL; + +} + +void del_property(extend **entry, const char *key) +{ + + while (*entry != NULL) { + if (strcmp((*entry)->key, key) == 0) { + extend *pEntry = *entry; + *entry = (*entry)->next; + + switch(pEntry->type) { + case EXTEND_STR: + free(pEntry->val); + break; + case EXTEND_JSON: + free_json_item(pEntry->val); + break; + default: + break; + } + + free(pEntry); + + return; + } + entry = &(*entry)->next; + } + +} + +/* TODO : use del_property */ +void clear_properties(extend **entry) +{ + extend *pEntry = *entry, *pTmp; + + while (pEntry != NULL) { + pTmp = pEntry->next; + switch(pEntry->type) { + case EXTEND_STR: + free(pEntry->val); + break; + case EXTEND_JSON: + free_json_item(pEntry->val); + break; + default: + break; + } + free(pEntry); + pEntry = pTmp; + } + *entry = NULL; +} + + +#if 0 +extend *add_property_str(extend **entry, char *key, char *val) +{ + extend *new_property = NULL, *eTmp; + + if (strlen(key) > EXTEND_KEY_LENGTH) { + return NULL; + } + if ((eTmp = get_property(*entry, key)) != NULL) { + if (strlen(val) > strlen(eTmp->val)) { + eTmp->val = xrealloc(eTmp->val, sizeof(char) * (strlen(val)+1)); + } + + strcpy(eTmp->key, key); + strcpy(eTmp->val, val); + + return eTmp; + } + + eTmp = *entry; + + new_property = xmalloc(sizeof(*new_property)); + new_property->val = xmalloc(sizeof(char) * (strlen(val)+1)); + new_property->allocval = 1; + strcpy(new_property->key, key); + strcpy(new_property->val, val); + new_property->next = eTmp; + + *entry = new_property; + + return new_property; + +} + +extend *add_property(extend **entry, const char *key, void *val) +{ + extend *new_property = NULL, *eTmp; + + if (strlen(key) > EXTEND_KEY_LENGTH || (eTmp = get_property(*entry, key)) != NULL) { + return NULL; + } + + eTmp = *entry; + + new_property = xmalloc(sizeof(*new_property)); + + strcpy(new_property->key, key); + new_property->val = val; + new_property->allocval = 0; + new_property->next = eTmp; + + *entry = new_property; + + return new_property; + +} +#endif + diff --git a/ape-server/src/extend.h b/ape-server/src/extend.h new file mode 100755 index 0000000..9c07f76 --- /dev/null +++ b/ape-server/src/extend.h @@ -0,0 +1,56 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* extend.h */ + +#ifndef _EXTEND_H +#define _EXTEND_H + +#define EXTEND_KEY_LENGTH 32 + +typedef enum { + EXTEND_STR, + EXTEND_JSON, + EXTEND_POINTER +} EXTEND_TYPE; + +typedef enum { + EXTEND_ISPUBLIC, + EXTEND_ISPRIVATE +} EXTEND_PUBLIC; + +typedef struct _extend extend; + +struct _extend +{ + void *val; + + EXTEND_TYPE type; + EXTEND_PUBLIC visibility; + + struct _extend *next; + char key[EXTEND_KEY_LENGTH+1]; +}; + +extend *get_property(extend *entry, const char *key); +void clear_properties(extend **entry); +void del_property(extend **entry, const char *key); +//extend *add_property_str(extend **entry, char *key, char *val); +extend *add_property(extend **entry, const char *key, void *val, EXTEND_TYPE etype, EXTEND_PUBLIC visibility); +#endif diff --git a/ape-server/src/handle_http.c b/ape-server/src/handle_http.c new file mode 100755 index 0000000..cf32863 --- /dev/null +++ b/ape-server/src/handle_http.c @@ -0,0 +1,191 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* handle_http.c */ + +#include + +#include "handle_http.h" +#include "utils.h" +#include "config.h" +#include "cmd.h" +#include "main.h" +#include "sock.h" +#include "http.h" +#include "parser.h" +#include "md5.h" + +static int gettransport(char *input) +{ + char *start = strchr(input, '/'); + + if (start != NULL && start[1] >= 48 && start[1] <= 54 && start[2] == '/') { + return start[1]-48; + } + + return 0; +} + +subuser *checkrecv_websocket(ape_socket *co, acetables *g_ape) +{ + unsigned int op; + clientget cget; + websocket_state *websocket = co->parser.data; + subuser *user = NULL; + + cget.client = co; + cget.ip_get = co->ip_client; + cget.get = websocket->data; + cget.host = websocket->http->host; + cget.hlines = websocket->http->hlines; + + op = checkcmd(&cget, TRANSPORT_WEBSOCKET, &user, g_ape); + + switch (op) { + case CONNECT_SHUTDOWN: + case CONNECT_KEEPALIVE: + break; + } + + return user; +} + +/* + WebSockets protocol rev 76 (Opening handshake) + http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76 +*/ +static unsigned int ws_compute_key(const char *value) +{ + const char *pValue; + long int val = 0; + int spaces = 0; + + for (pValue = value; *pValue != '\0'; pValue++) { + if (*pValue >= 48 && *pValue <= 57) { + val = (val * 10) + (*pValue-48); + } else if (*pValue == ' ') { + spaces++; + } + } + if (spaces == 0) { + return 0; + } + + return val / spaces; +} + +subuser *checkrecv(ape_socket *co, acetables *g_ape) +{ + unsigned int op; + http_state *http = co->parser.data; + subuser *user = NULL; + clientget cget; + + if (http->host == NULL) { + shutdown(co->fd, 2); + return NULL; + } + + if (gettransport(http->uri) == TRANSPORT_WEBSOCKET) { + int is_rev_76 = 0; + char *origin = get_header_line(http->hlines, "Origin"); + char *key1 = get_header_line(http->hlines, "Sec-WebSocket-Key1"); + char *key2 = get_header_line(http->hlines, "Sec-WebSocket-Key2"); + + websocket_state *websocket; + unsigned char md5sum[16]; + + if (origin == NULL) { + shutdown(co->fd, 2); + return NULL; + } + + if (key1 != NULL && key2 != NULL) { + md5_context ctx; + + long int ckey1 = htonl(ws_compute_key(key1)); + long int ckey2 = htonl(ws_compute_key(key2)); + + is_rev_76 = 1; /* draft rev 76 detected (used in Firefox 4.0 alpha2) */ + + md5_starts(&ctx); + + md5_update(&ctx, (uint8 *)&ckey1, 4); + md5_update(&ctx, (uint8 *)&ckey2, 4); + md5_update(&ctx, (uint8 *)http->data, 8); + + md5_finish(&ctx, md5sum); + } + + PACK_TCP(co->fd); + + if (is_rev_76) { + sendbin(co->fd, CONST_STR_LEN(WEBSOCKET_HARDCODED_HEADERS_NEW), 0, g_ape); + sendbin(co->fd, CONST_STR_LEN("Sec-WebSocket-Origin: "), 0, g_ape); + sendbin(co->fd, origin, strlen(origin), 0, g_ape); + sendbin(co->fd, CONST_STR_LEN("\r\nSec-WebSocket-Location: ws://"), 0, g_ape); + } else { + sendbin(co->fd, CONST_STR_LEN(WEBSOCKET_HARDCODED_HEADERS_OLD), 0, g_ape); + sendbin(co->fd, CONST_STR_LEN("WebSocket-Origin: "), 0, g_ape); + sendbin(co->fd, origin, strlen(origin), 0, g_ape); + sendbin(co->fd, CONST_STR_LEN("\r\nWebSocket-Location: ws://"), 0, g_ape); + } + sendbin(co->fd, http->host, strlen(http->host), 0, g_ape); + sendbin(co->fd, http->uri, strlen(http->uri), 0, g_ape); + sendbin(co->fd, CONST_STR_LEN("\r\n\r\n"), 0, g_ape); + if (is_rev_76) { + sendbin(co->fd, (char *)md5sum, 16, 0, g_ape); + } + FLUSH_TCP(co->fd); + + + co->parser = parser_init_stream(co); + websocket = co->parser.data; + websocket->http = http; /* keep http data */ + + return NULL; + } + + if (http->data == NULL) { + sendbin(co->fd, HEADER_DEFAULT, HEADER_DEFAULT_LEN, 0, g_ape); + sendbin(co->fd, CONST_STR_LEN(CONTENT_NOTFOUND), 0, g_ape); + + safe_shutdown(co->fd, g_ape); + return NULL; + } + + cget.client = co; + cget.ip_get = co->ip_client; + cget.get = http->data; + cget.host = http->host; + cget.hlines = http->hlines; + + op = checkcmd(&cget, gettransport(http->uri), &user, g_ape); + + switch (op) { + case CONNECT_SHUTDOWN: + safe_shutdown(co->fd, g_ape); + break; + case CONNECT_KEEPALIVE: + break; + } + + return user; +} + diff --git a/ape-server/src/handle_http.h b/ape-server/src/handle_http.h new file mode 100755 index 0000000..8cd7cf2 --- /dev/null +++ b/ape-server/src/handle_http.h @@ -0,0 +1,46 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* handle_http.h */ + +#ifndef _HANDLE_HTTP_H +#define _HANDLE_HTTP_H + +#include "main.h" +#include "users.h" + +subuser *checkrecv(ape_socket *co, acetables *g_ape); +subuser *checkrecv_websocket(ape_socket *co, acetables *g_ape); + +typedef struct clientget +{ + struct _http_header_line *hlines; + ape_socket *client; + const char *ip_get; + const char *get; + const char *host; +} clientget ; + +enum { + CONNECT_SHUTDOWN = 0, + CONNECT_KEEPALIVE +}; + +#endif + diff --git a/ape-server/src/hash.c b/ape-server/src/hash.c new file mode 100755 index 0000000..b24e667 --- /dev/null +++ b/ape-server/src/hash.c @@ -0,0 +1,191 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include + +#include "hash.h" +#include "users.h" +#include "utils.h" + +static unsigned int hach_string(const char *str) +{ + int hash = 5381; // DJB Hash + const char *s; + + + for (s = str; *s != '\0'; s++) { + hash = ((hash << 5) + hash) + tolower(*s); + } + + return (hash & 0x7FFFFFFF)%(HACH_TABLE_MAX-1); +} + +HTBL *hashtbl_init() +{ + HTBL_ITEM **htbl_item; + HTBL *htbl; + + htbl = xmalloc(sizeof(*htbl)); + + htbl_item = (HTBL_ITEM **) xmalloc( sizeof(*htbl_item) * (HACH_TABLE_MAX + 1) ); + + memset(htbl_item, 0, sizeof(*htbl_item) * (HACH_TABLE_MAX + 1)); + + htbl->first = NULL; + htbl->table = htbl_item; + + return htbl; +} + +void hashtbl_free(HTBL *htbl) +{ + size_t i; + HTBL_ITEM *hTmp; + HTBL_ITEM *hNext; + + for (i = 0; i < (HACH_TABLE_MAX + 1); i++) { + hTmp = htbl->table[i]; + while (hTmp != 0) { + hNext = hTmp->next; + free(hTmp->key); + hTmp->key = NULL; + free(hTmp); + hTmp = hNext; + } + } + + free(htbl->table); + free(htbl); +} + +void hashtbl_append(HTBL *htbl, const char *key, void *structaddr) +{ + unsigned int key_hash, key_len; + HTBL_ITEM *hTmp, *hDbl; + + if (key == NULL) { + return; + } + key_len = strlen(key); + key_hash = hach_string(key); + + hTmp = (HTBL_ITEM *)xmalloc(sizeof(*hTmp)); + + hTmp->next = NULL; + hTmp->lnext = htbl->first; + hTmp->lprev = NULL; + + if (htbl->first != NULL) { + htbl->first->lprev = hTmp; + } + + hTmp->key = xmalloc(sizeof(char) * (key_len+1)); + + hTmp->addrs = (void *)structaddr; + + memcpy(hTmp->key, key, key_len+1); + + if (htbl->table[key_hash] != NULL) { + hDbl = htbl->table[key_hash]; + + while (hDbl != NULL) { + if (strcasecmp(hDbl->key, key) == 0) { + free(hTmp->key); + free(hTmp); + hDbl->addrs = (void *)structaddr; + return; + } else { + hDbl = hDbl->next; + } + } + hTmp->next = htbl->table[key_hash]; + } + + htbl->first = hTmp; + + htbl->table[key_hash] = hTmp; +} + + +void hashtbl_erase(HTBL *htbl, const char *key) +{ + unsigned int key_hash; + HTBL_ITEM *hTmp, *hPrev; + + if (key == NULL) { + return; + } + + key_hash = hach_string(key); + + hTmp = htbl->table[key_hash]; + hPrev = NULL; + + while (hTmp != NULL) { + if (strcasecmp(hTmp->key, key) == 0) { + if (hPrev != NULL) { + hPrev->next = hTmp->next; + } else { + htbl->table[key_hash] = hTmp->next; + } + + if (hTmp->lprev == NULL) { + htbl->first = hTmp->lnext; + } else { + hTmp->lprev->lnext = hTmp->lnext; + } + if (hTmp->lnext != NULL) { + hTmp->lnext->lprev = hTmp->lprev; + } + + free(hTmp->key); + free(hTmp); + return; + } + hPrev = hTmp; + hTmp = hTmp->next; + } +} + +void *hashtbl_seek(HTBL *htbl, const char *key) +{ + unsigned int key_hash; + HTBL_ITEM *hTmp; + + if (key == NULL) { + return NULL; + } + + key_hash = hach_string(key); + + hTmp = htbl->table[key_hash]; + + while (hTmp != NULL) { + if (strcasecmp(hTmp->key, key) == 0) { + return (void *)(hTmp->addrs); + } + hTmp = hTmp->next; + } + + return NULL; +} + diff --git a/ape-server/src/hash.h b/ape-server/src/hash.h new file mode 100755 index 0000000..74e2397 --- /dev/null +++ b/ape-server/src/hash.h @@ -0,0 +1,50 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _LHTBL_H +#define _LHTBL_H + +#define HACH_TABLE_MAX 5381 + +typedef struct HTBL +{ + struct _htbl_item *first; + struct _htbl_item **table; +} HTBL; + + +typedef struct _htbl_item +{ + char *key; + void *addrs; + struct _htbl_item *next; + + struct _htbl_item *lnext; + struct _htbl_item *lprev; + +} HTBL_ITEM; + +HTBL *hashtbl_init(); + +void hashtbl_free(HTBL *htbl); +void *hashtbl_seek(HTBL *htbl, const char *key); +void hashtbl_erase(HTBL *htbl, const char *key); +void hashtbl_append(HTBL *htbl, const char *key, void *structaddr); + +#endif diff --git a/ape-server/src/http.c b/ape-server/src/http.c new file mode 100644 index 0000000..d8ea791 --- /dev/null +++ b/ape-server/src/http.c @@ -0,0 +1,500 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* http.c */ + +#include + +#include "http.h" +#include "sock.h" +#include "main.h" +#include "utils.h" +#include "dns.h" + +#define HTTP_PREFIX "http://" +#define WS_MAGIC_VALUE 0x00003600 + +struct _http_attach { + char host[1024]; + char file[1024]; + + const char *post; + unsigned short int port; +}; + +static struct _http_header_line *parse_header_line(const char *line) +{ + unsigned int i; + unsigned short int state = 0; + struct _http_header_line *hline = NULL; + for (i = 0; i < 1024 && line[i] != '\0' && line[i] != '\r' && line[i] != '\n'; i++) { + if (i == 0) { + hline = xmalloc(sizeof(*hline)); + hline->key.len = 0; + hline->value.len = 0; + } + switch(state) { + case 0: + if ((i == 0 && (line[i] == ':' || line[i] == ' ')) || (line[i] == ':' && line[i+1] != ' ') || (i > 63)) { + free(hline); + return NULL; + } + if (line[i] == ':') { + hline->key.val[hline->key.len] = '\0'; + state = 1; + i++; + } else { + hline->key.val[hline->key.len++] = line[i]; + } + break; + case 1: + hline->value.val[hline->value.len++] = line[i]; + break; + } + } + if (!state) { + free(hline); + return NULL; + } + hline->value.val[hline->value.len] = '\0'; + + return hline; +} + +char *get_header_line(struct _http_header_line *lines, const char *key) +{ + while (lines != NULL) { + if (strcasecmp(lines->key.val, key) == 0) { + return lines->value.val; + } + + lines = lines->next; + } + + return NULL; +} + +void process_websocket(ape_socket *co, acetables *g_ape) +{ + char *pData; + ape_buffer *buffer = &co->buffer_in; + websocket_state *websocket = co->parser.data; + ape_parser *parser = &co->parser; + + char *data = pData = &buffer->data[websocket->offset]; + + if (buffer->length == 0 || parser->ready == 1) { + return; + } + + if (buffer->length > 502400) { + shutdown(co->fd, 2); + return; + } + + data[buffer->length - websocket->offset] = '\0'; + + if (*data == '\0') { + data = &data[1]; + } + + while(data++ != &buffer->data[buffer->length]) { + + if ((unsigned char)*data == 0xFF) { + *data = '\0'; + + websocket->data = &pData[1]; + + parser->onready(parser, g_ape); + + websocket->offset += (data - pData)+1; + + if (websocket->offset == buffer->length) { + parser->ready = -1; + buffer->length = 0; + websocket->offset = 0; + + return; + } + + break; + } + } + + if (websocket->offset != buffer->length && data != &buffer->data[buffer->length+1]) { + process_websocket(co, g_ape); + } +} + +/* Just a lightweight http request processor */ +void process_http(ape_socket *co, acetables *g_ape) +{ + ape_buffer *buffer = &co->buffer_in; + http_state *http = co->parser.data; + ape_parser *parser = &co->parser; + + char *data = buffer->data; + int pos, read, p = 0; + + if (buffer->length == 0 || parser->ready == 1 || http->error == 1) { + return; + } + + /* 0 will be erased by the next read()'ing loop */ + data[buffer->length] = '\0'; + + data = &data[http->pos]; + + if (*data == '\0') { + return; + } + + /* Update the address of http->data and http->uri if buffer->data has changed (realloc) */ + if (http->buffer_addr != NULL && buffer->data != http->buffer_addr) { + http->data = &buffer->data[(void *)http->data - (void *)http->buffer_addr]; + http->uri = &buffer->data[(void *)http->uri - (void *)http->buffer_addr]; + http->buffer_addr = buffer->data; + } + + switch(http->step) { + case 0: + pos = seof(data, '\n'); + if (pos == -1) { + return; + } + + /* TODO : endieness */ + switch(*(unsigned int *)data) { + case 542393671: /* GET + space */ + http->type = HTTP_GET; + p = 4; + break; + case 1414745936: /* POST */ + http->type = HTTP_POST; + p = 5; + break; + default: + http->error = 1; + shutdown(co->fd, 2); + return; + } + + if (data[p] != '/') { + http->error = 1; + shutdown(co->fd, 2); + return; + } else { + int i = p; + while (p++) { + switch(data[p]) { + case ' ': + http->pos = pos; + http->step = 1; + http->uri = &data[i]; + http->buffer_addr = buffer->data; + data[p] = '\0'; + process_http(co, g_ape); + return; + case '?': + if (data[p+1] != ' ' && data[p+1] != '\r' && data[p+1] != '\n') { + http->buffer_addr = buffer->data; + http->data = &data[p+1]; + } + break; + case '\r': + case '\n': + case '\0': + http->error = 1; + shutdown(co->fd, 2); + return; + } + } + } + break; + case 1: + pos = seof(data, '\n'); + if (pos == -1) { + + return; + } + if (pos == 1 || (pos == 2 && *data == '\r')) { + if (http->type == HTTP_GET) { + /* Ok, at this point we have a blank line. Ready for GET */ + buffer->data[http->pos] = '\0'; + urldecode(http->uri); + parser->onready(parser, g_ape); + parser->ready = -1; + buffer->length = 0; + return; + } else if (http->type == HTTP_GET_WS) { /* WebSockets handshake needs to read 8 bytes */ + //urldecode(http->uri); + http->contentlength = 8; + http->buffer_addr = buffer->data; + http->data = &buffer->data[http->pos+(pos)]; + http->step = 2; + } else { + /* Content-Length is mandatory in case of POST */ + if (http->contentlength == 0) { + http->error = 1; + shutdown(co->fd, 2); + return; + } else { + http->buffer_addr = buffer->data; // save the addr + http->data = &buffer->data[http->pos+(pos)]; + http->step = 2; + } + } + } else { + struct _http_header_line *hl; + + if ((hl = parse_header_line(data)) != NULL) { + hl->next = http->hlines; + http->hlines = hl; + if (strcasecmp(hl->key.val, "host") == 0) { + http->host = hl->value.val; + } + } + if (http->type == HTTP_POST) { + /* looking for content-length instruction */ + if (pos <= 25 && strncasecmp("content-length: ", data, 16) == 0) { + int cl = atoi(&data[16]); + + /* Content-length can't be negative... */ + if (cl < 1 || cl > MAX_CONTENT_LENGTH) { + http->error = 1; + shutdown(co->fd, 2); + return; + } + /* At this time we are ready to read "cl" bytes contents */ + http->contentlength = cl; + + } + } else if (http->type == HTTP_GET) { + if (strncasecmp("Sec-WebSocket-Key1: ", data, 20) == 0) { + http->type = HTTP_GET_WS; + } + } + } + http->pos += pos; + process_http(co, g_ape); + break; + case 2: + read = buffer->length - http->pos; // data length + http->pos += read; + http->read += read; + + if (http->read >= http->contentlength) { + + parser->ready = 1; + urldecode(http->uri); + /* no more than content-length */ + buffer->data[http->pos - (http->read - http->contentlength)] = '\0'; + + parser->onready(parser, g_ape); + parser->ready = -1; + buffer->length = 0; + } + break; + default: + break; + } +} + + +/* taken from libevent */ + +int parse_uri(char *url, char *host, u_short *port, char *file) +{ + char *p; + const char *p2; + int len; + + len = strlen(HTTP_PREFIX); + if (strncasecmp(url, HTTP_PREFIX, len)) { + return -1; + } + + url += len; + + /* We might overrun */ + strncpy(host, url, 1023); + + + p = strchr(host, '/'); + if (p != NULL) { + *p = '\0'; + p2 = p + 1; + } else { + p2 = NULL; + } + if (file != NULL) { + /* Generate request file */ + if (p2 == NULL) + p2 = ""; + sprintf(file, "/%s", p2); + } + + p = strchr(host, ':'); + + if (p != NULL) { + *p = '\0'; + *port = atoi(p + 1); + + if (*port == 0) + return -1; + } else + *port = 80; + + return 0; +} + +http_headers_response *http_headers_init(int code, char *detail, int detail_len) +{ + http_headers_response *headers; + + if (detail_len > 63 || (code < 100 && code >= 600)) { + return NULL; + } + + headers = xmalloc(sizeof(*headers)); + + headers->code = code; + headers->detail.len = detail_len; + memcpy(headers->detail.val, detail, detail_len + 1); + + headers->fields = NULL; + headers->last = NULL; + + return headers; +} + +void http_headers_set_field(http_headers_response *headers, const char *key, int keylen, const char *value, int valuelen) +{ + struct _http_headers_fields *field = NULL, *look_field; + int value_l, key_l; + + value_l = (valuelen ? valuelen : strlen(value)); + key_l = (keylen ? keylen : strlen(key)); + + if (key_l >= 32) { + return; + } + + for(look_field = headers->fields; look_field != NULL; look_field = look_field->next) { + if (strncasecmp(look_field->key.val, key, key_l) == 0) { + field = look_field; + break; + } + } + + if (field == NULL) { + field = xmalloc(sizeof(*field)); + field->next = NULL; + + if (headers->fields == NULL) { + headers->fields = field; + } else { + headers->last->next = field; + } + headers->last = field; + } else { + free(field->value.val); + } + + field->value.val = xmalloc(sizeof(char) * (value_l + 1)); + + memcpy(field->key.val, key, key_l + 1); + memcpy(field->value.val, value, value_l + 1); + + field->value.len = value_l; + field->key.len = key_l; + +} + +/* +http_headers_response *headers = http_headers_init(200, "OK", 2); +http_headers_set_field(headers, "Content-Length", 0, "100", 0); +http_send_headers(headers, cget->client, g_ape); +*/ + +int http_send_headers(http_headers_response *headers, const char *default_h, unsigned int default_len, ape_socket *client, acetables *g_ape) +{ + char code[4]; + int finish = 1; + struct _http_headers_fields *fields; + //HTTP/1.1 200 OK\r\n + + if (headers == NULL) { + finish &= sendbin(client->fd, (char *)default_h, default_len, 0, g_ape); + } else { + /* We have a lot of write syscall here. TODO : use of writev */ + itos(headers->code, code, 4); + finish &= sendbin(client->fd, "HTTP/1.1 ", 9, 0, g_ape); + finish &= sendbin(client->fd, code, 3, 0, g_ape); + finish &= sendbin(client->fd, " ", 1, 0, g_ape); + finish &= sendbin(client->fd, headers->detail.val, headers->detail.len, 0, g_ape); + finish &= sendbin(client->fd, "\r\n", 2, 0, g_ape); + + for (fields = headers->fields; fields != NULL; fields = fields->next) { + finish &= sendbin(client->fd, fields->key.val, fields->key.len, 0, g_ape); + finish &= sendbin(client->fd, ": ", 2, 0, g_ape); + finish &= sendbin(client->fd, fields->value.val, fields->value.len, 0, g_ape); + finish &= sendbin(client->fd, "\r\n", 2, 0, g_ape); + + fields = fields->next; + } + + finish &= sendbin(client->fd, "\r\n", 2, 0, g_ape); + } + + return finish; +} + +void http_headers_free(http_headers_response *headers) +{ + struct _http_headers_fields *fields; + + if (headers == NULL) { + return; + } + + fields = headers->fields; + + while(fields != NULL) { + struct _http_headers_fields *tmpfields = fields->next; + + free(fields->value.val); + + free(fields); + fields = tmpfields; + } + free(headers); +} + +void free_header_line(struct _http_header_line *line) +{ + struct _http_header_line *tline; + + while (line != NULL) { + tline = line->next; + free(line); + line = tline; + } +} + diff --git a/ape-server/src/http.h b/ape-server/src/http.h new file mode 100755 index 0000000..7969242 --- /dev/null +++ b/ape-server/src/http.h @@ -0,0 +1,91 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* http.h */ + +#ifndef _HTTP_H +#define _HTTP_H + +#include "main.h" + +#define MAX_CONTENT_LENGTH 51200 // 50kb + +struct _http_headers_fields +{ + struct { + char val[32]; + int len; + } key; + + struct { + char *val; + int len; + } value; + + struct _http_headers_fields *next; +}; + +typedef struct _http_headers_response http_headers_response; +struct _http_headers_response +{ + int code; + struct { + char val[64]; + int len; + } detail; + + struct _http_headers_fields *fields; + struct _http_headers_fields *last; +}; + +typedef enum { + HTTP_NULL = 0, + HTTP_GET, + HTTP_GET_WS, + HTTP_POST, + HTTP_OPTIONS +} http_method; + + +struct _http_header_line +{ + struct { + char val[64]; + unsigned int len; + } key; + + struct { + char val[1024]; + unsigned int len; + } value; + + struct _http_header_line *next; +}; + +void process_websocket(ape_socket *co, acetables *g_ape); +void process_http(ape_socket *co, acetables *g_ape); +http_headers_response *http_headers_init(int code, char *detail, int detail_len); +void http_headers_set_field(http_headers_response *headers, const char *key, int keylen, const char *value, int valuelen); +int http_send_headers(http_headers_response *headers, const char *default_h, unsigned int default_len, ape_socket *client, acetables *g_ape); +void http_headers_free(http_headers_response *headers); +void free_header_line(struct _http_header_line *line); +char *get_header_line(struct _http_header_line *lines, const char *key); + +#endif + diff --git a/ape-server/src/json.c b/ape-server/src/json.c new file mode 100755 index 0000000..dcd8dda --- /dev/null +++ b/ape-server/src/json.c @@ -0,0 +1,897 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* json.c */ + +#include +#include +#include + +#include "json.h" +#include "utils.h" + +void set_json(const char *name, const char *value, struct json **jprev) +{ + struct json *new_json, *old_json = *jprev; + + new_json = xmalloc(sizeof(struct json)); + + new_json->name.len = strlen(name); + new_json->name.buf = xmalloc(sizeof(char) * (new_json->name.len + 1)); + memcpy(new_json->name.buf, name, new_json->name.len + 1); + + new_json->value.len = (value != NULL ? strlen(value) : 0); + + if (new_json->value.len) { + new_json->value.buf = xmalloc(sizeof(char) * (new_json->value.len + 1)); + memcpy(new_json->value.buf, value, new_json->value.len + 1); + } else { + new_json->value.buf = NULL; + } + + new_json->jfather = NULL; + new_json->jchilds = NULL; + + new_json->next = old_json; + new_json->prev = NULL; + + if (old_json != NULL) { + old_json->prev = new_json; + } + + *jprev = new_json; +} + +struct json *json_copy(struct json *jbase) +{ + struct json *new_json = xmalloc(sizeof(struct json)); + struct json_childs *jchilds = jbase->jchilds; + + new_json->name.len = jbase->name.len; + new_json->name.buf = xmalloc(sizeof(char) * (new_json->name.len + 1)); + + memcpy(new_json->name.buf, jbase->name.buf, new_json->name.len + 1); + + new_json->value.len = jbase->value.len; + + if (jbase->value.len) { + + new_json->value.buf = xmalloc(sizeof(char) * (new_json->value.len + 1)); + memcpy(new_json->value.buf, jbase->value.buf, new_json->value.len + 1); + + } else { + new_json->value.buf = NULL; + } + new_json->prev = NULL; + new_json->next = NULL; + new_json->jfather = NULL; + + new_json->jchilds = NULL; + + if (jbase->next != NULL) { + new_json->next = json_copy(jbase->next); + new_json->next->prev = new_json; + } + + + while (jchilds != NULL) { + struct json_childs *new_child = xmalloc(sizeof(struct json_childs)); + + new_child->type = jchilds->type; + new_child->child = json_copy(jchilds->child); + new_child->child->jfather = new_json; + new_child->next = new_json->jchilds; + + new_json->jchilds = new_child; + + jchilds = jchilds->next; + } + + + return new_json; +} + +void json_free(struct json *jbase) +{ + struct json_childs *jchilds = jbase->jchilds; + + free(jbase->name.buf); + + if (jbase->value.buf != NULL) { + free(jbase->value.buf); + } + + if (jbase->next != NULL) { + json_free(jbase->next); + } + + while (jchilds != NULL) { + json_free(jchilds->child); + + jchilds = jchilds->next; + } + + free(jbase); +} + +void json_attach(struct json *json_father, struct json *json_child, unsigned int type) +{ + struct json_childs *ochild = json_father->jchilds, *nchild; + + json_child->jfather = json_father; + + nchild = xmalloc(sizeof(struct json_childs)); + + nchild->child = json_child; + nchild->next = ochild; + nchild->type = type; + + json_father->jchilds = nchild; +} + +void json_concat(struct json *json_father, struct json *json_child) +{ + struct json *jTmp = json_father->next; + + if (jTmp == NULL) { + json_father->next = json_child; + json_child->prev = json_father; + + return; + } + + while (jTmp != NULL) { + if (jTmp->next == NULL) { + jTmp->next = json_child; + json_child->prev = jTmp; + return; + } + jTmp = jTmp->next; + } + +} + +/* Determinate an heuristic final string size */ +static int json_evaluate_string_size(json_item *head) +{ + int evalsize = 2; + + while (head != NULL) { + if (head->key.val != NULL) { + evalsize += head->key.len + 3; + } + + if (head->jval.vu.str.value != NULL) { + evalsize += head->jval.vu.str.length + 3; + } else if (head->jchild.child == NULL) { + evalsize += 16; /* uch ! TODO : int length */ + } + if (head->jchild.child != NULL) { + evalsize += json_evaluate_string_size(head->jchild.child); + } + head = head->next; + } + + return evalsize; +} + +static int escape_json_string(char *in, char *out, int len) +{ + int i, e; + + for (i = 0, e = 0; i < len; i++, e++) { + + switch(in[i]) { + case '"': + out[e++] = '\\'; + out[e] = '"'; + break; + case '\\': + out[e++] = '\\'; + out[e] = '\\'; + break; + case '\n': + out[e++] = '\\'; + out[e] = 'n'; + break; + case '\b': + out[e++] = '\\'; + out[e] = 'b'; + break; + case '\t': + out[e++] = '\\'; + out[e] = 't'; + break; + case '\f': + out[e++] = '\\'; + out[e] = 'f'; + break; + case '\r': + out[e++] = '\\'; + out[e] = 'r'; + break; + default: + out[e] = in[i]; + break; + } + + } + return e; +} + +struct jsontring *json_to_string(json_item *head, struct jsontring *string, int free_tree) +{ + if (string == NULL) { + string = xmalloc(sizeof(struct jsontring)); + + /* Ok, this can cost a lot (traversing the tree), but avoid realloc at each iteration */ + string->jsize = json_evaluate_string_size(head) * 2; /* TODO : Remove * 2, add some padding, realloc when necessary (or at least just x2 str val) */ + + string->jstring = xmalloc(sizeof(char) * (string->jsize + 1)); + string->len = 0; + } + + while (head != NULL) { + + if (head->key.val != NULL) { + string->jstring[string->len++] = '"'; + memcpy(string->jstring + string->len, head->key.val, head->key.len); + string->len += head->key.len; + string->jstring[string->len++] = '"'; + string->jstring[string->len++] = ':'; + + if (free_tree) { + free(head->key.val); + } + } + + if (head->jval.vu.str.value != NULL) { + + string->jstring[string->len++] = '"'; + string->len += escape_json_string(head->jval.vu.str.value, string->jstring + string->len, head->jval.vu.str.length); /* TODO : Add a "escape" argument to json_to_string */ + string->jstring[string->len++] = '"'; + + if (free_tree) { + free(head->jval.vu.str.value); + } + } else if (head->jval.vu.integer_value) { + + long int l = LENGTH_N(head->jval.vu.integer_value); + long int offset; + char integer_str[l+2]; + + offset = itos(head->jval.vu.integer_value, integer_str, l+2); + + memcpy(string->jstring + string->len, &integer_str[offset], ((l+2)-1)-offset); + + string->len += ((l+2)-1)-offset; + + } else if (head->jval.vu.float_value) { + int length; + + /* TODO: check for -1 */ + /* TODO: fix max length 16 together with json_evaluate_string_size() */ + length = snprintf(string->jstring + string->len, 16 + 1, "%Lf", head->jval.vu.float_value); + if(length > 16) /* cut-off number */ + length = 16; + + string->len += length; + } else if (head->type == JSON_T_TRUE) { + memcpy(string->jstring + string->len, "true", 4); + string->len += 4; + } else if (head->type == JSON_T_FALSE) { + memcpy(string->jstring + string->len, "false", 5); + string->len += 5; + } else if (head->type == JSON_T_NULL) { + memcpy(string->jstring + string->len, "null", 4); + string->len += 4; + } else if (head->jchild.child == NULL) { + memcpy(string->jstring + string->len, "0", 1); + string->len++; + } + + if (head->jchild.child != NULL) { + switch(head->jchild.type) { + case JSON_C_T_OBJ: + string->jstring[string->len++] = '{'; + break; + case JSON_C_T_ARR: + string->jstring[string->len++] = '['; + break; + default: + break; + } + json_to_string(head->jchild.child, string, free_tree); + + } + + if (head->father != NULL) { + if (head->next != NULL) { + string->jstring[string->len++] = ','; + } else { + switch(head->father->jchild.type) { + case JSON_C_T_OBJ: + string->jstring[string->len++] = '}'; + break; + case JSON_C_T_ARR: + string->jstring[string->len++] = ']'; + break; + default: + break; + } + } + } + if (free_tree) { + json_item *jtmp = head->next; + free(head); + head = jtmp; + } else { + head = head->next; + } + } + string->jstring[string->len] = '\0'; + + return string; +} + +static json_item *init_json_item() +{ + + json_item *jval = xmalloc(sizeof(*jval)); + + jval->father = NULL; + jval->jchild.child = NULL; + jval->jchild.head = NULL; + jval->jchild.type = JSON_C_T_NULL; + jval->next = NULL; + + jval->key.val = NULL; + jval->key.len = 0; + + jval->jval.vu.str.value = NULL; + jval->jval.vu.integer_value = 0; + jval->jval.vu.float_value = 0.; + + jval->type = -1; + + return jval; +} + +void free_json_item(json_item *cx) +{ + while (cx != NULL) { + json_item *tcx; + + if (cx->key.val != NULL) { + free(cx->key.val); + } + if (cx->jval.vu.str.value != NULL) { + free(cx->jval.vu.str.value); + } + if (cx->jchild.child != NULL) { + free_json_item(cx->jchild.child); + } + tcx = cx->next; + free(cx); + cx = tcx; + } +} + +json_item *json_item_copy(json_item *cx, json_item *father) +{ + json_item *new_item = NULL, *return_item = NULL; + json_item *temp_item = NULL; + + while (cx != NULL) { + new_item = init_json_item(); + new_item->father = father; + + if (return_item == NULL) { + return_item = new_item; + } + + new_item->type = cx->type; + new_item->jchild.type = cx->jchild.type; + + if (temp_item != NULL) { + temp_item->next = new_item; + } + + if (cx->key.val != NULL) { + new_item->key.len = cx->key.len; + new_item->key.val = xmalloc(sizeof(char) * (cx->key.len + 1)); + memcpy(new_item->key.val, cx->key.val, cx->key.len + 1); + } + + if (cx->jval.vu.str.value != NULL) { + new_item->jval.vu.str.length = cx->jval.vu.str.length; + new_item->jval.vu.str.value = xmalloc(sizeof(char) * (cx->jval.vu.str.length + 1)); + memcpy(new_item->jval.vu.str.value, cx->jval.vu.str.value, cx->jval.vu.str.length + 1); + } else if (cx->jval.vu.integer_value) { + new_item->jval.vu.integer_value = cx->jval.vu.integer_value; + } else if (cx->jval.vu.float_value) { + new_item->jval.vu.float_value = cx->jval.vu.float_value; + } + if (cx->jchild.child != NULL) { + new_item->jchild.child = json_item_copy(cx->jchild.child, new_item); + } + if (new_item->father != NULL) { + new_item->father->jchild.head = new_item; + } + temp_item = new_item; + + cx = cx->next; + } + + return return_item; +} + +json_item *json_new_object() +{ + json_item *obj = init_json_item(); + obj->jchild.type = JSON_C_T_OBJ; + + return obj; +} + +json_item *json_new_array() +{ + json_item *obj = init_json_item(); + obj->jchild.type = JSON_C_T_ARR; + + return obj; +} + +void json_set_property_objN(json_item *obj, const char *key, int keylen, json_item *value) +{ + json_item *new_item = value; + + if (key != NULL) { + new_item->key.val = xmalloc(sizeof(char) * (keylen + 1)); + memcpy(new_item->key.val, key, keylen + 1); + new_item->key.len = keylen; + } + + new_item->father = obj; + + if (obj->jchild.child == NULL) { + obj->jchild.child = new_item; + } else { + obj->jchild.head->next = new_item; + } + + obj->jchild.head = new_item; +} + +void json_set_property_objZ(json_item *obj, const char *key, json_item *value) +{ + json_set_property_objN(obj, key, strlen(key), value); +} + +void json_set_property_intN(json_item *obj, const char *key, int keylen, long int value) +{ + json_item *new_item = init_json_item(); + + if (key != NULL) { + new_item->key.val = xmalloc(sizeof(char) * (keylen + 1)); + memcpy(new_item->key.val, key, keylen + 1); + new_item->key.len = keylen; + } + new_item->father = obj; + new_item->jval.vu.integer_value = value; + new_item->type = JSON_T_INTEGER; + + if (obj->jchild.child == NULL) { + obj->jchild.child = new_item; + } else { + obj->jchild.head->next = new_item; + } + + obj->jchild.head = new_item; +} + +void json_set_property_intZ(json_item *obj, const char *key, long int value) +{ + int len = (key != NULL ? strlen(key) : 0); + + json_set_property_intN(obj, key, len, value); +} + +void json_set_property_floatN(json_item *obj, const char *key, int keylen, long double value) +{ + json_item *new_item = init_json_item(); + + if (key != NULL) { + new_item->key.val = xmalloc(sizeof(char) * (keylen + 1)); + memcpy(new_item->key.val, key, keylen + 1); + new_item->key.len = keylen; + } + new_item->father = obj; + new_item->jval.vu.float_value = value; + new_item->type = JSON_T_FLOAT; + + if (obj->jchild.child == NULL) { + obj->jchild.child = new_item; + } else { + obj->jchild.head->next = new_item; + } + + obj->jchild.head = new_item; +} + +void json_set_property_boolean(json_item *obj, const char *key, int keylen, int value) +{ + json_item *new_item = init_json_item(); + + if (key != NULL) { + new_item->key.val = xmalloc(sizeof(char) * (keylen + 1)); + memcpy(new_item->key.val, key, keylen + 1); + new_item->key.len = keylen; + } + new_item->father = obj; + if(value) + new_item->type = JSON_T_TRUE; + else + new_item->type = JSON_T_FALSE; + + if (obj->jchild.child == NULL) { + obj->jchild.child = new_item; + } else { + obj->jchild.head->next = new_item; + } + + obj->jchild.head = new_item; +} + +void json_set_property_null(json_item *obj, const char *key, int keylen) +{ + json_item *new_item = init_json_item(); + + if (key != NULL) { + new_item->key.val = xmalloc(sizeof(char) * (keylen + 1)); + memcpy(new_item->key.val, key, keylen + 1); + new_item->key.len = keylen; + } + new_item->father = obj; + new_item->type = JSON_T_NULL; + + if (obj->jchild.child == NULL) { + obj->jchild.child = new_item; + } else { + obj->jchild.head->next = new_item; + } + + obj->jchild.head = new_item; +} + + +void json_set_property_strN(json_item *obj, const char *key, int keylen, const char *value, int valuelen) +{ + + json_item *new_item = init_json_item(); + + if (key != NULL) { + new_item->key.val = xmalloc(sizeof(char) * (keylen + 1)); + memcpy(new_item->key.val, key, keylen + 1); + new_item->key.len = keylen; + } + new_item->jval.vu.str.value = xmalloc(sizeof(char) * (valuelen + 1)); + memcpy(new_item->jval.vu.str.value, value, valuelen + 1); + new_item->jval.vu.str.length = valuelen; + new_item->type = JSON_T_STRING; + + new_item->father = obj; + + if (obj->jchild.child == NULL) { + obj->jchild.child = new_item; + } else { + obj->jchild.head->next = new_item; + } + + obj->jchild.head = new_item; +} + +void json_set_property_strZ(json_item *obj, const char *key, const char *value) +{ + int len = (key != NULL ? strlen(key) : 0); + + json_set_property_strN(obj, key, len, value, strlen(value)); +} + +void json_set_element_strN(json_item *obj, const char *value, int valuelen) +{ + json_set_property_strN(obj, NULL, 0, value, valuelen); +} + +void json_set_element_strZ(json_item *obj, const char *value) +{ + json_set_property_strZ(obj, NULL, value); +} + +void json_set_element_obj(json_item *obj, json_item *value) +{ + json_set_property_objN(obj, NULL, 0, value); +} + +void json_set_element_int(json_item *obj, long int value) +{ + json_set_property_intN(obj, NULL, 0, value); +} + +void json_set_element_float(json_item *obj, long double value) +{ + json_set_property_floatN(obj, NULL, 0, value); +} + +void json_set_element_boolean(json_item *obj, int value) +{ + json_set_property_boolean(obj, NULL, 0, value); +} + +void json_set_element_null(json_item *obj) +{ + json_set_property_null(obj, NULL, 0); +} + +void json_merge(json_item *obj_out, json_item *obj_in) +{ + +} + + +static int json_callback(void *ctx, int type, const JSON_value* value) +{ + json_context *cx = (json_context *)ctx; + json_item *jval = NULL; + + switch(type) { + case JSON_T_OBJECT_BEGIN: + case JSON_T_ARRAY_BEGIN: + + if (!cx->key_under) { + jval = init_json_item(); + + if (cx->current_cx != NULL) { + if (cx->start_depth) { + cx->current_cx->jchild.child = jval; + jval->father = cx->current_cx; + } else { + jval->father = cx->current_cx->father; + cx->current_cx->next = jval; + } + } + cx->current_cx = jval; + } + + cx->current_cx->jchild.type = (type == JSON_T_OBJECT_BEGIN ? JSON_C_T_OBJ : JSON_C_T_ARR); + cx->start_depth = 1; + cx->key_under = 0; + break; + case JSON_T_OBJECT_END: + case JSON_T_ARRAY_END: + + /* If the father node exists, back to it */ + if (cx->current_cx->father != NULL && !cx->start_depth) { + cx->current_cx = cx->current_cx->father; + } + + cx->start_depth = 0; + cx->key_under = 0; + break; + + case JSON_T_KEY: + jval = init_json_item(); + + if (cx->start_depth) { + cx->current_cx->jchild.child = jval; + jval->father = cx->current_cx; + cx->start_depth = 0; + } else { + jval->father = cx->current_cx->father; + cx->current_cx->next = jval; + } + + cx->current_cx = jval; + cx->key_under = 1; + + cx->current_cx->key.val = xmalloc(sizeof(char) * (value->vu.str.length+1)); + memcpy(cx->current_cx->key.val, value->vu.str.value, value->vu.str.length+1); + + cx->current_cx->key.len = value->vu.str.length; + + break; + + case JSON_T_INTEGER: + case JSON_T_FLOAT: + case JSON_T_NULL: + case JSON_T_TRUE: + case JSON_T_FALSE: + case JSON_T_STRING: + + if (!cx->key_under) { + + jval = init_json_item(); + + if (cx->start_depth) { + cx->current_cx->jchild.child = jval; + jval->father = cx->current_cx; + cx->start_depth = 0; + } else { + jval->father = cx->current_cx->father; + cx->current_cx->next = jval; + } + + cx->current_cx = jval; + } + cx->key_under = 0; + + switch(type) { + case JSON_T_INTEGER: + cx->current_cx->jval.vu.integer_value = value->vu.integer_value; + break; + case JSON_T_FLOAT: + cx->current_cx->jval.vu.float_value = value->vu.float_value; + break; + case JSON_T_NULL: + case JSON_T_FALSE: + cx->current_cx->jval.vu.integer_value = 0; + break; + case JSON_T_TRUE: + cx->current_cx->jval.vu.integer_value = 1; + break; + case JSON_T_STRING: + cx->current_cx->jval.vu.str.value = xmalloc(sizeof(char) * (value->vu.str.length+1)); + memcpy(cx->current_cx->jval.vu.str.value, value->vu.str.value, value->vu.str.length+1); + cx->current_cx->jval.vu.str.length = value->vu.str.length; + break; + } + cx->current_cx->type = type; + break; + default: + + break; + } + + if (cx->head == NULL && cx->current_cx != NULL) { + cx->head = cx->current_cx; + } + + return 1; +} + +json_item *init_json_parser(const char *json_string) +{ + const char *pRaw; + JSON_config config; + + struct JSON_parser_struct* jc = NULL; + + json_context jcx = {0, 0, NULL, NULL}; + + init_JSON_config(&config); + + config.depth = 15; + config.callback = &json_callback; + config.callback_ctx = &jcx; + + config.allow_comments = 0; + config.handle_floats_manually = 0; + + jc = new_JSON_parser(&config); + + for (pRaw = json_string; *pRaw; pRaw++) { + if (!JSON_parser_char(jc, *pRaw)) { + free_json_item(jcx.head); + delete_JSON_parser(jc); + return NULL; + } + } + + if (!JSON_parser_done(jc)) { + free_json_item(jcx.head); + delete_JSON_parser(jc); + return NULL; + } + + delete_JSON_parser(jc); + + return jcx.head; +} + +void json_aff(json_item *cx, int depth) +{ + while (cx != NULL) { + if (cx->key.val != NULL) { + printf("Key %s\n", cx->key.val); + } + if (cx->jval.vu.str.value != NULL) { + printf("Value : %s\n", cx->jval.vu.str.value); + } + if (depth && cx->jchild.child != NULL) { + json_aff(cx->jchild.child, depth - 1); + } + cx = cx->next; + } +} + +/* "val[32]" return 32, "val" return -1 */ +#if 0 +static int key_is_array(char *key, int i) +{ + int ret = 0, f = 1; + + if (i < 4 || key[i--] != ']' || *key == '[') { + return -1; + } + + while (i != 0 && key[i] != '[') { + if (f == 10000) { + return -1; + } + if (key[i] < 48 || key[i] > 57) { + return -1; + } + + ret += (key[i] - 48) * f; + f *= 10; + i--; + } + + return ret; +} +#endif + +json_item *json_lookup(json_item *head, char *path) +{ + char *split[16]; + char *base; + size_t nTok; + int i = 0; + + if (head == NULL || path == NULL) { + return NULL; + } + + base = xstrdup(path); + + nTok = explode('.', base, split, 15); + + while (head != NULL && i <= nTok) { + + if (head->key.val != NULL && strcasecmp(split[i], head->key.val) == 0) { + /* + printf("Array %s : %i\n", split[i], key_is_array(split[i], strlen(split[i])-1)); + printf("Comparing : %s with %s\n", head->key.val, split[i]); + printf("Find !\n"); + */ + if (i == nTok) { + free(base); + return (head->jchild.child != NULL ? head->jchild.child : head); + } + i++; + head = head->jchild.child; + continue; + } + + head = head->next; + } + free(base); + return NULL; +} + diff --git a/ape-server/src/json.h b/ape-server/src/json.h new file mode 100755 index 0000000..f7aa11f --- /dev/null +++ b/ape-server/src/json.h @@ -0,0 +1,173 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* json.h */ + +#ifndef _JSON_H +#define _JSON_H + + +#include "json_parser.h" + + +typedef char* jpath; + +enum { + JSON_ARRAY = 0, + JSON_OBJECT +}; + +typedef struct json { + struct { + char *buf; + size_t len; + } name; + + struct { + char *buf; + size_t len; + } value; + + struct json_childs *jchilds; + + struct json *jfather; + + struct json *next; + struct json *prev; +} json; + +struct json_childs { + struct json *child; + struct json_childs *next; + unsigned int type; +}; + +struct jsontring { + char *jstring; + size_t jsize; + size_t len; +}; + +typedef enum { + JSON_C_T_OBJ, + JSON_C_T_ARR, + JSON_C_T_VAL, + JSON_C_T_NULL, +} json_child_t; + +typedef struct _json_item { + struct JSON_value_struct jval; + struct { + struct _json_item *child; + struct _json_item *head; + json_child_t type; + } jchild; + + struct { + char *val; + size_t len; + } key; + + struct _json_item *father; + struct _json_item *next; + + int type; + +} json_item; + + +typedef struct _json_context { + int key_under; + int start_depth; + + json_item *head; + json_item *current_cx; + +} json_context; + + +void set_json(const char *name, const char *value, struct json **jprev); +struct json *json_copy(struct json *jbase); +void json_attach(struct json *json_father, struct json *json_child, unsigned int type); +void json_concat(struct json *json_father, struct json *json_child); +void json_free(struct json *jbase); +json_item *init_json_parser(const char *json_string); +json_item *json_lookup(json_item *head, char *path); +void free_json_item(json_item *cx); + +json_item *json_new_object(); +json_item *json_new_array(); + +void json_set_property_objN(json_item *obj, const char *key, int keylen, json_item *value); +void json_set_property_objZ(json_item *obj, const char *key, json_item *value); + +void json_set_property_strN(json_item *obj, const char *key, int keylen, const char *value, int valuelen); +void json_set_property_strZ(json_item *obj, const char *key, const char *value); + +void json_set_element_strN(json_item *obj, const char *value, int valuelen); +void json_set_element_strZ(json_item *obj, const char *value); +void json_set_element_int(json_item *obj, long int value); +void json_set_element_float(json_item *obj, long double value); +void json_set_element_boolean(json_item *obj, int value); +void json_set_element_null(json_item *obj); +void json_set_element_obj(json_item *obj, json_item *value); + +void json_set_property_intN(json_item *obj, const char *key, int keylen, long int value); +void json_set_property_intZ(json_item *obj, const char *key, long int value); +void json_set_property_floatN(json_item *obj, const char *key, int keylen, long double value); +void json_set_property_boolean(json_item *obj, const char *key, int keylen, int value); +void json_set_property_null(json_item *obj, const char *key, int keylen); +struct jsontring *json_to_string(json_item *head, struct jsontring *string, int free_tree); +json_item *json_item_copy(json_item *cx, json_item *father); + +void json_aff(json_item *cx, int depth); + +#define APE_PARAMS_INIT() \ + int json_iterator; \ + json_iterator = 0; \ + json_item *json_params = NULL + + +/* Iterate over a JSON array */ +#define JFOREACH(fkey, outvar) \ + for (json_params = json_lookup(callbacki->param, #fkey), json_iterator = 0; json_params != NULL; json_params = json_params->next) \ + if ((outvar = (char *)json_params->jval.vu.str.value) != NULL && ++json_iterator) + +/* Iterate over a JSON Object */ +#define JFOREACH_K(fkey, outkey, outvar) \ + for (json_params = json_lookup(callbacki->param, #fkey), json_iterator = 0; json_params != NULL; json_params = json_params->next) \ + if ((outvar = (char *)json_params->jval.vu.str.value) != NULL && (outkey = (char *)json_params->key.val) != NULL && ++json_iterator) + +#define JFOREACH_ELSE \ + if (json_iterator == 0) + +#define JSTR(key) \ + (char *)(callbacki->param != NULL && (json_params = json_lookup(callbacki->param, #key)) != NULL ? json_params->jval.vu.str.value : NULL) + +#define JINT(key) \ + (int)(callbacki->param != NULL && (json_params = json_lookup(callbacki->param, #key)) != NULL ? json_params->jval.vu.integer_value : 0) + +#define JFLOAT(key) \ + (callbacki->param != NULL && (json_params = json_lookup(callbacki->param, #key)) != NULL ? json_params->jval.vu.float_value : 0.) + +#define JGET_STR(head, key) \ + json_lookup(head, #key)->jval.vu.str.value + +#endif + diff --git a/ape-server/src/json_parser.c b/ape-server/src/json_parser.c new file mode 100755 index 0000000..2832b87 --- /dev/null +++ b/ape-server/src/json_parser.c @@ -0,0 +1,1015 @@ +/* json_parser.c */ + +/* 2007-08-24 */ + +/* +Copyright (c) 2005 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/* + Callbacks, comments, Unicode handling by Jean Gressmann (jean@0x42.de), 2007-2009. + + For the added features the license above applies also. + + Changelog: + 2009-05-17 + Incorporated benrudiak@googlemail.com fix for UTF16 decoding. + + 2009-05-14 + Fixed float parsing bug related to a locale being set that didn't + use '.' as decimal point character (charles@transmissionbt.com). + + 2008-10-14 + Renamed states.IN to states.IT to avoid name clash which IN macro + defined in windef.h (alexey.pelykh@gmail.com) + + 2008-07-19 + Removed some duplicate code & debugging variable (charles@transmissionbt.com) + + 2008-05-28 + Made JSON_value structure ansi C compliant. This bug was report by + trisk@acm.jhu.edu + + 2008-05-20 + Fixed bug reported by charles@transmissionbt.com where the switching + from static to dynamic parse buffer did not copy the static parse + buffer's content. +*/ + + + +#define _ISOC99_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +#include "json_parser.h" + +#ifdef _MSC_VER +# if _MSC_VER >= 1400 /* Visual Studio 2005 and up */ +# pragma warning(disable:4996) // unsecure sscanf +# endif +#endif + + +#define true 1 +#define false 0 +#define __ -1 /* the universal error code */ + +/* values chosen so that the object size is approx equal to one page (4K) */ +#ifndef JSON_PARSER_STACK_SIZE +# define JSON_PARSER_STACK_SIZE 128 +#endif + +#ifndef JSON_PARSER_PARSE_BUFFER_SIZE +# define JSON_PARSER_PARSE_BUFFER_SIZE 3500 +#endif + + +typedef unsigned short UTF16; + +struct JSON_parser_struct { + JSON_parser_callback callback; + void* ctx; + signed char state, before_comment_state, type, escaped, comment, allow_comments, handle_floats_manually; + UTF16 utf16_high_surrogate; + long depth; + long top; + signed char* stack; + long stack_capacity; + char decimal_point; + char* parse_buffer; + size_t parse_buffer_capacity; + size_t parse_buffer_count; + size_t comment_begin_offset; + signed char static_stack[JSON_PARSER_STACK_SIZE]; + char static_parse_buffer[JSON_PARSER_PARSE_BUFFER_SIZE]; +}; + +#define COUNTOF(x) (sizeof(x)/sizeof(x[0])) + +/* + Characters are mapped into these character classes. This allows for + a significant reduction in the size of the state transition table. +*/ + + + +enum classes { + C_SPACE, /* space */ + C_WHITE, /* other whitespace */ + C_LCURB, /* { */ + C_RCURB, /* } */ + C_LSQRB, /* [ */ + C_RSQRB, /* ] */ + C_COLON, /* : */ + C_COMMA, /* , */ + C_QUOTE, /* " */ + C_BACKS, /* \ */ + C_SLASH, /* / */ + C_PLUS, /* + */ + C_MINUS, /* - */ + C_POINT, /* . */ + C_ZERO , /* 0 */ + C_DIGIT, /* 123456789 */ + C_LOW_A, /* a */ + C_LOW_B, /* b */ + C_LOW_C, /* c */ + C_LOW_D, /* d */ + C_LOW_E, /* e */ + C_LOW_F, /* f */ + C_LOW_L, /* l */ + C_LOW_N, /* n */ + C_LOW_R, /* r */ + C_LOW_S, /* s */ + C_LOW_T, /* t */ + C_LOW_U, /* u */ + C_ABCDF, /* ABCDF */ + C_E, /* E */ + C_ETC, /* everything else */ + C_STAR, /* * */ + NR_CLASSES +}; + +static int ascii_class[128] = { +/* + This array maps the 128 ASCII characters into character classes. + The remaining Unicode characters should be mapped to C_ETC. + Non-whitespace control characters are errors. +*/ + __, __, __, __, __, __, __, __, + __, C_WHITE, C_WHITE, __, __, C_WHITE, __, __, + __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, + + C_SPACE, C_ETC, C_QUOTE, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, + C_ETC, C_ETC, C_STAR, C_PLUS, C_COMMA, C_MINUS, C_POINT, C_SLASH, + C_ZERO, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, + C_DIGIT, C_DIGIT, C_COLON, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, + + C_ETC, C_ABCDF, C_ABCDF, C_ABCDF, C_ABCDF, C_E, C_ABCDF, C_ETC, + C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, + C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, + C_ETC, C_ETC, C_ETC, C_LSQRB, C_BACKS, C_RSQRB, C_ETC, C_ETC, + + C_ETC, C_LOW_A, C_LOW_B, C_LOW_C, C_LOW_D, C_LOW_E, C_LOW_F, C_ETC, + C_ETC, C_ETC, C_ETC, C_ETC, C_LOW_L, C_ETC, C_LOW_N, C_ETC, + C_ETC, C_ETC, C_LOW_R, C_LOW_S, C_LOW_T, C_LOW_U, C_ETC, C_ETC, + C_ETC, C_ETC, C_ETC, C_LCURB, C_ETC, C_RCURB, C_ETC, C_ETC +}; + + +/* + The state codes. +*/ +enum states { + GO, /* start */ + OK, /* ok */ + OB, /* object */ + KE, /* key */ + CO, /* colon */ + VA, /* value */ + AR, /* array */ + ST, /* string */ + ES, /* escape */ + U1, /* u1 */ + U2, /* u2 */ + U3, /* u3 */ + U4, /* u4 */ + MI, /* minus */ + ZE, /* zero */ + IT, /* integer */ + FR, /* fraction */ + E1, /* e */ + E2, /* ex */ + E3, /* exp */ + T1, /* tr */ + T2, /* tru */ + T3, /* true */ + F1, /* fa */ + F2, /* fal */ + F3, /* fals */ + F4, /* false */ + N1, /* nu */ + N2, /* nul */ + N3, /* null */ + C1, /* / */ + C2, /* / * */ + C3, /* * */ + FX, /* *.* *eE* */ + D1, /* second UTF-16 character decoding started by \ */ + D2, /* second UTF-16 character proceeded by u */ + NR_STATES +}; + +enum actions +{ + CB = -10, /* comment begin */ + CE = -11, /* comment end */ + FA = -12, /* false */ + TR = -13, /* false */ + NU = -14, /* null */ + DE = -15, /* double detected by exponent e E */ + DF = -16, /* double detected by fraction . */ + SB = -17, /* string begin */ + MX = -18, /* integer detected by minus */ + ZX = -19, /* integer detected by zero */ + IX = -20, /* integer detected by 1-9 */ + EX = -21, /* next char is escaped */ + UC = -22 /* Unicode character read */ +}; + + +static int state_transition_table[NR_STATES][NR_CLASSES] = { +/* + The state transition table takes the current state and the current symbol, + and returns either a new state or an action. An action is represented as a + negative number. A JSON text is accepted if at the end of the text the + state is OK and if the mode is MODE_DONE. + + white 1-9 ABCDF etc + space | { } [ ] : , " \ / + - . 0 | a b c d e f l n r s t u | E | * */ +/*start GO*/ {GO,GO,-6,__,-5,__,__,__,__,__,CB,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, +/*ok OK*/ {OK,OK,__,-8,__,-7,__,-3,__,__,CB,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, +/*object OB*/ {OB,OB,__,-9,__,__,__,__,SB,__,CB,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, +/*key KE*/ {KE,KE,__,__,__,__,__,__,SB,__,CB,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, +/*colon CO*/ {CO,CO,__,__,__,__,-2,__,__,__,CB,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, +/*value VA*/ {VA,VA,-6,__,-5,__,__,__,SB,__,CB,__,MX,__,ZX,IX,__,__,__,__,__,FA,__,NU,__,__,TR,__,__,__,__,__}, +/*array AR*/ {AR,AR,-6,__,-5,-7,__,__,SB,__,CB,__,MX,__,ZX,IX,__,__,__,__,__,FA,__,NU,__,__,TR,__,__,__,__,__}, +/*string ST*/ {ST,__,ST,ST,ST,ST,ST,ST,-4,EX,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST}, +/*escape ES*/ {__,__,__,__,__,__,__,__,ST,ST,ST,__,__,__,__,__,__,ST,__,__,__,ST,__,ST,ST,__,ST,U1,__,__,__,__}, +/*u1 U1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U2,U2,U2,U2,U2,U2,U2,U2,__,__,__,__,__,__,U2,U2,__,__}, +/*u2 U2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U3,U3,U3,U3,U3,U3,U3,U3,__,__,__,__,__,__,U3,U3,__,__}, +/*u3 U3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U4,U4,U4,U4,U4,U4,U4,U4,__,__,__,__,__,__,U4,U4,__,__}, +/*u4 U4*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,UC,UC,UC,UC,UC,UC,UC,UC,__,__,__,__,__,__,UC,UC,__,__}, +/*minus MI*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,ZE,IT,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, +/*zero ZE*/ {OK,OK,__,-8,__,-7,__,-3,__,__,CB,__,__,DF,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, +/*int IT*/ {OK,OK,__,-8,__,-7,__,-3,__,__,CB,__,__,DF,IT,IT,__,__,__,__,DE,__,__,__,__,__,__,__,__,DE,__,__}, +/*frac FR*/ {OK,OK,__,-8,__,-7,__,-3,__,__,CB,__,__,__,FR,FR,__,__,__,__,E1,__,__,__,__,__,__,__,__,E1,__,__}, +/*e E1*/ {__,__,__,__,__,__,__,__,__,__,__,E2,E2,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, +/*ex E2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, +/*exp E3*/ {OK,OK,__,-8,__,-7,__,-3,__,__,__,__,__,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, +/*tr T1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,T2,__,__,__,__,__,__,__}, +/*tru T2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,T3,__,__,__,__}, +/*true T3*/ {__,__,__,__,__,__,__,__,__,__,CB,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__,__,__,__}, +/*fa F1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F2,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, +/*fal F2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F3,__,__,__,__,__,__,__,__,__}, +/*fals F3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F4,__,__,__,__,__,__}, +/*false F4*/ {__,__,__,__,__,__,__,__,__,__,CB,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__,__,__,__}, +/*nu N1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,N2,__,__,__,__}, +/*nul N2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,N3,__,__,__,__,__,__,__,__,__}, +/*null N3*/ {__,__,__,__,__,__,__,__,__,__,CB,__,__,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__,__}, +/*/ C1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,C2}, +/*/* C2*/ {C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C3}, +/** C3*/ {C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,CE,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C3}, +/*_. FX*/ {OK,OK,__,-8,__,-7,__,-3,__,__,__,__,__,__,FR,FR,__,__,__,__,E1,__,__,__,__,__,__,__,__,E1,__,__}, +/*\ D1*/ {__,__,__,__,__,__,__,__,__,D2,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, +/*\ D2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,U1,__,__,__,__}, +}; + + +/* + These modes can be pushed on the stack. +*/ +enum modes { + MODE_ARRAY = 1, + MODE_DONE = 2, + MODE_KEY = 3, + MODE_OBJECT = 4 +}; + +static int +push(JSON_parser jc, int mode) +{ +/* + Push a mode onto the stack. Return false if there is overflow. +*/ + jc->top += 1; + if (jc->depth < 0) { + if (jc->top >= jc->stack_capacity) { + size_t bytes_to_allocate; + jc->stack_capacity *= 2; + bytes_to_allocate = jc->stack_capacity * sizeof(jc->static_stack[0]); + if (jc->stack == &jc->static_stack[0]) { + jc->stack = (signed char*)malloc(bytes_to_allocate); + memcpy(jc->stack, jc->static_stack, sizeof(jc->static_stack)); + } else { + jc->stack = (signed char*)realloc(jc->stack, bytes_to_allocate); + } + } + } else { + if (jc->top >= jc->depth) { + return false; + } + } + + jc->stack[jc->top] = mode; + return true; +} + + +static int +pop(JSON_parser jc, int mode) +{ +/* + Pop the stack, assuring that the current mode matches the expectation. + Return false if there is underflow or if the modes mismatch. +*/ + if (jc->top < 0 || jc->stack[jc->top] != mode) { + return false; + } + jc->top -= 1; + return true; +} + + +#define parse_buffer_clear(jc) \ + do {\ + jc->parse_buffer_count = 0;\ + jc->parse_buffer[0] = 0;\ + } while (0) + +#define parse_buffer_pop_back_char(jc)\ + do {\ + assert(jc->parse_buffer_count >= 1);\ + --jc->parse_buffer_count;\ + jc->parse_buffer[jc->parse_buffer_count] = 0;\ + } while (0) + +void delete_JSON_parser(JSON_parser jc) +{ + if (jc) { + if (jc->stack != &jc->static_stack[0]) { + free((void*)jc->stack); + } + if (jc->parse_buffer != &jc->static_parse_buffer[0]) { + free((void*)jc->parse_buffer); + } + free((void*)jc); + } +} + + +JSON_parser +new_JSON_parser(JSON_config* config) +{ +/* + new_JSON_parser starts the checking process by constructing a JSON_parser + object. It takes a depth parameter that restricts the level of maximum + nesting. + + To continue the process, call JSON_parser_char for each character in the + JSON text, and then call JSON_parser_done to obtain the final result. + These functions are fully reentrant. +*/ + + int depth = 0; + JSON_config default_config; + + JSON_parser jc = malloc(sizeof(struct JSON_parser_struct)); + + memset(jc, 0, sizeof(*jc)); + + + /* initialize configuration */ + init_JSON_config(&default_config); + + /* set to default configuration if none was provided */ + if (config == NULL) { + config = &default_config; + } + + depth = config->depth; + + /* We need to be able to push at least one object */ + if (depth == 0) { + depth = 1; + } + + jc->state = GO; + jc->top = -1; + + /* Do we want non-bound stack? */ + if (depth > 0) { + jc->stack_capacity = depth; + jc->depth = depth; + if (depth <= (int)COUNTOF(jc->static_stack)) { + jc->stack = &jc->static_stack[0]; + } else { + jc->stack = (signed char*)malloc(jc->stack_capacity * sizeof(jc->static_stack[0])); + } + } else { + jc->stack_capacity = COUNTOF(jc->static_stack); + jc->depth = -1; + jc->stack = &jc->static_stack[0]; + } + + /* set parser to start */ + push(jc, MODE_DONE); + + /* set up the parse buffer */ + jc->parse_buffer = &jc->static_parse_buffer[0]; + jc->parse_buffer_capacity = COUNTOF(jc->static_parse_buffer); + parse_buffer_clear(jc); + + /* set up callback, comment & float handling */ + jc->callback = config->callback; + jc->ctx = config->callback_ctx; + jc->allow_comments = config->allow_comments != 0; + jc->handle_floats_manually = config->handle_floats_manually != 0; + + /* set up decimal point */ + jc->decimal_point = *localeconv()->decimal_point; + + return jc; +} + +static void grow_parse_buffer(JSON_parser jc) +{ + size_t bytes_to_allocate; + jc->parse_buffer_capacity *= 2; + bytes_to_allocate = jc->parse_buffer_capacity * sizeof(jc->parse_buffer[0]); + if (jc->parse_buffer == &jc->static_parse_buffer[0]) { + jc->parse_buffer = (char*)malloc(bytes_to_allocate); + memcpy(jc->parse_buffer, jc->static_parse_buffer, jc->parse_buffer_count); + } else { + jc->parse_buffer = (char*)realloc(jc->parse_buffer, bytes_to_allocate); + } +} + +#define parse_buffer_push_back_char(jc, c)\ + do {\ + if (jc->parse_buffer_count + 1 >= jc->parse_buffer_capacity) grow_parse_buffer(jc);\ + jc->parse_buffer[jc->parse_buffer_count++] = c;\ + jc->parse_buffer[jc->parse_buffer_count] = 0;\ + } while (0) + +#define assert_is_non_container_type(jc) \ + assert( \ + jc->type == JSON_T_NULL || \ + jc->type == JSON_T_FALSE || \ + jc->type == JSON_T_TRUE || \ + jc->type == JSON_T_FLOAT || \ + jc->type == JSON_T_INTEGER || \ + jc->type == JSON_T_STRING) + + +static int parse_parse_buffer(JSON_parser jc) +{ + if (jc->callback) { + JSON_value value, *arg = NULL; + + if (jc->type != JSON_T_NONE) { + assert_is_non_container_type(jc); + + switch(jc->type) { + case JSON_T_FLOAT: + arg = &value; + if (jc->handle_floats_manually) { + value.vu.str.value = jc->parse_buffer; + value.vu.str.length = jc->parse_buffer_count; + } else { + /*sscanf(jc->parse_buffer, "%Lf", &value.vu.float_value);*/ + + /* not checking with end pointer b/c there may be trailing ws */ + value.vu.float_value = strtold(jc->parse_buffer, NULL); + } + break; + case JSON_T_INTEGER: + arg = &value; + sscanf(jc->parse_buffer, JSON_PARSER_INTEGER_SSCANF_TOKEN, &value.vu.integer_value); + break; + case JSON_T_STRING: + arg = &value; + value.vu.str.value = jc->parse_buffer; + value.vu.str.length = jc->parse_buffer_count; + break; + } + + if (!(*jc->callback)(jc->ctx, jc->type, arg)) { + return false; + } + } + } + + parse_buffer_clear(jc); + + return true; +} + +#define IS_HIGH_SURROGATE(uc) (((uc) & 0xFC00) == 0xD800) +#define IS_LOW_SURROGATE(uc) (((uc) & 0xFC00) == 0xDC00) +#define DECODE_SURROGATE_PAIR(hi,lo) ((((hi) & 0x3FF) << 10) + ((lo) & 0x3FF) + 0x10000) +static unsigned char utf8_lead_bits[4] = { 0x00, 0xC0, 0xE0, 0xF0 }; + +static int decode_unicode_char(JSON_parser jc) +{ + int i; + unsigned uc = 0; + char* p; + int trail_bytes; + + assert(jc->parse_buffer_count >= 6); + + p = &jc->parse_buffer[jc->parse_buffer_count - 4]; + + for (i = 12; i >= 0; i -= 4, ++p) { + unsigned x = *p; + + if (x >= 'a') { + x -= ('a' - 10); + } else if (x >= 'A') { + x -= ('A' - 10); + } else { + x &= ~0x30u; + } + + assert(x < 16); + + uc |= x << i; + } + + /* clear UTF-16 char from buffer */ + jc->parse_buffer_count -= 6; + jc->parse_buffer[jc->parse_buffer_count] = 0; + + /* attempt decoding ... */ + if (jc->utf16_high_surrogate) { + if (IS_LOW_SURROGATE(uc)) { + uc = DECODE_SURROGATE_PAIR(jc->utf16_high_surrogate, uc); + trail_bytes = 3; + jc->utf16_high_surrogate = 0; + } else { + /* high surrogate without a following low surrogate */ + return false; + } + } else { + if (uc < 0x80) { + trail_bytes = 0; + } else if (uc < 0x800) { + trail_bytes = 1; + } else if (IS_HIGH_SURROGATE(uc)) { + /* save the high surrogate and wait for the low surrogate */ + jc->utf16_high_surrogate = uc; + return true; + } else if (IS_LOW_SURROGATE(uc)) { + /* low surrogate without a preceding high surrogate */ + return false; + } else { + trail_bytes = 2; + } + } + + jc->parse_buffer[jc->parse_buffer_count++] = (char) ((uc >> (trail_bytes * 6)) | utf8_lead_bits[trail_bytes]); + + for (i = trail_bytes * 6 - 6; i >= 0; i -= 6) { + jc->parse_buffer[jc->parse_buffer_count++] = (char) (((uc >> i) & 0x3F) | 0x80); + } + + jc->parse_buffer[jc->parse_buffer_count] = 0; + + return true; +} + +static int add_escaped_char_to_parse_buffer(JSON_parser jc, int next_char) +{ + jc->escaped = 0; + /* remove the backslash */ + parse_buffer_pop_back_char(jc); + switch(next_char) { + case 'b': + parse_buffer_push_back_char(jc, '\b'); + break; + case 'f': + parse_buffer_push_back_char(jc, '\f'); + break; + case 'n': + parse_buffer_push_back_char(jc, '\n'); + break; + case 'r': + parse_buffer_push_back_char(jc, '\r'); + break; + case 't': + parse_buffer_push_back_char(jc, '\t'); + break; + case '"': + parse_buffer_push_back_char(jc, '"'); + break; + case '\\': + parse_buffer_push_back_char(jc, '\\'); + break; + case '/': + parse_buffer_push_back_char(jc, '/'); + break; + case 'u': + parse_buffer_push_back_char(jc, '\\'); + parse_buffer_push_back_char(jc, 'u'); + break; + default: + return false; + } + + return true; +} + +#define add_char_to_parse_buffer(jc, next_char, next_class) \ + do { \ + if (jc->escaped) { \ + if (!add_escaped_char_to_parse_buffer(jc, next_char)) \ + return false; \ + } else if (!jc->comment) { \ + if ((jc->type != JSON_T_NONE) | !((next_class == C_SPACE) | (next_class == C_WHITE)) /* non-white-space */) { \ + parse_buffer_push_back_char(jc, (char)next_char); \ + } \ + } \ + } while (0) + + +#define assert_type_isnt_string_null_or_bool(jc) \ + assert(jc->type != JSON_T_FALSE); \ + assert(jc->type != JSON_T_TRUE); \ + assert(jc->type != JSON_T_NULL); \ + assert(jc->type != JSON_T_STRING) + + +int +JSON_parser_char(JSON_parser jc, int next_char) +{ +/* + After calling new_JSON_parser, call this function for each character (or + partial character) in your JSON text. It can accept UTF-8, UTF-16, or + UTF-32. It returns true if things are looking ok so far. If it rejects the + text, it returns false. +*/ + int next_class, next_state; + +/* + Determine the character's class. +*/ + if (next_char < 0) { + return false; + } + if (next_char >= 128) { + next_class = C_ETC; + } else { + next_class = ascii_class[next_char]; + if (next_class <= __) { + return false; + } + } + + add_char_to_parse_buffer(jc, next_char, next_class); + +/* + Get the next state from the state transition table. +*/ + next_state = state_transition_table[jc->state][next_class]; + if (next_state >= 0) { +/* + Change the state. +*/ + jc->state = next_state; + } else { +/* + Or perform one of the actions. +*/ + switch (next_state) { +/* Unicode character */ + case UC: + if(!decode_unicode_char(jc)) { + return false; + } + /* check if we need to read a second UTF-16 char */ + if (jc->utf16_high_surrogate) { + jc->state = D1; + } else { + jc->state = ST; + } + break; +/* escaped char */ + case EX: + jc->escaped = 1; + jc->state = ES; + break; +/* integer detected by minus */ + case MX: + jc->type = JSON_T_INTEGER; + jc->state = MI; + break; +/* integer detected by zero */ + case ZX: + jc->type = JSON_T_INTEGER; + jc->state = ZE; + break; +/* integer detected by 1-9 */ + case IX: + jc->type = JSON_T_INTEGER; + jc->state = IT; + break; + +/* floating point number detected by exponent*/ + case DE: + assert_type_isnt_string_null_or_bool(jc); + jc->type = JSON_T_FLOAT; + jc->state = E1; + break; + +/* floating point number detected by fraction */ + case DF: + assert_type_isnt_string_null_or_bool(jc); + if (!jc->handle_floats_manually) { +/* + Some versions of strtod (which underlies sscanf) don't support converting + C-locale formated floating point values. +*/ + assert(jc->parse_buffer[jc->parse_buffer_count-1] == '.'); + jc->parse_buffer[jc->parse_buffer_count-1] = jc->decimal_point; + } + jc->type = JSON_T_FLOAT; + jc->state = FX; + break; +/* string begin " */ + case SB: + parse_buffer_clear(jc); + assert(jc->type == JSON_T_NONE); + jc->type = JSON_T_STRING; + jc->state = ST; + break; + +/* n */ + case NU: + assert(jc->type == JSON_T_NONE); + jc->type = JSON_T_NULL; + jc->state = N1; + break; +/* f */ + case FA: + assert(jc->type == JSON_T_NONE); + jc->type = JSON_T_FALSE; + jc->state = F1; + break; +/* t */ + case TR: + assert(jc->type == JSON_T_NONE); + jc->type = JSON_T_TRUE; + jc->state = T1; + break; + +/* closing comment */ + case CE: + jc->comment = 0; + assert(jc->parse_buffer_count == 0); + assert(jc->type == JSON_T_NONE); + jc->state = jc->before_comment_state; + break; + +/* opening comment */ + case CB: + if (!jc->allow_comments) { + return false; + } + parse_buffer_pop_back_char(jc); + if (!parse_parse_buffer(jc)) { + return false; + } + assert(jc->parse_buffer_count == 0); + assert(jc->type != JSON_T_STRING); + switch (jc->stack[jc->top]) { + case MODE_ARRAY: + case MODE_OBJECT: + switch(jc->state) { + case VA: + case AR: + jc->before_comment_state = jc->state; + break; + default: + jc->before_comment_state = OK; + break; + } + break; + default: + jc->before_comment_state = jc->state; + break; + } + jc->type = JSON_T_NONE; + jc->state = C1; + jc->comment = 1; + break; +/* empty } */ + case -9: + parse_buffer_clear(jc); + if (jc->callback && !(*jc->callback)(jc->ctx, JSON_T_OBJECT_END, NULL)) { + return false; + } + if (!pop(jc, MODE_KEY)) { + return false; + } + jc->state = OK; + break; + +/* } */ case -8: + parse_buffer_pop_back_char(jc); + if (!parse_parse_buffer(jc)) { + return false; + } + if (jc->callback && !(*jc->callback)(jc->ctx, JSON_T_OBJECT_END, NULL)) { + return false; + } + if (!pop(jc, MODE_OBJECT)) { + return false; + } + jc->type = JSON_T_NONE; + jc->state = OK; + break; + +/* ] */ case -7: + parse_buffer_pop_back_char(jc); + if (!parse_parse_buffer(jc)) { + return false; + } + if (jc->callback && !(*jc->callback)(jc->ctx, JSON_T_ARRAY_END, NULL)) { + return false; + } + if (!pop(jc, MODE_ARRAY)) { + return false; + } + + jc->type = JSON_T_NONE; + jc->state = OK; + break; + +/* { */ case -6: + parse_buffer_pop_back_char(jc); + if (jc->callback && !(*jc->callback)(jc->ctx, JSON_T_OBJECT_BEGIN, NULL)) { + return false; + } + if (!push(jc, MODE_KEY)) { + return false; + } + assert(jc->type == JSON_T_NONE); + jc->state = OB; + break; + +/* [ */ case -5: + parse_buffer_pop_back_char(jc); + if (jc->callback && !(*jc->callback)(jc->ctx, JSON_T_ARRAY_BEGIN, NULL)) { + return false; + } + if (!push(jc, MODE_ARRAY)) { + return false; + } + assert(jc->type == JSON_T_NONE); + jc->state = AR; + break; + +/* string end " */ case -4: + parse_buffer_pop_back_char(jc); + switch (jc->stack[jc->top]) { + case MODE_KEY: + assert(jc->type == JSON_T_STRING); + jc->type = JSON_T_NONE; + jc->state = CO; + + if (jc->callback) { + JSON_value value; + value.vu.str.value = jc->parse_buffer; + value.vu.str.length = jc->parse_buffer_count; + if (!(*jc->callback)(jc->ctx, JSON_T_KEY, &value)) { + return false; + } + } + parse_buffer_clear(jc); + break; + case MODE_ARRAY: + case MODE_OBJECT: + assert(jc->type == JSON_T_STRING); + if (!parse_parse_buffer(jc)) { + return false; + } + jc->type = JSON_T_NONE; + jc->state = OK; + break; + default: + return false; + } + break; + +/* , */ case -3: + parse_buffer_pop_back_char(jc); + if (!parse_parse_buffer(jc)) { + return false; + } + switch (jc->stack[jc->top]) { + case MODE_OBJECT: +/* + A comma causes a flip from object mode to key mode. +*/ + if (!pop(jc, MODE_OBJECT) || !push(jc, MODE_KEY)) { + return false; + } + assert(jc->type != JSON_T_STRING); + jc->type = JSON_T_NONE; + jc->state = KE; + break; + case MODE_ARRAY: + assert(jc->type != JSON_T_STRING); + jc->type = JSON_T_NONE; + jc->state = VA; + break; + default: + return false; + } + break; + +/* : */ case -2: +/* + A colon causes a flip from key mode to object mode. +*/ + parse_buffer_pop_back_char(jc); + if (!pop(jc, MODE_KEY) || !push(jc, MODE_OBJECT)) { + return false; + } + assert(jc->type == JSON_T_NONE); + jc->state = VA; + break; +/* + Bad action. +*/ + default: + return false; + } + } + return true; +} + + +int +JSON_parser_done(JSON_parser jc) +{ + const int result = jc->state == OK && pop(jc, MODE_DONE); + + return result; +} + + +int JSON_parser_is_legal_white_space_string(const char* s) +{ + int c, char_class; + + if (s == NULL) { + return false; + } + + for (; *s; ++s) { + c = *s; + + if (c < 0 || c >= 128) { + return false; + } + + char_class = ascii_class[c]; + + if (char_class != C_SPACE && char_class != C_WHITE) { + return false; + } + } + + return true; +} + + + +void init_JSON_config(JSON_config* config) +{ + if (config) { + memset(config, 0, sizeof(*config)); + + config->depth = JSON_PARSER_STACK_SIZE - 1; + } +} + diff --git a/ape-server/src/json_parser.h b/ape-server/src/json_parser.h new file mode 100755 index 0000000..299446c --- /dev/null +++ b/ape-server/src/json_parser.h @@ -0,0 +1,152 @@ +#ifndef JSON_PARSER_H +#define JSON_PARSER_H + +/* json_parser.h */ + + +#include + +/* Windows DLL stuff */ +#ifdef _WIN32 +# ifdef JSON_PARSER_DLL_EXPORTS +# define JSON_PARSER_DLL_API __declspec(dllexport) +# else +# define JSON_PARSER_DLL_API __declspec(dllimport) +# endif +#else +# define JSON_PARSER_DLL_API +#endif + +/* Determine the integer type use to parse non-floating point numbers */ +#if 0 +typedef long long JSON_int_t; +#define JSON_PARSER_INTEGER_SSCANF_TOKEN "%lld" +#define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%lld" +#else +typedef long JSON_int_t; +#define JSON_PARSER_INTEGER_SSCANF_TOKEN "%ld" +#define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%ld" +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum +{ + JSON_T_NONE = 0, + JSON_T_ARRAY_BEGIN, + JSON_T_ARRAY_END, + JSON_T_OBJECT_BEGIN, + JSON_T_OBJECT_END, + JSON_T_INTEGER, + JSON_T_FLOAT, + JSON_T_NULL, + JSON_T_TRUE, + JSON_T_FALSE, + JSON_T_STRING, + JSON_T_KEY, + JSON_T_MAX +} JSON_type; + +typedef struct JSON_value_struct { + struct { + long double float_value; + JSON_int_t integer_value; + + struct { + char* value; + size_t length; + } str; + } vu; +} JSON_value; + +typedef struct JSON_parser_struct* JSON_parser; + +/*! \brief JSON parser callback + + \param ctx The pointer passed to new_JSON_parser. + \param type An element of JSON_type but not JSON_T_NONE. + \param value A representation of the parsed value. This parameter is NULL for + JSON_T_ARRAY_BEGIN, JSON_T_ARRAY_END, JSON_T_OBJECT_BEGIN, JSON_T_OBJECT_END, + JSON_T_NULL, JSON_T_TRUE, and SON_T_FALSE. String values are always returned + as zero-terminated C strings. + + \return Non-zero if parsing should continue, else zero. +*/ +typedef int (*JSON_parser_callback)(void* ctx, int type, const struct JSON_value_struct* value); + + +/*! \brief The structure used to configure a JSON parser object + + \param depth If negative, the parser can parse arbitrary levels of JSON, otherwise + the depth is the limit + \param Pointer to a callback. This parameter may be NULL. In this case the input is merely checked for validity. + \param Callback context. This parameter may be NULL. + \param depth. Specifies the levels of nested JSON to allow. Negative numbers yield unlimited nesting. + \param allowComments. To allow C style comments in JSON, set to non-zero. + \param handleFloatsManually. To decode floating point numbers manually set this parameter to non-zero. + + \return The parser object. +*/ +typedef struct { + JSON_parser_callback callback; + void* callback_ctx; + int depth; + int allow_comments; + int handle_floats_manually; +} JSON_config; + + +/*! \brief Initializes the JSON parser configuration structure to default values. + + The default configuration is + - 127 levels of nested JSON (depends on JSON_PARSER_STACK_SIZE, see json_parser.c) + - no parsing, just checking for JSON syntax + - no comments + + \param config. Used to configure the parser. +*/ +JSON_PARSER_DLL_API void init_JSON_config(JSON_config* config); + +/*! \brief Create a JSON parser object + + \param config. Used to configure the parser. Set to NULL to use the default configuration. + See init_JSON_config + + \return The parser object. +*/ +JSON_PARSER_DLL_API extern JSON_parser new_JSON_parser(JSON_config* config); + +/*! \brief Destroy a previously created JSON parser object. */ +JSON_PARSER_DLL_API extern void delete_JSON_parser(JSON_parser jc); + +/*! \brief Parse a character. + + \return Non-zero, if all characters passed to this function are part of are valid JSON. +*/ +JSON_PARSER_DLL_API extern int JSON_parser_char(JSON_parser jc, int next_char); + +/*! \brief Finalize parsing. + + Call this method once after all input characters have been consumed. + + \return Non-zero, if all parsed characters are valid JSON, zero otherwise. +*/ +JSON_PARSER_DLL_API extern int JSON_parser_done(JSON_parser jc); + +/*! \brief Determine if a given string is valid JSON white space + + \return Non-zero if the string is valid, zero otherwise. +*/ +JSON_PARSER_DLL_API extern int JSON_parser_is_legal_white_space_string(const char* s); + + +#ifdef __cplusplus +} +#endif + + +#endif /* JSON_PARSER_H */ + diff --git a/ape-server/src/log.c b/ape-server/src/log.c new file mode 100755 index 0000000..451d78a --- /dev/null +++ b/ape-server/src/log.c @@ -0,0 +1,80 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* log.c */ + +#include +#include +#include +#include +#include + +#include "utils.h" +#include "log.h" +#include "main.h" +#include "config.h" + + +void ape_log_init(acetables *g_ape) +{ + int debug = atoi(CONFIG_VAL(Log, debug, g_ape->srv)); + + g_ape->logs.fd = STDERR_FILENO; + g_ape->logs.lvl = (debug ? APE_DEBUG : 0); + g_ape->logs.lvl |= APE_ERR | APE_WARN; + if (!(g_ape->logs.use_syslog = atoi(CONFIG_VAL(Log, use_syslog, g_ape->srv)))) { + if ((g_ape->logs.fd = open(CONFIG_VAL(Log, logfile, g_ape->srv), O_APPEND | O_WRONLY | O_CREAT, 0644)) == -1) { + g_ape->logs.fd = STDERR_FILENO; + } + } +} + +void ape_log(ape_log_lvl_t lvl, const char *file, unsigned long int line, acetables *g_ape, char *buf, ...) +{ + if (lvl == APE_DEBUG && !g_ape->logs.lvl&APE_DEBUG) { + return; + } else { + time_t log_ts; + char *buff; + char date[32]; + + int len, datelen; + va_list val; + log_ts = time(NULL); + + va_start(val, buf); + len = vasprintf(&buff, buf, val); + va_end(val); + + datelen = strftime(date, 32, "%Y-%m-%d %H:%M:%S - ", localtime(&log_ts)); + + write(g_ape->logs.fd, date, datelen); + if (g_ape->logs.lvl&APE_DEBUG) { + char *debug_file; + int dlen; + dlen = asprintf(&debug_file, "%s:%li - ", file, line); + write(g_ape->logs.fd, debug_file, dlen); + free(debug_file); + } + write(g_ape->logs.fd, buff, len); + write(g_ape->logs.fd, "\n", 1); + + free(buff); + } +} diff --git a/ape-server/src/log.h b/ape-server/src/log.h new file mode 100755 index 0000000..45b4fa3 --- /dev/null +++ b/ape-server/src/log.h @@ -0,0 +1,37 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* log.h */ + +#ifndef _LOG_H +#define _LOG_H + +#include "main.h" + +typedef enum { + APE_DEBUG = 0x01, + APE_WARN = 0x02, + APE_ERR = 0x04, + APE_INFO = 0x08 +} ape_log_lvl_t; + +void ape_log_init(acetables *g_ape); +void ape_log(ape_log_lvl_t lvl, const char *file, unsigned long int line, acetables *g_ape, char *buf, ...); + +#endif diff --git a/ape-server/src/main.h b/ape-server/src/main.h new file mode 100755 index 0000000..04cb2a8 --- /dev/null +++ b/ape-server/src/main.h @@ -0,0 +1,288 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* main.h */ + + +#ifndef _MAIN_H +#define _MAIN_H + + +#include +#include +#include +#include +#include +#include +#include + +#include "hash.h" + +#define MAX_IO 4096 +#define DEFAULT_BUFFER_SIZE 2048 + +#define MAX_NICK_LEN 16 // move to module +#define MAX_CHAN_LEN 40 +#define MAX_MSG_LEN 1024 +#define MAX_RAW_LEN 1024 + +#define TIMEOUT_SEC 45 + +#define SERVER_NAME "APE.Server" +#define _VERSION "1.01dev" + +int server_is_running; + +struct _transport_properties { + struct { + struct { + char *val; + int len; + } left; + struct { + char *val; + int len; + } right; + } padding; +}; + +struct _ape_transports { + struct { + struct _transport_properties properties; + } jsonp; + + struct { + struct _transport_properties properties; + } xhrstreaming; + + struct { + struct _transport_properties properties; + } sse; + + struct { + struct _transport_properties properties; + } websocket; +}; + +typedef struct _http_state http_state; +struct _http_state +{ + struct _http_header_line *hlines; + + char *uri; + + void *buffer_addr; + const char *data; + const char *host; + + int pos; + int contentlength; + int read; + + unsigned short int step; + unsigned short int type; /* HTTP_GET or HTTP_POST */ + unsigned short int error; +}; + +typedef struct _websocket_state websocket_state; +struct _websocket_state +{ + struct _http_state *http; + const char *data; + unsigned short int offset; + unsigned short int error; +}; + +typedef enum { + STREAM_IN, + STREAM_OUT, + STREAM_SERVER, + STREAM_DELEGATE +} ape_socket_t; + +typedef enum { + STREAM_ONLINE, + STREAM_PROGRESS +} ape_socket_state_t; + +typedef struct _ape_buffer ape_buffer; +struct _ape_buffer { + char *data; + void *slot; + + unsigned int size; + unsigned int length; + + int islot; +}; + +typedef struct _acetables +{ + struct { + struct _callback_hook *head; + struct _callback_hook *foot; + } cmd_hook; + + struct { + struct _ape_proxy *list; + struct _ape_proxy_cache *hosts; + } proxy; + + struct { + struct _ticks_callback *timers; + unsigned int ntimers; + } timers; + + struct { + unsigned int lvl; + unsigned int use_syslog; + int fd; + } logs; + + struct _ape_transports transports; + + HTBL *hLogin; + HTBL *hSessid; + HTBL *hLusers; + HTBL *hCallback; + HTBL *hPubid; + + struct apeconfig *srv; + struct _callback_hook *bad_cmd_callbacks; + struct USERS *uHead; + struct _socks_bufout *bufout; + struct _ace_plugins *plugins; + struct _fdevent *events; + struct _ape_socket **co; + struct _extend *properties; + + const char *confs_path; + + int is_daemon; + int basemem; + unsigned int nConnected; +} acetables; + + +typedef struct _ape_parser ape_parser; +struct _ape_parser { + void (*parser_func)(struct _ape_socket *, acetables *); + void (*destroy)(struct _ape_parser *); + void (*onready)(struct _ape_parser *, acetables *); + void *data; + struct _ape_socket *socket; + short int ready; +}; + +typedef struct _ape_socket ape_socket; +struct _ape_socket { + struct { + void (*on_accept)(struct _ape_socket *client, acetables *g_ape); + void (*on_connect)(struct _ape_socket *client, acetables *g_ape); /* ajouter in/out ? ou faire un onaccept */ + void (*on_disconnect)(struct _ape_socket *client, acetables *g_ape); + void (*on_read)(struct _ape_socket *client, struct _ape_buffer *buf, size_t offset, acetables *g_ape); + void (*on_read_lf)(struct _ape_socket *client, char *data, acetables *g_ape); + void (*on_data_completly_sent)(struct _ape_socket *client, acetables *g_ape); + void (*on_write)(struct _ape_socket *client, acetables *g_ape); + } callbacks; + + ape_parser parser; + + ape_buffer buffer_in; + + char ip_client[16]; + long int idle; + + void *attach; + void *data; + + int fd; + int burn_after_writing; + + ape_socket_state_t state; + ape_socket_t stream_type; +}; + +#define HEADER_DEFAULT "HTTP/1.1 200 OK\r\nPragma: no-cache\r\nCache-Control: no-cache, must-revalidate\r\nExpires: Thu, 27 Dec 1986 07:30:00 GMT\r\nContent-Type: text/html\r\n\r\n" +#define HEADER_DEFAULT_LEN 144 + +#define HEADER_SSE "HTTP/1.1 200 OK\r\nPragma: no-cache\r\nCache-Control: no-cache, must-revalidate\r\nExpires: Thu, 27 Dec 1986 07:30:00 GMT\r\nContent-Type: application/x-dom-event-stream\r\n\r\n" +#define HEADER_SSE_LEN 165 + +#define HEADER_XHR "HTTP/1.1 200 OK\r\nPragma: no-cache\r\nCache-Control: no-cache, must-revalidate\r\nExpires: Thu, 27 Dec 1986 07:30:00 GMT\r\nContent-Type: application/x-ape-event-stream\r\n\r\n " +#define HEADER_XHR_LEN 421 + +#define CONTENT_NOTFOUND "APE Server

APE Server

No command given.


http://www.ape-project.org/ - Server "_VERSION" (Build "__DATE__" "__TIME__")
" + +/* http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-55 : The first three lines in each case are hard-coded (the exact case and order matters); */ +#define WEBSOCKET_HARDCODED_HEADERS_OLD "HTTP/1.1 101 Web Socket Protocol Handshake\r\nUpgrade: WebSocket\r\nConnection: Upgrade\r\n" +#define WEBSOCKET_HARDCODED_HEADERS_NEW "HTTP/1.1 101 WebSocket Protocol Handshake\r\nUpgrade: WebSocket\r\nConnection: Upgrade\r\n" + +#define FIRE_EVENT(event, ret, arg...) \ + if (g_ape->plugins != NULL) { \ + ace_plugins *cplug = g_ape->plugins; \ + while (cplug != NULL) { \ + if (cplug->cb != NULL && cplug->cb->c_##event != NULL && cplug->fire.c_##event == 0) { \ + cplug->fire.c_##event = 1; \ + ret = cplug->cb->c_##event(arg); \ + cplug->fire.c_##event = 0; \ + \ + if (ret == NULL) { \ + return NULL; \ + } \ + \ + break; \ + } \ + cplug = cplug->next; \ + } \ + } \ + if (ret != NULL) { \ + return ret; \ + } + +#define FIRE_EVENT_NULL(event, arg...) \ + if (g_ape->plugins != NULL) { \ + ace_plugins *cplug = g_ape->plugins; \ + while (cplug != NULL) { \ + if (cplug->cb != NULL && cplug->cb->c_##event != NULL && cplug->fire.c_##event == 0) { \ + cplug->fire.c_##event = 1; \ + cplug->cb->c_##event(arg); \ + cplug->fire.c_##event = 0; \ + return; \ + break; \ + } \ + cplug = cplug->next; \ + } \ + } + +#define FIRE_EVENT_NONSTOP(event, arg...) \ + if (g_ape->plugins != NULL) { \ + ace_plugins *cplug = g_ape->plugins; \ + while (cplug != NULL) { \ + if (cplug->cb != NULL && cplug->cb->c_##event != NULL && cplug->fire.c_##event == 0) { \ + cplug->fire.c_##event = 1; \ + cplug->cb->c_##event(arg); \ + cplug->fire.c_##event = 0; \ + } \ + cplug = cplug->next; \ + } \ + } +#endif + + diff --git a/ape-server/src/md5.c b/ape-server/src/md5.c new file mode 100644 index 0000000..e7714e3 --- /dev/null +++ b/ape-server/src/md5.c @@ -0,0 +1,255 @@ +/* +Christophe Devine +c.devine@cr0.net +http://www.cr0.net:8040/code/crypto/ +*/ +/* + * RFC 1321 compliant MD5 implementation + * + * Copyright (C) 2001-2003 Christophe Devine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "md5.h" +/* uncomment the following line to run the test suite */ + +/* #define TEST */ + +#define GET_UINT32(n,b,i) \ +{ \ + (n) = ( (uint32) (b)[(i) ] ) \ + | ( (uint32) (b)[(i) + 1] << 8 ) \ + | ( (uint32) (b)[(i) + 2] << 16 ) \ + | ( (uint32) (b)[(i) + 3] << 24 ); \ +} + +#define PUT_UINT32(n,b,i) \ +{ \ + (b)[(i) ] = (uint8) ( (n) ); \ + (b)[(i) + 1] = (uint8) ( (n) >> 8 ); \ + (b)[(i) + 2] = (uint8) ( (n) >> 16 ); \ + (b)[(i) + 3] = (uint8) ( (n) >> 24 ); \ +} + +void md5_starts( md5_context *ctx ) +{ + ctx->total[0] = 0; + ctx->total[1] = 0; + + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; +} + +void md5_process( md5_context *ctx, uint8 data[64] ) +{ + uint32 X[16], A, B, C, D; + + GET_UINT32( X[0], data, 0 ); + GET_UINT32( X[1], data, 4 ); + GET_UINT32( X[2], data, 8 ); + GET_UINT32( X[3], data, 12 ); + GET_UINT32( X[4], data, 16 ); + GET_UINT32( X[5], data, 20 ); + GET_UINT32( X[6], data, 24 ); + GET_UINT32( X[7], data, 28 ); + GET_UINT32( X[8], data, 32 ); + GET_UINT32( X[9], data, 36 ); + GET_UINT32( X[10], data, 40 ); + GET_UINT32( X[11], data, 44 ); + GET_UINT32( X[12], data, 48 ); + GET_UINT32( X[13], data, 52 ); + GET_UINT32( X[14], data, 56 ); + GET_UINT32( X[15], data, 60 ); + +#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) + +#define P(a,b,c,d,k,s,t) \ +{ \ + a += F(b,c,d) + X[k] + t; a = S(a,s) + b; \ +} + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + +#define F(x,y,z) (z ^ (x & (y ^ z))) + + P( A, B, C, D, 0, 7, 0xD76AA478 ); + P( D, A, B, C, 1, 12, 0xE8C7B756 ); + P( C, D, A, B, 2, 17, 0x242070DB ); + P( B, C, D, A, 3, 22, 0xC1BDCEEE ); + P( A, B, C, D, 4, 7, 0xF57C0FAF ); + P( D, A, B, C, 5, 12, 0x4787C62A ); + P( C, D, A, B, 6, 17, 0xA8304613 ); + P( B, C, D, A, 7, 22, 0xFD469501 ); + P( A, B, C, D, 8, 7, 0x698098D8 ); + P( D, A, B, C, 9, 12, 0x8B44F7AF ); + P( C, D, A, B, 10, 17, 0xFFFF5BB1 ); + P( B, C, D, A, 11, 22, 0x895CD7BE ); + P( A, B, C, D, 12, 7, 0x6B901122 ); + P( D, A, B, C, 13, 12, 0xFD987193 ); + P( C, D, A, B, 14, 17, 0xA679438E ); + P( B, C, D, A, 15, 22, 0x49B40821 ); + +#undef F + +#define F(x,y,z) (y ^ (z & (x ^ y))) + + P( A, B, C, D, 1, 5, 0xF61E2562 ); + P( D, A, B, C, 6, 9, 0xC040B340 ); + P( C, D, A, B, 11, 14, 0x265E5A51 ); + P( B, C, D, A, 0, 20, 0xE9B6C7AA ); + P( A, B, C, D, 5, 5, 0xD62F105D ); + P( D, A, B, C, 10, 9, 0x02441453 ); + P( C, D, A, B, 15, 14, 0xD8A1E681 ); + P( B, C, D, A, 4, 20, 0xE7D3FBC8 ); + P( A, B, C, D, 9, 5, 0x21E1CDE6 ); + P( D, A, B, C, 14, 9, 0xC33707D6 ); + P( C, D, A, B, 3, 14, 0xF4D50D87 ); + P( B, C, D, A, 8, 20, 0x455A14ED ); + P( A, B, C, D, 13, 5, 0xA9E3E905 ); + P( D, A, B, C, 2, 9, 0xFCEFA3F8 ); + P( C, D, A, B, 7, 14, 0x676F02D9 ); + P( B, C, D, A, 12, 20, 0x8D2A4C8A ); + +#undef F + +#define F(x,y,z) (x ^ y ^ z) + + P( A, B, C, D, 5, 4, 0xFFFA3942 ); + P( D, A, B, C, 8, 11, 0x8771F681 ); + P( C, D, A, B, 11, 16, 0x6D9D6122 ); + P( B, C, D, A, 14, 23, 0xFDE5380C ); + P( A, B, C, D, 1, 4, 0xA4BEEA44 ); + P( D, A, B, C, 4, 11, 0x4BDECFA9 ); + P( C, D, A, B, 7, 16, 0xF6BB4B60 ); + P( B, C, D, A, 10, 23, 0xBEBFBC70 ); + P( A, B, C, D, 13, 4, 0x289B7EC6 ); + P( D, A, B, C, 0, 11, 0xEAA127FA ); + P( C, D, A, B, 3, 16, 0xD4EF3085 ); + P( B, C, D, A, 6, 23, 0x04881D05 ); + P( A, B, C, D, 9, 4, 0xD9D4D039 ); + P( D, A, B, C, 12, 11, 0xE6DB99E5 ); + P( C, D, A, B, 15, 16, 0x1FA27CF8 ); + P( B, C, D, A, 2, 23, 0xC4AC5665 ); + +#undef F + +#define F(x,y,z) (y ^ (x | ~z)) + + P( A, B, C, D, 0, 6, 0xF4292244 ); + P( D, A, B, C, 7, 10, 0x432AFF97 ); + P( C, D, A, B, 14, 15, 0xAB9423A7 ); + P( B, C, D, A, 5, 21, 0xFC93A039 ); + P( A, B, C, D, 12, 6, 0x655B59C3 ); + P( D, A, B, C, 3, 10, 0x8F0CCC92 ); + P( C, D, A, B, 10, 15, 0xFFEFF47D ); + P( B, C, D, A, 1, 21, 0x85845DD1 ); + P( A, B, C, D, 8, 6, 0x6FA87E4F ); + P( D, A, B, C, 15, 10, 0xFE2CE6E0 ); + P( C, D, A, B, 6, 15, 0xA3014314 ); + P( B, C, D, A, 13, 21, 0x4E0811A1 ); + P( A, B, C, D, 4, 6, 0xF7537E82 ); + P( D, A, B, C, 11, 10, 0xBD3AF235 ); + P( C, D, A, B, 2, 15, 0x2AD7D2BB ); + P( B, C, D, A, 9, 21, 0xEB86D391 ); + +#undef F + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; +} + +void md5_update( md5_context *ctx, uint8 *input, uint32 length ) +{ + uint32 left, fill; + + if( ! length ) return; + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += length; + ctx->total[0] &= 0xFFFFFFFF; + + if( ctx->total[0] < length ) + ctx->total[1]++; + + if( left && length >= fill ) + { + memcpy( (void *) (ctx->buffer + left), + (void *) input, fill ); + md5_process( ctx, ctx->buffer ); + length -= fill; + input += fill; + left = 0; + } + + while( length >= 64 ) + { + md5_process( ctx, input ); + length -= 64; + input += 64; + } + + if( length ) + { + memcpy( (void *) (ctx->buffer + left), + (void *) input, length ); + } +} + +static uint8 md5_padding[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +void md5_finish( md5_context *ctx, uint8 digest[16] ) +{ + uint32 last, padn; + uint32 high, low; + uint8 msglen[8]; + + high = ( ctx->total[0] >> 29 ) + | ( ctx->total[1] << 3 ); + low = ( ctx->total[0] << 3 ); + + PUT_UINT32( low, msglen, 0 ); + PUT_UINT32( high, msglen, 4 ); + + last = ctx->total[0] & 0x3F; + padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); + + md5_update( ctx, md5_padding, padn ); + md5_update( ctx, msglen, 8 ); + + PUT_UINT32( ctx->state[0], digest, 0 ); + PUT_UINT32( ctx->state[1], digest, 4 ); + PUT_UINT32( ctx->state[2], digest, 8 ); + PUT_UINT32( ctx->state[3], digest, 12 ); +} + + diff --git a/ape-server/src/md5.h b/ape-server/src/md5.h new file mode 100644 index 0000000..d443787 --- /dev/null +++ b/ape-server/src/md5.h @@ -0,0 +1,30 @@ +/* +Christophe Devine +c.devine@cr0.net +http://www.cr0.net:8040/code/crypto/ +*/ +#ifndef _MD5_H +#define _MD5_H + +#ifndef uint8 +#define uint8 unsigned char +#endif + +#ifndef uint32 +#define uint32 unsigned long int +#endif + +typedef struct +{ + uint32 total[2]; + uint32 state[4]; + uint8 buffer[64]; +} +md5_context; + +void md5_starts( md5_context *ctx ); +void md5_update( md5_context *ctx, uint8 *input, uint32 length ); +void md5_finish( md5_context *ctx, uint8 digest[16] ); + +#endif /* md5.h */ + diff --git a/ape-server/src/parser.c b/ape-server/src/parser.c new file mode 100755 index 0000000..3269bce --- /dev/null +++ b/ape-server/src/parser.c @@ -0,0 +1,127 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* parser.c */ + +#include "parser.h" +#include "http.h" +#include "utils.h" +#include "handle_http.h" + +static void parser_destroy_http(ape_parser *http_parser) +{ + free_header_line(((http_state *)http_parser->data)->hlines); + free(http_parser->data); + http_parser->data = NULL; + http_parser->ready = 0; + http_parser->parser_func = NULL; + http_parser->onready = NULL; + http_parser->destroy = NULL; + http_parser->socket = NULL; +} + +static void parser_ready_http(ape_parser *http_parser, acetables *g_ape) +{ + ape_socket *co = http_parser->socket; + + co->attach = checkrecv(co, g_ape); +} + +static void parser_ready_websocket(ape_parser *websocket_parser, acetables *g_ape) +{ + ape_socket *co = websocket_parser->socket; + + co->attach = checkrecv_websocket(co, g_ape); +} + +ape_parser parser_init_http(ape_socket *co) +{ + ape_parser http_parser; + http_state *http; + + http_parser.ready = 0; + http_parser.data = xmalloc(sizeof(struct _http_state)); + + http = http_parser.data; + + http->hlines = NULL; + http->pos = 0; + http->contentlength = -1; + http->read = 0; + http->step = 0; + http->type = HTTP_NULL; + http->error = 0; + http->uri = NULL; + http->data = NULL; + http->host = NULL; + http->buffer_addr = NULL; + + http_parser.parser_func = process_http; + http_parser.destroy = parser_destroy_http; + http_parser.onready = parser_ready_http; + http_parser.socket = co; + + return http_parser; +} + + +static void parser_destroy_stream(ape_parser *stream_parser) +{ + websocket_state *websocket = stream_parser->data; + + free_header_line(websocket->http->hlines); + + stream_parser->data = NULL; + stream_parser->ready = 0; + stream_parser->parser_func = NULL; + stream_parser->destroy = NULL; + stream_parser->socket = NULL; + + free(websocket->http); + free(websocket); +} + +ape_parser parser_init_stream(ape_socket *co) +{ + ape_parser stream_parser; + websocket_state *websocket; + + stream_parser.ready = 0; + stream_parser.data = xmalloc(sizeof(struct _websocket_state)); + + websocket = stream_parser.data; + websocket->offset = 0; + websocket->data = NULL; + websocket->error = 0; + + stream_parser.parser_func = process_websocket; + stream_parser.onready = parser_ready_websocket; + stream_parser.destroy = parser_destroy_stream; + stream_parser.socket = co; + + return stream_parser; +} + +void parser_destroy(ape_parser *parser) +{ + if (parser != NULL && parser->destroy != NULL) { + parser->destroy(parser); + } +} + diff --git a/ape-server/src/parser.h b/ape-server/src/parser.h new file mode 100755 index 0000000..d27c6de --- /dev/null +++ b/ape-server/src/parser.h @@ -0,0 +1,31 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* parser.h */ + +#ifndef _PARSER_H +#define _PARSER_H + +#include "main.h" + +ape_parser parser_init_http(ape_socket *co); +ape_parser parser_init_stream(ape_socket *co); +void parser_destroy(ape_parser *parser); + +#endif diff --git a/ape-server/src/pipe.c b/ape-server/src/pipe.c new file mode 100755 index 0000000..ea22e96 --- /dev/null +++ b/ape-server/src/pipe.c @@ -0,0 +1,197 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* pipe.c */ + +#include "pipe.h" +#include "utils.h" + + +const char basic_chars[16] = { 'a', 'b', 'c', 'd', 'e', 'f', '0', '1', + '2', '3', '4', '5', '6', '7', '8', '9' + }; + + +/* Generate a string (32 chars) used for sessid and pubid */ +void gen_sessid_new(char *input, acetables *g_ape) +{ + unsigned int i; + + do { + for (i = 0; i < 32; i++) { + input[i] = basic_chars[rand_n(15)]; + } + input[32] = '\0'; + } while(seek_user_id(input, g_ape) != NULL || get_pipe(input, g_ape) != NULL); // Colision verification +} + +/* Init a pipe (user, channel, proxy) */ +transpipe *init_pipe(void *pipe, int type, acetables *g_ape) +{ + transpipe *npipe = NULL; + + npipe = xmalloc(sizeof(*npipe)); + npipe->pipe = pipe; + npipe->type = type; + npipe->link = NULL; + npipe->data = NULL; + npipe->on_send = NULL; + npipe->properties = NULL; + + gen_sessid_new(npipe->pubid, g_ape); + hashtbl_append(g_ape->hPubid, npipe->pubid, (void *)npipe); + return npipe; +} + +void destroy_pipe(transpipe *pipe, acetables *g_ape) +{ + unlink_all_pipe(pipe, g_ape); + hashtbl_erase(g_ape->hPubid, pipe->pubid); + free(pipe); +} + +/* Link a pipe to another (e.g. user <=> proxy) */ +void link_pipe(transpipe *pipe_origin, transpipe *pipe_to, void (*on_unlink)(struct _transpipe *, struct _transpipe *, acetables *)) +{ + struct _pipe_link *link; + + if (pipe_origin == NULL || pipe_to == NULL) { + return; + } + link = xmalloc(sizeof(*link)); + + /* on_unlink is called when the pipe is deleted */ + link->on_unlink = on_unlink; + + link->plink = pipe_to; + link->next = pipe_origin->link; + pipe_origin->link = link; + +} + +void unlink_all_pipe(transpipe *origin, acetables *g_ape) +{ + struct _pipe_link *link, *plink; + + if (origin == NULL) { + return; + } + link = origin->link; + + while (link != NULL) { + plink = link->next; + if (link->on_unlink != NULL) { + + /* Calling callback if any */ + link->on_unlink(origin, link->plink, g_ape); + } + free(link); + link = plink; + } + origin->link = NULL; +} + +transpipe *get_pipe(const char *pubid, acetables *g_ape) +{ + if (strlen(pubid) != 32) { + return NULL; + } + return hashtbl_seek(g_ape->hPubid, pubid); +} + +/* pubid : recver; user = sender */ +transpipe *get_pipe_strict(const char *pubid, USERS *user, acetables *g_ape) +{ + transpipe *pipe = get_pipe(pubid, g_ape); + + if (pipe != NULL && pipe->type == CHANNEL_PIPE && !isonchannel(user, pipe->pipe)) { + return NULL; + } + + return pipe; + +} + +void post_json_custom(json_item *jstr, USERS *user, transpipe *pipe, acetables *g_ape) +{ + if (pipe->on_send == NULL) { + free_json_item(jstr); + return; + } + + pipe->on_send(pipe, user, jstr, g_ape); +} + +json_item *get_json_object_pipe_custom(transpipe *pipe) +{ + json_item *jstr = NULL; + + if (pipe != NULL) { + jstr = json_new_object(); + json_set_property_strN(jstr, "casttype", 8, "custom", 6); + json_set_property_strN(jstr, "pubid", 5, pipe->pubid, 32); + + if (pipe->properties != NULL) { + int has_prop = 0; + + json_item *jprop = NULL; + + extend *eTmp = pipe->properties; + + while (eTmp != NULL) { + if (eTmp->visibility == EXTEND_ISPUBLIC) { + if (!has_prop) { + has_prop = 1; + jprop = json_new_object(); + } + if (eTmp->type == EXTEND_JSON) { + json_item *jcopy = json_item_copy(eTmp->val, NULL); + + json_set_property_objZ(jprop, eTmp->key, jcopy); + } else { + json_set_property_strZ(jprop, eTmp->key, eTmp->val); + + } + } + eTmp = eTmp->next; + } + if (has_prop) { + json_set_property_objN(jstr, "properties", 10, jprop); + } + } + + } + return jstr; +} + +json_item *get_json_object_pipe(transpipe *pipe) +{ + switch(pipe->type) { + case USER_PIPE: + return get_json_object_user(pipe->pipe); + case CHANNEL_PIPE: + return get_json_object_channel(pipe->pipe); + case CUSTOM_PIPE: + return get_json_object_pipe_custom(pipe); + case PROXY_PIPE: + default: + return NULL; + } +} + diff --git a/ape-server/src/pipe.h b/ape-server/src/pipe.h new file mode 100755 index 0000000..5fe05f6 --- /dev/null +++ b/ape-server/src/pipe.h @@ -0,0 +1,72 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* proxy.h */ + +#ifndef _PIPE_H +#define _PIPE_H + +#include "main.h" +#include "users.h" +#include "json.h" + +enum { + CHANNEL_PIPE = 0, + USER_PIPE, + PROXY_PIPE, + CUSTOM_PIPE +}; + +typedef struct _pipe_link pipe_link; +struct _pipe_link { + struct _transpipe *plink; + struct _pipe_link *next; + + void (*on_unlink)(struct _transpipe *, struct _transpipe *, acetables *); +}; + +typedef struct _transpipe transpipe; +struct _transpipe +{ + void *pipe; + void *data; + + struct _pipe_link *link; + struct _extend *properties; + + void (*on_send)(struct _transpipe *, struct USERS *, json_item *, acetables *); + + int type; + + char pubid[33]; +}; + +transpipe *init_pipe(void *pipe, int type, acetables *g_ape); +void destroy_pipe(transpipe *pipe, acetables *g_ape); + +void link_pipe(transpipe *pipe_origin, transpipe *pipe_to, void (*on_unlink)(struct _transpipe *, struct _transpipe *, acetables *)); +transpipe *get_pipe(const char *pubid, acetables *g_ape); +transpipe *get_pipe_strict(const char *pubid, struct USERS *user, acetables *g_ape); +void post_json_custom(json_item *jstr, struct USERS *user, struct _transpipe *pipe, acetables *g_ape); +void gen_sessid_new(char *input, acetables *g_ape); +void unlink_all_pipe(transpipe *origin, acetables *g_ape); +json_item *get_json_object_pipe(transpipe *pipe); +json_item *get_json_object_pipe_custom(transpipe *pipe); +#endif + diff --git a/ape-server/src/plugins.c b/ape-server/src/plugins.c new file mode 100644 index 0000000..cd38188 --- /dev/null +++ b/ape-server/src/plugins.c @@ -0,0 +1,177 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* plugins.c */ + +#include "plugins.h" +#include "../modules/plugins.h" +#include +#include +#include "utils.h" +#include "config.h" + + +ace_plugins *loadplugin(char *file) +{ + void *hwnd; + //ace_plugin_infos *infos; + ace_plugins *plug; + + hwnd = dlopen(file, RTLD_LAZY); + + if (!hwnd) { + printf("[Module] Failed to load %s [Invalid library] (%s)\n", file, dlerror()); + return NULL; + } + + plug = xmalloc(sizeof(*plug)); + + plug->hPlug = hwnd; + + /* + plug->conf.file = infos->conf; + plug->conf.entry = dlsym(hwnd, "this_config");*/ + + + plug->next = NULL; + plug->cb = NULL; + + return plug; +} + +void findandloadplugin(acetables *g_ape) +{ + int i; + char modules_path[1024]; + + sprintf(modules_path, "%s*.so", CONFIG_VAL(Config, modules, g_ape->srv)); + + void (*load)(ace_plugins *module); + + glob_t globbuf; + glob(modules_path, 0, NULL, &globbuf); + + + for (i = 0; i < globbuf.gl_pathc; i++) { + ace_plugins *pcurrent; + pcurrent = loadplugin(globbuf.gl_pathv[i]); + + if (pcurrent != NULL) { + ace_plugins *plist = g_ape->plugins; + + load = dlsym(pcurrent->hPlug, "ape_module_init"); + if (load == NULL) { + printf("[Module] Failed to load %s [No load entry point]\n", globbuf.gl_pathv[i]); + free(pcurrent); + continue; + } + + memset(&pcurrent->fire, 0, sizeof(pcurrent->fire)); /* unfire all events */ + + /* Calling entry point load function */ + load(pcurrent); + + plugin_read_config(pcurrent, CONFIG_VAL(Config, modules_conf, g_ape->srv)); + + if (!g_ape->is_daemon) { + printf("[Module] [%s] Loading module : %s (%s) - %s\n", pcurrent->modulename, pcurrent->infos->name, pcurrent->infos->version, pcurrent->infos->author); + } + pcurrent->next = plist; + g_ape->plugins = pcurrent; + + /* Calling init module */ + pcurrent->loader(g_ape); + + } + + } + globfree(&globbuf); + +} + +void free_all_plugins(acetables *g_ape) +{ + ace_plugins *prev; + + while (g_ape->plugins != NULL) { + g_ape->plugins->unloader(g_ape); +// dlclose(g_ape->plugins->hPlug); + + prev = g_ape->plugins; + g_ape->plugins = g_ape->plugins->next; + free(g_ape->plugins); + } +} + +plug_config *plugin_parse_conf(const char *file) +{ + char lines[2048], *tkn[2]; + FILE *fp = fopen(file, "r"); + if (fp == NULL) { + return NULL; + } + plug_config *tmpconf = NULL, *new_conf = NULL; + + while(fgets(lines, 2048, fp)) { + int nTok = 0; + + if (*lines == '#' || *lines == '\r' || *lines == '\n' || *lines == '\0') { + continue; + } + + nTok = explode('=', lines, tkn, 2); + + if (nTok == 1) { + new_conf = xmalloc(sizeof(struct _plug_config)); + new_conf->key = xstrdup(trim(tkn[0])); + new_conf->value = xstrdup(trim(tkn[1])); + new_conf->next = tmpconf; + tmpconf = new_conf; + } + } + fclose(fp); + return new_conf; +} + +void plugin_read_config(ace_plugins *plug, const char *path) +{ + char conf_file[1024]; + if (plug->infos->conf_file != NULL) { + sprintf(conf_file, "%s%s", path, plug->infos->conf_file); + if ((plug->infos->conf = plugin_parse_conf(conf_file)) == NULL) { + printf("[Module] [%s] [WARN] Cannot open configuration (%s)\n", plug->modulename, plug->infos->conf_file); + return; + } + } +} + +char *plugin_get_conf(struct _plug_config *conf, char *key) +{ + plug_config *current = conf; + + while(current != NULL) { + if (strcmp(current->key, key) == 0) { + return current->value; + } + current = current->next; + } + + return NULL; +} + diff --git a/ape-server/src/plugins.h b/ape-server/src/plugins.h new file mode 100755 index 0000000..90bbcd1 --- /dev/null +++ b/ape-server/src/plugins.h @@ -0,0 +1,89 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* plugins.h */ + + +#ifndef _PLUGINS_H +#define _PLUGINS_H + +#include "main.h" +#include "../modules/plugins.h" + + +typedef struct _ace_plugin_infos ace_plugin_infos; +typedef struct _ace_plugins ace_plugins; + + +struct _ace_plugin_infos +{ + const char *name; + const char *version; + const char *author; + + const char *conf_file; + + struct _plug_config *conf; +}; + + +struct _ace_plugins +{ + struct { + unsigned short int c_adduser; + unsigned short int c_deluser; + unsigned short int c_mkchan; + unsigned short int c_rmchan; + unsigned short int c_join; + unsigned short int c_left; + unsigned short int c_tickuser; + unsigned short int c_post_raw_sub; + unsigned short int c_allocateuser; + unsigned short int c_addsubuser; + unsigned short int c_delsubuser; + } fire; + + /* Module Handle */ + void *hPlug; + void (*loader)(acetables *g_ape); + void (*unloader)(acetables *g_ape); + + const char *modulename; + + struct _ace_callbacks *cb; + /* Module info */ + ace_plugin_infos *infos; + ace_plugins *next; +}; + + +enum { + CALLBACK_PLUGIN = 0, + CALLBACK_NULL +}; + +ace_plugins *loadplugin(char *file); +void findandloadplugin(acetables *g_ape); +void free_all_plugins(acetables *g_ape); +struct _plug_config *plugin_parse_conf(const char *file); +void plugin_read_config(ace_plugins *plug, const char *path); +char *plugin_get_conf(struct _plug_config *conf, char *key); + +#endif + diff --git a/ape-server/src/proxy.c b/ape-server/src/proxy.c new file mode 100755 index 0000000..65b6ea9 --- /dev/null +++ b/ape-server/src/proxy.c @@ -0,0 +1,395 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* proxy.c */ + +/* /!\ Not used anymore : see proxy.js */ + +#include "utils.h" +#include "proxy.h" +#include "handle_http.h" +#include "sock.h" +#include "errno.h" +#include "http.h" +#include "config.h" +#include "base64.h" +#include "pipe.h" +#include "raw.h" +#include "events.h" + +#include +#include +#include + + + +void proxy_init_from_conf(acetables *g_ape) +{ + apeconfig *conf = g_ape->srv; + + while (conf != NULL) { + if (strcasecmp(conf->section, "Proxy") == 0) { + char *host, *port, *readonly, *id; + int iPort; + + host = ape_config_get_key(conf, "host"); + port = ape_config_get_key(conf, "port"); + readonly = ape_config_get_key(conf, "readonly"); + id = ape_config_get_key(conf, "id"); + + if (host != NULL && port != NULL && readonly != NULL && id != NULL) { + struct hostent *h; + + iPort = atoi(port); + if ((h = gethostbyname(host)) != NULL) { + printf("Cache : (%s) : %s\n", host, inet_ntoa(*((struct in_addr *)h->h_addr))); + proxy_cache_addip(host, inet_ntoa(*((struct in_addr *)h->h_addr)), g_ape); + + if (strcasecmp(readonly, "true") == 0) { + proxy_init(id, host, iPort, g_ape); + } else { + ;// + } + } else { + printf("[Warn] Unable to resolve : %s\n", host); + } + } else { + printf("[Warn] Proxy : Configuration error\n"); + } + + } + conf = conf->next; + } +} + + +ape_proxy *proxy_init_by_host_port(char *host, char *port, acetables *g_ape) +{ + ape_proxy *proxy; + apeconfig *conf = g_ape->srv; + + while (conf != NULL) { + if (strcasecmp(conf->section, "Proxy") == 0) { + char *h, *p, *id; + + h = ape_config_get_key(conf, "host"); + p = ape_config_get_key(conf, "port"); + id = ape_config_get_key(conf, "id"); + + + if (h != NULL && p != NULL && id != NULL && strcasecmp(host, h) == 0 && strcasecmp(port, p) == 0) { + if ((proxy = proxy_init(id, host, atoi(port), g_ape)) != NULL) { + + return proxy; + } + } + + } + conf = conf->next; + } + + return NULL; +} + + +ape_proxy *proxy_init(char *ident, char *host, int port, acetables *g_ape) +{ + ape_proxy *proxy; + + ape_proxy_cache *host_cache; + + + if (strlen(ident) > 32 || ((host_cache = proxy_cache_gethostbyname(host, g_ape)) == NULL)) { + return NULL; + } + + proxy = xmalloc(sizeof(*proxy)); + + memcpy(proxy->identifier, ident, strlen(ident)+1); + + proxy->sock.host = host_cache; + proxy->sock.port = port; + proxy->sock.fd = -1; + proxy->eol = 0; + + proxy->state = PROXY_NOT_CONNECTED; + proxy->nlink = 0; + + proxy->to = NULL; + proxy->next = NULL; + proxy->properties = NULL; + + proxy->pipe = init_pipe(proxy, PROXY_PIPE, g_ape); + + + proxy->prev = NULL; + + proxy->next = g_ape->proxy.list; + if (proxy->next != NULL) { + proxy->next->prev = proxy; + } + + g_ape->proxy.list = proxy; + + return proxy; +} + + +/* IP are resolved during the "boot period" */ +ape_proxy_cache *proxy_cache_gethostbyname(char *name, acetables *g_ape) +{ + ape_proxy_cache *host_cache = g_ape->proxy.hosts; + + while (host_cache != NULL) { + if (strcasecmp(host_cache->host, name) == 0 && strlen(host_cache->ip)) { + return host_cache; + } + host_cache = host_cache->next; + } + return NULL; +} + +void proxy_cache_addip(char *name, char *ip, acetables *g_ape) +{ + ape_proxy_cache *cache; + + if (strlen(name) > 512 || strlen(ip) > 15) { + return; + } + cache = xmalloc(sizeof(*cache)); + cache->host = xstrdup(name); + strncpy(cache->ip, ip, 16); + + cache->next = g_ape->proxy.hosts; + g_ape->proxy.hosts = cache; +} + +void proxy_attach(ape_proxy *proxy, char *pipe, int allow_write, acetables *g_ape) +{ + ape_proxy_pipe *to; + transpipe *gpipe; + + if (proxy == NULL || ((gpipe = get_pipe(pipe, g_ape)) == NULL)) { + return; + } + to = xmalloc(sizeof(*to)); + memcpy(to->pipe, gpipe->pubid, strlen(gpipe->pubid)+1); + + to->allow_write = allow_write; + + to->next = proxy->to; + proxy->to = to; + + proxy->nlink++; + + link_pipe(gpipe, proxy->pipe, proxy_detach); +} + + +void proxy_detach(transpipe *unlinker, transpipe *tproxy, acetables *g_ape) +{ + ape_proxy_pipe **to; + ape_proxy *proxy = tproxy->pipe; + + to = &(proxy->to); + + while (*to != NULL) { + + if (strcmp((*to)->pipe, unlinker->pubid) == 0) { + + ape_proxy_pipe *pTo = *to; + *to = (*to)->next; + free(pTo); + proxy->nlink--; + break; + } + to = &(*to)->next; + } + if (!proxy->nlink) { + + proxy_shutdown(proxy, g_ape); + } +} + +// proxy->to must be clean +void proxy_shutdown(ape_proxy *proxy, acetables *g_ape) +{ + + if (proxy->state == PROXY_CONNECTED) { + shutdown(proxy->sock.fd, 2); + proxy->state = PROXY_TOFREE; + } + if (proxy->prev != NULL) { + proxy->prev->next = proxy->next; + } else { + g_ape->proxy.list = proxy->next; + } + if (proxy->next != NULL) { + proxy->next->prev = proxy->prev; + } + + destroy_pipe(proxy->pipe, g_ape); + + if (proxy->state != PROXY_TOFREE) { + free(proxy); + } +} + + +ape_proxy *proxy_are_linked(char *pubid, char *pubid_proxy, acetables *g_ape) +{ + transpipe *pipe = get_pipe(pubid_proxy, g_ape); + struct _ape_proxy_pipe *ppipe; + + if (pipe == NULL || pipe->type != PROXY_PIPE || ((ppipe = ((ape_proxy *)(pipe->pipe))->to) == NULL)) { + return NULL; + } + + while (ppipe != NULL) { + if (strcmp(pubid, ppipe->pipe) == 0) { + return ((ape_proxy *)(pipe->pipe)); + } + ppipe = ppipe->next; + } + + return NULL; +} + +void proxy_process_eol(ape_socket *co, acetables *g_ape) +{ + char *b64; + ape_proxy *proxy = co->attach; + char *data = co->buffer_in.data; + + RAW *newraw; + json_item *jlist = json_new_object(); + + data[co->buffer_in.length] = '\0'; + b64 = base64_encode(data, strlen(data)); + + json_set_property_strZ(jlist, "data", b64); + json_set_property_strN(jlist, "event", 5, "READ", 4); + json_set_property_objN(jlist, "pipe", 4, get_json_object_proxy(proxy)); + + newraw = forge_raw("PROXY_EVENT", jlist); + + proxy_post_raw(newraw, proxy, g_ape); + + free(b64); +} + +/* Not used for now */ +void proxy_connect_all(acetables *g_ape) +{ + ape_proxy *proxy = g_ape->proxy.list; + + while (proxy != NULL) { + if (proxy->state == PROXY_NOT_CONNECTED) { + proxy_connect(proxy, g_ape); + } + proxy = proxy->next; + } +} + +int proxy_connect(ape_proxy *proxy, acetables *g_ape) +{ + int sock; + struct sockaddr_in addr; + + + if (proxy == NULL || proxy->state != PROXY_NOT_CONNECTED || !strlen(proxy->sock.host->ip)) { + return 0; + } + + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) + { + printf("ERREUR: socket().. (%s line: %i)\n",__FILE__, __LINE__); + return 0; + } + + addr.sin_family = AF_INET; + addr.sin_port = htons(proxy->sock.port); + addr.sin_addr.s_addr = inet_addr(proxy->sock.host->ip); + memset(&(addr.sin_zero), '\0', 8); + + setnonblocking(sock); + + if (connect(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == 0 || errno != EINPROGRESS) { + return 0; + } + proxy->state = PROXY_IN_PROGRESS; + + events_add(g_ape->events, sock, EVENT_READ|EVENT_WRITE); + + return sock; + +} + +void proxy_onevent(ape_proxy *proxy, char *event, acetables *g_ape) +{ + RAW *newraw; + json_item *jlist = json_new_object(); + + json_set_property_strZ(jlist, "event", event); + json_set_property_objN(jlist, "pipe", 4, get_json_object_proxy(proxy)); + + newraw = forge_raw("PROXY_EVENT", jlist); + + proxy_post_raw(newraw, proxy, g_ape); +} + +void proxy_write(ape_proxy *proxy, char *data, acetables *g_ape) +{ + char *b64; + int len; + if (proxy->state != PROXY_CONNECTED) { + return; + } + + b64 = xmalloc(strlen(data)+1); + len = base64_decode(b64, data, strlen(data)+1); + + sendbin(proxy->sock.fd, b64, len, 0, g_ape); + free(b64); +} + + +json_item *get_json_object_proxy(ape_proxy *proxy) +{ + json_item *jstr = json_new_object(); + json_item *jprop = json_new_object(); + extend *eTmp = proxy->properties; + + json_set_property_strN(jstr, "pubid", 5, proxy->pipe->pubid, 32); + json_set_property_strN(jstr, "casttype", 8, "proxy", 5); + + json_set_property_strZ(jprop, "host", proxy->sock.host->host); + json_set_property_strZ(jprop, "ip", proxy->sock.host->ip); + json_set_property_intZ(jprop, "port", proxy->sock.port); + + while (eTmp != NULL) { + json_set_property_strZ(jprop, eTmp->key, eTmp->val); + eTmp = eTmp->next; + } + + json_set_property_objN(jstr, "properties", 10, jprop); + + return jstr; +} + diff --git a/ape-server/src/proxy.h b/ape-server/src/proxy.h new file mode 100755 index 0000000..258d41f --- /dev/null +++ b/ape-server/src/proxy.h @@ -0,0 +1,103 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* proxy.h */ + +#ifndef _PROXY_H +#define _PROXY_H + +#include "main.h" +#include "http.h" +#include "sock.h" +#include "pipe.h" + +typedef struct _ape_proxy_pipe ape_proxy_pipe; +struct _ape_proxy_pipe +{ + int allow_write; + char pipe[33]; + + struct _ape_proxy_pipe *next; +}; + + +typedef struct _ape_proxy ape_proxy; +struct _ape_proxy +{ + char identifier[33]; + + int eol; // FLushing at CRLF + + /* proxy pipe */ + struct _transpipe *pipe; + + struct { + struct _ape_proxy_cache *host; + int port; + int fd; + } sock; + + int state; + + int nlink; + /* List of allowed user/pipe */ + ape_proxy_pipe *to; + + struct _ape_proxy *next; + struct _ape_proxy *prev; + + struct _extend *properties; +}; + + +typedef struct _ape_proxy_cache ape_proxy_cache; +struct _ape_proxy_cache +{ + char *host; + char ip[16]; + + struct _ape_proxy_cache *next; +}; + +enum { + PROXY_NOT_CONNECTED = 0, + PROXY_IN_PROGRESS, + PROXY_CONNECTED, + PROXY_THROTTLED, + PROXY_TOFREE +}; + +ape_proxy *proxy_init(char *ident, char *host, int port, acetables *g_ape); +ape_proxy_cache *proxy_cache_gethostbyname(char *name, acetables *g_ape); +void proxy_cache_addip(char *name, char *ip, acetables *g_ape); +void proxy_attach(ape_proxy *proxy, char *pipe, int allow_write, acetables *g_ape); +int proxy_connect(ape_proxy *proxy, acetables *g_ape); +void proxy_connect_all(acetables *g_ape); +void proxy_onevent(ape_proxy *proxy, char *event, acetables *g_ape); +void proxy_process_eol(ape_socket *co, acetables *g_ape); +void proxy_init_from_conf(acetables *g_ape); +ape_proxy *proxy_init_by_host_port(char *host, char *port, acetables *g_ape); +json_item *get_json_object_proxy(ape_proxy *proxy); +ape_proxy *proxy_are_linked(char *pubid, char *pubid_proxy, acetables *g_ape); +void proxy_write(ape_proxy *proxy, char *data, acetables *g_ape); +void proxy_detach(struct _transpipe *unlinker, struct _transpipe *tproxy, acetables *g_ape); +void proxy_shutdown(ape_proxy *proxy, acetables *g_ape); + +#endif + diff --git a/ape-server/src/raw.c b/ape-server/src/raw.c new file mode 100755 index 0000000..e3c3638 --- /dev/null +++ b/ape-server/src/raw.c @@ -0,0 +1,439 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* raw.c */ + +#include "raw.h" +#include "users.h" +#include "channel.h" +#include "proxy.h" +#include "utils.h" +#include "plugins.h" +#include "pipe.h" +#include "transports.h" + +RAW *forge_raw(const char *raw, json_item *jlist) +{ + RAW *new_raw; + char unixtime[16]; + struct jsontring *string; + + json_item *jstruct = NULL; + + sprintf(unixtime, "%li", time(NULL)); + + jstruct = json_new_object(); + + json_set_property_strN(jstruct, "time", 4, unixtime, strlen(unixtime)); + json_set_property_strN(jstruct, "raw", 3, raw, strlen(raw)); + json_set_property_objN(jstruct, "data", 4, jlist); + + string = json_to_string(jstruct, NULL, 1); + + new_raw = xmalloc(sizeof(*new_raw)); + new_raw->len = string->len; + new_raw->next = NULL; + new_raw->priority = RAW_PRI_LO; + new_raw->refcount = 0; + + new_raw->data = string->jstring; + + free(string); + + return new_raw; +} + +int free_raw(RAW *fraw) +{ + if (--(fraw->refcount) <= 0) { + free(fraw->data); + free(fraw); + + return 0; + } + return fraw->refcount; +} + +RAW *copy_raw(RAW *input) +{ + RAW *new_raw; + + new_raw = xmalloc(sizeof(*new_raw)); + new_raw->len = input->len; + new_raw->next = input->next; + new_raw->priority = input->priority; + new_raw->refcount = 0; + new_raw->data = xmalloc(sizeof(char) * (new_raw->len + 1)); + + memcpy(new_raw->data, input->data, new_raw->len + 1); + + return new_raw; +} + +RAW *copy_raw_z(RAW *input) +{ + (input->refcount)++; + + return input; +} + + +/************* Users related functions ****************/ + +/* Post raw to a subuser */ +void post_raw_sub(RAW *raw, subuser *sub, acetables *g_ape) +{ + FIRE_EVENT_NULL(post_raw_sub, raw, sub, g_ape); + + int add_size = 16; + struct _raw_pool_user *pool = (raw->priority == RAW_PRI_LO ? &sub->raw_pools.low : &sub->raw_pools.high); + + if (++pool->nraw == pool->size) { + pool->size += add_size; + expend_raw_pool(pool->rawfoot, add_size); + } + + pool->rawfoot->raw = raw; + pool->rawfoot = pool->rawfoot->next; + + (sub->raw_pools.nraw)++; + +} + +/* Post raw to a user and propagate it to all of it's subuser */ +void post_raw(RAW *raw, USERS *user, acetables *g_ape) +{ + subuser *sub = user->subuser; + while (sub != NULL) { + post_raw_sub(copy_raw_z(raw), sub, g_ape); + sub = sub->next; + } +} + +/* Post raw to a user and propagate it to all of it's subuser with *sub exception */ +void post_raw_restricted(RAW *raw, USERS *user, subuser *sub, acetables *g_ape) +{ + subuser *tSub = user->subuser; + + if (sub == NULL) { + return; + } + while (tSub != NULL) { + if (sub != tSub) { + post_raw_sub(copy_raw_z(raw), tSub, g_ape); + } + tSub = tSub->next; + } +} + +/************* Channels related functions ****************/ + +/* Post raw to a channel and propagate it to all of it's users */ +void post_raw_channel(RAW *raw, struct CHANNEL *chan, acetables *g_ape) +{ + userslist *list; + + if (chan == NULL || raw == NULL || chan->head == NULL) { + return; + } + list = chan->head; + while (list) { + post_raw(raw, list->userinfo, g_ape); + list = list->next; + } + +} + +/* Post raw to a channel and propagate it to all of it's users with a *ruser exception */ +void post_raw_channel_restricted(RAW *raw, struct CHANNEL *chan, USERS *ruser, acetables *g_ape) +{ + userslist *list; + + if (chan == NULL || raw == NULL || chan->head == NULL) { + return; + } + list = chan->head; + + while (list) { + if (list->userinfo != ruser) { + post_raw(raw, list->userinfo, g_ape); + } + list = list->next; + } +} + + +/************* Proxys related functions ****************/ + +/* Post raw to a proxy and propagate it to all of it's attached users */ +void proxy_post_raw(RAW *raw, ape_proxy *proxy, acetables *g_ape) +{ + ape_proxy_pipe *to = proxy->to; + transpipe *pipe; + + while (to != NULL) { + pipe = get_pipe(to->pipe, g_ape); + if (pipe != NULL && pipe->type == USER_PIPE) { + post_raw(raw, pipe->pipe, g_ape); + } else { + ;// + } + to = to->next; + } + +} + +/* to manage subuser use post_to_pipe() instead */ +int post_raw_pipe(RAW *raw, const char *pipe, acetables *g_ape) +{ + transpipe *spipe; + + if ((spipe = get_pipe(pipe, g_ape)) != NULL) { + + if (spipe->type == CHANNEL_PIPE) { + post_raw_channel(raw, spipe->pipe, g_ape); + return 1; + } else { + post_raw(raw, spipe->pipe, g_ape); + return 1; + } + } + return 0; +} + +int post_to_pipe(json_item *jlist, const char *rawname, const char *pipe, subuser *from, acetables *g_ape) +{ + USERS *sender = from->user; + transpipe *recver = get_pipe_strict(pipe, sender, g_ape); + json_item *jlist_copy = NULL; + RAW *newraw; + + if (sender != NULL) { + if (recver == NULL) { + send_error(sender, "UNKNOWN_PIPE", "109", g_ape); + return 0; + } + json_set_property_objN(jlist, "from", 4, get_json_object_user(sender)); + + } + + if (sender != NULL && sender->nsub > 1) { + jlist_copy = json_item_copy(jlist, NULL); + + json_set_property_objN(jlist_copy, "pipe", 4, get_json_object_pipe(recver)); + newraw = forge_raw(rawname, jlist_copy); + post_raw_restricted(newraw, sender, from, g_ape); + } + switch(recver->type) { + case USER_PIPE: + json_set_property_objN(jlist, "pipe", 4, get_json_object_user(sender)); + newraw = forge_raw(rawname, jlist); + post_raw(newraw, recver->pipe, g_ape); + break; + case CHANNEL_PIPE: + if (((CHANNEL*)recver->pipe)->head != NULL && ((CHANNEL*)recver->pipe)->head->next != NULL) { + json_set_property_objN(jlist, "pipe", 4, get_json_object_channel(recver->pipe)); + newraw = forge_raw(rawname, jlist); + post_raw_channel_restricted(newraw, recver->pipe, sender, g_ape); + } + break; + case CUSTOM_PIPE: + json_set_property_objN(jlist, "pipe", 4, get_json_object_user(sender)); + post_json_custom(jlist, sender, recver, g_ape); + break; + default: + break; + } + + return 1; +} + + +int send_raw_inline(ape_socket *client, transport_t transport, RAW *raw, acetables *g_ape) +{ + struct _transport_properties *properties; + int finish = 1; + + properties = transport_get_properties(transport, g_ape); + + switch(transport) { + case TRANSPORT_XHRSTREAMING: + finish &= http_send_headers(NULL, HEADER_XHR, HEADER_XHR_LEN, client, g_ape); + break; + case TRANSPORT_SSE_LONGPOLLING: + finish &= http_send_headers(NULL, HEADER_SSE, HEADER_SSE_LEN, client, g_ape); + break; + case TRANSPORT_WEBSOCKET: + break; + default: + finish &= http_send_headers(NULL, HEADER_DEFAULT, HEADER_DEFAULT_LEN, client, g_ape); + break; + } + + if (properties != NULL && properties->padding.left.val != NULL) { + finish &= sendbin(client->fd, properties->padding.left.val, properties->padding.left.len, 0, g_ape); + } + + finish &= sendbin(client->fd, "[", 1, 0, g_ape); + + finish &= sendbin(client->fd, raw->data, raw->len, 0, g_ape); + + finish &= sendbin(client->fd, "]", 1, 0, g_ape); + + if (properties != NULL && properties->padding.right.val != NULL) { + finish &= sendbin(client->fd, properties->padding.right.val, properties->padding.right.len, 0, g_ape); + } + + free_raw(raw); + + return finish; +} + +/* + Send queue to socket +*/ +int send_raws(subuser *user, acetables *g_ape) +{ + int finish = 1, state = 0; + struct _raw_pool *pool; + struct _transport_properties *properties; + + if (user->raw_pools.nraw == 0) { + return 1; + } + + PACK_TCP(user->client->fd); /* Activate TCP_CORK */ + + properties = transport_get_properties(user->user->transport, g_ape); + + if (!user->headers.sent) { + user->headers.sent = 1; + + switch(user->user->transport) { + case TRANSPORT_XHRSTREAMING: + finish &= http_send_headers(user->headers.content, HEADER_XHR, HEADER_XHR_LEN, user->client, g_ape); + break; + case TRANSPORT_SSE_LONGPOLLING: + finish &= http_send_headers(user->headers.content, HEADER_SSE, HEADER_SSE_LEN, user->client, g_ape); + break; + case TRANSPORT_WEBSOCKET: + break; + default: + finish &= http_send_headers(user->headers.content, HEADER_DEFAULT, HEADER_DEFAULT_LEN, user->client, g_ape); + break; + } + + } + + if (properties != NULL && properties->padding.left.val != NULL) { + finish &= sendbin(user->client->fd, properties->padding.left.val, properties->padding.left.len, 0, g_ape); + } + + finish &= sendbin(user->client->fd, "[", 1, 0, g_ape); + + if (user->raw_pools.high.nraw) { + pool = user->raw_pools.high.rawfoot->prev; + } else { + pool = user->raw_pools.low.rawhead; + state = 1; + } + + while (pool->raw != NULL) { + struct _raw_pool *pool_next = (state ? pool->next : pool->prev); + + finish &= sendbin(user->client->fd, pool->raw->data, pool->raw->len, 0, g_ape); + + if ((pool_next != NULL && pool_next->raw != NULL) || (!state && user->raw_pools.low.nraw)) { + finish &= sendbin(user->client->fd, ",", 1, 0, g_ape); + } else { + finish &= sendbin(user->client->fd, "]", 1, 0, g_ape); + + if (properties != NULL && properties->padding.right.val != NULL) { + finish &= sendbin(user->client->fd, properties->padding.right.val, properties->padding.right.len, 0, g_ape); + } + } + + free_raw(pool->raw); + pool->raw = NULL; + + pool = pool_next; + + if ((pool == NULL || pool->raw == NULL) && !state) { + pool = user->raw_pools.low.rawhead; + state = 1; + } + } + + user->raw_pools.high.nraw = 0; + user->raw_pools.low.nraw = 0; + user->raw_pools.nraw = 0; + + user->raw_pools.high.rawfoot = user->raw_pools.high.rawhead; + user->raw_pools.low.rawfoot = user->raw_pools.low.rawhead; + + FLUSH_TCP(user->client->fd); + + return finish; +} + +struct _raw_pool *init_raw_pool(int n) +{ + int i; + struct _raw_pool *pool = xmalloc(sizeof(*pool) * n); + + for (i = 0; i < n; i++) { + pool[i].raw = NULL; + pool[i].next = (i == n-1 ? NULL : &pool[i+1]); + pool[i].prev = (i == 0 ? NULL : &pool[i-1]); + pool[i].start = (i == 0); + } + + return pool; +} + +struct _raw_pool *expend_raw_pool(struct _raw_pool *ptr, int n) +{ + struct _raw_pool *pool = init_raw_pool(n); + + ptr->next = pool; + pool->prev = ptr; + + return pool; +} + +void destroy_raw_pool(struct _raw_pool *ptr) +{ + struct _raw_pool *pool = ptr, *tpool = NULL; + + while (pool != NULL) { + if (pool->raw != NULL) { + free_raw(pool->raw); + } + if (pool->start) { + if (tpool != NULL) { + free(tpool); + } + tpool = pool; + } + pool = pool->next; + } + if (tpool != NULL) { + free(tpool); + } +} diff --git a/ape-server/src/raw.h b/ape-server/src/raw.h new file mode 100755 index 0000000..0a0d5f1 --- /dev/null +++ b/ape-server/src/raw.h @@ -0,0 +1,70 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* raw.h */ + +#ifndef _RAW_H +#define _RAW_H + +#include "main.h" +#include "users.h" +#include "channel.h" +#include "proxy.h" +#include "transports.h" +#include "sock.h" + +typedef enum { + RAW_PRI_LO, + RAW_PRI_HI +} raw_priority_t; + +typedef struct RAW +{ + char *data; + struct RAW *next; + + raw_priority_t priority; + + int len; + int refcount; +} RAW; + + +RAW *forge_raw(const char *raw, json_item *jlist); +int free_raw(RAW *fraw); +RAW *copy_raw(RAW *input); +RAW *copy_raw_z(RAW *input); + +void post_raw(RAW *raw, USERS *user, acetables *g_ape); +void post_raw_sub(RAW *raw, subuser *sub, acetables *g_ape); +void post_raw_channel(RAW *raw, struct CHANNEL *chan, acetables *g_ape); +void post_raw_restricted(RAW *raw, USERS *user, subuser *sub, acetables *g_ape); +void post_raw_channel_restricted(RAW *raw, struct CHANNEL *chan, USERS *ruser, acetables *g_ape); +void proxy_post_raw(RAW *raw, ape_proxy *proxy, acetables *g_ape); +int post_raw_pipe(RAW *raw, const char *pipe, acetables *g_ape); +int post_to_pipe(json_item *jlist, const char *rawname, const char *pipe, subuser *from, acetables *g_ape); + +int send_raw_inline(ape_socket *client, transport_t transport, RAW *raw, acetables *g_ape); +int send_raws(subuser *user, acetables *g_ape); + +struct _raw_pool *init_raw_pool(int n); +struct _raw_pool *expend_raw_pool(struct _raw_pool *ptr, int n); +void destroy_raw_pool(struct _raw_pool *ptr); + +#endif diff --git a/ape-server/src/servers.c b/ape-server/src/servers.c new file mode 100755 index 0000000..b7f8c53 --- /dev/null +++ b/ape-server/src/servers.c @@ -0,0 +1,89 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* servers.c */ + +#include "servers.h" +#include "sock.h" +#include "utils.h" +#include "config.h" +#include "http.h" +#include "handle_http.h" +#include "transports.h" +#include "parser.h" +#include "main.h" + +static void ape_read(ape_socket *co, ape_buffer *buffer, size_t offset, acetables *g_ape) +{ + co->parser.parser_func(co, g_ape); +} + +static void ape_sent(ape_socket *co, acetables *g_ape) +{ + if (co->attach != NULL && ((subuser *)(co->attach))->burn_after_writing) { + transport_data_completly_sent((subuser *)(co->attach), ((subuser *)(co->attach))->user->transport); + ((subuser *)(co->attach))->burn_after_writing = 0; + } +} + +static void ape_disconnect(ape_socket *co, acetables *g_ape) +{ + if (co->attach != NULL) { + + if (((subuser *)(co->attach))->wait_for_free == 1) { + free(co->attach); + co->attach = NULL; + return; + } + + if (co->fd == ((subuser *)(co->attach))->client->fd) { + + ((subuser *)(co->attach))->headers.sent = 0; + ((subuser *)(co->attach))->state = ADIED; + http_headers_free(((subuser *)(co->attach))->headers.content); + ((subuser *)(co->attach))->headers.content = NULL; + if (((subuser *)(co->attach))->user->istmp) { + deluser(((subuser *)(co->attach))->user, g_ape); + co->attach = NULL; + } + } + + } +} + +static void ape_onaccept(ape_socket *co, acetables *g_ape) +{ + co->parser = parser_init_http(co); +} + +int servers_init(acetables *g_ape) +{ + ape_socket *main_server; + if ((main_server = ape_listen(atoi(CONFIG_VAL(Server, port, g_ape->srv)), CONFIG_VAL(Server, ip_listen, g_ape->srv), g_ape)) == NULL) { + return 0; + } + + main_server->callbacks.on_read = ape_read; + main_server->callbacks.on_disconnect = ape_disconnect; + main_server->callbacks.on_data_completly_sent = ape_sent; + main_server->callbacks.on_accept = ape_onaccept; + + return main_server->fd; +} + diff --git a/ape-server/src/servers.h b/ape-server/src/servers.h new file mode 100755 index 0000000..4136dc4 --- /dev/null +++ b/ape-server/src/servers.h @@ -0,0 +1,29 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* servers.h */ + +#ifndef _SERVERS_H +#define _SERVERS_H + +#include "main.h" + +int servers_init(acetables *g_ape); + +#endif diff --git a/ape-server/src/sha1.c b/ape-server/src/sha1.c new file mode 100755 index 0000000..bc56bd1 --- /dev/null +++ b/ape-server/src/sha1.c @@ -0,0 +1,371 @@ +/* + * FIPS-180-1 compliant SHA-1 implementation + * + * Copyright (C) 2003-2006 Christophe Devine + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License, version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +/* + * The SHA-1 standard was published by NIST in 1993. + * + * http://www.itl.nist.gov/fipspubs/fip180-1.htm + */ + + +#include +#include + +#include "sha1.h" + +/* + * 32-bit integer manipulation macros (big endian) + */ +#ifndef GET_UINT32_BE +#define GET_UINT32_BE(n,b,i) \ +{ \ + (n) = ( (ulong) (b)[(i) ] << 24 ) \ + | ( (ulong) (b)[(i) + 1] << 16 ) \ + | ( (ulong) (b)[(i) + 2] << 8 ) \ + | ( (ulong) (b)[(i) + 3] ); \ +} +#endif +#ifndef PUT_UINT32_BE +#define PUT_UINT32_BE(n,b,i) \ +{ \ + (b)[(i) ] = (uchar) ( (n) >> 24 ); \ + (b)[(i) + 1] = (uchar) ( (n) >> 16 ); \ + (b)[(i) + 2] = (uchar) ( (n) >> 8 ); \ + (b)[(i) + 3] = (uchar) ( (n) ); \ +} +#endif + +/* + * Core SHA-1 functions + */ +void sha1_starts( sha1_context *ctx ) +{ + ctx->total[0] = 0; + ctx->total[1] = 0; + + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; + ctx->state[4] = 0xC3D2E1F0; +} + +void sha1_process( sha1_context *ctx, uchar data[64] ) +{ + ulong temp, W[16], A, B, C, D, E; + + GET_UINT32_BE( W[0], data, 0 ); + GET_UINT32_BE( W[1], data, 4 ); + GET_UINT32_BE( W[2], data, 8 ); + GET_UINT32_BE( W[3], data, 12 ); + GET_UINT32_BE( W[4], data, 16 ); + GET_UINT32_BE( W[5], data, 20 ); + GET_UINT32_BE( W[6], data, 24 ); + GET_UINT32_BE( W[7], data, 28 ); + GET_UINT32_BE( W[8], data, 32 ); + GET_UINT32_BE( W[9], data, 36 ); + GET_UINT32_BE( W[10], data, 40 ); + GET_UINT32_BE( W[11], data, 44 ); + GET_UINT32_BE( W[12], data, 48 ); + GET_UINT32_BE( W[13], data, 52 ); + GET_UINT32_BE( W[14], data, 56 ); + GET_UINT32_BE( W[15], data, 60 ); + +#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) + +#define R(t) \ +( \ + temp = W[(t - 3) & 0x0F] ^ W[(t - 8) & 0x0F] ^ \ + W[(t - 14) & 0x0F] ^ W[ t & 0x0F], \ + ( W[t & 0x0F] = S(temp,1) ) \ +) + +#define P(a,b,c,d,e,x) \ +{ \ + e += S(a,5) + F(b,c,d) + K + x; b = S(b,30); \ +} + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + E = ctx->state[4]; + +#define F(x,y,z) (z ^ (x & (y ^ z))) +#define K 0x5A827999 + + P( A, B, C, D, E, W[0] ); + P( E, A, B, C, D, W[1] ); + P( D, E, A, B, C, W[2] ); + P( C, D, E, A, B, W[3] ); + P( B, C, D, E, A, W[4] ); + P( A, B, C, D, E, W[5] ); + P( E, A, B, C, D, W[6] ); + P( D, E, A, B, C, W[7] ); + P( C, D, E, A, B, W[8] ); + P( B, C, D, E, A, W[9] ); + P( A, B, C, D, E, W[10] ); + P( E, A, B, C, D, W[11] ); + P( D, E, A, B, C, W[12] ); + P( C, D, E, A, B, W[13] ); + P( B, C, D, E, A, W[14] ); + P( A, B, C, D, E, W[15] ); + P( E, A, B, C, D, R(16) ); + P( D, E, A, B, C, R(17) ); + P( C, D, E, A, B, R(18) ); + P( B, C, D, E, A, R(19) ); + +#undef K +#undef F + +#define F(x,y,z) (x ^ y ^ z) +#define K 0x6ED9EBA1 + + P( A, B, C, D, E, R(20) ); + P( E, A, B, C, D, R(21) ); + P( D, E, A, B, C, R(22) ); + P( C, D, E, A, B, R(23) ); + P( B, C, D, E, A, R(24) ); + P( A, B, C, D, E, R(25) ); + P( E, A, B, C, D, R(26) ); + P( D, E, A, B, C, R(27) ); + P( C, D, E, A, B, R(28) ); + P( B, C, D, E, A, R(29) ); + P( A, B, C, D, E, R(30) ); + P( E, A, B, C, D, R(31) ); + P( D, E, A, B, C, R(32) ); + P( C, D, E, A, B, R(33) ); + P( B, C, D, E, A, R(34) ); + P( A, B, C, D, E, R(35) ); + P( E, A, B, C, D, R(36) ); + P( D, E, A, B, C, R(37) ); + P( C, D, E, A, B, R(38) ); + P( B, C, D, E, A, R(39) ); + +#undef K +#undef F + +#define F(x,y,z) ((x & y) | (z & (x | y))) +#define K 0x8F1BBCDC + + P( A, B, C, D, E, R(40) ); + P( E, A, B, C, D, R(41) ); + P( D, E, A, B, C, R(42) ); + P( C, D, E, A, B, R(43) ); + P( B, C, D, E, A, R(44) ); + P( A, B, C, D, E, R(45) ); + P( E, A, B, C, D, R(46) ); + P( D, E, A, B, C, R(47) ); + P( C, D, E, A, B, R(48) ); + P( B, C, D, E, A, R(49) ); + P( A, B, C, D, E, R(50) ); + P( E, A, B, C, D, R(51) ); + P( D, E, A, B, C, R(52) ); + P( C, D, E, A, B, R(53) ); + P( B, C, D, E, A, R(54) ); + P( A, B, C, D, E, R(55) ); + P( E, A, B, C, D, R(56) ); + P( D, E, A, B, C, R(57) ); + P( C, D, E, A, B, R(58) ); + P( B, C, D, E, A, R(59) ); + +#undef K +#undef F + +#define F(x,y,z) (x ^ y ^ z) +#define K 0xCA62C1D6 + + P( A, B, C, D, E, R(60) ); + P( E, A, B, C, D, R(61) ); + P( D, E, A, B, C, R(62) ); + P( C, D, E, A, B, R(63) ); + P( B, C, D, E, A, R(64) ); + P( A, B, C, D, E, R(65) ); + P( E, A, B, C, D, R(66) ); + P( D, E, A, B, C, R(67) ); + P( C, D, E, A, B, R(68) ); + P( B, C, D, E, A, R(69) ); + P( A, B, C, D, E, R(70) ); + P( E, A, B, C, D, R(71) ); + P( D, E, A, B, C, R(72) ); + P( C, D, E, A, B, R(73) ); + P( B, C, D, E, A, R(74) ); + P( A, B, C, D, E, R(75) ); + P( E, A, B, C, D, R(76) ); + P( D, E, A, B, C, R(77) ); + P( C, D, E, A, B, R(78) ); + P( B, C, D, E, A, R(79) ); + +#undef K +#undef F + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; + ctx->state[4] += E; +} + +void sha1_update( sha1_context *ctx, uchar *input, uint length ) +{ + ulong left, fill; + + if( ! length ) return; + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += length; + ctx->total[0] &= 0xFFFFFFFF; + + if( ctx->total[0] < length ) + ctx->total[1]++; + + if( left && length >= fill ) + { + memcpy( (void *) (ctx->buffer + left), + (void *) input, fill ); + sha1_process( ctx, ctx->buffer ); + length -= fill; + input += fill; + left = 0; + } + + while( length >= 64 ) + { + sha1_process( ctx, input ); + length -= 64; + input += 64; + } + + if( length ) + { + memcpy( (void *) (ctx->buffer + left), + (void *) input, length ); + } +} + +static uchar sha1_padding[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +void sha1_finish( sha1_context *ctx, uchar digest[20] ) +{ + ulong last, padn; + ulong high, low; + uchar msglen[8]; + + high = ( ctx->total[0] >> 29 ) + | ( ctx->total[1] << 3 ); + low = ( ctx->total[0] << 3 ); + + PUT_UINT32_BE( high, msglen, 0 ); + PUT_UINT32_BE( low, msglen, 4 ); + + last = ctx->total[0] & 0x3F; + padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); + + sha1_update( ctx, sha1_padding, padn ); + sha1_update( ctx, msglen, 8 ); + + PUT_UINT32_BE( ctx->state[0], digest, 0 ); + PUT_UINT32_BE( ctx->state[1], digest, 4 ); + PUT_UINT32_BE( ctx->state[2], digest, 8 ); + PUT_UINT32_BE( ctx->state[3], digest, 12 ); + PUT_UINT32_BE( ctx->state[4], digest, 16 ); +} + +/* + * Output SHA-1(file contents), returns 0 if successful. + */ +int sha1_file( char *filename, uchar digest[20] ) +{ + FILE *f; + size_t n; + sha1_context ctx; + uchar buf[1024]; + + if( ( f = fopen( filename, "rb" ) ) == NULL ) + return( 1 ); + + sha1_starts( &ctx ); + + while( ( n = fread( buf, 1, sizeof( buf ), f ) ) > 0 ) + sha1_update( &ctx, buf, (uint) n ); + + sha1_finish( &ctx, digest ); + + fclose( f ); + return( 0 ); +} + +/* + * Output SHA-1(buf) + */ +void sha1_csum( uchar *buf, uint buflen, uchar digest[20] ) +{ + sha1_context ctx; + + sha1_starts( &ctx ); + sha1_update( &ctx, buf, buflen ); + sha1_finish( &ctx, digest ); +} + +/* + * Output HMAC-SHA-1(key,buf) + */ +void sha1_hmac( uchar *key, uint keylen, uchar *buf, uint buflen, + uchar digest[20] ) +{ + uint i; + sha1_context ctx; + uchar k_ipad[64]; + uchar k_opad[64]; + uchar tmpbuf[20]; + + memset( k_ipad, 0x36, 64 ); + memset( k_opad, 0x5C, 64 ); + + for( i = 0; i < keylen; i++ ) + { + if( i >= 64 ) break; + + k_ipad[i] ^= key[i]; + k_opad[i] ^= key[i]; + } + + sha1_starts( &ctx ); + sha1_update( &ctx, k_ipad, 64 ); + sha1_update( &ctx, buf, buflen ); + sha1_finish( &ctx, tmpbuf ); + + sha1_starts( &ctx ); + sha1_update( &ctx, k_opad, 64 ); + sha1_update( &ctx, tmpbuf, 20 ); + sha1_finish( &ctx, digest ); + + memset( k_ipad, 0, 64 ); + memset( k_opad, 0, 64 ); + memset( tmpbuf, 0, 20 ); + memset( &ctx, 0, sizeof( sha1_context ) ); +} diff --git a/ape-server/src/sha1.h b/ape-server/src/sha1.h new file mode 100755 index 0000000..447e2b5 --- /dev/null +++ b/ape-server/src/sha1.h @@ -0,0 +1,52 @@ +#ifndef _SHA1_H +#define _SHA1_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _STD_TYPES +#define _STD_TYPES + +#define uchar unsigned char +#define uint unsigned int +#define ulong unsigned long int + +#endif + +typedef struct +{ + ulong total[2]; + ulong state[5]; + uchar buffer[64]; +} +sha1_context; + +/* + * Core SHA-1 functions + */ +void sha1_starts( sha1_context *ctx ); +void sha1_update( sha1_context *ctx, uchar *input, uint length ); +void sha1_finish( sha1_context *ctx, uchar digest[20] ); + +/* + * Output SHA-1(file contents), returns 0 if successful. + */ +int sha1_file( char *filename, uchar digest[20] ); + +/* + * Output SHA-1(buf) + */ +void sha1_csum( uchar *buf, uint buflen, uchar digest[20] ); + +/* + * Output HMAC-SHA-1(key,buf) + */ +void sha1_hmac( uchar *key, uint keylen, uchar *buf, uint buflen, + uchar digest[20] ); + + +#ifdef __cplusplus +} +#endif +#endif /* sha1.h */ diff --git a/ape-server/src/sock.c b/ape-server/src/sock.c new file mode 100755 index 0000000..51e8756 --- /dev/null +++ b/ape-server/src/sock.c @@ -0,0 +1,609 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* sock.c */ + +#include +#include +#include +#include + +#include "sock.h" +#include "http.h" +#include "users.h" + +#include "utils.h" +#include "ticks.h" +#include "proxy.h" +#include "config.h" +#include "raw.h" +#include "events.h" +#include "transports.h" +#include "handle_http.h" +#include "dns.h" +#include "log.h" +#include "parser.h" + +static int sendqueue(int sock, acetables *g_ape); + + +static void growup(int *basemem, ape_socket ***conn_ptr, struct _fdevent *ev, struct _socks_bufout **bufout) +{ + int old_basemem = *basemem; + *basemem *= 2; + + events_growup(ev); + + *conn_ptr = xrealloc(*conn_ptr, sizeof(ape_socket) * (*basemem)); + memset(&((*conn_ptr)[*basemem - old_basemem]), 0, sizeof(**conn_ptr) * (*basemem - old_basemem)); + + *bufout = xrealloc(*bufout, sizeof(struct _socks_bufout) * (*basemem)); + memset(&((*bufout)[*basemem - old_basemem]), 0, sizeof(**bufout) * (*basemem - old_basemem)); +} + +ape_socket *ape_listen(unsigned int port, char *listen_ip, acetables *g_ape) +{ + int sock; + struct sockaddr_in addr; + int reuse_addr = 1; + + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) + { + ape_log(APE_ERR, __FILE__, __LINE__, g_ape, + "ape_listen() - socket()"); + return NULL; + } + + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + //addr.sin_addr.s_addr = inet_addr(CONFIG_VAL(Server, ip_listen, g_ape->srv)); + + addr.sin_addr.s_addr = inet_addr(listen_ip); + + memset(&(addr.sin_zero), '\0', 8); + + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr)); + + if (bind(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1) + { + ape_log(APE_ERR, __FILE__, __LINE__, g_ape, + "ape_listen() - bind()"); + printf("ERREUR: bind(%i) (non-root ?).. (%s line: %i)\n", port, __FILE__, __LINE__); + return NULL; + } + + if (listen(sock, 2048) == -1) + { + ape_log(APE_ERR, __FILE__, __LINE__, g_ape, + "ape_listen() - listen()"); + return NULL; + } + + setnonblocking(sock); + + prepare_ape_socket(sock, g_ape); + + g_ape->co[sock]->fd = sock; + g_ape->co[sock]->state = STREAM_ONLINE; + g_ape->co[sock]->stream_type = STREAM_SERVER; + + events_add(g_ape->events, sock, EVENT_READ); + + return g_ape->co[sock]; +} + +ape_socket *ape_connect(char *ip, int port, acetables *g_ape) +{ + int sock, ret; + struct sockaddr_in addr; + + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + ape_log(APE_ERR, __FILE__, __LINE__, g_ape, + "ape_connect() - socket() : %s", strerror(errno)); + return NULL; + } + + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = inet_addr(ip); + memset(&(addr.sin_zero), '\0', 8); + + setnonblocking(sock); + + if (connect(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == 0 || errno != EINPROGRESS) { + return NULL; + } + + prepare_ape_socket(sock, g_ape); + + g_ape->co[sock]->buffer_in.data = xmalloc(sizeof(char) * (DEFAULT_BUFFER_SIZE + 1)); + g_ape->co[sock]->buffer_in.size = DEFAULT_BUFFER_SIZE; + + g_ape->co[sock]->fd = sock; + g_ape->co[sock]->state = STREAM_PROGRESS; + g_ape->co[sock]->stream_type = STREAM_OUT; + + g_ape->bufout[sock].fd = sock; + g_ape->bufout[sock].buf = NULL; + g_ape->bufout[sock].buflen = 0; + g_ape->bufout[sock].allocsize = 0; + + ret = events_add(g_ape->events, sock, EVENT_READ|EVENT_WRITE); + + return g_ape->co[sock]; +} + +static void ape_connect_name_cb(char *ip, void *data, acetables *g_ape) +{ + struct _ape_sock_connect_async *asca = data; + ape_socket *sock; + + if ((sock = ape_connect(ip, asca->port, g_ape)) != NULL) { + + sock->attach = asca->sock->attach; + + sock->callbacks.on_accept = asca->sock->callbacks.on_accept; + sock->callbacks.on_connect = asca->sock->callbacks.on_connect; + sock->callbacks.on_disconnect = asca->sock->callbacks.on_disconnect; + sock->callbacks.on_read = asca->sock->callbacks.on_read; + sock->callbacks.on_read_lf = asca->sock->callbacks.on_read_lf; + sock->callbacks.on_data_completly_sent = asca->sock->callbacks.on_data_completly_sent; + sock->callbacks.on_write = asca->sock->callbacks.on_write; + } + + free(ip); + free(asca->sock); + free(asca); + +} + +void ape_connect_name(char *name, int port, ape_socket *pattern, acetables *g_ape) +{ + struct _ape_sock_connect_async *asca = xmalloc(sizeof(*asca)); + + asca->sock = pattern; + asca->port = port; + + ape_gethostbyname(name, ape_connect_name_cb, asca, g_ape); +} + +void setnonblocking(int fd) +{ + int old_flags; + + old_flags = fcntl(fd, F_GETFL, 0); + + if (!(old_flags & O_NONBLOCK)) { + old_flags |= O_NONBLOCK; + } + fcntl(fd, F_SETFL, old_flags); +} + +/* Close socket but preserve ape_socket struct */ +void close_socket(int fd, acetables *g_ape) +{ + ape_socket *co = g_ape->co[fd]; + + if (g_ape->bufout[fd].buf != NULL) { + free(g_ape->bufout[fd].buf); + g_ape->bufout[fd].buflen = 0; + g_ape->bufout[fd].buf = NULL; + g_ape->bufout[fd].allocsize = 0; + } + + if (co->buffer_in.data != NULL) { + free(co->buffer_in.data); + } + + if (co->parser.data != NULL) { + parser_destroy(&co->parser); + } + + close(fd); +} + +/* Create socket struct if not exists */ +void prepare_ape_socket(int fd, acetables *g_ape) +{ + while (fd >= g_ape->basemem) { + /* Increase connection & events size */ + growup(&g_ape->basemem, &g_ape->co, g_ape->events, &g_ape->bufout); + } + + if (g_ape->co[fd] == NULL) { + g_ape->co[fd] = xmalloc(sizeof(*g_ape->co[fd])); + } + + memset(g_ape->co[fd], 0, sizeof(*g_ape->co[fd])); +} + +#if 0 +static void check_idle(struct _socks_list *sl) +{ + int i = 0, x = 0; + long int current_time = time(NULL); + printf("Tick tac \n"); + for (i = 0; x < *sl->tfd; i++) { + if (sl->co[i].buffer.size) { + x++; + if (sl->co[i].attach == NULL && sl->co[i].idle <= current_time-TCP_TIMEOUT) { + shutdown(i, 2); + } + } + + } +} +#endif + +unsigned int sockroutine(acetables *g_ape) +{ + struct _socks_list sl; + + int new_fd, nfds, sin_size = sizeof(struct sockaddr_in), i, tfd = 0; + + struct timeval t_start, t_end; + long int ticks = 0, uticks = 0, lticks = 0; + struct sockaddr_in their_addr; + + //sl.co = co; + sl.tfd = &tfd; + + #if 0 + add_periodical(5, 0, check_idle, &sl, g_ape); + #endif + gettimeofday(&t_start, NULL); + while (server_is_running) { + /* Linux 2.6.25 provides a fd-driven timer system. It could be usefull to implement */ + int timeout_to_hang = get_first_timer_ms(g_ape); + nfds = events_poll(g_ape->events, timeout_to_hang); + + if (nfds < 0) { + ape_log(APE_ERR, __FILE__, __LINE__, g_ape, + "events_poll() : %s", strerror(errno)); + continue; + } + + if (nfds > 0) { + for (i = 0; i < nfds; i++) { + + int active_fd = events_get_current_fd(g_ape->events, i); + + if (g_ape->co[active_fd]->stream_type == STREAM_SERVER) { + int bitev = events_revent(g_ape->events, i); + + if (!(bitev & EVENT_READ)) { + /* Close server socket */ + close_socket(active_fd, g_ape); + continue; + } + + while (1) { + + //http_state http = {NULL, 0, -1, 0, 0, HTTP_NULL, 0, 0}; + + new_fd = accept(active_fd, + (struct sockaddr *)&their_addr, + (unsigned int *)&sin_size); + + if (new_fd == -1) { + break; + } + + prepare_ape_socket(new_fd, g_ape); + + strncpy(g_ape->co[new_fd]->ip_client, inet_ntoa(their_addr.sin_addr), 16); + + g_ape->co[new_fd]->buffer_in.data = xmalloc(sizeof(char) * (DEFAULT_BUFFER_SIZE + 1)); + g_ape->co[new_fd]->buffer_in.size = DEFAULT_BUFFER_SIZE; + + g_ape->co[new_fd]->idle = time(NULL); + g_ape->co[new_fd]->fd = new_fd; + + g_ape->co[new_fd]->state = STREAM_ONLINE; + g_ape->co[new_fd]->stream_type = STREAM_IN; + + g_ape->bufout[new_fd].fd = new_fd; + g_ape->bufout[new_fd].buf = NULL; + g_ape->bufout[new_fd].buflen = 0; + g_ape->bufout[new_fd].allocsize = 0; + + g_ape->co[new_fd]->callbacks.on_disconnect = g_ape->co[active_fd]->callbacks.on_disconnect; + g_ape->co[new_fd]->callbacks.on_read = g_ape->co[active_fd]->callbacks.on_read; + g_ape->co[new_fd]->callbacks.on_read_lf = g_ape->co[active_fd]->callbacks.on_read_lf; + g_ape->co[new_fd]->callbacks.on_data_completly_sent = g_ape->co[active_fd]->callbacks.on_data_completly_sent; + g_ape->co[new_fd]->callbacks.on_write = g_ape->co[active_fd]->callbacks.on_write; + + g_ape->co[new_fd]->attach = g_ape->co[active_fd]->attach; + + setnonblocking(new_fd); + + events_add(g_ape->events, new_fd, EVENT_READ|EVENT_WRITE); + + tfd++; + + if (g_ape->co[active_fd]->callbacks.on_accept != NULL) { + g_ape->co[active_fd]->callbacks.on_accept(g_ape->co[new_fd], g_ape); + } + + } + continue; + } else { + int readb = 0; + int bitev = events_revent(g_ape->events, i); + + if (bitev & EVENT_WRITE) { + + if (g_ape->co[active_fd]->stream_type == STREAM_OUT && g_ape->co[active_fd]->state == STREAM_PROGRESS) { + + int serror = 0, ret; + socklen_t serror_len = sizeof(serror); + + ret = getsockopt(active_fd, SOL_SOCKET, SO_ERROR, &serror, &serror_len); + + if (ret == 0 && serror == 0) { + + g_ape->co[active_fd]->state = STREAM_ONLINE; + if (g_ape->co[active_fd]->callbacks.on_connect != NULL) { + + g_ape->co[active_fd]->callbacks.on_connect(g_ape->co[active_fd], g_ape); + } + } else { /* This can happen ? epoll seems set EPOLLIN as if the host is disconnecting */ + + if (g_ape->co[active_fd]->callbacks.on_disconnect != NULL) { + g_ape->co[active_fd]->callbacks.on_disconnect(g_ape->co[active_fd], g_ape); + } + + close_socket(active_fd, g_ape); + tfd--; + } + } else if (g_ape->bufout[active_fd].buf != NULL) { + + if (sendqueue(active_fd, g_ape) == 1) { + + if (g_ape->co[active_fd]->callbacks.on_data_completly_sent != NULL) { + g_ape->co[active_fd]->callbacks.on_data_completly_sent(g_ape->co[active_fd], g_ape); + } + + if (g_ape->co[active_fd]->burn_after_writing) { + shutdown(active_fd, 2); + g_ape->co[active_fd]->burn_after_writing = 0; + } + + } + } else if (g_ape->co[active_fd]->stream_type == STREAM_DELEGATE) { + if (g_ape->co[active_fd]->callbacks.on_write != NULL) { + g_ape->co[active_fd]->callbacks.on_write(g_ape->co[active_fd], g_ape); + + } + } + } + + if (bitev & EVENT_READ) { + if (g_ape->co[active_fd]->stream_type == STREAM_DELEGATE) { + if (g_ape->co[active_fd]->callbacks.on_read != NULL) { + g_ape->co[active_fd]->callbacks.on_read(g_ape->co[active_fd], NULL, 0, g_ape); + continue; + } + } + do { + /* + TODO : Check if maximum data read can improve perf + Huge data may attempt to increase third parameter + */ + readb = read(active_fd, + g_ape->co[active_fd]->buffer_in.data + g_ape->co[active_fd]->buffer_in.length, + g_ape->co[active_fd]->buffer_in.size - g_ape->co[active_fd]->buffer_in.length); + + + if (readb == -1 && errno == EAGAIN) { + + if (g_ape->co[active_fd]->stream_type == STREAM_OUT) { + + //proxy_process_eol(&co[active_fd], g_ape); + //co[active_fd].buffer_in.length = 0; + } else { + // co[active_fd].buffer_in.data[co[active_fd].buffer_in.length] = '\0'; + } + break; + } else { + if (readb < 1) { + + if (g_ape->co[active_fd]->callbacks.on_disconnect != NULL) { + g_ape->co[active_fd]->callbacks.on_disconnect(g_ape->co[active_fd], g_ape); + } + + close_socket(active_fd, g_ape); + tfd--; + + break; + } else { + + g_ape->co[active_fd]->buffer_in.length += readb; + + /* realloc the buffer for the next read (x2) */ + if (g_ape->co[active_fd]->buffer_in.length == g_ape->co[active_fd]->buffer_in.size) { + g_ape->co[active_fd]->buffer_in.size *= 2; + + g_ape->co[active_fd]->buffer_in.data = xrealloc(g_ape->co[active_fd]->buffer_in.data, + sizeof(char) * (g_ape->co[active_fd]->buffer_in.size + 1)); + + } + if (g_ape->co[active_fd]->callbacks.on_read_lf != NULL) { + int eol, len = g_ape->co[active_fd]->buffer_in.length; + char *pBuf = g_ape->co[active_fd]->buffer_in.data; + + while ((eol = sneof(pBuf, len, 4096)) != -1) { + pBuf[eol-1] = '\0'; + g_ape->co[active_fd]->callbacks.on_read_lf(g_ape->co[active_fd], pBuf, g_ape); + pBuf = &pBuf[eol]; + len -= eol; + } + if (len > 4096 || !len) { + g_ape->co[active_fd]->buffer_in.length = 0; + } else if (len) { + memmove(g_ape->co[active_fd]->buffer_in.data, &g_ape->co[active_fd]->buffer_in.data[g_ape->co[active_fd]->buffer_in.length - len], len); + g_ape->co[active_fd]->buffer_in.length = len; + } + + } + + /* on_read can't get along with on_read_lf */ + if (g_ape->co[active_fd]->callbacks.on_read != NULL && g_ape->co[active_fd]->callbacks.on_read_lf == NULL) { + g_ape->co[active_fd]->callbacks.on_read(g_ape->co[active_fd], &g_ape->co[active_fd]->buffer_in, g_ape->co[active_fd]->buffer_in.length - readb, g_ape); + } + } + } + } while(readb >= 0); + } + } + } + } + + gettimeofday(&t_end, NULL); + + ticks = 0; + + uticks = 1000000L * (t_end.tv_sec - t_start.tv_sec); + uticks += (t_end.tv_usec - t_start.tv_usec); + t_start = t_end; + lticks += uticks; + /* Tic tac, tic tac */ + + while (lticks >= 1000) { + lticks -= 1000; + process_tick(g_ape); + } + } + + return 0; +} + +int sendf(int sock, acetables *g_ape, char *buf, ...) +{ + char *buff; + int len; + int finish; + + va_list val; + + va_start(val, buf); + len = vasprintf(&buff, buf, val); + va_end(val); + + finish = sendbin(sock, buff, len, 0, g_ape); + + free(buff); + + return finish; +} + +static int sendqueue(int sock, acetables *g_ape) +{ + int t_bytes = 0, r_bytes, n = 0; + struct _socks_bufout *bufout = &g_ape->bufout[sock]; + + if (bufout->buf == NULL) { + return 1; + } + + r_bytes = bufout->buflen; + + while(t_bytes < bufout->buflen) { + n = write(sock, bufout->buf + t_bytes, r_bytes); + if (n == -1) { + if (errno == EAGAIN && r_bytes > 0) { + /* Still not complete */ + memmove(bufout->buf, bufout->buf + t_bytes, r_bytes); + /* TODO : avoid memmove */ + bufout->buflen = r_bytes; + return 0; + } + break; + } + t_bytes += n; + r_bytes -= n; + } + + bufout->buflen = 0; + free(bufout->buf); + + bufout->buf = NULL; + + return 1; +} + +/* TODO : add "nowrite" flag to avoid write syscall when calling several time sendbin() */ +int sendbin(int sock, const char *bin, unsigned int len, unsigned int burn_after_writing, acetables *g_ape) +{ + int t_bytes = 0, r_bytes, n = 0; + + r_bytes = len; + + if (sock != 0) { + while(t_bytes < len) { + if (g_ape->bufout[sock].buf == NULL) { + n = write(sock, bin + t_bytes, r_bytes); + } else { + n = -2; + } + if (n < 0) { + if ((errno == EAGAIN && r_bytes > 0) || (n == -2)) { + if (g_ape->bufout[sock].buf == NULL) { + g_ape->bufout[sock].allocsize = r_bytes + 128; /* add padding to prevent extra data to be reallocated */ + g_ape->bufout[sock].buf = xmalloc(sizeof(char) * g_ape->bufout[sock].allocsize); + g_ape->bufout[sock].buflen = r_bytes; + } else { + g_ape->bufout[sock].buflen += r_bytes; + if (g_ape->bufout[sock].buflen > g_ape->bufout[sock].allocsize) { + g_ape->bufout[sock].allocsize = g_ape->bufout[sock].buflen + 128; + g_ape->bufout[sock].buf = xrealloc(g_ape->bufout[sock].buf, sizeof(char) * g_ape->bufout[sock].allocsize); + } + } + + memcpy(g_ape->bufout[sock].buf + (g_ape->bufout[sock].buflen - r_bytes), bin + t_bytes, r_bytes); + + if (burn_after_writing) { + g_ape->co[sock]->burn_after_writing = 1; + } + + return 0; + } + + break; + } + t_bytes += n; + r_bytes -= n; + } + } + + if (burn_after_writing) { + shutdown(sock, 2); + } + + return 1; +} + +void safe_shutdown(int sock, acetables *g_ape) +{ + if (g_ape->bufout[sock].buf == NULL) { + shutdown(sock, 2); + } else { + g_ape->co[sock]->burn_after_writing = 1; + } +} diff --git a/ape-server/src/sock.h b/ape-server/src/sock.h new file mode 100755 index 0000000..52d2138 --- /dev/null +++ b/ape-server/src/sock.h @@ -0,0 +1,95 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* sock.h */ + +#ifndef _SOCK_H +#define _SOCK_H + +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" + +#define TCP_TIMEOUT 20 // ~Timeout if the socket is not identified to APE + + +struct _socks_bufout +{ + char *buf; + int fd; + int buflen; + int allocsize; +}; + +struct _socks_list +{ + struct _ape_socket *co; + int *tfd; +}; + +struct _ape_sock_connect_async +{ + ape_socket *sock; + int port; +}; + +ape_socket *ape_listen(unsigned int port, char *listen_ip, acetables *g_ape); +ape_socket *ape_connect(char *ip, int port, acetables *g_ape); +void ape_connect_name(char *name, int port, ape_socket *pattern, acetables *g_ape); +void prepare_ape_socket(int fd, acetables *g_ape); +void setnonblocking(int fd); +int sendf(int sock, acetables *g_ape, char *buf, ...); +int sendbin(int sock, const char *bin, unsigned int len, unsigned int burn_after_writing, acetables *g_ape); +void safe_shutdown(int sock, acetables *g_ape); +unsigned int sockroutine(acetables *g_ape); + + +#define SENDH(x, y, g_ape) \ + sendbin(x, HEADER_DEFAULT, HEADER_DEFAULT_LEN, 0, g_ape);\ + sendbin(x, y, strlen(y), 0, g_ape) + + +#define QUIT(x, g_ape) \ + sendbin(x, HEADER_DEFAULT, HEADER_DEFAULT_LEN, 0, g_ape);\ + sendbin(x, "QUIT", 4, 0, g_ape) + +#ifdef TCP_CORK + #define PACK_TCP(fd) \ + do { \ + int __state = 1; \ + setsockopt(fd, IPPROTO_TCP, TCP_CORK, &__state, sizeof(__state)); \ + } while(0) + + #define FLUSH_TCP(fd) \ + do { \ + int __state = 0; \ + setsockopt(fd, IPPROTO_TCP, TCP_CORK, &__state, sizeof(__state)); \ + } while(0) +#else + #define PACK_TCP(fd) + #define FLUSH_TCP(fd) +#endif + +#endif diff --git a/ape-server/src/ticks.c b/ape-server/src/ticks.c new file mode 100755 index 0000000..fcad854 --- /dev/null +++ b/ape-server/src/ticks.c @@ -0,0 +1,169 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* ticks.c */ + + +#include "ticks.h" +#include "utils.h" + +#include +#include + +inline void process_tick(acetables *g_ape) +{ + struct _ticks_callback *timers = g_ape->timers.timers; + + while (timers != NULL && --timers->delta <= 0) { + int lastcall = (timers->times > 0 && --timers->times == 0); + void (*func_timer)(void *param, int *) = timers->func; + + func_timer(timers->params, &lastcall); + + g_ape->timers.timers = timers->next; + + if (!lastcall) { + struct _ticks_callback *new_timer = add_timeout(timers->ticks_need, timers->func, timers->params, g_ape); + + g_ape->timers.ntimers--; + new_timer->identifier = timers->identifier; + new_timer->times = timers->times; + new_timer->protect = timers->protect; + } + + free(timers); + + if ((timers = g_ape->timers.timers) != NULL) { + timers->delta++; + } + } +} + +struct _ticks_callback *add_timeout(unsigned int msec, void *callback, void *params, acetables *g_ape) +{ + struct _ticks_callback *timers = g_ape->timers.timers; + struct _ticks_callback *prev = NULL; + + struct _ticks_callback *new_timer; + new_timer = xmalloc(sizeof(*new_timer)); + + new_timer->ticks_need = msec; + new_timer->delta = msec; + new_timer->times = 1; + new_timer->identifier = g_ape->timers.ntimers; + new_timer->protect = 1; + new_timer->func = callback; + new_timer->params = params; + new_timer->next = NULL; + + while (timers != NULL) { + if (new_timer->delta <= timers->delta) { + new_timer->next = timers; + timers->delta -= new_timer->delta; + break; + } + + new_timer->delta -= timers->delta; + prev = timers; + timers = timers->next; + } + + if (prev != NULL) { + prev->next = new_timer; + } else { + g_ape->timers.timers = new_timer; + } + + g_ape->timers.ntimers++; + + return new_timer; +} + +/* Exec callback "times"x each "sec" */ +/* If "times" is 0, the function is executed indefinitifvly */ + +struct _ticks_callback *add_periodical(unsigned int msec, int times, void *callback, void *params, acetables *g_ape) +{ + struct _ticks_callback *new_timer = add_timeout(msec, callback, params, g_ape); + + new_timer->times = times; + + return new_timer; +} + +struct _ticks_callback *get_timer_identifier(unsigned int identifier, acetables *g_ape) +{ + struct _ticks_callback *timers = g_ape->timers.timers; + + while (timers != NULL) { + if (timers->identifier == identifier) { + return timers; + } + timers = timers->next; + } + + return NULL; +} + +void del_timer_identifier(unsigned int identifier, acetables *g_ape) +{ + struct _ticks_callback *timers = g_ape->timers.timers; + struct _ticks_callback *prev = NULL; + + while (timers != NULL) { + if (timers->identifier == identifier) { + if (prev != NULL) { + prev->next = timers->next; + } else { + g_ape->timers.timers = timers->next; + } + + free(timers); + break; + } + + prev = timers; + timers = timers->next; + } +} + +/* Returns closest timer execution time (in ms) */ +int get_first_timer_ms(acetables *g_ape) +{ + struct _ticks_callback *timers = g_ape->timers.timers; + + if (timers != NULL) { + return timers->delta; + } + + return -1; +} + +/* Delete all timers and deallocate memory */ +void timers_free(acetables *g_ape) +{ + struct _ticks_callback *timers = g_ape->timers.timers; + struct _ticks_callback *prev; + + while (timers != NULL) { + prev = timers; + timers = timers->next; + free(prev); + } +} diff --git a/ape-server/src/ticks.h b/ape-server/src/ticks.h new file mode 100755 index 0000000..2a29da8 --- /dev/null +++ b/ape-server/src/ticks.h @@ -0,0 +1,54 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* ticks.h */ + +#ifndef _TICKS_H +#define _TICKS_H + +#include "main.h" + +#define VTICKS_RATE 50 // 50 ms + +struct _ticks_callback +{ + int ticks_need; + int delta; + int times; + unsigned int identifier; + unsigned int protect; + + void *func; + void *params; + + struct _ticks_callback *next; +}; + +void process_tick(acetables *g_ape); +struct _ticks_callback *add_timeout(unsigned int msec, void *callback, void *params, acetables *g_ape); +struct _ticks_callback *add_periodical(unsigned int msec, int times, void *callback, void *params, acetables *g_ape); +void del_timer_identifier(unsigned int identifier, acetables *g_ape); +struct _ticks_callback *get_timer_identifier(unsigned int identifier, acetables *g_ape); +int get_first_timer_ms(acetables *g_ape); +void timers_free(acetables *g_ape); + +#define add_ticked(x, y) add_periodical(VTICKS_RATE, 0, x, y, g_ape) + +#endif + diff --git a/ape-server/src/transports.c b/ape-server/src/transports.c new file mode 100755 index 0000000..81d6472 --- /dev/null +++ b/ape-server/src/transports.c @@ -0,0 +1,142 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* transports.c */ + +#include "transports.h" +#include "config.h" +#include "utils.h" + +struct _transport_open_same_host_p transport_open_same_host(subuser *sub, ape_socket *client, transport_t transport) +{ + struct _transport_open_same_host_p ret; + + switch(transport) { + case TRANSPORT_LONGPOLLING: + case TRANSPORT_JSONP: + case TRANSPORT_WEBSOCKET: + default: + ret.client_close = sub->client; + ret.client_listener = client; + ret.substate = ADIED; + ret.attach = 1; + break; + case TRANSPORT_PERSISTANT: + case TRANSPORT_XHRSTREAMING: + case TRANSPORT_SSE_LONGPOLLING: + ret.client_close = client; + ret.client_listener = sub->client; + ret.substate = ALIVE; + ret.attach = 0; + break; + } + + return ret; +} + +void transport_data_completly_sent(subuser *sub, transport_t transport) +{ + switch(transport) { + case TRANSPORT_LONGPOLLING: + case TRANSPORT_JSONP: + default: + do_died(sub); + break; + case TRANSPORT_PERSISTANT: + case TRANSPORT_XHRSTREAMING: + case TRANSPORT_SSE_LONGPOLLING: + case TRANSPORT_WEBSOCKET: + break; + } +} + +struct _transport_properties *transport_get_properties(transport_t transport, acetables *g_ape) +{ + switch(transport) { + case TRANSPORT_LONGPOLLING: + case TRANSPORT_PERSISTANT: + default: + break; + case TRANSPORT_XHRSTREAMING: + return &(g_ape->transports.xhrstreaming.properties); + break; + case TRANSPORT_JSONP: + return &(g_ape->transports.jsonp.properties); + break; + case TRANSPORT_SSE_LONGPOLLING: + return &(g_ape->transports.sse.properties); + break; + case TRANSPORT_WEBSOCKET: + return &(g_ape->transports.websocket.properties); + break; + } + return NULL; +} + +void transport_start(acetables *g_ape) +{ + char *eval_func = CONFIG_VAL(JSONP, eval_func, g_ape->srv); + int len = strlen(eval_func); + + if (len) { + g_ape->transports.jsonp.properties.padding.left.val = xmalloc(sizeof(char) * (len + 3)); + strcpy(g_ape->transports.jsonp.properties.padding.left.val, eval_func); + strcat(g_ape->transports.jsonp.properties.padding.left.val, "('"); + g_ape->transports.jsonp.properties.padding.left.len = len+2; + + g_ape->transports.jsonp.properties.padding.right.val = xstrdup("')"); + g_ape->transports.jsonp.properties.padding.right.len = 2; + + } else { + g_ape->transports.jsonp.properties.padding.left.val = NULL; + } + + g_ape->transports.xhrstreaming.properties.padding.left.val = NULL; + g_ape->transports.xhrstreaming.properties.padding.left.len = 0; + + g_ape->transports.xhrstreaming.properties.padding.right.val = xstrdup("\n\n"); + g_ape->transports.xhrstreaming.properties.padding.right.len = 2; + + g_ape->transports.sse.properties.padding.left.val = xstrdup("Event: ape-event\nData: "); + g_ape->transports.sse.properties.padding.left.len = 24; + + g_ape->transports.sse.properties.padding.right.val = xstrdup("\n\n"); + g_ape->transports.sse.properties.padding.right.len = 2; + + g_ape->transports.websocket.properties.padding.left.val = xstrdup("\x00"); + g_ape->transports.websocket.properties.padding.left.len = 1; + + g_ape->transports.websocket.properties.padding.right.val = xstrdup("\xFF"); + g_ape->transports.websocket.properties.padding.right.len = 1; + +} + +void transport_free(acetables *g_ape) +{ + free(g_ape->transports.websocket.properties.padding.right.val); + free(g_ape->transports.websocket.properties.padding.left.val); + free(g_ape->transports.sse.properties.padding.right.val); + free(g_ape->transports.sse.properties.padding.left.val); + free(g_ape->transports.xhrstreaming.properties.padding.right.val); + + if (g_ape->transports.jsonp.properties.padding.left.val != NULL) { + free(g_ape->transports.jsonp.properties.padding.left.val); + free(g_ape->transports.jsonp.properties.padding.right.val); + } +} diff --git a/ape-server/src/transports.h b/ape-server/src/transports.h new file mode 100755 index 0000000..2a81860 --- /dev/null +++ b/ape-server/src/transports.h @@ -0,0 +1,53 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* transports.h */ + +#ifndef _TRANSPORTS_H +#define _TRANSPORTS_H + +#include "main.h" +#include "users.h" + +struct _transport_open_same_host_p +{ + ape_socket *client_close; + ape_socket *client_listener; + int attach; + int substate; +}; + +typedef enum { + TRANSPORT_LONGPOLLING, + TRANSPORT_XHRSTREAMING, + TRANSPORT_JSONP, + TRANSPORT_PERSISTANT, + TRANSPORT_SSE_LONGPOLLING, + TRANSPORT_SSE_JSONP, + TRANSPORT_WEBSOCKET +} transport_t; + + +struct _transport_open_same_host_p transport_open_same_host(subuser *sub, ape_socket *client, transport_t transport); +void transport_data_completly_sent(subuser *sub, transport_t transport); +void transport_start(acetables *g_ape); +void transport_free(acetables *g_ape); +struct _transport_properties *transport_get_properties(transport_t transport, acetables *g_ape); + +#endif diff --git a/ape-server/src/users.c b/ape-server/src/users.c new file mode 100644 index 0000000..fe3f7e5 --- /dev/null +++ b/ape-server/src/users.c @@ -0,0 +1,724 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* users.c */ + + +#include +#include + +#include "users.h" + +#include "hash.h" +#include "handle_http.h" +#include "sock.h" +#include "extend.h" + +#include "config.h" +#include "json.h" +#include "plugins.h" +#include "pipe.h" +#include "raw.h" + +#include "utils.h" +#include "transports.h" +#include "log.h" + +/* Checking whether the user is in a channel */ +unsigned int isonchannel(USERS *user, CHANNEL *chan) +{ + CHANLIST *clist; + + if (user == NULL || chan == NULL) { + return 0; + } + + clist = user->chan_foot; + + while (clist != NULL) { + if (clist->chaninfo == chan) { + return 1; + } + clist = clist->next; + } + + return 0; +} + +void grant_aceop(USERS *user) +{ + user->flags = FLG_AUTOOP | FLG_NOKICK | FLG_BOTMANAGE; +} + +// return user with a channel pubid +USERS *seek_user(const char *pubid, const char *linkid, acetables *g_ape) +{ + USERS *suser; + CHANLIST *clist; + + if ((suser = seek_user_simple(pubid, g_ape)) == NULL) { + return NULL; + } + + clist = suser->chan_foot; + + while (clist != NULL) { + if (strcasecmp(clist->chaninfo->pipe->pubid, linkid) == 0) { + return suser; + } + clist = clist->next; + } + + return NULL; +} + +USERS *seek_user_simple(const char *pubid, acetables *g_ape) +{ + transpipe *gpipe; + + gpipe = get_pipe(pubid, g_ape); + + if (gpipe == NULL || gpipe->type != USER_PIPE) { + return NULL; + } + + return gpipe->pipe; + +} + +USERS *seek_user_id(const char *sessid, acetables *g_ape) +{ + if (strlen(sessid) != 32) { + return NULL; + } + return ((USERS *)hashtbl_seek(g_ape->hSessid, sessid)); +} + + +USERS *init_user(acetables *g_ape) +{ + USERS *nuser; + + nuser = xmalloc(sizeof(*nuser)); + + nuser->idle = time(NULL); + nuser->next = g_ape->uHead; + nuser->prev = NULL; + nuser->nraw = 0; + + nuser->flags = FLG_NOFLAG; + nuser->chan_foot = NULL; + + nuser->sessions.data = NULL; + nuser->sessions.length = 0; + + nuser->properties = NULL; + nuser->subuser = NULL; + nuser->nsub = 0; + nuser->type = HUMAN; + + nuser->links.ulink = NULL; + nuser->links.nlink = 0; + nuser->transport = TRANSPORT_LONGPOLLING; + + nuser->cmdqueue = NULL; + + nuser->lastping[0] = '\0'; + + if (nuser->next != NULL) { + nuser->next->prev = nuser; + } + g_ape->uHead = nuser; + gen_sessid_new(nuser->sessid, g_ape); + + return nuser; +} + +USERS *adduser(ape_socket *client, const char *host, const char *ip, USERS *allocated, acetables *g_ape) +{ + USERS *nuser = NULL; + + /* Calling module */ + if (allocated == NULL) { + FIRE_EVENT(allocateuser, nuser, client, host, ip, g_ape); + + nuser = init_user(g_ape); + strncpy(nuser->ip, ip, 16); + + nuser->pipe = init_pipe(nuser, USER_PIPE, g_ape); + nuser->type = (client != NULL ? HUMAN : BOT); + + nuser->istmp = 1; + + hashtbl_append(g_ape->hSessid, nuser->sessid, (void *)nuser); + + addsubuser(client, host, nuser, g_ape); + } else { + FIRE_EVENT(adduser, nuser, allocated, g_ape); + + nuser = allocated; + nuser->istmp = 0; + + g_ape->nConnected++; + + ape_log(APE_INFO, __FILE__, __LINE__, g_ape, + "New user - (ip : %s)", nuser->ip); + } + + return nuser; + +} + +void deluser(USERS *user, acetables *g_ape) +{ + + if (user == NULL) { + return; + } + + left_all(user, g_ape); + + FIRE_EVENT_NULL(deluser, user, user->istmp, g_ape); + + /* kill all users connections */ + + clear_subusers(user, g_ape); + + hashtbl_erase(g_ape->hSessid, user->sessid); + + g_ape->nConnected--; + + if (user->prev == NULL) { + g_ape->uHead = user->next; + } else { + user->prev->next = user->next; + } + if (user->next != NULL) { + user->next->prev = user->prev; + } + + clear_sessions(user); + clear_properties(&user->properties); + + destroy_pipe(user->pipe, g_ape); + + /* TODO Add Event */ + free(user); + +} + +void do_died(subuser *sub) +{ + if (sub->state == ALIVE) { + sub->state = ADIED; + sub->headers.sent = 0; + http_headers_free(sub->headers.content); + sub->headers.content = NULL; + + shutdown(sub->client->fd, 2); + } +} + +void check_timeout(acetables *g_ape, int *last) +{ + USERS *list, *wait; + long int ctime = time(NULL); + + list = g_ape->uHead; + + while (list != NULL) { + + wait = list->next; + + if ((ctime - list->idle) >= TIMEOUT_SEC && list->type == HUMAN) { + deluser(list, g_ape); + } else if (list->type == HUMAN) { + subuser **n = &(list->subuser); + while (*n != NULL) { + if ((ctime - (*n)->idle) >= TIMEOUT_SEC) { + delsubuser(n, g_ape); + continue; + } + if ((*n)->state == ALIVE && (*n)->raw_pools.nraw && !(*n)->need_update) { + + /* Data completetly sent => closed */ + if (send_raws(*n, g_ape)) { + transport_data_completly_sent(*n, (*n)->user->transport); // todo : hook + } else { + + (*n)->burn_after_writing = 1; + } + } else { + FIRE_EVENT_NONSTOP(tickuser, *n, g_ape); + } + n = &(*n)->next; + } + } + + list = wait; + } + +} + +void send_error(USERS *user, const char *msg, const char *code, acetables *g_ape) +{ + RAW *newraw; + json_item *jlist = json_new_object(); + + json_set_property_strZ(jlist, "code", code); + json_set_property_strZ(jlist, "value", msg); + + newraw = forge_raw(RAW_ERR, jlist); + + post_raw(newraw, user, g_ape); +} + +void send_msg(USERS *user, const char *msg, const char *type, acetables *g_ape) +{ + RAW *newraw; + json_item *jlist = json_new_object(); + + json_set_property_strZ(jlist, "value", msg); + + newraw = forge_raw(type, jlist); + + post_raw(newraw, user, g_ape); +} + +void send_msg_channel(CHANNEL *chan, const char *msg, const char *type, acetables *g_ape) +{ + RAW *newraw; + json_item *jlist = json_new_object(); + + json_set_property_strZ(jlist, "value", msg); + + newraw = forge_raw(type, jlist); + + post_raw_channel(newraw, chan, g_ape); +} + +void send_msg_sub(subuser *sub, const char *msg, const char *type, acetables *g_ape) +{ + RAW *newraw; + json_item *jlist = json_new_object(); + + json_set_property_strZ(jlist, "value", msg); + + newraw = forge_raw(type, jlist); + + post_raw_sub(newraw, sub, g_ape); +} + +session *get_session(USERS *user, const char *key) +{ + session *current = user->sessions.data; + if (strlen(key) > 32) { + return NULL; + } + while (current != NULL) { + if (strcmp(current->key, key) == 0) { + return current; + } + current = current->next; + } + + return NULL; + +} + +void clear_sessions(USERS *user) +{ + session *pSession, *pTmp; + + pSession = user->sessions.data; + + while (pSession != NULL) { + pTmp = pSession->next; + free(pSession->val); + free(pSession); + pSession = pTmp; + } + user->sessions.data = NULL; + user->sessions.length = 0; +} + +session *set_session(USERS *user, const char *key, const char *val, int update, acetables *g_ape) +{ + session *new_session = NULL, *sTmp = NULL; + int vlen = strlen(val); + + if (strlen(key) > 32 || user->sessions.length+vlen > MAX_SESSION_LENGTH) { + return NULL; + } + + if ((sTmp = get_session(user, key)) != NULL) { + int tvlen = strlen(sTmp->val); + + if (vlen > tvlen) { + sTmp->val = xrealloc(sTmp->val, sizeof(char) * (vlen+1)); // if new val is bigger than previous + } + user->sessions.length += (vlen - tvlen); // update size + //strcpy(sTmp->key, key); + memcpy(sTmp->val, val, vlen + 1); + + if (update) { + sendback_session(user, sTmp, g_ape); + } + return sTmp; + } + + sTmp = user->sessions.data; + + new_session = xmalloc(sizeof(*new_session)); + new_session->val = xmalloc(sizeof(char) * (vlen+1)); + + user->sessions.length += vlen; + + strcpy(new_session->key, key); + strcpy(new_session->val, val); + new_session->next = sTmp; + + user->sessions.data = new_session; + if (update) { + sendback_session(user, new_session, g_ape); + } + return new_session; +} + +void sendback_session(USERS *user, session *sess, acetables *g_ape) +{ + subuser *current = user->subuser; + + while (current != NULL) { + if (current->need_update) { + json_item *jlist = json_new_object(), *jobj_item = json_new_object(); + RAW *newraw; + + current->need_update = 0; + + json_set_property_strZ(jobj_item, sess->key, sess->val); + json_set_property_objN(jlist, "sessions", 8, jobj_item); + + newraw = forge_raw("SESSIONS", jlist); + newraw->priority = RAW_PRI_HI; + + post_raw_sub(copy_raw_z(newraw), current, g_ape); + } + current = current->next; + } + +} + +subuser *addsubuser(ape_socket *client, const char *channel, USERS *user, acetables *g_ape) +{ + subuser *sub; + + if (getsubuser(user, channel) != NULL || strlen(channel) > MAX_HOST_LENGTH) { + return NULL; + } + + sub = xmalloc(sizeof(*sub)); + sub->client = client; + sub->state = ADIED; + sub->user = user; + + memcpy(sub->channel, channel, strlen(channel)+1); + sub->next = user->subuser; + + sub->nraw = 0; + sub->wait_for_free = 0; + + sub->properties = NULL; + + sub->headers.sent = 0; + sub->headers.content = NULL; + + sub->burn_after_writing = 0; + + sub->idle = time(NULL); + sub->need_update = 0; + sub->current_chl = 0; + + sub->raw_pools.nraw = 0; + + /* Pre-allocate a pool of raw to reduce the number of malloc calls */ + + /* Low priority raws */ + sub->raw_pools.low.nraw = 0; + sub->raw_pools.low.size = 32; + sub->raw_pools.low.rawhead = init_raw_pool(sub->raw_pools.low.size); + sub->raw_pools.low.rawfoot = sub->raw_pools.low.rawhead; + + /* High priority raws */ + sub->raw_pools.high.nraw = 0; + sub->raw_pools.high.size = 8; + sub->raw_pools.high.rawhead = init_raw_pool(sub->raw_pools.high.size); + sub->raw_pools.high.rawfoot = sub->raw_pools.high.rawhead; + + (user->nsub)++; + + user->subuser = sub; + + /* if the previous subuser have some messages in queue, copy them to the new subuser */ + if (sub->next != NULL && sub->next->raw_pools.low.nraw) { + struct _raw_pool *rTmp; + for (rTmp = sub->next->raw_pools.low.rawhead; rTmp->raw != NULL; rTmp = rTmp->next) { + if (rTmp->raw->refcount == 0) { + rTmp->raw->refcount = 1; + } + post_raw_sub(copy_raw_z(rTmp->raw), sub, g_ape); + } + + } + + FIRE_EVENT_NONSTOP(addsubuser, sub, g_ape); + + return sub; +} + +void subuser_restor(subuser *sub, acetables *g_ape) +{ + CHANLIST *chanl; + CHANNEL *chan; + + json_item *jlist; + RAW *newraw; + USERS *user = sub->user; + userslist *ulist; + + chanl = user->chan_foot; + + while (chanl != NULL) { + jlist = json_new_object(); + + chan = chanl->chaninfo; + + if (!(chan->flags & CHANNEL_NONINTERACTIVE) && chan->head != NULL) { + json_item *user_list = json_new_array(); + + ulist = chan->head; + + while (ulist != NULL) { + + json_item *juser = get_json_object_user(ulist->userinfo); + + if (ulist->userinfo != user) { + //make_link(user, ulist->userinfo); + } + + json_set_property_intN(juser, "level", 5, ulist->level); + + json_set_element_obj(user_list, juser); + + ulist = ulist->next; + } + + json_set_property_objN(jlist, "users", 5, user_list); + } + json_set_property_objN(jlist, "pipe", 4, get_json_object_channel(chan)); + + newraw = forge_raw(RAW_CHANNEL, jlist); + newraw->priority = RAW_PRI_HI; + post_raw_sub(newraw, sub, g_ape); + chanl = chanl->next; + } + + jlist = json_new_object(); + json_set_property_objN(jlist, "user", 4, get_json_object_user(user)); + + newraw = forge_raw("IDENT", jlist); + newraw->priority = RAW_PRI_HI; + post_raw_sub(newraw, sub, g_ape); + +} + +subuser *getsubuser(USERS *user, const char *channel) +{ + subuser *current = user->subuser; + + while (current != NULL) { + if (strcmp(current->channel, channel) == 0) { + + return current; + } + current = current->next; + } + + return NULL; +} + +void delsubuser(subuser **current, acetables *g_ape) +{ + subuser *del = *current; + + FIRE_EVENT_NONSTOP(delsubuser, del, g_ape); + ((*current)->user->nsub)--; + + *current = (*current)->next; + + destroy_raw_pool(del->raw_pools.low.rawhead); + destroy_raw_pool(del->raw_pools.high.rawhead); + + clear_properties(&del->properties); + + if (del->state == ALIVE) { + del->wait_for_free = 1; + do_died(del); + } else { + free(del); + } + +} + +void clear_subusers(USERS *user, acetables *g_ape) +{ + while (user->subuser != NULL) { + delsubuser(&(user->subuser), g_ape); + } +} + +#if 0 +void ping_request(USERS *user, acetables *g_ape) +{ + + struct timeval t; + gettimeofday(&t, NULL); + + sprintf(user->lastping, "%li%d", t.tv_sec, t.tv_usec); + + send_msg(user, user->lastping, "KING", g_ape); +} +#endif +struct _users_link *are_linked(USERS *a, USERS *b) +{ + USERS *aUser, *bUser; + struct _link_list *ulink; + + if (!a->links.nlink || !b->links.nlink) { + return NULL; + } + + /* Walk on the smallest list */ + if (a->links.nlink <= b->links.nlink) { + aUser = a; + bUser = b; + } else { + aUser = b; + bUser = a; + } + + ulink = aUser->links.ulink; + + while (ulink != NULL) { + if (ulink->link->b == bUser || ulink->link->a == bUser) { + return ulink->link; + } + ulink = ulink->next; + } + + return NULL; + +} + +void make_link(USERS *a, USERS *b) +{ + struct _users_link *link; + struct _link_list *link_a, *link_b; + + if (are_linked(a, b) != NULL) { + link = xmalloc(sizeof(*link)); + + link_a = xmalloc(sizeof(*link_a)); + link_b = xmalloc(sizeof(*link_b)); + + link_a->link = link; + link_b->link = link; + + link_a->next = a->links.ulink; + link_b->next = b->links.ulink; + + link->a = a; + link->b = b; + + a->links.ulink = link_a; + (a->links.nlink)++; + + b->links.ulink = link_b; + (b->links.nlink)++; + + link->link_type = 0; + printf("Link etablished between %s and %s\n", a->pipe->pubid, b->pipe->pubid); + } else { + printf("%s and %s are already linked\n", a->pipe->pubid, b->pipe->pubid); + } +} + +void destroy_link(USERS *a, USERS *b) +{ + struct _users_link *link; + + if ((link = are_linked(a, b)) != NULL) { + + } +} + +json_item *get_json_object_user(USERS *user) +{ + json_item *jstr = NULL; + + if (user != NULL) { + jstr = json_new_object(); + json_set_property_strN(jstr, "casttype", 8, "uni", 3); + json_set_property_strN(jstr, "pubid", 5, user->pipe->pubid, 32); + + if (user->properties != NULL) { + int has_prop = 0; + + json_item *jprop = NULL; + + extend *eTmp = user->properties; + + while (eTmp != NULL) { + if (eTmp->visibility == EXTEND_ISPUBLIC) { + if (!has_prop) { + has_prop = 1; + jprop = json_new_object(); + } + if (eTmp->type == EXTEND_JSON) { + json_item *jcopy = json_item_copy(eTmp->val, NULL); + + json_set_property_objZ(jprop, eTmp->key, jcopy); + } else { + json_set_property_strZ(jprop, eTmp->key, eTmp->val); + + } + } + eTmp = eTmp->next; + } + if (has_prop) { + json_set_property_objN(jstr, "properties", 10, jprop); + } + } + + } else { + json_set_property_strZ(jstr, "pubid", SERVER_NAME); + } + return jstr; +} + diff --git a/ape-server/src/users.h b/ape-server/src/users.h new file mode 100755 index 0000000..6a28c02 --- /dev/null +++ b/ape-server/src/users.h @@ -0,0 +1,252 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* users.h */ + +#ifndef _USERS_H +#define _USERS_H + +#include "main.h" + +#include "channel.h" +#include "json.h" +#include "extend.h" + + +#define FLG_NOFLAG 0x00 +#define FLG_AUTOOP 0x01 +#define FLG_NOKICK 0x02 +#define FLG_BOTMANAGE 0x04 +#define FLG_PCONNECT 0x08 + + +#define MAX_SESSION_LENGTH 102400 // 100 ko +#define MAX_HOST_LENGTH 256 + + +// Le 25/12/2006 à 02:15:19 Joyeux Noël + + +struct _raw_pool { + struct RAW *raw; + struct _raw_pool *next; + struct _raw_pool *prev; + int start; +}; + +typedef struct USERS +{ + struct { + struct _link_list *ulink; + int nlink; + } links; + + struct { + struct _session *data; + int length; + } sessions; + + struct USERS *next; + struct USERS *prev; + struct CHANLIST *chan_foot; + struct _transpipe *pipe; + struct _extend *properties; + struct _subuser *subuser; + + json_item *cmdqueue; + + time_t idle; + int transport; + int nsub; + unsigned int nraw; + unsigned int flags; + + unsigned short int type; + unsigned short int istmp; + + char ip[16]; // ipv4 + char lastping[24]; + char sessid[33]; + +} USERS; + + +struct _raw_pool_user { + int nraw; + int size; + struct _raw_pool *rawhead; + struct _raw_pool *rawfoot; +}; + +typedef struct _subuser subuser; +struct _subuser +{ + + struct { + struct _raw_pool_user low; + struct _raw_pool_user high; + int nraw; + } raw_pools; + + struct { + struct _http_headers_response *content; + int sent; + } headers; + + struct _extend *properties; + struct _subuser *next; + ape_socket *client; + USERS *user; + time_t idle; + + int state; + int need_update; + int wait_for_free; + int nraw; + int burn_after_writing; + int current_chl; + char channel[MAX_HOST_LENGTH+1]; +}; + + +typedef struct CHANLIST +{ + struct CHANNEL *chaninfo; + struct CHANLIST *next; + +} CHANLIST; + + +struct _users_link +{ + USERS *a; + USERS *b; + + int link_type; +}; + +struct _link_list +{ + struct _users_link *link; + struct _link_list *next; +}; + + +typedef struct userslist +{ + struct USERS *userinfo; + struct userslist *next; + + unsigned int level; + /* TODO: it can be intersting to extend this */ +} userslist; + + +typedef struct _session session; + +struct _session +{ + char *val; + struct _session *next; + char key[33]; +}; + + +enum { + ALIVE = 0, + ADIED +}; + +enum { + HUMAN = 0, + BOT +}; + +enum { + PRIVMSG = 0, + CHANMSG, + INFOMSG +}; +enum { + RAW_MSG = 0, + RAW_LOGIN, + RAW_JOIN, + RAW_LEFT, + RAW_SETLEVEL +}; + +#define RAW_DATA "DATA" +#define RAW_CHANMSG "CHANMSG" +#define RAW_TOPIC "TOPIC" +#define RAW_LOGIN "LOGIN" +#define RAW_JOIN "JOIN" +#define RAW_LEFT "LEFT" +#define RAW_SETLEVEL "SETLVL" +#define RAW_SETTOPIC "SETTOPIC" +#define RAW_USER "USER" +#define RAW_ERR "ERR" +#define RAW_CHANNEL "CHANNEL" +#define RAW_KICK "KICKED" +#define RAW_BAN "BANNED" +#define RAW_PROXY "PROXY" + +USERS *seek_user(const char *nick, const char *linkid, acetables *g_ape); +USERS *init_user(acetables *g_ape); +USERS *adduser(ape_socket *client, const char *host, const char *ip, USERS *allocated, acetables *g_ape); +USERS *seek_user_id(const char *sessid, acetables *g_ape); +USERS *seek_user_simple(const char *nick, acetables *g_ape); + + + +void deluser(USERS *user, acetables *g_ape); + +void do_died(subuser *user); + +void check_timeout(acetables *g_ape, int *last); +void grant_aceop(USERS *user); + +void send_error(USERS *user, const char *msg, const char *code, acetables *g_ape); +void send_msg(USERS *user, const char *msg, const char *type, acetables *g_ape); +void send_msg_sub(subuser *sub, const char *msg, const char *type, acetables *g_ape); +void send_msg_channel(struct CHANNEL *chan, const char *msg, const char *type, acetables *g_ape); + +unsigned int isonchannel(USERS *user, struct CHANNEL *chan); + +json_item *get_json_object_user(USERS *user); + +session *get_session(USERS *user, const char *key); +session *set_session(USERS *user, const char *key, const char *val, int update, acetables *g_ape); +void clear_sessions(USERS *user); +void sendback_session(USERS *user, session *sess, acetables *g_ape); + +subuser *addsubuser(ape_socket *client, const char *channel, USERS *user, acetables *g_ape); +subuser *getsubuser(USERS *user, const char *channel); +void delsubuser(subuser **current, acetables *g_ape); +void subuser_restor(subuser *sub, acetables *g_ape); + +void clear_subusers(USERS *user, acetables *g_ape); +void ping_request(USERS *user, acetables *g_ape); + +void make_link(USERS *a, USERS *b); +struct _users_link *are_linked(USERS *a, USERS *b); +void destroy_link(USERS *a, USERS *b); + + +#endif + diff --git a/ape-server/src/utils.c b/ape-server/src/utils.c new file mode 100755 index 0000000..3594822 --- /dev/null +++ b/ape-server/src/utils.c @@ -0,0 +1,264 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* utils.c */ + +#include +#include +#include +#include + +#include "log.h" + +void *xmalloc(size_t size) +{ + void *r = malloc(size); + if (r == NULL) { + printf("[ERR] Not enougth memory\n"); + exit(0); + } + return r; +} + +void *xrealloc(void *ptr, size_t size) +{ + void *r = realloc(ptr, size); + if (r == NULL) { + printf("[ERR] Not enougth memory\n"); + exit(0); + } + return r; +} + +void s_tolower(char *upper, unsigned int len) +{ + unsigned int i; + + for (i = 0; i < len; i++) { + upper[i] = tolower(upper[i]); + } +} + +long int itos(long int input, char *output, long int len) +{ + int sign = 0, i = 1; + + if (input < 0) { + sign = 1; + input = -input; + } + output[(len - i)] = '\0'; + + for (i = 2; input != 0; i++) { + + output[len - i] = '0' + (input % 10); + + input /= 10; + } + if (sign) { + output[len - i++] = '-'; + } + + return len-(i-1); +} + +/* Taken from a random source */ +char *trim(char *s) +{ + int i = 0, j; + + while (s[i] == ' ' || s[i] == '\t' || s[i] == '\n' || s[i] == '\r') { + i++; + } + if (i > 0) { + for(j = 0; j < strlen(s); j++) { + s[j] = s[j+i]; + } + s[j] = '\0'; + } + + i = strlen(s) - 1; + + while (s[i] == ' ' || s[i] == '\t' || s[i] == '\n' || s[i] == '\r') { + i--; + } + if (i < (strlen(s) - 1)) { + s[i+1] = '\0'; + } + return s; +} + +char *removelast(char *input, unsigned int n) +{ + if ((strlen(input)-n) < 1) { + return NULL; + } + input[strlen(input)-n] = '\0'; + return input; +} + +int seof(char *buf, unsigned short int stop) +{ + char *pBuf; + int pos = 0; + + for (pBuf = buf; pBuf[pos] != '\0'; pos++) { + /*if (pos == 4096) { + return -1; + }*/ + if (pBuf[pos] == stop) { + return pos+1; + } + } + return -1; +} + +int sneof(char *buf, size_t len, size_t max) +{ + char *pBuf; + int pos = 0; + + for (pBuf = buf; pos < len && pos < max; pos++) { + if (pBuf[pos] == '\n') { + return pos+1; + } + } + return -1; +} + +int rand_n(int n) +{ + int partSize = 1 + (n == RAND_MAX ? 0 : (RAND_MAX - n) / (n + 1)); + int maxUsefull = partSize * n + (partSize - 1); + int draw; + + do { + draw = rand(); + } while (draw > maxUsefull); + + return draw / partSize; +} + +size_t explode(const char split, char *input, char **tP, unsigned int limit) // Explode a string in an array. +{ + size_t i = 0; + + tP[0] = input; + for (i = 0; *input; input++) { + if (*input == split) { + i++; + *input = '\0'; + if(*(input + 1) != '\0' && *(input + 1) != split) { + tP[i] = input + 1; + } else { + i--; + } + } + if ((i+1) == limit) { + return i; + } + } + + return i; +} + + +char *xstrdup(const char *s) +{ + char *x = strdup(s); + if (x == NULL) { + printf("[ERR] Not enougth memory\n"); + exit(0); + } + return x; +} + +char *get_path(const char *full_path) +{ + char *new_path; + char *last; + new_path = xstrdup(full_path); + + last = strrchr(new_path, '/'); + if (last == NULL) { + free(new_path); + return NULL; + } + + last[1] = '\0'; + + return new_path; +} + +char hex2int(unsigned char hex) +{ + hex = hex - '0'; + if (hex > 9) { + hex = (hex + '0' - 1) | 0x20; + hex = hex - 'a' + 11; + } + if (hex > 15) { + hex = 0xFF; + } + + return hex; +} + +/* taken from lighttp */ +int urldecode(char *string) +{ + unsigned char high, low; + const char *src; + char *dst; + + if (string == NULL || !string) return -1; + + src = (const char *) string; + dst = (char *) string; + + while ((*src) != '\0') { + if (*src == '%') { + *dst = '%'; + + high = hex2int(*(src + 1)); + if (high != 0xFF) { + low = hex2int(*(src + 2)); + if (low != 0xFF) { + high = (high << 4) | low; + + if (high < 32 || high == 127) high = '_'; + + *dst = high; + src += 2; + } + } + } else { + *dst = *src; + } + + dst++; + src++; + } + + *dst = '\0'; + + return 1; +} + + diff --git a/ape-server/src/utils.h b/ape-server/src/utils.h new file mode 100755 index 0000000..2d2c087 --- /dev/null +++ b/ape-server/src/utils.h @@ -0,0 +1,56 @@ +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010 Anthony Catel + + This file is part of APE Server. + APE 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. + + APE 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 APE ; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* utils.h */ + + +#ifndef _UTILS_H +#define _UTILS_H + +#include +#include + +void *xmalloc(size_t size); +void *xrealloc(void *ptr, size_t size); +char *xstrdup(const char *s); + +int seof(char *buf, unsigned short int stop); +int sneof(char *buf, size_t len, size_t max); +long int itos(long int input, char *output, long int len); +char *trim(char *s); +char *removelast(char *input, unsigned int n); +size_t explode(const char split, char *input, char **tP, unsigned int limit); +char hex2int(unsigned char hex); +int urldecode(char *string); +int rand_n(int n); +void s_tolower(char *upper, unsigned int len); +char *get_path(const char *full_path); + +/* CONST_STR_LEN from lighttpd */ +#define CONST_STR_LEN(x) x, x ? sizeof(x) - 1 : 0 + +#define LENGTH_N(num) ((num<10 && num >= 0)?1:(long int)log10(fabs(num))+1); +#ifndef MAX + #define MAX(a,b) (((a) > (b)) ? (a) : (b)) +#endif +#ifndef MIN + #define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#endif diff --git a/lib/Alien/APE/Server.pm b/lib/Alien/APE/Server.pm new file mode 100755 index 0000000..42fcebe --- /dev/null +++ b/lib/Alien/APE/Server.pm @@ -0,0 +1,87 @@ +package Alien::APE::Server; +use strict; +use warnings; +use File::ShareDir ':ALL'; + +our $VERSION = '1.000000049001'; +use 5.008; + +sub root { + my ( $self ) = @_; + return dist_dir('Alien-APE-Server'); +} + +sub aped { + my ( $self ) = @_; + return dist_file('Alien-APE-Server','bin/aped'); +} + +1; + +=head1 NAME + +Alien::APE::Server - Packages the APE-Project (Ajax Push Engine) Server. + +=head1 SYNOPSIS + + use Alien::APE::Server; + + print Alien::APE::Server->root; + print Alien::APE::Server->aped; + + chdir Alien::APE::Server->root.'/bin'; + exec Alien::APE::Server->aped; + +=head1 DESCRIPTION + +This version is based on the github version of APE as described by: B + +=head1 METHODS + +=item B + +Gives back the directory root of the ape-server installation + +=item B + +Gives back the filename of the ape-server aped binary + +=head1 SEE ALSO + +L + +APE-Project home page: L + +=head1 AUTHOR + +Torsten Raudssus + +=head1 BUGS + +Please report any bugs or feature requests through the methods following. + +=head1 SUPPORT + +IRC + + You can join #ape-project on irc.freenode.net, talk to Getty + or you can connect to irc.perl.org and talk there to Getty + +Repository + + http://github.com/Getty/alien-ape-server + Pull request and additional contributors are welcome + +Issue Tracker + + http://github.com/Getty/alien-ape-server/issues + +=head1 COPYRIGHT & LICENSE + +Copyright 2010 Torsten Raudssus, all rights reserved. + +This library is free software; you can redistribute it and/or modify it under the same terms as +Perl itself, either Perl version 5.8.8 or, at your option, any later version of Perl 5 you may +have available. + +=cut